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:

  1. 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).

  2. 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.
  3. In the main function, we create a new Hunchentoot server instance listening on port 8090 and start it.

  4. 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.