|
20 | 20 | ConnectionTimeoutException
|
21 | 21 | PoolTimeoutException
|
22 | 22 | ReadTimeoutException
|
| 23 | + RequestCancellationException |
23 | 24 | RequestTimeoutException)
|
24 | 25 | (io.aleph.dirigiste Pools)
|
25 | 26 | (io.netty.handler.codec Headers)
|
|
336 | 337 | by [clj-http](https://github.com/dakrone/clj-http), and returns a deferred representing
|
337 | 338 | the HTTP response. Also allows for a custom `pool` or `middleware` to be defined.
|
338 | 339 |
|
| 340 | + Putting the returned deferred into an error state will cancel the underlying request if it is |
| 341 | + still in flight. |
| 342 | +
|
339 | 343 | Param key | Description
|
340 | 344 | -------------------- | -----------------------------------------------------------------------------------------------------------------------------------------------------------------
|
341 | 345 | `connection-timeout` | timeout in milliseconds for the connection to become established
|
|
358 | 362 | middleware identity
|
359 | 363 | connection-timeout 6e4} ;; 60 seconds
|
360 | 364 | :as req}]
|
361 |
| - |
362 |
| - (executor/with-executor response-executor |
363 |
| - ((middleware |
364 |
| - (fn [req] |
365 |
| - (let [k (client/req->domain req) |
366 |
| - start (System/currentTimeMillis)] |
367 |
| - |
368 |
| - ;; acquire a connection |
369 |
| - (-> (flow/acquire pool k) |
370 |
| - (maybe-timeout! pool-timeout) |
371 |
| - |
372 |
| - ;; pool timeout triggered |
373 |
| - (d/catch' TimeoutException |
374 |
| - (fn [^Throwable e] |
375 |
| - (d/error-deferred (PoolTimeoutException. e)))) |
376 |
| - |
377 |
| - (d/chain' |
378 |
| - (fn [conn] |
379 |
| - |
380 |
| - ;; get the wrapper for the connection, which may or may not be realized yet |
381 |
| - (-> (first conn) |
382 |
| - (maybe-timeout! connection-timeout) |
383 |
| - |
384 |
| - ;; connection timeout triggered, dispose of the connetion |
385 |
| - (d/catch' TimeoutException |
386 |
| - (fn [^Throwable e] |
387 |
| - (log/error e "Timed out waiting for connection to be established") |
388 |
| - (flow/dispose pool k conn) |
389 |
| - (d/error-deferred (ConnectionTimeoutException. e)))) |
390 |
| - |
391 |
| - ;; connection failed, bail out |
392 |
| - (d/catch' |
393 |
| - (fn [e] |
394 |
| - (log/error e "Connection failure") |
395 |
| - (flow/dispose pool k conn) |
396 |
| - (d/error-deferred e))) |
397 |
| - |
398 |
| - ;; actually make the request now |
399 |
| - (d/chain' |
400 |
| - (fn [conn'] |
401 |
| - (when-not (nil? conn') |
402 |
| - (let [end (System/currentTimeMillis)] |
403 |
| - (-> (conn' req) |
404 |
| - (maybe-timeout! request-timeout) |
405 |
| - |
406 |
| - ;; request timeout triggered, dispose of the connection |
407 |
| - (d/catch' TimeoutException |
408 |
| - (fn [^Throwable e] |
409 |
| - (flow/dispose pool k conn) |
410 |
| - (d/error-deferred (RequestTimeoutException. e)))) |
411 |
| - |
412 |
| - ;; request failed, dispose of the connection |
413 |
| - (d/catch' |
414 |
| - (fn [e] |
415 |
| - (log/trace "Request failed. Disposing of connection...") |
416 |
| - (flow/dispose pool k conn) |
417 |
| - (d/error-deferred e))) |
418 |
| - |
419 |
| - ;; clean up the connection |
420 |
| - (d/chain' |
421 |
| - (fn cleanup-conn [rsp] |
422 |
| - |
423 |
| - ;; either destroy/dispose of the conn, or release it back for reuse |
424 |
| - (-> (:aleph/destroy-conn? rsp) |
425 |
| - (maybe-timeout! read-timeout) |
426 |
| - |
427 |
| - (d/catch' TimeoutException |
428 |
| - (fn [^Throwable e] |
429 |
| - (log/trace "Request timed out. Disposing of connection...") |
430 |
| - (flow/dispose pool k conn) |
431 |
| - (d/error-deferred (ReadTimeoutException. e)))) |
432 |
| - |
433 |
| - (d/chain' |
434 |
| - (fn [early?] |
435 |
| - (if (or early? |
436 |
| - (not (:aleph/keep-alive? rsp)) |
437 |
| - (<= 400 (:status rsp))) |
438 |
| - (do |
439 |
| - (log/trace "Connection finished. Disposing...") |
440 |
| - (flow/dispose pool k conn)) |
441 |
| - (flow/release pool k conn))))) |
442 |
| - (-> rsp |
443 |
| - (dissoc :aleph/destroy-conn?) |
444 |
| - (assoc :connection-time (- end start))))))))) |
445 |
| - |
446 |
| - (fn handle-response [rsp] |
447 |
| - (->> rsp |
448 |
| - (middleware/handle-cookies req) |
449 |
| - (middleware/handle-redirects request req))))))))))) |
450 |
| - req)))) |
| 365 | + (let [dispose-conn! (atom (fn [])) |
| 366 | + result (d/deferred response-executor) |
| 367 | + response (executor/with-executor response-executor |
| 368 | + ((middleware |
| 369 | + (fn [req] |
| 370 | + (let [k (client/req->domain req) |
| 371 | + start (System/currentTimeMillis)] |
| 372 | + |
| 373 | + ;; acquire a connection |
| 374 | + (-> (flow/acquire pool k) |
| 375 | + (maybe-timeout! pool-timeout) |
| 376 | + |
| 377 | + ;; pool timeout triggered |
| 378 | + (d/catch' TimeoutException |
| 379 | + (fn [^Throwable e] |
| 380 | + (d/error-deferred (PoolTimeoutException. e)))) |
| 381 | + |
| 382 | + (d/chain' |
| 383 | + (fn [conn] |
| 384 | + ;; NOTE: All error handlers below delegate disposal of the |
| 385 | + ;; connection to the error handler on `result` which uses this |
| 386 | + ;; function. |
| 387 | + (reset! dispose-conn! (fn [] (flow/dispose pool k conn))) |
| 388 | + |
| 389 | + (if (realized? result) |
| 390 | + ;; to account for race condition between setting `dispose-conn!` |
| 391 | + ;; and putting `result` into error state for cancellation |
| 392 | + (@dispose-conn!) |
| 393 | + ;; get the wrapper for the connection, which may or may not be realized yet |
| 394 | + (-> (first conn) |
| 395 | + (maybe-timeout! connection-timeout) |
| 396 | + |
| 397 | + ;; connection timeout triggered |
| 398 | + (d/catch' TimeoutException |
| 399 | + (fn [^Throwable e] |
| 400 | + (log/error e "Timed out waiting for connection to be established") |
| 401 | + (d/error-deferred (ConnectionTimeoutException. e)))) |
| 402 | + |
| 403 | + ;; connection failed, bail out |
| 404 | + (d/catch' |
| 405 | + (fn [e] |
| 406 | + (log/error e "Connection failure") |
| 407 | + (d/error-deferred e))) |
| 408 | + |
| 409 | + ;; actually make the request now |
| 410 | + (d/chain' |
| 411 | + (fn [conn'] |
| 412 | + (when-not (nil? conn') |
| 413 | + (let [end (System/currentTimeMillis)] |
| 414 | + (-> (conn' req) |
| 415 | + (maybe-timeout! request-timeout) |
| 416 | + |
| 417 | + ;; request timeout triggered |
| 418 | + (d/catch' TimeoutException |
| 419 | + (fn [^Throwable e] |
| 420 | + (d/error-deferred (RequestTimeoutException. e)))) |
| 421 | + |
| 422 | + ;; clean up the connection |
| 423 | + (d/chain' |
| 424 | + (fn cleanup-conn [rsp] |
| 425 | + |
| 426 | + ;; either destroy/dispose of the conn, or release it back for reuse |
| 427 | + (-> (:aleph/destroy-conn? rsp) |
| 428 | + (maybe-timeout! read-timeout) |
| 429 | + |
| 430 | + ;; read timeout triggered |
| 431 | + (d/catch' TimeoutException |
| 432 | + (fn [^Throwable e] |
| 433 | + (log/trace "Request timed out.") |
| 434 | + (d/error-deferred (ReadTimeoutException. e)))) |
| 435 | + |
| 436 | + (d/chain' |
| 437 | + (fn [early?] |
| 438 | + (if (or early? |
| 439 | + (not (:aleph/keep-alive? rsp)) |
| 440 | + (<= 400 (:status rsp))) |
| 441 | + (do |
| 442 | + (log/trace "Connection finished. Disposing...") |
| 443 | + (flow/dispose pool k conn)) |
| 444 | + (flow/release pool k conn))))) |
| 445 | + (-> rsp |
| 446 | + (dissoc :aleph/destroy-conn?) |
| 447 | + (assoc :connection-time (- end start))))))))) |
| 448 | + |
| 449 | + (fn handle-response [rsp] |
| 450 | + (->> rsp |
| 451 | + (middleware/handle-cookies req) |
| 452 | + (middleware/handle-redirects request req)))))))))))) |
| 453 | + req))] |
| 454 | + (d/connect response result) |
| 455 | + (d/catch' result |
| 456 | + (fn [e] |
| 457 | + (log/trace e "Request failed. Disposing of connection...") |
| 458 | + (@dispose-conn!) |
| 459 | + (d/error-deferred e))) |
| 460 | + result))) |
| 461 | + |
| 462 | +(defn cancel-request! |
| 463 | + "Accepts a response deferred as returned by `request` and cancels the underlying request if it is |
| 464 | + still in flight. |
| 465 | +
|
| 466 | + This is done by putting the deferred into error state with an |
| 467 | + `aleph.utils.RequestCancellationException` instance as its value." |
| 468 | + [r] |
| 469 | + (d/error! r (RequestCancellationException. "Request cancelled"))) |
451 | 470 |
|
452 | 471 | (defn- req
|
453 | 472 | ([method url]
|
|
0 commit comments