fogus (owner)

Fork Of

Revisions

  • 20ff55 cgrand Mon Apr 27 07:41:13 -0700 2009
gist: 104426 Download_button fork
public
Public Clone URL: git://gist.github.com/104426.git
Embed All Files: show embed
httpcore.clj #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
(ns org.apache.http.examples
 "Basic HTTP Server.
A quick port of http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/httpcore/src/examples/org/apache/http/examples/ElementalHttpServer.java to Clojure"
  (:import (java.io File OutputStreamWriter InterruptedIOException IOException)
           (java.net ServerSocket URLDecoder)
           (java.util Locale)
           (org.apache.http.protocol BasicHttpProcessor HttpContext BasicHttpContext HttpRequestHandler HttpRequestHandlerRegistry HttpService ResponseConnControl ResponseContent ResponseDate ResponseServer)
           (org.apache.http ConnectionClosedException HttpEntity HttpEntityEnclosingRequest HttpException HttpRequest HttpResponse HttpServerConnection HttpStatus MethodNotSupportedException)
           (org.apache.http.entity ContentProducer EntityTemplate FileEntity)
           (org.apache.http.impl DefaultConnectionReuseStrategy DefaultHttpResponseFactory DefaultHttpServerConnection)
           (org.apache.http.params BasicHttpParams CoreConnectionPNames HttpParams CoreProtocolPNames)
           (org.apache.http.util EntityUtils)))
 
(defn entity [& ss]
  (doto (EntityTemplate.
          (proxy [ContentProducer] []
            (writeTo [outstream]
              (let [w (OutputStreamWriter. outstream "UTF-8")]
                (doseq [s ss] (.write w s))
                (.flush w)))))
    (.setContentType "text/html; charset=UTF-8")))
 
(defn file-handler [docroot]
  (proxy [HttpRequestHandler] []
    (handle [request response context]
      (println "file-handler")
      (let [method (-> request .getRequestLine .getMethod (.toUpperCase Locale/ENGLISH))]
        (if-not (#{"GET" "HEAD" "POST"} method)
          (throw (MethodNotSupportedException. (str method " method not supported")))
          (let [target (-> request .getRequestLine .getUri)]
            (when (instance? HttpEntityEnclosingRequest request)
              (let [entity (.getEntity request)
                    entityContent (EntityUtils/toByteArray entity)]
                (println "Incoming entity content (bytes):" (count entityContent))))
            (let [file (File. docroot (URLDecoder/decode target))]
              (cond
                (-> file .exists not)
                  (let [body (entity "<html><body><h1>"
                               "File " (.getPath file) " not found"
                               "</h1></body></html>")]
                    (.setStatusCode response HttpStatus/SC_NOT_FOUND)
                    (.setEntity response body)
                    (println "File" (.getPath file) "not found"))
                (or (-> file .canRead not) (.isDirectory file))
                  (let [body (entity "<html><body><h1>"
                               "Access denied"
                               "</h1></body></html>")]
                    (.setStatusCode response HttpStatus/SC_FORBIDDEN)
                    (.setEntity response body)
                    (println "Cannot read file" (.getPath file)))
                :else
                  (let [body (FileEntity. file "text/html")]
                    (.setStatusCode response HttpStatus/SC_OK)
                    (.setEntity response body)
                    (println "Serving file" (.getPath file)))))))))))
   
 
(defn request-handler [httpservice conn]
  #(do
     (println "New connection thread")
     (let [context (BasicHttpContext. nil)]
       (try
         (while (.isOpen conn)
           (.handleRequest httpservice conn context))
         (catch ConnectionClosedException ex
           (println "Client closed connection"))
         (catch IOException ex
           (println "I/O error:" (.getMessage ex)))
         (catch HttpException ex
           (println "Unrecoverable HTTP protocol violation:" (.getMessage ex)))
         (finally
           (try
             (.shutdown conn)
             (catch IOException _
               nil)))))))
 
(defn request-listener [port docroot]
  (let [serversocket (ServerSocket. port)
        params (doto (BasicHttpParams.)
                 (.setIntParameter CoreConnectionPNames/SO_TIMEOUT 5000)
                 (.setIntParameter CoreConnectionPNames/SOCKET_BUFFER_SIZE (* 8 1024))
                 (.setBooleanParameter CoreConnectionPNames/STALE_CONNECTION_CHECK false)
                 (.setBooleanParameter CoreConnectionPNames/TCP_NODELAY true)
                 (.setParameter CoreProtocolPNames/ORIGIN_SERVER "HttpComponents/1.1"))
        httpproc (doto (BasicHttpProcessor.)
                   (.addInterceptor (ResponseDate.))
                   (.addInterceptor (ResponseServer.))
                   (.addInterceptor (ResponseContent.))
                   (.addInterceptor (ResponseConnControl.)))
            
        reqistry (doto (HttpRequestHandlerRegistry.)
                   (.register "*" (file-handler docroot)))
            
        httpservice (doto (HttpService. httpproc
                            (DefaultConnectionReuseStrategy.)
                            (DefaultHttpResponseFactory.))
                      (.setParams params)
                      (.setHandlerResolver reqistry))]
        
        (println "Listening on port" (.getLocalPort serversocket))
        (loop []
          (try
            (let [socket (.accept serversocket)
                  conn (doto (DefaultHttpServerConnection.)
                         (.bind socket params))]
              (doto (Thread. (request-handler httpservice conn))
                (.setDaemon true)
                (.start)))
            (catch InterruptedIOException ex
                nil)
            (catch IOException e
                (println "I/O error initialising connection thread:"
                  (.getMessage e))))
            (recur))))
 
(doto (Thread. #(request-listener 8080 "c:/var/www"))
      (.setDaemon false)
      (.start))