(:require [org.httpkit.client :as http])
Like the Server, the client uses an event-driven, non-blocking I/O model. It's lightweight and efficient.
{:keepalive -1} to disable;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
Synchronous programming is easy to understand, just like clj-http:
;; 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)))
Sent request concurrently, half the waiting time
(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)))
(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))
))
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
HTTPS persistent connection is also supported.
By default, http-kit keeps idle connection for 120s.
That can be configured by the keepalive option:
; 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})
Sometimes, it's handy to pass some state to callback. You can do it this way:
(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))
;; 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})