Skip to content

Commit c468566

Browse files
committed
Merge branch 'master' into fix-clj-http-readme
2 parents a76cc6a + d33c76d commit c468566

File tree

12 files changed

+279
-168
lines changed

12 files changed

+279
-168
lines changed

project.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(def netty-version "4.1.27.Final")
1+
(def netty-version "4.1.28.Final")
22

33
(def netty-modules
44
'[transport

src/aleph/http.clj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
| `name-resolver` | specify the mechanism to resolve the address of the unresolved named address. When not set or equals to `:default`, JDK's built-in domain name lookup mechanism is used (blocking). Set to`:noop` not to resolve addresses or pass an instance of `io.netty.resolver.AddressResolverGroup` you need. Note, that if the appropriate connection-pool is created with dns-options shared DNS resolver would be used
118118
| `proxy-options` | a map to specify proxy settings. HTTP, SOCKS4 and SOCKS5 proxies are supported. Note, that when using proxy `connections-per-host` configuration is still applied to the target host disregarding tunneling settings. If you need to limit number of connections to the proxy itself use `total-connections` setting.
119119
| `response-executor` | optional `java.util.concurrent.Executor` that will execute response callbacks
120+
| `log-activity` | when set, logs all events on each channel (connection) with a log level given. Accepts either one of `:trace`, `:debug`, `:info`, `:warn`, `:error` or an instance of `io.netty.handler.logging.LogLevel`. Note, that this setting *does not* enforce any changes to the logging configuration (default configuration is `INFO`, so you won't see any `DEBUG` or `TRACE` level messages, unless configured explicitly)
120121
121122
Supported `proxy-options` are
122123
@@ -235,7 +236,8 @@
235236
| `pool-timeout` | timeout in milliseconds for the pool to generate a connection
236237
| `connection-timeout` | timeout in milliseconds for the connection to become established
237238
| `request-timeout` | timeout in milliseconds for the arrival of a response over the established connection
238-
| `read-timeout` | timeout in milliseconds for the response to be completed"
239+
| `read-timeout` | timeout in milliseconds for the response to be completed
240+
| `follow-redirects?` | whether to follow redirects, defaults to `true`; see `aleph.http.client-middleware/handle-redirects`"
239241
[{:keys [pool
240242
middleware
241243
pool-timeout
@@ -247,8 +249,7 @@
247249
:or {pool default-connection-pool
248250
response-executor default-response-executor
249251
middleware identity
250-
connection-timeout 6e4 ;; 60 seconds
251-
follow-redirects? true}
252+
connection-timeout 6e4} ;; 60 seconds
252253
:as req}]
253254

254255
(executor/with-executor response-executor

src/aleph/http/client.clj

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@
2424
HttpRequest
2525
HttpResponse
2626
HttpContent
27+
HttpUtil
28+
HttpHeaderNames
2729
LastHttpContent
2830
FullHttpResponse
2931
HttpObjectAggregator]
3032
[io.netty.channel
3133
Channel
3234
ChannelHandler ChannelHandlerContext
3335
ChannelPipeline]
36+
[io.netty.handler.stream ChunkedWriteHandler]
37+
[io.netty.handler.codec.http FullHttpRequest]
3438
[io.netty.handler.codec.http.websocketx
3539
CloseWebSocketFrame
3640
PingWebSocketFrame
@@ -51,6 +55,9 @@
5155
HttpProxyHandler
5256
Socks4ProxyHandler
5357
Socks5ProxyHandler]
58+
[io.netty.handler.logging
59+
LoggingHandler
60+
LogLevel]
5461
[java.util.concurrent.atomic
5562
AtomicInteger]
5663
[aleph.utils
@@ -171,7 +178,7 @@
171178

172179
(instance? HttpResponse msg)
173180
(let [rsp msg]
174-
(if (HttpHeaders/isTransferEncodingChunked rsp)
181+
(if (HttpUtil/isTransferEncodingChunked rsp)
175182
(let [s (netty/buffered-source (netty/channel ctx) #(alength ^bytes %) buffer-capacity)
176183
c (d/deferred)]
177184
(reset! stream s)
@@ -262,7 +269,7 @@
262269
;; * `curl` uses separate option `--proxytunnel` flag to switch tunneling on
263270
;; * `curl` uses CONNECT when sending request to HTTPS destination through HTTP proxy
264271
;;
265-
;; Explicitily setting `tunnel?` to false when it's expected to use CONNECT
272+
;; Explicitly setting `tunnel?` to false when it's expected to use CONNECT
266273
;; throws `IllegalArgumentException` to reduce the confusion
267274
(defn http-proxy-handler
268275
[^InetSocketAddress address
@@ -343,6 +350,21 @@
343350
(.remove (.pipeline ctx) this))
344351
(.fireUserEventTriggered ^ChannelHandlerContext ctx evt))))
345352

353+
(defn coerce-log-level [level]
354+
(if (instance? LogLevel level)
355+
level
356+
(let [netty-level (case level
357+
:trace LogLevel/TRACE
358+
:debug LogLevel/DEBUG
359+
:info LogLevel/INFO
360+
:warn LogLevel/WARN
361+
:error LogLevel/ERROR
362+
nil)]
363+
(when (nil? netty-level)
364+
(throw (IllegalArgumentException.
365+
(str "unknown log level given: " level))))
366+
netty-level)))
367+
346368
(defn pipeline-builder
347369
[response-stream
348370
{:keys
@@ -354,7 +376,8 @@
354376
raw-stream?
355377
proxy-options
356378
ssl?
357-
idle-timeout]
379+
idle-timeout
380+
log-activity]
358381
:or
359382
{pipeline-transform identity
360383
response-buffer-size 65536
@@ -365,7 +388,11 @@
365388
(fn [^ChannelPipeline pipeline]
366389
(let [handler (if raw-stream?
367390
(raw-client-handler response-stream response-buffer-size)
368-
(client-handler response-stream response-buffer-size))]
391+
(client-handler response-stream response-buffer-size))
392+
logger (when (some? log-activity)
393+
(LoggingHandler.
394+
"aleph-client"
395+
^LogLevel (coerce-log-level log-activity)))]
369396
(doto pipeline
370397
(.addLast "http-client"
371398
(HttpClientCodec.
@@ -374,6 +401,7 @@
374401
max-chunk-size
375402
false
376403
false))
404+
(.addLast "streamer" ^ChannelHandler (ChunkedWriteHandler.))
377405
(.addLast "handler" ^ChannelHandler handler)
378406
(http/attach-idle-handlers idle-timeout))
379407
(when (some? proxy-options)
@@ -388,6 +416,8 @@
388416
"pending-proxy-connection"
389417
^ChannelHandler
390418
(pending-proxy-connection-handler response-stream)))))
419+
(when (some? logger)
420+
(.addFirst pipeline "activity-logger" logger))
391421
(pipeline-transform pipeline))))
392422

393423
(defn close-connection [f]
@@ -448,20 +478,43 @@
448478
(assoc req :uri (:request-url req))
449479
req))]
450480
(when-not (.get (.headers req') "Host")
451-
(HttpHeaders/setHost req' (str host (when explicit-port? (str ":" port)))))
481+
(.set (.headers req') HttpHeaderNames/HOST (str host (when explicit-port? (str ":" port)))))
452482
(when-not (.get (.headers req') "Connection")
453-
(HttpHeaders/setKeepAlive req' keep-alive?))
483+
(HttpUtil/setKeepAlive req' keep-alive?))
454484
(when (and (non-tunnel-proxy? proxy-options')
455485
(get proxy-options :keep-alive? true)
456486
(not (.get (.headers req') "Proxy-Connection")))
457487
(.set (.headers req') "Proxy-Connection" "Keep-Alive"))
458488

459-
(let [body (if-let [parts (get req :multipart)]
460-
(let [boundary (multipart/boundary)
461-
content-type (str "multipart/form-data; boundary=" boundary)]
462-
(HttpHeaders/setHeader req' "Content-Type" content-type)
463-
(multipart/encode-body boundary parts))
464-
(get req :body))]
489+
(let [body (:body req)
490+
parts (:multipart req)
491+
multipart? (some? parts)
492+
[req' body] (cond
493+
;; RFC #7231 4.3.8. TRACE
494+
;; A client MUST NOT send a message body...
495+
(= :trace (:request-method req))
496+
(do
497+
(when (or (some? body) multipart?)
498+
(log/warn "TRACE request body was omitted"))
499+
[req' nil])
500+
501+
(not multipart?)
502+
[req' body]
503+
504+
:else
505+
(multipart/encode-request req' parts))]
506+
507+
(when-let [save-message (get req :aleph/save-request-message)]
508+
;; debug purpose only
509+
;; note, that req' is effectively mutable, so
510+
;; it will "capture" all changes made during "send-message"
511+
;; execution
512+
(reset! save-message req'))
513+
514+
(when-let [save-body (get req :aleph/save-request-body)]
515+
;; might be different in case we use :multipart
516+
(reset! save-body body))
517+
465518
(netty/safe-execute ch
466519
(http/send-message ch true ssl? req' body))))
467520

@@ -584,14 +637,14 @@
584637
#(when (.isOpen ch)
585638
(d/chain'
586639
(netty/wrap-future (.close handshaker ch (CloseWebSocketFrame.)))
587-
(fn [_] (netty/close ch))))))))
640+
(fn [_] (netty/close ctx))))))))
588641

589642
(instance? FullHttpResponse msg)
590643
(let [rsp ^FullHttpResponse msg]
591644
(throw
592645
(IllegalStateException.
593646
(str "unexpected HTTP response, status: "
594-
(.getStatus rsp)
647+
(.status rsp)
595648
", body: '"
596649
(bs/to-string (.content rsp))
597650
"'"))))

src/aleph/http/client_middleware.clj

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
(:refer-clojure :exclude [update])
55
(:require
66
[potemkin :as p]
7-
[clojure.stacktrace :refer [root-cause]]
87
[clojure.string :as str]
98
[clojure.walk :refer [prewalk]]
109
[manifold.deferred :as d]
@@ -786,7 +785,7 @@
786785
(write-cookie-header cookies cookie-spec req)))
787786

788787
(defn wrap-cookies
789-
"Middleware that set 'Cookie' header based on the contentn of cookies passed
788+
"Middleware that set 'Cookie' header based on the content of cookies passed
790789
with the request or from cookies storage (when provided). Source for 'Cookie'
791790
header content by priorities:
792791
@@ -906,6 +905,34 @@
906905
(defmethod coerce-response-body :default [_ resp]
907906
resp)
908907

908+
(defn wrap-request-debug [req]
909+
(cond-> req
910+
(opt req :save-request)
911+
(assoc :aleph/save-request-message (atom nil))
912+
913+
(opt req :debug-body)
914+
(assoc :aleph/save-request-body (atom nil))))
915+
916+
(defn handle-response-debug [req rsp]
917+
(let [saved-message (get req :aleph/save-request-message)
918+
saved-body (get req :aleph/save-request-body)
919+
req' (dissoc req
920+
:aleph/save-request-body
921+
:aleph/save-request-message
922+
:save-request
923+
:save-request?
924+
:debug-body
925+
:debug-body?)]
926+
(cond-> rsp
927+
(some? saved-message)
928+
(assoc :aleph/netty-request @saved-message)
929+
930+
(some? saved-body)
931+
(assoc :aleph/request-body @saved-body)
932+
933+
(opt req :save-request)
934+
(assoc :aleph/request req'))))
935+
909936
(def default-middleware
910937
[wrap-method
911938
wrap-url
@@ -918,7 +945,8 @@
918945
wrap-accept
919946
wrap-accept-encoding
920947
wrap-content-type
921-
wrap-cookies])
948+
wrap-cookies
949+
wrap-request-debug])
922950

923951
(defn wrap-request
924952
"Returns a batteries-included HTTP request function corresponding to the given
@@ -938,7 +966,8 @@
938966

939967
;; coerce the response body
940968
(fn [{:keys [body] :as rsp}]
941-
(if body
942-
(d/future-with (or executor (ex/wait-pool))
943-
(coerce-response-body req' rsp))
944-
rsp)))))))))
969+
(let [rsp' (handle-response-debug req' rsp)]
970+
(if (nil? body)
971+
rsp'
972+
(d/future-with (or executor (ex/wait-pool))
973+
(coerce-response-body req' rsp'))))))))))))

0 commit comments

Comments
 (0)