fukamachi / clack

Web server abstraction layer for Common Lisp
MIT License
1.04k stars 86 forks source link

Once the Server is Stopped It is Impossible to Start Again #148

Open artforlife opened 6 years ago

artforlife commented 6 years ago

I followed the basic instructions on how to Start and Stop the Clack located on the front page of this repo. I discovered that once the server is stopped using:

(clack:stop *handler*)

It is impossible to start it again using:

(defvar *handler*
    (clack:clackup
      (lambda (env)
        (declare (ignore env))
        '(200 (:content-type "text/plain") ("Hello, Clack!")))))

To start it again, I always need to restart the Emacs. It appears that the *handler* variable does not fully capture the running server. Hence, when one stops it and then tries to start the server again on the same port, there are still some remnants of the previous instance present.

aarvid commented 6 years ago

This is a common "gotcha" in common lisp.

This is because *handler* already has a value, the second call using defvar does nothing. defvar defines a global variable, if it already has a value it does nothing more. if does not yet have a value (i.e. unbound), it will evaluate the expression and sets the variable to the result of the expression.

To restart you should use the following:

(setf *handler*
    (clack:clackup
      (lambda (env)
        (declare (ignore env))
        '(200 (:content-type "text/plain") ("Hello, Clack!")))))
aarvid commented 6 years ago

a bit of advice and an aside. In slime, you can restart common lisp without restarting emacs. At the REPL with a blank prompt, enter , (comma), then write restart-inferior-lisp followed by enter key.

artforlife commented 6 years ago

Thanks for the lesson! 👍

artforlife commented 6 years ago

If I use this method of restarting the inferior-lisp process, will I lose what has been defined at the Top level?

aarvid commented 6 years ago

yes, because you are starting a fresh new common lisp process, you will need to re-load into lisp what you want to work on. Using your example, you will need to reload clack. The advantage is that emacs maintains all the other buffers open (you will not need to open the files you are working on) and the slime repl will maintain its history.

ghost commented 4 years ago

These three helper procedures might be handy for this case?


;; Using defparameter instead of defconstant
;; to stop SLIME from complaining
;; about re-defining +default-port+.
(defparameter +default-port+ 2500)
(defvar *server-handler* nil)

(defun start-server (&optional (port +default-port+))
  (if *server-handler*
      (princ "Server is already running. Use restart-server instead?")
      (setf *server-handler*
            (clack:clackup #'my-server-logic
                           :port port
                           :server :hunchentoot))))

(defun stop-server ()
  (if *server-handler*
      (progn
        (clack:stop *server-handler*)
        (setf *server-handler* nil)
        (princ "Server stopped."))
      (princ "Server is not even started yet.")))

(defun restart-server (&optional (port +default-port+))
  (if (null *server-handler*)
      (princ "Server is not even started yet.")
      (progn
        (princ "Restarting server..")
        (stop-server)
        (start-server port))))
justinpowers commented 3 years ago

What is the cleanest way to stop a server without a handler? I started the server without binding the return value to a variable, so I have no obvious way (as a lisp newbie) of stopping it except to kill common lisp. Is there a better way?

Thanks in advance for responding to this tangential question! This little thread already has some good tidbits for beginners so I figure why not add on...

EDIT: I managed to answer my own question...

Within the REPL, the user can use * to re-return the previous output. So, if we have started the server and did not bind the returned handler object to a variable, the bare object will be output to the REPL instead. Assuming there aren't any intervening commands, we can then bind this object to a variable with the form: (defvar *server-handler* *), where the final * is a special variable that stands for the previous output. And then we can continue to stop the server as normal: (clack:stop *server-handler*).

If there are intervening commands/ouput, this is still quite doable, but you will have to use a different special variable depending on how far back you need to reference. The backreferences section of the SLY documentation will guide you.