13
13
//===----------------------------------------------------------------------===//
14
14
15
15
import Algorithms
16
- import Foundation
17
16
import Logging
18
17
import NIOConcurrencyHelpers
19
18
import NIOCore
20
19
import NIOHTTP1
21
20
import NIOPosix
22
21
import NIOSSL
23
22
23
+ #if compiler(>=6.0)
24
+ import Foundation
25
+ #else
26
+ @preconcurrency import Foundation
27
+ #endif
28
+
24
29
extension HTTPClient {
25
30
/// A request body.
26
- public struct Body {
31
+ public struct Body : Sendable {
27
32
/// A streaming uploader.
28
33
///
29
34
/// ``StreamWriter`` abstracts
@@ -209,7 +214,7 @@ extension HTTPClient {
209
214
}
210
215
211
216
/// Represents an HTTP request.
212
- public struct Request {
217
+ public struct Request : Sendable {
213
218
/// Request HTTP method, defaults to `GET`.
214
219
public let method : HTTPMethod
215
220
/// Remote URL.
@@ -377,6 +382,13 @@ extension HTTPClient {
377
382
public var headers : HTTPHeaders
378
383
/// Response body.
379
384
public var body : ByteBuffer ?
385
+ /// The history of all requests and responses in redirect order.
386
+ public var history : [ RequestResponse ]
387
+
388
+ /// The target URL (after redirects) of the response.
389
+ public var url : URL ? {
390
+ self . history. last? . request. url
391
+ }
380
392
381
393
/// Create HTTP `Response`.
382
394
///
@@ -392,6 +404,7 @@ extension HTTPClient {
392
404
self . version = HTTPVersion ( major: 1 , minor: 1 )
393
405
self . headers = headers
394
406
self . body = body
407
+ self . history = [ ]
395
408
}
396
409
397
410
/// Create HTTP `Response`.
@@ -414,6 +427,32 @@ extension HTTPClient {
414
427
self . version = version
415
428
self . headers = headers
416
429
self . body = body
430
+ self . history = [ ]
431
+ }
432
+
433
+ /// Create HTTP `Response`.
434
+ ///
435
+ /// - parameters:
436
+ /// - host: Remote host of the request.
437
+ /// - status: Response HTTP status.
438
+ /// - version: Response HTTP version.
439
+ /// - headers: Reponse HTTP headers.
440
+ /// - body: Response body.
441
+ /// - history: History of all requests and responses in redirect order.
442
+ public init (
443
+ host: String ,
444
+ status: HTTPResponseStatus ,
445
+ version: HTTPVersion ,
446
+ headers: HTTPHeaders ,
447
+ body: ByteBuffer ? ,
448
+ history: [ RequestResponse ]
449
+ ) {
450
+ self . host = host
451
+ self . status = status
452
+ self . version = version
453
+ self . headers = headers
454
+ self . body = body
455
+ self . history = history
417
456
}
418
457
}
419
458
@@ -457,6 +496,16 @@ extension HTTPClient {
457
496
}
458
497
}
459
498
}
499
+
500
+ public struct RequestResponse : Sendable {
501
+ public var request : Request
502
+ public var responseHead : HTTPResponseHead
503
+
504
+ public init ( request: Request , responseHead: HTTPResponseHead ) {
505
+ self . request = request
506
+ self . responseHead = responseHead
507
+ }
508
+ }
460
509
}
461
510
462
511
/// The default ``HTTPClientResponseDelegate``.
@@ -485,6 +534,7 @@ public final class ResponseAccumulator: HTTPClientResponseDelegate {
485
534
}
486
535
}
487
536
537
+ var history = [ HTTPClient . RequestResponse] ( )
488
538
var state = State . idle
489
539
let requestMethod : HTTPMethod
490
540
let requestHost : String
@@ -521,6 +571,14 @@ public final class ResponseAccumulator: HTTPClientResponseDelegate {
521
571
self . maxBodySize = maxBodySize
522
572
}
523
573
574
+ public func didVisitURL(
575
+ task: HTTPClient . Task < HTTPClient . Response > ,
576
+ _ request: HTTPClient . Request ,
577
+ _ head: HTTPResponseHead
578
+ ) {
579
+ self . history. append ( . init( request: request, responseHead: head) )
580
+ }
581
+
524
582
public func didReceiveHead( task: HTTPClient . Task < Response > , _ head: HTTPResponseHead ) -> EventLoopFuture < Void > {
525
583
switch self . state {
526
584
case . idle:
@@ -596,15 +654,17 @@ public final class ResponseAccumulator: HTTPClientResponseDelegate {
596
654
status: head. status,
597
655
version: head. version,
598
656
headers: head. headers,
599
- body: nil
657
+ body: nil ,
658
+ history: self . history
600
659
)
601
660
case . body( let head, let body) :
602
661
return Response (
603
662
host: self . requestHost,
604
663
status: head. status,
605
664
version: head. version,
606
665
headers: head. headers,
607
- body: body
666
+ body: body,
667
+ history: self . history
608
668
)
609
669
case . end:
610
670
preconditionFailure ( " request already processed " )
@@ -668,7 +728,16 @@ public protocol HTTPClientResponseDelegate: AnyObject {
668
728
/// - task: Current request context.
669
729
func didSendRequest( task: HTTPClient . Task < Response > )
670
730
671
- /// Called when response head is received. Will be called once.
731
+ /// Called each time a response head is received (including redirects), and always called before ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``.
732
+ /// You can use this method to keep an entire history of the request/response chain.
733
+ ///
734
+ /// - parameters:
735
+ /// - task: Current request context.
736
+ /// - request: The request that was sent.
737
+ /// - head: Received response head.
738
+ func didVisitURL( task: HTTPClient . Task < Response > , _ request: HTTPClient . Request , _ head: HTTPResponseHead )
739
+
740
+ /// Called when the final response head is received (after redirects).
672
741
/// You must return an `EventLoopFuture<Void>` that you complete when you have finished processing the body part.
673
742
/// You can create an already succeeded future by calling `task.eventLoop.makeSucceededFuture(())`.
674
743
///
@@ -734,6 +803,11 @@ extension HTTPClientResponseDelegate {
734
803
/// By default, this does nothing.
735
804
public func didSendRequest( task: HTTPClient . Task < Response > ) { }
736
805
806
+ /// Default implementation of ``HTTPClientResponseDelegate/didVisitURL(task:_:_:)-2el9y``.
807
+ ///
808
+ /// By default, this does nothing.
809
+ public func didVisitURL( task: HTTPClient . Task < Response > , _: HTTPClient . Request , _: HTTPResponseHead ) { }
810
+
737
811
/// Default implementation of ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``.
738
812
///
739
813
/// By default, this does nothing.
0 commit comments