diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1.1/HTTP1ConnectionStateMachine.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTP1.1/HTTP1ConnectionStateMachine.swift index 88107bda0..cebc8f1c1 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1.1/HTTP1ConnectionStateMachine.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/HTTP1.1/HTTP1ConnectionStateMachine.swift @@ -256,7 +256,7 @@ struct HTTP1ConnectionStateMachine { let action = requestStateMachine.channelRead(part) if case .head(let head) = part, close == false { - close = head.headers[canonicalForm: "connection"].contains(where: { $0.lowercased() == "close" }) + close = !head.isKeepAlive } state = .inRequest(requestStateMachine, close: close) return state.modify(with: action) diff --git a/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests+XCTest.swift b/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests+XCTest.swift index 9abc3741a..17e45387a 100644 --- a/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests+XCTest.swift +++ b/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests+XCTest.swift @@ -28,6 +28,8 @@ extension HTTP1ConnectionStateMachineTests { ("testPOSTRequestWithWriteAndReadBackpressure", testPOSTRequestWithWriteAndReadBackpressure), ("testResponseReadingWithBackpressure", testResponseReadingWithBackpressure), ("testAConnectionCloseHeaderInTheRequestLeadsToConnectionCloseAfterRequest", testAConnectionCloseHeaderInTheRequestLeadsToConnectionCloseAfterRequest), + ("testAHTTP1_0ResponseWithoutKeepAliveHeaderLeadsToConnectionCloseAfterRequest", testAHTTP1_0ResponseWithoutKeepAliveHeaderLeadsToConnectionCloseAfterRequest), + ("testAHTTP1_0ResponseWithKeepAliveHeaderLeadsToConnectionBeingKeptAlive", testAHTTP1_0ResponseWithKeepAliveHeaderLeadsToConnectionBeingKeptAlive), ("testAConnectionCloseHeaderInTheResponseLeadsToConnectionCloseAfterRequest", testAConnectionCloseHeaderInTheResponseLeadsToConnectionCloseAfterRequest), ("testNIOTriggersChannelActiveTwice", testNIOTriggersChannelActiveTwice), ("testIdleConnectionBecomesInactive", testIdleConnectionBecomesInactive), diff --git a/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift b/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift index 3ca42249b..11fac1d04 100644 --- a/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift @@ -97,6 +97,37 @@ class HTTP1ConnectionStateMachineTests: XCTestCase { let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, .init([responseBody]))) + XCTAssertEqual(state.channelInactive(), .fireChannelInactive) + } + + func testAHTTP1_0ResponseWithoutKeepAliveHeaderLeadsToConnectionCloseAfterRequest() { + var state = HTTP1ConnectionStateMachine() + XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) + let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") + let metadata = RequestFramingMetadata(connectionClose: false, body: .none) + XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata), .sendRequestHead(requestHead, startBody: false)) + + let responseHead = HTTPResponseHead(version: .http1_0, status: .ok, headers: ["content-length": "4"]) + XCTAssertEqual(state.channelRead(.head(responseHead)), .forwardResponseHead(responseHead, pauseRequestBodyStream: false)) + let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) + XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) + XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, .init([responseBody]))) + XCTAssertEqual(state.channelInactive(), .fireChannelInactive) + } + + func testAHTTP1_0ResponseWithKeepAliveHeaderLeadsToConnectionBeingKeptAlive() { + var state = HTTP1ConnectionStateMachine() + XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) + let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") + let metadata = RequestFramingMetadata(connectionClose: false, body: .none) + XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata), .sendRequestHead(requestHead, startBody: false)) + + let responseHead = HTTPResponseHead(version: .http1_0, status: .ok, headers: ["content-length": "4", "connection": "keep-alive"]) + XCTAssertEqual(state.channelRead(.head(responseHead)), .forwardResponseHead(responseHead, pauseRequestBodyStream: false)) + let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) + XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) + XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.informConnectionIsIdle, .init([responseBody]))) + XCTAssertEqual(state.channelInactive(), .fireChannelInactive) } func testAConnectionCloseHeaderInTheResponseLeadsToConnectionCloseAfterRequest() {