Skip to content

Instantly share code, notes, and snippets.

@souenzzo
Last active May 6, 2020 13:16
Show Gist options
  • Save souenzzo/a959a4c5b8c0c90df76fe33bb7dfe201 to your computer and use it in GitHub Desktop.
Save souenzzo/a959a4c5b8c0c90df76fe33bb7dfe201 to your computer and use it in GitHub Desktop.
tiny http server via java sockets in clojure
#!/usr/bin/env clojure
(import (java.net ServerSocket))
(require '[clojure.string :as string] '[clojure.java.io :as io])
(letfn [(req-line+headers [in]
(binding [*in* in]
(loop [headers []]
(let [line (read-line)]
(if (string/blank? line)
headers
(recur (conj headers line)))))))
(hiccup [el]
(string/join
(cond
(and (coll? el)
(keyword? (first el))) (let [[tag attrs & els] el
kvs (when (map? attrs)
(for [[k v] attrs]
(format "%s=%s" (name k) (str v))))]
(concat ["<" (string/join " " (cons (name tag) kvs)) ">"]
(map hiccup (if (map? attrs)
els
(cons attrs els)))
["</" (name tag) ">"]))
(coll? el) (map hiccup el)
:else [el])))]
(with-open [server-socket (ServerSocket. 8081)
client-socket (.accept server-socket)]
(let [out (io/writer (.getOutputStream client-socket))
in (io/reader (.getInputStream client-socket))
[req-line & headers] (req-line+headers in)
[_ _ path _] (re-find #"([^\s]+)\s([^\s]+)\s([^\s]+)" req-line)
f (io/file (format "./%s" path))
found? (.exists f)
status (if found? 200 404)
body (cond
(not found?) ""
(.isFile f) (slurp f)
(.isDirectory f) (format "<!DOCTYPE html>\n%s"
(hiccup [:html {}
[:head {}
[:title {} path]]
[:body {}
[:h1 {} path]
[:tt {}
[:pre {}
(for [i (.list f)]
[:div [:a {:href (format "\"%s\"" i)} i]])]]]])))]
(.write out (format "HTTP/1.1 %s OK\r\nContent-Length: %s\r\n\r\n%s"
status
(count (.getBytes body))
body))
(binding [*out* out]
(flush)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment