async-http-client icon indicating copy to clipboard operation
async-http-client copied to clipboard

Delegate is not called after redirect.

Open scottandrew opened this issue 2 years ago • 2 comments

I am trying to read the header of a request that gets redirected. However it seems that the callback is is never called after the redirect. The new request has no reference to the delegate that was passed.

scottandrew avatar Jan 06 '22 00:01 scottandrew

cc @fabianfett

Lukasa avatar Jan 06 '22 10:01 Lukasa

Hi @scottandrew, sorry for taking such a long time to come back to you. I just tried to verify your described behavior and everything worked as expected for me. Can you maybe post the your code, which causes the issue, here, so that I can have a look... Thanks.

Testcode:

    func testDelegateMethodsAreInvokedAfterRedirect() {
        let web = NIOHTTP1TestServer(group: self.serverGroup)
        defer { XCTAssertNoThrow(try web.stop()) }
        let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
        defer { XCTAssertNoThrow(try httpClient.syncShutdown()) }

        class TestDelegate: HTTPClientResponseDelegate {
            typealias Response = ByteBuffer

            var headFuture: EventLoopFuture<HTTPResponseHead> {
                self.headPromise.futureResult
            }

            var bodyFuture: EventLoopFuture<ByteBuffer> {
                self.bodyPromise.futureResult
            }

            private var headPromise: EventLoopPromise<HTTPResponseHead>
            private var bodyPromise: EventLoopPromise<ByteBuffer>
            private var body: ByteBuffer?

            init(eventLoop: EventLoop) {
                self.headPromise = eventLoop.makePromise(of: HTTPResponseHead.self)
                self.bodyPromise = eventLoop.makePromise(of: ByteBuffer.self)
            }

            func didReceiveHead(task: HTTPClient.Task<Int>, _ head: HTTPResponseHead) -> EventLoopFuture<Void> {
                self.headPromise.succeed(head)
                return task.eventLoop.makeSucceededVoidFuture()
            }

            func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void> {
                if self.body == nil {
                    self.body = buffer
                } else {
                    var buffer = buffer
                    self.body!.writeBuffer(&buffer)
                }
                return task.eventLoop.makeSucceededFuture(())
            }

            func didFinishRequest(task: HTTPClient.Task<Response>) throws -> ByteBuffer {
                return self.body ?? ByteBuffer()
            }

            func didReceiveError(task: HTTPClient.Task<ByteBuffer>, _ error: Error) {
                self.headPromise.fail(error)
                self.bodyPromise.fail(error)
            }
        }

        let delegate = TestDelegate(eventLoop: httpClient.eventLoopGroup.any())
        var maybeRequest: HTTPClient.Request?
        XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost:\(web.serverPort)/foo"))
        guard let request = maybeRequest else { return XCTFail("Expected to have a request here.") }

        let responseFuture = httpClient.execute(request: request, delegate: delegate)

        XCTAssertNoThrow(try web.receiveHeadAndVerify { received in
            let expected = HTTPRequestHead(
                version: .http1_1,
                method: .GET,
                uri: "/foo",
                headers: ["Host": "localhost:\(web.serverPort)"]
            )
            XCTAssertEqual(expected, received)
        })
        XCTAssertNoThrow(try web.receiveEnd())
        let forwardHTTPResponseHead = HTTPResponseHead(
            version: .http1_1,
            status: .movedPermanently,
            headers: ["Location": "http://localhost:\(web.serverPort)/bar"]
        )
        XCTAssertNoThrow(try web.writeOutbound(.head(forwardHTTPResponseHead)))
        XCTAssertNoThrow(try web.writeOutbound(.end(nil)))

        XCTAssertNoThrow(try web.receiveHeadAndVerify { received in
            let expected = HTTPRequestHead(
                version: .http1_1,
                method: .GET,
                uri: "/bar",
                headers: ["Host": "localhost:\(web.serverPort)"]
            )
            XCTAssertEqual(expected, received)
        })
        XCTAssertNoThrow(try web.receiveEnd())

        let actualResponseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["content-length": "7"])
        let actualResponseBody = ByteBuffer(string: "foo bar")
        XCTAssertNoThrow(try web.writeOutbound(.head(actualResponseHead)))
        XCTAssertNoThrow(try web.writeOutbound(.body(.byteBuffer(actualResponseBody))))
        XCTAssertNoThrow(try web.writeOutbound(.end(nil)))

        XCTAssertEqual(try delegate.headFuture.wait(), actualResponseHead)
        XCTAssertEqual(try delegate.bodyFuture.wait(), actualResponseBody)

        XCTAssertNoThrow(try responseFuture.wait())
    }

fabianfett avatar Jan 24 '22 13:01 fabianfett