ChatGPT's - Simple HTTP Server (clojure)



when i want to transfer a file in a network, I normal use the following command to host a simple http server in python that host the current directory

python3 -m http.server 8888

Here we are using the http.server module and telling it to run in 8888 port

One of my junior developer uses chatgpt at work to solve some problems

I was impressed very much with the relevant answer, I logged in yesterday and tried bunch of other things and i thought

HMM why not ask the ChatGPT to create a simple HTTP server in clojure I started asking ton of question and the answer is just pouring out, It’s like raining but you cann’t harvest all the water. Time to harvest some answers.

I started connecting the pieces together and voila, HTTP server that can serve a directory in clojure. It makes few mistakes though not biggy

Let’s discuss the code piece by piece

We need HTTP server lib first so i choosed http-kit.

deps.edn

{:deps {http-kit/http-kit {:mvn/version "2.7.0-alpha1"}}}

Full code: https://github.com/ThangaAyyanar/Learning-Adventure/blob/master/Clojure/simple-http-server.clj

Let’s get straight into the coding

(ns simple-http-server
  (:require [org.httpkit.server :as http]
            [clojure.java.io :as io]
            [clojure.string :as string]))

here we required

(defonce server (atom nil))

(defn stop-server []
  (when-not (nil? @server)
    ;; graceful shutdown: wait 100ms for existing requests to be finished
    ;; :timeout is optional, when no timeout, stop immediately
    (@server :timeout 100)
    (reset! server nil)))

(defn -main [& args]
  (reset! server (http/run-server #'handler {:port 8080})))
(defn get-path [uri]
    (if (= uri "/") dir-path (str dir-path uri)))

(def error-response {:status 404 :body "404 Not Found"})

(defn file-download-response [file-path]
  {:status 200
   :headers {"Content-Type" "application/octet-stream"
             "Content-Disposition" (str "attachment; filename="(.getName file-path))}
   :body (io/input-stream file-path)})

(defn folder-html-response [uri path]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (contents uri path)})

(defn handler [request]
  (let [uri (or (:uri request) "/")
        path (get-path uri)
        file-path (io/file path)]
    (if file-path
      (cond
        (.isFile file-path) (file-download-response file-path)
        (.isDirectory file-path) (folder-html-response uri path)
        :else error-response
        )
      error-response)))
(defn list-folders
  ([path] (list-folders "/" path))
  ([uri path]
    (->> (io/file path)
        (.listFiles)
        (map #(dirs-to-string uri %))
        (string/join)
        )))
defn folder-html [uri name]
 (str "<li><a href='" (str uri name) "'>" (str name "/") "</a> </li>"))

(defn file-html [uri name]
 (str "<li><a href='" (str uri name) "' download>" (str name) "</a> </li>"))
 
(defn dirs-to-string [uri file]
  (let [name (.getName file)
        new-uri (if (= uri "/") uri (str uri "/"))]
    (cond
      (.isDirectory file) (folder-html new-uri name)
      (.isFile file) (file-html new-uri name)
      :else "")))
(defn prepend-up-dir [uri]
  (if (= uri "/") nil
      (let [prev-folder (string/join "/" (pop (string/split uri #"/")))]
        (str "<li><a href='" (if (empty? prev-folder) "/" prev-folder) "'>..</a></li>"))))
(defn generate-html
  [title uri items]
  (let [title-html (str "<h1>" title "</h1>")
        uri-html  (str "<b>URI:</b>" uri)
        prev-dir (or (prepend-up-dir uri) "")
        contents (str "<hr/><ul>" prev-dir items "</ul><hr/>")]
     (str title-html uri-html contents)))

(defn contents [uri path]
  (let [folders (list-folders uri path)]
    (generate-html "Simple HTTP Server" uri folders)))

Would you like to go further, Try few things below

Resources