It's a bit of a pain to have to restart the server all the time, let's make a redefinable handler:
(defun handler (env) '(200 nil ("Hello World, redefinable!")))
And start the server; we call the function by name to allow redefinition
(defparameter *clack-server*
(clack:clackup (lambda (env) (funcall 'handler env))))
Hunchentoot server is started.
Listening on localhost:5000.
Check that it works…
curl -s http://localhost:5000
Hello World, redefinable!
Now let's redefine it and take a look at what is in the environment:
(defun handler (env)
`(200 nil (,(prin1-to-string env))))
View results…
curl -s http://localhost:5000
(:REQUEST-METHOD :GET :SCRIPT-NAME "" :PATH-INFO "/" :SERVER-NAME "localhost"
:SERVER-PORT 5000 :SERVER-PROTOCOL :HTTP/1.1 :REQUEST-URI "/" :URL-SCHEME
"http" :REMOTE-ADDR "127.0.0.1" :REMOTE-PORT 53824 :QUERY-STRING NIL :RAW-BODY
#<FLEXI-STREAMS:FLEXI-IO-STREAM {1021B536E3}> :CONTENT-LENGTH NIL
:CONTENT-TYPE NIL :CLACK.STREAMING T :CLACK.IO
#<CLACK.HANDLER.HUNCHENTOOT::CLIENT {1021B537F3}> :HEADERS
#<HASH-TABLE :TEST EQUAL :COUNT 3 {1021B53C13}>)
This is the core part of clack; the environment plist.
Documentation for it is available in the lack README
The fact that it is a plist means capturing values of interest can be done with destructuring-bind
:
(defun handler (env)
(destructuring-bind (&key request-method path-info request-uri
query-string headers &allow-other-keys)
env
`(200
nil
(,(format nil "Method: ~S Path: ~S URI: ~A Query: ~S~%Headers: ~S"
request-method path-info request-uri query-string
(alexandria:hash-table-alist headers))))))
curl -s http://localhost:5000
Method: :GET Path: "/" URI: / Query: NIL
Headers: (("accept" . "*/*") ("user-agent" . "curl/7.53.0")
("host" . "localhost:5000"))
Optima can be useful too:
(defun handler (env)
(optima:match env
((guard (property :path-info path)
(alexandria:starts-with-subseq "/foo/" path))
`(200 nil (,(format nil "The path '~A' is in /foo/~%" path))))
((guard (property :path-info path)
(alexandria:starts-with-subseq "/bar/" path))
`(200 nil (,(format nil "The path '~A' is in /bar/~%" path))))
((property :path-info path)
`(404 nil (,(format nil "Path ~A not found~%" path))))))
curl -s http://localhost:5000/foo/quux
curl -s http://localhost:5000/bar/quux
curl -s http://localhost:5000/baz/quux
The path '/foo/quux' is in /foo/
The path '/bar/quux' is in /bar/
Path /baz/quux not found