On Fri, 2021-12-31 at 15:48 +1100, Duncan Bayne wrote: > Chun Tian writes: > > > The difference here is that now (lambda (stream) ...) is a closure > > which will contain a local version of *foo* at the time when > > (create-server) is called. This kind of uses of lambda functions > > is > > like a cheap object with a member variable. > > Thanks for the suggestion - yes, this does work as expected, but > introduces a difficulty with the API of the library. > > The germinal code is as follows (edited to remove large swathes and > just > focus on the relevant bits): > > ===== > (defvar *germinal-cert* "/etc/germinal/cert.pem") > (defvar *germinal-cert-key* "/etc/germinal/key.pem") > (defvar *germinal-tls-context* nil "Variable used to store global TLS > context") > > ;; snip > > (with-global-context (*germinal-tls-context* :auto-free-p (not > background)) > (usocket:socket-server host port #'gemini-handler () > :multi-threading t > :element-type '(unsigned-byte 8) > :in-new-thread background))) > > ;; snip > > (defun gemini-handler (stream) > "The main Gemini request handler. Sets up TLS and sets up request > and response" > (handler-case > (let* ((tls-stream (make-ssl-server-stream stream > :certificate > *germinal-cert* > :key *germinal-cert- > key*)) > ;; snip > ===== > > So replacing the handler function with a lambda that creates a > closure > works ... but breaks the non-testing case where you just want to setq > the special variables in your app startup and be done with it. > > The best approach I can think of is something like ... > > ===== > ;; snip > (with-global-context (*germinal-tls-context* :auto-free-p (not > background)) > (usocket:socket-server host port > (let ((*threaded-cert* *germinal-cert*) > (*threaded-cert-key* *germinal-cert- > key*)) > (lambda (stream) (gemini-handler > stream))) > ;; snip > (let* ((tls-stream (make-ssl-server-stream stream > :certificate > *threaded-cert* > :key *threaded-cert- > key*)) > ;; snip > ===== > > Which seems weird, but also gives the best of both worlds; the > ability > to shadow variables for testing purposes, but also setq the *same* > variables for global configuration. > > Thoughts / opinions?
You're overusing special variables. You should also make up your mind on the concurrency model and try to avoid allowing both foreground and background operations. (defun gemini-handler (stream cert key) "The main Gemini request handler. Sets up TLS and sets up request and response" (handler-case (let* ((tls-stream (make-ssl-server-stream stream :certificate cert :key key)))))) (defun make-gemini-handler (cert key) (lambda (stream) (gemini-handler stream cert key))) (with-global-context (*germinal-tls-context* :auto-free-p (not background)) (usocket:socket-server host port (make-gemini-handler *germinal-cert* *germinal-cert-key*))) -- Stelian Ionescu
signature.asc
Description: This is a digitally signed message part