Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@alandipert
Forked from anonymous/qbert.clj
Created October 6, 2009 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save alandipert/203329 to your computer and use it in GitHub Desktop.
Save alandipert/203329 to your computer and use it in GitHub Desktop.
; minihttpd, tiny barebones clojure web server
; http://alan.xen.prgmr.com/
(ns alandipert.minihttpd
(:use [clojure.contrib.duck-streams :only (reader writer read-lines spit to-byte-array)]
[clojure.contrib.str-utils :only (re-split str-join re-gsub)])
(:import (java.net ServerSocket URLDecoder)
(java.io File)))
(def codes {200 "HTTP/1.0 200 OK"
404 "HTTP/1.0 404 Not Found"
403 "HTTP/1.0 403 Forbidden"})
(def mimes {"html" "Content-type: text/html"
"png" "Content-type: image/png"
"txt" "Content-type: text/plain"})
(def error {403 "The first soft snow!\nEnough to bend the leaves\nYour access denied\n\n403"
404 "Sick and feverish\nGlimpse of cherry blossoms\nThe file was not found\n\n404"})
(defn build-headers [& headers]
"Build a header string and end it with two spaces"
(str (str-join "\r\n" headers) "\r\n\r\n"))
(defn log [& info]
(println (str-join "\t" info)))
(defn send-resource [sock file]
"Open the socket's output stream and send headers and content"
(with-open [os (.getOutputStream sock)]
(let [path (.getAbsolutePath file)
headers (build-headers
(codes 200)
(mimes (last (re-split #"\." path)) "txt")
(format "Content-length: %s" (.length file)))]
(.write os (to-byte-array headers) 0 (count headers))
(.write os (to-byte-array file) 0 (.length file))
(log (java.util.Date.) (.getInetAddress sock) path))))
(defn send-error [sock code]
"Open the socket's output stream and send header and error message"
(with-open [wrt (writer (.getOutputStream sock))]
(spit wrt (str (build-headers (codes code) (mimes "txt")) (error code)))))
(defn check-index [uri]
"Check if we should serve out default index page"
(if (.endsWith "/" uri) (str uri "index.html") uri))
(defn handle-request [sock doc-root]
"Send the file if it exists, or a 404"
(with-open [rdr (reader (.getInputStream sock))]
(let [uri (second (re-split #"\s+" (first (line-seq rdr))))
file (File. (str doc-root (check-index (re-gsub #"\.{2,}" "" (URLDecoder/decode uri)))))]
(if (.exists file)
(if (.isDirectory file)
(send-error sock 403)
(send-resource sock file))
(send-error sock 404)))))
(defn http-listen [socket doc-root]
"Bind to a socket and keep listening"
(let [client-socket (.accept socket)]
(try
(handle-request client-socket doc-root)
(catch Exception _ (log (java.util.Date.) (.toString (.getInetAddress client-socket)) "Peer disconnected"))))
(recur socket doc-root))
(http-listen (ServerSocket. 8081) "/Users/alan/Sites")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment