forked from http-kit/http-kit.github.com
-
Notifications
You must be signed in to change notification settings - Fork 1
/
client.html
126 lines (111 loc) · 5.12 KB
/
client.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
---
layout: doc
title: HTTP client
---
<h2 id="title">HTTP client</h2>
{% highlight clojure %}
(:require [org.httpkit.client :as http])
{% endhighlight %}
<p>
Like the Server, the client uses an event-driven, non-blocking I/O model. It's lightweight and efficient.
</p>
<ul>
<li>Concurrent made easy by asynchronous and promise</li>
<li>Keep-alived, quite a difference in performance, <code>{:keepalive -1}</code> to disable</li>
<li>Timeout per request</li>
<li>HTTPS supported, ~100k of RAM for issuing a HTTPS request. TCP connection, SSLEngine get reused if possible</li>
<li>Easy to use API, modeled after <a href="https://github.com/dakrone/clj-http">clj-http</a></li>
</ul>
<h3 id="async" class="anchor">asynchronous with promise, callback</h3>
{% highlight clojure %}
;fire and forget, returns immediately[1], returned promise is ignored
(http/get "http://host.com/path")
(def options {:timeout 200 ; ms
:basic-auth ["user" "pass"]
:query-params {:param "value" :param2 ["value1" "value2"]}
:user-agent "User-Agent-string"
:headers {"X-Header" "Value"}})
(http/get "http://host.com/path" options
(fn [{:keys [status headers body error]}] ;; asynchronous handle response
(if error
(println "Failed, exception is " error)
(println "Async HTTP GET: " status))))
; [1] may not always true, since DNS lookup maybe slow
{% endhighlight %}
<h3 class="anchor" id="sync"> synchronous with @promise</h3>
<p>Synchronous programming is easy to understand, just like clj-http:</p>
{% highlight clojure %}
;; synchronous
(let [{:keys [status headers body error] :as resp} @(http/get "http://host.com/path")]
(if error
(println "Failed, exception: " error)
(println "HTTP GET success: " status)))
;; Form params
(let [options {:form-params {:name "http-kit" :features ["async" "client" "server"]}}
{:keys [status error]} @(http/post "http://host.com/path1" options)]
(if error
(println "Failed, exception is " error)
(println "Async HTTP POST: " status)))
{% endhighlight %}
<h3 class="anchor" id="combined">Combined, concurrent requests, handle results synchronously</h3>
<p>Sent request concurrently, half the waiting time</p>
{% highlight clojure %}
(let [resp1 (http/get "http://http-kit.org/")
resp2 (http/get "http://clojure.org/")]
(println "Response 1's status: " (:status @resp1)) ; wait as necessary
(println "Response 2's status: " (:status @resp2)))
{% endhighlight %}
{% highlight clojure %}
(let [urls ["http://server.com/api/1" "http://server.com/api/2"
"http://server.com/api/3"]
futures (doall (map http/get urls))]
(doseq [f futures]
;; wait for server response concurrently
(println (-> @f :opt :url) " status: " (:status @f))
))
{% endhighlight %}
<h3 class="anchor" id="reuse">Persistent connection</h3>
<p>HTTP persistent connection, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new connection for every single request/response pair</p>
<p>HTTPS persistent connection is also supported.</p>
<p>By default, http-kit keeps idle connection for 120s.
That can be configured by the <code>keepalive</code> option:</p>
{% highlight clojure %}
; keepalive for 30s
@(http/get "http://http-kit.org" {:keepalive 30000})
; will reuse the previous TCP connection
@(http/get "http://http-kit.org" {:keepalive 30000})
; disable keepalive for this request
@(http/get "http://http-kit.org" {:keepalive -1})
{% endhighlight %}
<h3 class="anchor" id="state">Pass state to asynchronous callback</h3>
<p>Sometimes, it's handy to pass some state to callback. You can do it this way:</p>
{% highlight clojure %}
(defn callback [{:keys [status headers body error opts]}]
;; opts contains :url :method :header + user defined key(s)
(let [{:keys [method start-time url]} opts]
(println method url "status" status "takes time"
(- (System/currentTimeMillis) start-time) "ms")))
;;; save state for callback, useful for async request
(let [opts {:start-time (System/currentTimeMillis)}]
(http/get "http://http-kit.org" opts callback))
{% endhighlight %}
<h3 class="anchor" id="coercion">Output coercion</h3>
{% highlight clojure %}
;; Return the body as a byte stream
(http/get "http://site.com/favicon.ico" {:as :stream}
(fn [{:keys [status headers body error opts]}]
;; body is a java.io.InputStream
))
;; Coerce as a byte-array
(http/get "http://site.com/favicon.ico" {:as :byte-array}
(fn [{:keys [status headers body error opts]}]
;; body is a byte[]
))
;; return the body as a string body
(http/get "http://site.com/string.txt" {:as :text}
(fn [{:keys [status headers body error opts]}]
;; body is a java.lang.String
))
;; Try to automatically coerce the output based on the content-type header, currently supports :text :stream, (with automatic charset detection)
(http/get "http://site.com/string.txt" {:as :auto})
{% endhighlight %}