-
-
Save zaphar/313050 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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