Skip to content
This repository was archived by the owner on Dec 1, 2023. It is now read-only.

Commit 00934ba

Browse files
committed
Big refactor of connection pooling stuff
1 parent cf31060 commit 00934ba

File tree

6 files changed

+89
-52
lines changed

6 files changed

+89
-52
lines changed

Readme.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,21 @@ This is MUCH faster than sequentially performing all requests, because
461461
a persistent connection can be used instead creating a new connection
462462
for each request.
463463

464-
This feature is fairly new, please let me know if you have any feedback!
464+
If you would prefer to handle managing the connection manager
465+
yourself, you can create a connection manager yourself and specify it
466+
for each request:
467+
468+
```clojure
469+
(def cm (clj-http.conn-mgr/make-reusable-conn-manager {:timeout 2 :threads 3}))
470+
(def cm2 (clj-http.conn-mgr/make-reusable-conn-manager {:timeout 10 :threads 1}))
471+
472+
(get "http://aoeu.com/1" {:connection-manager cm2})
473+
(post "http://aoeu.com/2" {:connection-manager cm})
474+
(get "http://aoeu.com/3" {:connection-manager cm2})
475+
```
476+
477+
See the docstring on `make-reusable-conn-manager` for options and
478+
default values.
465479

466480
### Redirects handling
467481
clj-http conforms its behaviour regarding automatic redirects to the

changelog.org

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,11 @@
232232
* Work log
233233
Log of merges/issues/work that's gone in so I know what to put in
234234
the changelog for the next release
235+
** 2012-01-30
236+
- merged https://github.com/dakrone/clj-http/pull/113 to update
237+
the connection pooling code
238+
- refactor pooled connection managers to allow specifying
239+
the :connection-manager option
235240
** 2012-01-19
236241
- merged https://github.com/dakrone/clj-http/pull/112 to allow
237242
json coercion on error responses when :as :auto is used

src/clj_http/client.clj

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -691,11 +691,9 @@
691691
(filter #(.startsWith (str (:name (meta %))) "wrap-"))
692692
vec))
693693

694-
(def dmcpr ConnPerRouteBean/DEFAULT_MAX_CONNECTIONS_PER_ROUTE)
695-
696694
(defmacro with-connection-pool
697695
"Macro to execute the body using a connection manager. Creates a
698-
ThreadSafeClientConnectionManager to use for all requests within the body of
696+
PoolingClientConnectionManager to use for all requests within the body of
699697
the expression. An option map is allowed to set options for the connection
700698
manager.
701699
@@ -720,32 +718,14 @@
720718
If the value 'nil' is specified or the value is not set, the default value
721719
will be used."
722720
[opts & body]
723-
`(let [timeout# (or (:timeout ~opts) 5)
724-
threads# (or (:threads ~opts) 4)
725-
default-per-route# (or (:default-per-route ~opts)
726-
clj-http.client/dmcpr)
727-
insecure?# (:insecure? ~opts)
728-
leftovers# (dissoc ~opts :timeout :threads :insecure?)]
729-
;; I'm leaving the connection bindable for now because in the
730-
;; future I'm toying with the idea of managing the connection
731-
;; manager yourself and passing it into the request
732-
(binding [conn/*connection-manager*
733-
(doto (conn/make-reusable-conn-manager
734-
(merge {:timeout timeout#
735-
:insecure? insecure?#}
736-
leftovers#))
737-
(.setMaxTotal threads#)
738-
(.setDefaultMaxPerRoute default-per-route#))]
721+
;; I'm leaving the connection bindable for now because in the
722+
;; future I'm toying with the idea of managing the connection
723+
;; manager yourself and passing it into the request
724+
`(let [cm# (conn/make-reusable-conn-manager ~opts)]
725+
(binding [conn/*connection-manager* cm#]
739726
(try
740727
~@body
741728
(finally
742729
(.shutdown
743-
^org.apache.http.impl.conn.PoolingClientConnectionManager
730+
^org.apache.http.impl.conn.PoolingClientConnectionManager
744731
conn/*connection-manager*))))))
745-
746-
(defmacro with-named-connection-pool
747-
"Implementation of the above comment for binding."
748-
[connection & body]
749-
`(binding [conn/*connection-manager*
750-
~connection]
751-
~@body))

src/clj_http/conn_mgr.clj

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@
8484
;; need the fully qualified class name because this fn is later used in a
8585
;; macro from a different ns
8686
(defn ^org.apache.http.impl.conn.PoolingClientConnectionManager
87-
make-reusable-conn-manager
87+
make-reusable-conn-manager*
8888
"Given an timeout and optional insecure? flag, create a
89-
PoolingClientConnectionManager with <timeout> seconds set as the timeout value."
89+
PoolingClientConnectionManager with <timeout> seconds set as the
90+
timeout value."
9091
[{:keys [timeout insecure? keystore trust-store] :as config}]
9192
(let [registry (cond
9293
insecure? insecure-scheme-registry
@@ -95,33 +96,51 @@
9596
(get-keystore-scheme-registry config)
9697

9798
:else regular-scheme-registry)]
98-
(PoolingClientConnectionManager.
99+
(PoolingClientConnectionManager.
99100
registry timeout java.util.concurrent.TimeUnit/SECONDS)))
100101

101-
;; needed to import/define this here since it being called outside the client ns
102102
(def dmcpr ConnPerRouteBean/DEFAULT_MAX_CONNECTIONS_PER_ROUTE)
103103

104-
(defn make-manager
105-
"returns reusable PoolingClientConnectionManager"
106-
[opts]
107-
(let [timeout# (or (:timeout opts) 5)
108-
threads# (or (:threads opts) 4)
109-
default-per-route# (or (:default-per-route opts) dmcpr)
110-
insecure?# (:insecure? opts)
111-
leftovers# (dissoc opts :timeout :threads :insecure?)]
112-
(doto (make-reusable-conn-manager
113-
(merge {:timeout timeout#
114-
:insecure? insecure?#}
115-
leftovers#))
116-
(.setMaxTotal threads#)
117-
(.setDefaultMaxPerRoute default-per-route#))))
104+
(defn make-reusable-conn-manager
105+
"Creates a default pooling connection manager with the specified options.
106+
107+
The following options are supported:
118108
109+
:timeout - Time that connections are left open before automatically closing
110+
default: 5
111+
:threads - Maximum number of threads that will be used for connecting
112+
default: 4
113+
:default-per-route - Maximum number of simultaneous connections per host
114+
default: 2
115+
:insecure? - Boolean flag to specify allowing insecure HTTPS connections
116+
default: false
119117
118+
:keystore - keystore file to be used for connection manager
119+
:keystore-pass - keystore password
120+
:trust-store - trust store file to be used for connection manager
121+
:trust-store-pass - trust store password
120122
121-
(defn shutdown-manager
123+
Note that :insecure? and :keystore/:trust-store options are mutually exclusive
124+
125+
If the value 'nil' is specified or the value is not set, the default value
126+
will be used."
127+
[opts]
128+
(let [timeout (or (:timeout opts) 5)
129+
threads (or (:threads opts) 4)
130+
default-per-route (or (:default-per-route opts) dmcpr)
131+
insecure? (:insecure? opts)
132+
leftovers (dissoc opts :timeout :threads :insecure?)]
133+
(doto (make-reusable-conn-manager* (merge {:timeout timeout
134+
:insecure? insecure?}
135+
leftovers))
136+
(.setMaxTotal threads)
137+
(.setDefaultMaxPerRoute default-per-route))))
138+
139+
(defn shutdown-manager
122140
"Define function to shutdown manager"
123141
[manager]
124-
(doto manager (.shutdown)))
142+
(and manager (.shutdown manager)))
125143

126-
;; connection manager to be rebound during request execution
127-
(def ^{:dynamic true} *connection-manager* nil)
144+
(def ^{:dynamic true
145+
:doc "connection manager to be rebound during request execution"}
146+
*connection-manager* nil)

src/clj_http/core.clj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,12 @@
173173
[{:keys [request-method scheme server-name server-port uri query-string
174174
headers body socket-timeout conn-timeout multipart debug debug-body
175175
insecure? save-request? proxy-host proxy-port as cookie-store
176-
retry-handler response-interceptor digest-auth
176+
retry-handler response-interceptor digest-auth connection-manager
177177
client-params] :as req}]
178178
(let [^ClientConnectionManager conn-mgr
179-
(or conn/*connection-manager* (conn/make-regular-conn-manager req))
179+
(or connection-manager
180+
conn/*connection-manager*
181+
(conn/make-regular-conn-manager req))
180182
^DefaultHttpClient http-client (set-routing
181183
(DefaultHttpClient. conn-mgr))
182184
scheme (name scheme)]

test/clj_http/test/client.clj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[clojure.java.io :only [resource]]
44
[clj-http.test.core :only [run-server]])
55
(:require [clj-http.client :as client]
6+
[clj-http.conn-mgr :as conn]
67
[clj-http.util :as util]
78
[cheshire.core :as json])
89
(:import (java.net UnknownHostException)
@@ -497,3 +498,19 @@
497498
(is (= 200 (:status resp)))
498499
(is (= "close" (get-in resp [:headers "connection"])))
499500
(is (= "get" (:body resp)))))
501+
502+
(deftest ^{:integration true} t-reusable-conn-mgrs
503+
(run-server)
504+
(Thread/sleep 1000)
505+
(let [cm (conn/make-reusable-conn-manager {:timeout 10 :insecure? false})
506+
resp1 (client/request (merge base-req {:uri "/redirect-to-get"
507+
:method :get
508+
:connection-manager cm}))
509+
resp2 (client/request (merge base-req {:uri "/redirect-to-get"
510+
:method :get}))]
511+
(is (= 200 (:status resp1) (:status resp2)))
512+
(is (nil? (get-in resp1 [:headers "connection"]))
513+
"connection should remain open")
514+
(is (= "close" (get-in resp2 [:headers "connection"]))
515+
"connection should be closed")
516+
(.shutdown cm)))

0 commit comments

Comments
 (0)