Skip to content

Instantly share code, notes, and snippets.

@tjweir
Forked from alandipert/minihttpd.clj
Created October 7, 2009 00:27
Show Gist options
  • Select an option

  • Save tjweir/203608 to your computer and use it in GitHub Desktop.

Select an option

Save tjweir/203608 to your computer and use it in GitHub Desktop.
(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)])
(:import (java.net ServerSocket Socket InetAddress URLDecoder)
(java.io File)))
(def codes {200 "HTTP/1.0 200 OK"
404 "HTTP/1.0 404 Not Found"})
(def mimes {"css" "Content-type: text/css"
"html" "Content-type: text/html"
"jpg" "Content-type: image/jpeg"
"png" "Content-type: image/png"
"gif" "Content-type: image/gif"
"clj" "Content-type: text/plain"
"txt" "Content-type: text/plain"})
(def error {404 "The file was not found."
500 "There was a server error."})
(def doc-root "/Users/alan/Sites")
(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 get-ext [abspath]
"Try to get a file's extension for use mapping to a MIME type"
(last (re-split #"\." abspath)))
(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 (get-ext path) "txt"))]
(.write os (to-byte-array headers) 0 (count headers))
(.write os (to-byte-array file) 0 (.length file)))))
(defn send-error [sock code msg]
"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")) msg))))
(defn handle-request [sock]
"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 (URLDecoder/decode uri)))]
(if (.exists file)
(send-resource sock file)
(send-error sock 404 "File not found")))))
(defmulti http-listen class)
(defmethod http-listen ServerSocket [socket]
"Bind to a socket and keep listening"
(let [client-socket (.accept socket)]
(handle-request client-socket))
(recur socket))
(defmethod http-listen Integer [port]
"Takes a port number to listen on; defers to the listen loop function"
(http-listen (ServerSocket. port)))
(http-listen 8081)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment