Skip to content

Add didVisitURL delegate method #816

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion Sources/AsyncHTTPClient/HTTPHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,16 @@ public protocol HTTPClientResponseDelegate: AnyObject {
/// - task: Current request context.
func didSendRequest(task: HTTPClient.Task<Response>)

/// Called when response head is received. Will be called once.
/// Called each time a response head is received (including redirects), and always called before ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``.
/// You can use this method to keep an entire history of the request/response chain.
///
/// - parameters:
/// - task: Current request context.
/// - request: The request that was sent.
/// - head: Received response head.
func didVisitURL(task: HTTPClient.Task<Response>, _ request: HTTPClient.Request, _ head: HTTPResponseHead)

/// Called when the final response head is received (after redirects).
/// You must return an `EventLoopFuture<Void>` that you complete when you have finished processing the body part.
/// You can create an already succeeded future by calling `task.eventLoop.makeSucceededFuture(())`.
///
Expand Down Expand Up @@ -734,6 +743,11 @@ extension HTTPClientResponseDelegate {
/// By default, this does nothing.
public func didSendRequest(task: HTTPClient.Task<Response>) {}

/// Default implementation of ``HTTPClientResponseDelegate/didVisitURL(task:_:_:)-2el9y``.
///
/// By default, this does nothing.
public func didVisitURL(task: HTTPClient.Task<Response>, _: HTTPClient.Request, _: HTTPResponseHead) {}

/// Default implementation of ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``.
///
/// By default, this does nothing.
Expand Down
2 changes: 2 additions & 0 deletions Sources/AsyncHTTPClient/RequestBag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ final class RequestBag<Delegate: HTTPClientResponseDelegate> {
private func receiveResponseHead0(_ head: HTTPResponseHead) {
self.task.eventLoop.assertInEventLoop()

self.delegate.didVisitURL(task: self.task, self.request, head)

// runs most likely on channel eventLoop
switch self.state.receiveResponseHead(head) {
case .none:
Expand Down
52 changes: 34 additions & 18 deletions Tests/AsyncHTTPClientTests/RequestBagTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ final class RequestBagTests: XCTestCase {
XCTAssertNoThrow(try executor.receiveEndOfStream())
XCTAssertEqual(receivedBytes, bytesToSent, "We have sent all request bytes...")

XCTAssertTrue(delegate.history.isEmpty)
XCTAssertNil(delegate.receivedHead, "Expected not to have a response head, before `receiveResponseHead`")
let responseHead = HTTPResponseHead(
version: .http1_1,
Expand All @@ -140,6 +141,10 @@ final class RequestBagTests: XCTestCase {
XCTAssertEqual(responseHead, delegate.receivedHead)
XCTAssertNoThrow(try XCTUnwrap(delegate.backpressurePromise).succeed(()))
XCTAssertTrue(executor.signalledDemandForResponseBody)

XCTAssertEqual(delegate.history.map(\.request.url), [request.url])
XCTAssertEqual(delegate.history.map(\.response), [responseHead])

executor.resetResponseStreamDemandSignal()

// we will receive 20 chunks with each 10 byteBuffers and 32 bytes
Expand Down Expand Up @@ -747,13 +752,15 @@ final class RequestBagTests: XCTestCase {
let executor = MockRequestExecutor(eventLoop: embeddedEventLoop)
executor.runRequest(bag)
XCTAssertFalse(executor.signalledDemandForResponseBody)
bag.receiveResponseHead(
.init(
version: .http1_1,
status: .permanentRedirect,
headers: ["content-length": "\(3 * 1024)", "location": "https://swift.org/sswg"]
)
XCTAssertTrue(delegate.history.isEmpty)
let responseHead = HTTPResponseHead(
version: .http1_1,
status: .permanentRedirect,
headers: ["content-length": "\(3 * 1024)", "location": "https://swift.org/sswg"]
)
bag.receiveResponseHead(responseHead)
XCTAssertEqual(delegate.history.map(\.request.url), [request.url])
XCTAssertEqual(delegate.history.map(\.response), [responseHead])
XCTAssertNil(delegate.backpressurePromise)
XCTAssertTrue(executor.signalledDemandForResponseBody)
executor.resetResponseStreamDemandSignal()
Expand Down Expand Up @@ -833,13 +840,15 @@ final class RequestBagTests: XCTestCase {
let executor = MockRequestExecutor(eventLoop: embeddedEventLoop)
executor.runRequest(bag)
XCTAssertFalse(executor.signalledDemandForResponseBody)
bag.receiveResponseHead(
.init(
version: .http1_1,
status: .permanentRedirect,
headers: ["content-length": "\(4 * 1024)", "location": "https://swift.org/sswg"]
)
XCTAssertTrue(delegate.history.isEmpty)
let responseHead = HTTPResponseHead(
version: .http1_1,
status: .permanentRedirect,
headers: ["content-length": "\(4 * 1024)", "location": "https://swift.org/sswg"]
)
bag.receiveResponseHead(responseHead)
XCTAssertEqual(delegate.history.map(\.request.url), [request.url])
XCTAssertEqual(delegate.history.map(\.response), [responseHead])
XCTAssertNil(delegate.backpressurePromise)
XCTAssertFalse(executor.signalledDemandForResponseBody)
XCTAssertTrue(executor.isCancelled)
Expand Down Expand Up @@ -893,13 +902,15 @@ final class RequestBagTests: XCTestCase {
let executor = MockRequestExecutor(eventLoop: embeddedEventLoop)
executor.runRequest(bag)
XCTAssertFalse(executor.signalledDemandForResponseBody)
bag.receiveResponseHead(
.init(
version: .http1_1,
status: .permanentRedirect,
headers: ["content-length": "\(3 * 1024)", "location": "https://swift.org/sswg"]
)
XCTAssertTrue(delegate.history.isEmpty)
let responseHead = HTTPResponseHead(
version: .http1_1,
status: .permanentRedirect,
headers: ["content-length": "\(3 * 1024)", "location": "https://swift.org/sswg"]
)
bag.receiveResponseHead(responseHead)
XCTAssertEqual(delegate.history.map(\.request.url), [request.url])
XCTAssertEqual(delegate.history.map(\.response), [responseHead])
XCTAssertNil(delegate.backpressurePromise)
XCTAssertTrue(executor.signalledDemandForResponseBody)
executor.resetResponseStreamDemandSignal()
Expand Down Expand Up @@ -1001,6 +1012,7 @@ class UploadCountingDelegate: HTTPClientResponseDelegate {
private(set) var hitDidReceiveBodyPart = 0
private(set) var hitDidReceiveError = 0

private(set) var history: [(request: HTTPClient.Request, response: HTTPResponseHead)] = []
private(set) var receivedHead: HTTPResponseHead?
private(set) var lastBodyPart: ByteBuffer?
private(set) var backpressurePromise: EventLoopPromise<Void>?
Expand All @@ -1022,6 +1034,10 @@ class UploadCountingDelegate: HTTPClientResponseDelegate {
self.hitDidSendRequest += 1
}

func didVisitURL(task: HTTPClient.Task<Void>, _ request: HTTPClient.Request, _ head: HTTPResponseHead) {
self.history.append((request, head))
}

func didReceiveHead(task: HTTPClient.Task<Void>, _ head: HTTPResponseHead) -> EventLoopFuture<Void> {
self.receivedHead = head
return self.createBackpressurePromise()
Expand Down