Why is the germinal library not shadowing configuration?

Viewed 49

I'm in the process of writing some characterisation tests for germinal, prior to adding functionality. To do this, I'd prefer to use a let form and temporarily shadow configuration than setq it.

However, this doesn't work the way I expected it to, for some of the configuration. *germinal-host* can be shadowed, but the certificate configuration *germinal-cert* can't:

CL-USER> (let ((germinal:*germinal-cert* "/foo/bar"))
           (print germinal:*germinal-cert*)
           (germinal:start :background nil))

"/foo/bar" ;; Germinal listening on 0.0.0.0 port 1965
gemini-handler error: SSL initialization error: Can't load certificate /etc/germinal/cert.pem
SSL error queue is empty.

Am I missing the correct way of configuring the germinal client during tests? I could just globally setq the values I want, but I'd like the config to be overridden only during tests, hence use of let.

1 Answers

Thanks to some smart folks on the usocket-devel mailing list, I have the answer: restructure the germinal server to add a helper function that returns a closure, closing over the shadowed variables. This closure then has the correct variable values regardless of which thread invokes it:

(defun make-gemini-handler (cert cert-key root-path)
  "Create a Gemini request handler for a specified root path, TLS certificate, and TLS key."
  (lambda (stream) (gemini-handler stream cert cert-key root-path)))

This is invoked from the main thread like so:

  (with-global-context (*germinal-tls-context* :auto-free-p (not background))
    (usocket:socket-server host port (make-gemini-handler *germinal-cert* *germinal-cert-key* *germinal-root*) ()
               :multi-threading t
               :element-type '(unsigned-byte 8)
               :in-new-thread background)))
Related