Http Server in Lisp
Here’s the translation of the HTTP Server example from Go to Lisp (Common Lisp), formatted in Markdown suitable for Hugo:
(ql:quickload '(:hunchentoot :cl-who))
(defpackage :http-server
(:use :cl :hunchentoot :cl-who))
(in-package :http-server)
;; A fundamental concept in web servers is handlers. In Hunchentoot,
;; we define handlers using the define-easy-handler macro.
(define-easy-handler (hello :uri "/hello") ()
;; This handler simply returns "hello" as the response.
"hello")
(define-easy-handler (headers :uri "/headers") ()
;; This handler does something a little more sophisticated
;; by reading all the HTTP request headers and echoing them
;; into the response body.
(with-html-output-to-string (s)
(loop for (key . value) in (hunchentoot:headers-in*)
do (format s "~A: ~A~%" key value))))
(defun main ()
;; We create a new Hunchentoot server instance
(let ((server (make-instance 'easy-acceptor :port 8090)))
;; Start the server
(start server)
;; Keep the program running
(loop (sleep 1))))
;; To run the server, evaluate (main) in your Lisp REPL
This Lisp code creates a simple HTTP server using the Hunchentoot web server library. Here’s a breakdown of what’s happening:
We load the necessary libraries: Hunchentoot for the web server and CL-WHO for HTML generation (although we don’t use it in this example, it’s often useful for web development in Common Lisp).
We define two handlers:
- The
hello
handler simply returns “hello” when accessed at the “/hello” URI. - The
headers
handler reads all the HTTP request headers and returns them as the response when accessed at the “/headers” URI.
- The
In the
main
function, we create a new Hunchentoot server instance listening on port 8090 and start it.The server runs indefinitely due to the infinite loop at the end of
main
.
To run the server, you would evaluate (main)
in your Lisp REPL. Then you can access it like this:
$ curl localhost:8090/hello
hello
$ curl localhost:8090/headers
Host: localhost:8090
User-Agent: curl/7.68.0
Accept: */*
Note that in Lisp, we don’t typically compile to standalone executables as often as in some other languages. Instead, it’s common to load the code into a Lisp image and run it from there. However, it is possible to create standalone executables from Lisp programs if needed, using tools like ASDF and your Lisp implementation’s saving facilities.