-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathserver_old.html
154 lines (131 loc) · 7.22 KB
/
server_old.html
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
---
layout: doc
title: HTTP server
---
<h2>Ring adapter(HTTP server) with async and websocket extenstion</h2>
{% highlight clojure %}
(:use org.httpkit.server)
{% endhighlight %}
<p>The server uses an event-driven, non-blocking I/O model that makes it lightweight and scalable.
It's written to conform to the standard Clojure web server
<a href="https://github.com/ring-clojure/ring">Ring spec</a>, with asynchronous and websocket extenstion.
HTTP Kit is (<a href="migration.html">almost</a>) drop-in replacement of ring-jetty-adapter
</p>
<p class="note"><a href="channel.html">A new unified Async/Websocket API(working in progress)</a></p>
<h3 id="options" class="anchor">Hello, Clojure HTTP server</h3>
<p> <code>run-server</code> starts a Ring-compatible HTTP server, <a href="#routing">routing using compojure</a></p>
{% highlight clojure %}
(defn app [req]
{:status 200
:headers {"Content-Type" "text/html"}
:body "hello HTTP!"})
(run-server app {:port 8080})
{% endhighlight %}
<h4>Options:</h4>
<ul>
<li><code>:ip</code>: which IP to bind, default to <code>0.0.0.0</code></li>
<li><code>:port</code>: which port listens incomming request, default to 8090</li>
<li><code>:thread</code>: How many threads to compute response from request, default to 4</li>
<li><code>:worker-name-prefix</code>: woker thread name prefix, default to <code>worker-</code>: <code>worker-1</code> <code>worker-2</code>....</li>
<li><code>:queue-size</code>: max requests queued waiting for threadpool to compute response before reject, 503(Service Unavailable) is returned to client if queue is full, default to 20K</li>
<li><code>:max-body</code>: length limit for request body in bytes, 413(Request Entity Too Large) is returned if exceeds this limit, default to 8388608(8M)</li>
<li><code>:max-line</code>: length limit for HTTP inital line and per header,
414(Request-URI Too Long) will be returned if exceeds this limit, default to 4096(4K),
<a href="http://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url">relevant discusstion on stackoverflow</a></li>
</ul>
<h3 class="anchor" id="websocket">Websocket extenstion</h3>
<ul>
<li>
<code>if-ws-request</code> or <code>when-ws-request</code>: get the websocket connection, returns websocket handshake
</li>
<li><code>on-mesg</code>: register a fn to be called when there is string message from client</li>
<li><code>send-mesg</code>: send string message to client. <i>Clojure data can be converted to string using
<a href="https://github.com/clojure/data.json">JSON</a> or <a href="https://github.com/edn-format/edn">edn</a></i></li>
<li><code>close-conn</code>: close the websocket connection</li>
<li><code>on-close</code>: register a fn to be called when the connecton is closed</li>
</ul>
<p>For WebSocket Secure connection, one option is <a href="https://github.com/bumptech/stud">stud</a> (self-signed certificate may not work with websocket)</p>
<p>A realtime chart example: <a href="https://github.com/http-kit/chat-websocket">https://github.com/http-kit/chat-websocket</a></p>
{% highlight clojure %}
(defn chat-handler [req]
(when-ws-request req ws-con ; ws-con bind to the websocket connection
(on-mesg ws-con (fn [msg]
(send-mesg ws-con msg) ;; echo back
(close-conn ws-con))) ;; close the connection
(on-close ws-con (fn [status] (println ws-con "closed")))))
(run-server chat-handler {:port 8080})
{% endhighlight %}
<h3 class="anchor" id="async">Asynchronous (long polling) extenstion</h3>
<p><code>respond</code> accept a standard ring-response (:status :headers :body) or just the :body</p>
<p>All request-level middlewares applied to <code>req</code> before passing to <code>aync-handler</code> (like any other handler)</p>
<p><b class="message">The response is sent to client without response-level middlewares applied (a limitation, suggestions welcome)</b> </p>
<p>Optional timeout facilities: <a href="timer.html">timer</a></p>
<p>A realtime chart example: <a href="https://github.com/http-kit/chat-polling">https://github.com/http-kit/chat-polling</a></p>
{% highlight clojure %}
(defn async-handler [req]
; respond is a one-time function, can be called on any thread to send the response to client
(async-response respond
; just :body, can also be {:status _ :headers _ :body _}
(future (respond "hello world async"))))
(run-server async-handler {:port 8080})
{% endhighlight %}
<h3 id="routing" class="anchor">Routing with Compojure</h3>
<p>
<a href="https://github.com/weavejester/compojure">Compojure</a> Can be used to do the routing, based on uri and method
</p>
{% highlight clojure %}
(:use [compojure.route :only [files not-found]]
[compojure.handler :only [site]] ; form, query params decode; cookie; session, etc
[compojure.core :only [defroutes GET POST DELETE ANY context]]
org.httpkit.server)
(defn show-landing-page [req] ;; ordinary clojure function, accept request map
;; return landing page's html string. possible template library:
;; mustache (https://github.com/shenfeng/mustache.clj, https://github.com/fhd/clostache...)
;; enlive (https://github.com/cgrand/enlive), hiccup(https://github.com/weavejester/hiccup)
)
(defn update-userinfo [req] ;; ordinary clojure function
(let [user-id (-> req :params :id) ; param from uri
password (-> req :params :password)] ; form param
....
))
(defroutes all-routes
(GET "/" [] show-landing-page)
(GET "/ws" [] chat-handler) ;; websocket
(GET "/async" [] async-handler) ;; asynchronous(long polling)
(context "/user/:id" []
(GET / [] get-user-by-id)
(POST / [] update-userinfo))
(route/files "/static/") ;; static file url prefix /static, in `public` folder
(route/not-found "<p>Page not found.</p>")) ;; all other, return 404
(run-server (site #'all-routes) {:port 8080})
{% endhighlight %}
<h3 class="anchor" id="deploy">Recommended server deployment</h3>
<p>http-kit runs alone happly, handy for development and quick deployment.
Use reverse proxy like <a href="http://wiki.nginx.org/Main">Nginx</a>,
<a href="http://www.lighttpd.net/">Lighthttpd</a>, etc in serious production is encouraged. They also <a href="migration.html#https">add https support</a>.</p>
<ul>
<li>They are fast, heavily optimized for static content.</li>
<li>They can be configured to compress the content sent to browsers</li>
</ul>
<p>Sample Nginx configration:</p>
{% highlight sh %}
upstream http_backend {
server 127.0.0.1:8090; # http-kit listen on 8090
# keepalive(resue TCP connection) improves performance
keepalive 32; # both http-kit and nginx are good at concurrency
}
server {
location /static/ { # static content
alias /var/www/xxxx/public/;
}
location / {
proxy_pass http://http_backend;
# tell http-kit to keep the connection
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
access_log /var/log/nginx/xxxx.access.log;
}
}
{% endhighlight %}