Context in Clojure

Here’s the translation of the Go code to Clojure, with explanations in Markdown format suitable for Hugo:

In this example, we’ll demonstrate how to create a simple HTTP server in Clojure and use the concept of contexts for controlling cancellation. While Clojure doesn’t have a direct equivalent to Go’s context.Context, we can achieve similar functionality using core.async channels.

First, let’s import the necessary libraries:

(ns example.context
  (:require [org.httpkit.server :as server]
            [clojure.core.async :as async]))

Now, let’s define our handler function:

(defn hello [req]
  (let [shutdown-ch (async/chan)]
    (async/go
      (println "server: hello handler started")
      (let [[_ ch] (async/alts! [(async/timeout 10000) shutdown-ch])]
        (if (= ch shutdown-ch)
          (do
            (println "server: request cancelled")
            {:status 500
             :body "Internal Server Error"})
          {:status 200
           :body "hello\n"})))
    (fn []
      (async/close! shutdown-ch)
      (println "server: hello handler ended"))))

In this handler:

  • We create a shutdown-ch channel to simulate the context.Done() channel in Go.
  • We use core.async/go block to handle the request asynchronously.
  • We use core.async/alts! to wait for either 10 seconds to pass or the shutdown channel to close.
  • If the shutdown channel closes first, we return a 500 error, simulating a cancelled request.
  • We return a function that closes the shutdown channel, simulating the deferred cleanup in Go.

Now, let’s set up our server:

(defn start-server []
  (let [stop-server (server/run-server #'hello {:port 8090})]
    (println "Server started on port 8090")
    stop-server))

(defn -main []
  (start-server)
  @(promise)) ; Keep the main thread alive

To run the server:

$ clj -m example.context
Server started on port 8090

To simulate a client request to /hello, you can use curl in another terminal:

$ curl localhost:8090
server: hello handler started
hello
server: hello handler ended

If you want to simulate a cancellation, you’ll need to modify the client to cancel the request after a short delay. In a real-world scenario, this might happen due to a timeout or user action.

This example demonstrates how to create a simple HTTP server in Clojure with a mechanism for handling long-running requests and potential cancellations. While it doesn’t use the exact same concepts as the original example, it achieves similar functionality using Clojure’s concurrency primitives.