diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift index bb7659a86..de09df5b8 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift @@ -63,13 +63,11 @@ extension RequestBodyLength { init(_ body: HTTPClientRequest.Body?) { switch body?.mode { case .none: - self = .fixed(length: 0) + self = .known(0) case .byteBuffer(let buffer): - self = .fixed(length: buffer.readableBytes) - case .sequence(nil, _, _), .asyncSequence(nil, _): - self = .dynamic - case .sequence(.some(let length), _, _), .asyncSequence(.some(let length), _): - self = .fixed(length: length) + self = .known(buffer.readableBytes) + case .sequence(let length, _, _), .asyncSequence(let length, _): + self = length } } } diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index 89689225d..193a1618d 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -35,15 +35,18 @@ struct HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest { struct Body { + @usableFromInline internal enum Mode { - case asyncSequence(length: Int?, (ByteBufferAllocator) async throws -> ByteBuffer?) - case sequence(length: Int?, canBeConsumedMultipleTimes: Bool, (ByteBufferAllocator) -> ByteBuffer) + case asyncSequence(length: RequestBodyLength, (ByteBufferAllocator) async throws -> ByteBuffer?) + case sequence(length: RequestBodyLength, canBeConsumedMultipleTimes: Bool, (ByteBufferAllocator) -> ByteBuffer) case byteBuffer(ByteBuffer) } - var mode: Mode + @usableFromInline + internal var mode: Mode - private init(_ mode: Mode) { + @inlinable + internal init(_ mode: Mode) { self.mode = mode } } @@ -51,16 +54,18 @@ extension HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest.Body { - static func byteBuffer(_ byteBuffer: ByteBuffer) -> Self { + static func bytes(_ byteBuffer: ByteBuffer) -> Self { self.init(.byteBuffer(byteBuffer)) } @inlinable - static func bytes( - length: Int?, + static func bytes( _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: length, canBeConsumedMultipleTimes: false) { allocator in + self.init(.sequence( + length: .known(bytes.count), + canBeConsumedMultipleTimes: true + ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath return buffer @@ -71,11 +76,14 @@ extension HTTPClientRequest.Body { } @inlinable - static func bytes( - length: Int?, - _ bytes: Bytes + static func bytes( + _ bytes: Bytes, + length: Length ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: length, canBeConsumedMultipleTimes: true) { allocator in + self.init(.sequence( + length: length.storage, + canBeConsumedMultipleTimes: false + ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath return buffer @@ -86,10 +94,14 @@ extension HTTPClientRequest.Body { } @inlinable - static func bytes( - _ bytes: Bytes + static func bytes( + _ bytes: Bytes, + length: Length ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: bytes.count, canBeConsumedMultipleTimes: true) { allocator in + self.init(.sequence( + length: length.storage, + canBeConsumedMultipleTimes: true + ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath return buffer @@ -101,11 +113,11 @@ extension HTTPClientRequest.Body { @inlinable static func stream( - length: Int?, - _ sequenceOfBytes: SequenceOfBytes + _ sequenceOfBytes: SequenceOfBytes, + length: Length ) -> Self where SequenceOfBytes.Element == ByteBuffer { var iterator = sequenceOfBytes.makeAsyncIterator() - let body = self.init(.asyncSequence(length: length) { _ -> ByteBuffer? in + let body = self.init(.asyncSequence(length: length.storage) { _ -> ByteBuffer? in try await iterator.next() }) return body @@ -113,11 +125,11 @@ extension HTTPClientRequest.Body { @inlinable static func stream( - length: Int?, - _ bytes: Bytes + _ bytes: Bytes, + length: Length ) -> Self where Bytes.Element == UInt8 { var iterator = bytes.makeAsyncIterator() - let body = self.init(.asyncSequence(length: length) { allocator -> ByteBuffer? in + let body = self.init(.asyncSequence(length: length.storage) { allocator -> ByteBuffer? in var buffer = allocator.buffer(capacity: 1024) // TODO: Magic number while buffer.writableBytes > 0, let byte = try await iterator.next() { buffer.writeInteger(byte) @@ -143,4 +155,19 @@ extension Optional where Wrapped == HTTPClientRequest.Body { } } +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +extension HTTPClientRequest.Body { + struct Length { + /// size of the request body is not known before starting the request + static let unknown: Self = .init(storage: .unknown) + /// size of the request body is fixed and exactly `count` bytes + static func known(_ count: Int) -> Self { + .init(storage: .known(count)) + } + + @usableFromInline + internal var storage: RequestBodyLength + } +} + #endif diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift index 1ecfed9e5..58f71979a 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift @@ -48,25 +48,22 @@ struct HTTPClientResponse { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientResponse.Body: AsyncSequence { - typealias Element = ByteBuffer - typealias AsyncIterator = Iterator - - struct Iterator: AsyncIteratorProtocol { - typealias Element = ByteBuffer + typealias Element = AsyncIterator.Element + struct AsyncIterator: AsyncIteratorProtocol { private let stream: IteratorStream fileprivate init(stream: IteratorStream) { self.stream = stream } - func next() async throws -> ByteBuffer? { + mutating func next() async throws -> ByteBuffer? { try await self.stream.next() } } - func makeAsyncIterator() -> Iterator { - Iterator(stream: IteratorStream(bag: self.bag)) + func makeAsyncIterator() -> AsyncIterator { + AsyncIterator(stream: IteratorStream(bag: self.bag)) } } diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift index a963677dc..38d90e057 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift @@ -12,9 +12,11 @@ // //===----------------------------------------------------------------------===// -enum RequestBodyLength: Hashable { +/// - Note: use `HTTPClientRequest.Body.Length` if you want to expose `RequestBodyLength` publicly +@usableFromInline +internal enum RequestBodyLength: Hashable { /// size of the request body is not known before starting the request - case dynamic - /// size of the request body is fixed and exactly `length` bytes - case fixed(length: Int) + case unknown + /// size of the request body is fixed and exactly `count` bytes + case known(_ count: Int) } diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 44ce2ecf5..0df0c3196 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -690,13 +690,13 @@ internal struct RedirectHandler { extension RequestBodyLength { init(_ body: HTTPClient.Body?) { guard let body = body else { - self = .fixed(length: 0) + self = .known(0) return } guard let length = body.length else { - self = .dynamic + self = .unknown return } - self = .fixed(length: length) + self = .known(length) } } diff --git a/Sources/AsyncHTTPClient/RequestValidation.swift b/Sources/AsyncHTTPClient/RequestValidation.swift index ea0370484..e23c35423 100644 --- a/Sources/AsyncHTTPClient/RequestValidation.swift +++ b/Sources/AsyncHTTPClient/RequestValidation.swift @@ -24,9 +24,9 @@ extension HTTPHeaders { if case .TRACE = method { switch bodyLength { - case .fixed(length: 0): + case .known(0): break - case .dynamic, .fixed: + case .unknown, .known: // A client MUST NOT send a message body in a TRACE request. // https://tools.ietf.org/html/rfc7230#section-4.3.8 throw HTTPClientError.traceRequestWithBody @@ -37,9 +37,9 @@ extension HTTPHeaders { let connectionClose = self[canonicalForm: "connection"].lazy.map { $0.lowercased() }.contains("close") switch bodyLength { - case .dynamic: + case .unknown: return .init(connectionClose: connectionClose, body: .stream) - case .fixed(let length): + case .known(let length): return .init(connectionClose: connectionClose, body: .fixedSize(length)) } } @@ -88,7 +88,7 @@ extension HTTPHeaders { self.remove(name: "Transfer-Encoding") switch bodyLength { - case .fixed(0): + case .known(0): // if we don't have a body we might not need to send the Content-Length field // https://tools.ietf.org/html/rfc7230#section-3.3.2 switch method { @@ -103,9 +103,9 @@ extension HTTPHeaders { // for an enclosed payload body. self.add(name: "Content-Length", value: "0") } - case .fixed(let length): + case .known(let length): self.add(name: "Content-Length", value: String(length)) - case .dynamic: + case .unknown: self.add(name: "Transfer-Encoding", value: "chunked") } } diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift index 906bdaffa..08a39ca0e 100644 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift @@ -90,7 +90,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) var request = HTTPClientRequest(url: "https://localhost:\(bin.port)/") request.method = .POST - request.body = .byteBuffer(ByteBuffer(string: "1234")) + request.body = .bytes(ByteBuffer(string: "1234")) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -115,7 +115,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) var request = HTTPClientRequest(url: "https://localhost:\(bin.port)/") request.method = .POST - request.body = .bytes(length: nil, AnySequence("1234".utf8)) + request.body = .bytes(AnySequence("1234".utf8), length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -140,7 +140,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) var request = HTTPClientRequest(url: "https://localhost:\(bin.port)/") request.method = .POST - request.body = .bytes(length: nil, AnyCollection("1234".utf8)) + request.body = .bytes(AnyCollection("1234".utf8), length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -190,11 +190,11 @@ final class AsyncAwaitEndToEndTests: XCTestCase { let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) var request = HTTPClientRequest(url: "https://localhost:\(bin.port)/") request.method = .POST - request.body = .stream(length: nil, [ + request.body = .stream([ ByteBuffer(string: "1"), ByteBuffer(string: "2"), ByteBuffer(string: "34"), - ].asAsyncSequence()) + ].asAsyncSequence(), length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -219,7 +219,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) var request = HTTPClientRequest(url: "https://localhost:\(bin.port)/") request.method = .POST - request.body = .stream(length: nil, "1234".utf8.asAsyncSequence()) + request.body = .stream("1234".utf8.asAsyncSequence(), length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -245,7 +245,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { var request = HTTPClientRequest(url: "https://localhost:\(bin.port)/") request.method = .POST let streamWriter = AsyncSequenceWriter() - request.body = .stream(length: nil, streamWriter) + request.body = .stream(streamWriter, length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -257,7 +257,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { ByteBuffer(string: "2"), ByteBuffer(string: "34"), ] - let bodyIterator = response.body.makeAsyncIterator() + var bodyIterator = response.body.makeAsyncIterator() for expectedFragment in fragments { streamWriter.write(expectedFragment) guard let actualFragment = await XCTAssertNoThrowWithResult( @@ -287,7 +287,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { var request = HTTPClientRequest(url: "https://localhost:\(bin.port)/") request.method = .POST let streamWriter = AsyncSequenceWriter() - request.body = .stream(length: nil, streamWriter) + request.body = .stream(streamWriter, length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -300,7 +300,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { ByteBuffer(string: String(repeating: "c", count: 4000)), ByteBuffer(string: String(repeating: "d", count: 4000)), ] - let bodyIterator = response.body.makeAsyncIterator() + var bodyIterator = response.body.makeAsyncIterator() for expectedFragment in fragments { streamWriter.write(expectedFragment) guard let actualFragment = await XCTAssertNoThrowWithResult( @@ -330,7 +330,7 @@ final class AsyncAwaitEndToEndTests: XCTestCase { var request = HTTPClientRequest(url: "http://localhost:\(bin.port)/offline") request.method = .POST let streamWriter = AsyncSequenceWriter() - request.body = .stream(length: nil, streamWriter) + request.body = .stream(streamWriter, length: .unknown) let task = Task { [request] in try await client.execute(request, deadline: .now() + .seconds(2), logger: logger) @@ -357,8 +357,12 @@ final class AsyncAwaitEndToEndTests: XCTestCase { let task = Task { [request] in try await client.execute(request, deadline: .now() + .milliseconds(100), logger: logger) } - await XCTAssertThrowsError(try await task.value) { - XCTAssertEqual($0 as? HTTPClientError, HTTPClientError.deadlineExceeded) + await XCTAssertThrowsError(try await task.value) { error in + guard let error = error as? HTTPClientError else { + return XCTFail("unexpected error \(error)") + } + // a race between deadline and connect timer can result in either error + XCTAssertTrue([.deadlineExceeded, .connectTimeout].contains(error)) } } #endif @@ -378,8 +382,12 @@ final class AsyncAwaitEndToEndTests: XCTestCase { let task = Task { [request] in try await client.execute(request, deadline: .now(), logger: logger) } - await XCTAssertThrowsError(try await task.value) { - XCTAssertEqual($0 as? HTTPClientError, HTTPClientError.deadlineExceeded) + await XCTAssertThrowsError(try await task.value) { error in + guard let error = error as? HTTPClientError else { + return XCTFail("unexpected error \(error)") + } + // a race between deadline and connect timer can result in either error + XCTAssertTrue([.deadlineExceeded, .connectTimeout].contains(error)) } } #endif diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index f47b0ef93..1ebe7e939 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -225,7 +225,7 @@ class HTTPClientRequestTests: XCTestCase { XCTAsyncTest { var request = Request(url: "http://example.com/post") request.method = .POST - request.body = .byteBuffer(ByteBuffer()) + request.body = .bytes(ByteBuffer()) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } @@ -261,7 +261,7 @@ class HTTPClientRequestTests: XCTestCase { XCTAsyncTest { var request = Request(url: "http://example.com/post") request.method = .POST - request.body = .byteBuffer(.init(string: "post body")) + request.body = .bytes(.init(string: "post body")) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } @@ -297,7 +297,7 @@ class HTTPClientRequestTests: XCTestCase { var request = Request(url: "http://example.com/post") request.method = .POST let sequence = AnySequence(ByteBuffer(string: "post body").readableBytesView) - request.body = .bytes(length: nil, sequence) + request.body = .bytes(sequence, length: .unknown) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } @@ -334,7 +334,7 @@ class HTTPClientRequestTests: XCTestCase { request.method = .POST let sequence = AnySequence(ByteBuffer(string: "post body").readableBytesView) - request.body = .bytes(length: 9, sequence) + request.body = .bytes(sequence, length: .known(9)) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } @@ -411,7 +411,7 @@ class HTTPClientRequestTests: XCTestCase { .asAsyncSequence() .map { ByteBuffer($0) } - request.body = .stream(length: nil, asyncSequence) + request.body = .stream(asyncSequence, length: .unknown) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } @@ -452,7 +452,7 @@ class HTTPClientRequestTests: XCTestCase { .asAsyncSequence() .map { ByteBuffer($0) } - request.body = .stream(length: 9, asyncSequence) + request.body = .stream(asyncSequence, length: .known(9)) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } @@ -500,7 +500,7 @@ extension Optional where Wrapped == HTTPClientRequest.Body { return buffer case .sequence(let announcedLength, _, let generate): let buffer = generate(ByteBufferAllocator()) - if let announcedLength = announcedLength, + if case .known(let announcedLength) = announcedLength, announcedLength != buffer.readableBytes { throw LengthMismatch(announcedLength: announcedLength, actualLength: buffer.readableBytes) } @@ -510,7 +510,7 @@ extension Optional where Wrapped == HTTPClientRequest.Body { while var buffer = try await generate(ByteBufferAllocator()) { accumulatedBuffer.writeBuffer(&buffer) } - if let announcedLength = announcedLength, + if case .known(let announcedLength) = announcedLength, announcedLength != accumulatedBuffer.readableBytes { throw LengthMismatch(announcedLength: announcedLength, actualLength: accumulatedBuffer.readableBytes) } diff --git a/Tests/AsyncHTTPClientTests/RequestValidationTests.swift b/Tests/AsyncHTTPClientTests/RequestValidationTests.swift index 193a41b6b..c50d3afd1 100644 --- a/Tests/AsyncHTTPClientTests/RequestValidationTests.swift +++ b/Tests/AsyncHTTPClientTests/RequestValidationTests.swift @@ -21,7 +21,7 @@ class RequestValidationTests: XCTestCase { func testContentLengthHeaderIsRemovedFromGETIfNoBody() { var headers = HTTPHeaders([("Content-Length", "0")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) XCTAssertNil(headers.first(name: "Content-Length")) XCTAssertEqual(metadata?.body, .fixedSize(0)) } @@ -29,13 +29,13 @@ class RequestValidationTests: XCTestCase { func testContentLengthHeaderIsAddedToPOSTAndPUTWithNoBody() { var putHeaders = HTTPHeaders() var putMetadata: RequestFramingMetadata? - XCTAssertNoThrow(putMetadata = try putHeaders.validateAndSetTransportFraming(method: .PUT, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(putMetadata = try putHeaders.validateAndSetTransportFraming(method: .PUT, bodyLength: .known(0))) XCTAssertEqual(putHeaders.first(name: "Content-Length"), "0") XCTAssertEqual(putMetadata?.body, .fixedSize(0)) var postHeaders = HTTPHeaders() var postMetadata: RequestFramingMetadata? - XCTAssertNoThrow(postMetadata = try postHeaders.validateAndSetTransportFraming(method: .POST, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(postMetadata = try postHeaders.validateAndSetTransportFraming(method: .POST, bodyLength: .known(0))) XCTAssertEqual(postHeaders.first(name: "Content-Length"), "0") XCTAssertEqual(postMetadata?.body, .fixedSize(0)) } @@ -43,7 +43,7 @@ class RequestValidationTests: XCTestCase { func testContentLengthHeaderIsChangedIfBodyHasDifferentLength() { var headers = HTTPHeaders([("Content-Length", "0")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .PUT, bodyLength: .fixed(length: 200))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .PUT, bodyLength: .known(200))) XCTAssertEqual(headers.first(name: "Content-Length"), "200") XCTAssertEqual(metadata?.body, .fixedSize(200)) } @@ -51,7 +51,7 @@ class RequestValidationTests: XCTestCase { func testTRACERequestMustNotHaveBody() { for header in [("Content-Length", "200"), ("Transfer-Encoding", "chunked")] { var headers = HTTPHeaders([header]) - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .fixed(length: 200))) { + XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .known(200))) { XCTAssertEqual($0 as? HTTPClientError, .traceRequestWithBody) } } @@ -62,7 +62,7 @@ class RequestValidationTests: XCTestCase { let allowedMethods: [HTTPMethod] = [.GET, .HEAD, .DELETE, .CONNECT] var headers = HTTPHeaders() for method in allowedMethods { - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 100))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(100))) } } @@ -72,7 +72,7 @@ class RequestValidationTests: XCTestCase { ("User Agent", "Haha"), ]) - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .fixed(length: 0))) { error in + XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) { error in XCTAssertEqual(error as? HTTPClientError, HTTPClientError.invalidHeaderFieldNames(["User Agent"])) } } @@ -85,7 +85,7 @@ class RequestValidationTests: XCTestCase { ("!#$%&'*+-.^_`|~", "Haha"), ]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) } func testMetadataDetectConnectionClose() { @@ -93,14 +93,14 @@ class RequestValidationTests: XCTestCase { ("Connection", "close"), ]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) XCTAssertEqual(metadata?.connectionClose, true) } func testMetadataDefaultIsConnectionCloseIsFalse() { var headers = HTTPHeaders([]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) XCTAssertEqual(metadata?.connectionClose, false) } @@ -114,7 +114,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { var headers: HTTPHeaders = .init() var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertTrue(headers["content-length"].isEmpty) XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(0)) @@ -123,7 +123,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init() var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertEqual(headers["content-length"].first, "0") XCTAssertFalse(headers["transfer-encoding"].contains("chunked")) XCTAssertEqual(metadata?.body, .fixedSize(0)) @@ -139,7 +139,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { var headers: HTTPHeaders = .init() var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers["content-length"].first, "1") XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(1)) @@ -149,7 +149,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { var headers: HTTPHeaders = .init() var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .dynamic)) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .unknown)) XCTAssertTrue(headers["content-length"].isEmpty) XCTAssertTrue(headers["transfer-encoding"].contains("chunked")) XCTAssertEqual(metadata?.body, .stream) @@ -159,7 +159,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init() var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers["content-length"].first, "1") XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(1)) @@ -169,7 +169,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init() var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .dynamic)) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .unknown)) XCTAssertTrue(headers["content-length"].isEmpty) XCTAssertTrue(headers["transfer-encoding"].contains("chunked")) XCTAssertEqual(metadata?.body, .stream) @@ -184,7 +184,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { var headers: HTTPHeaders = .init([("Content-Length", "1")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertTrue(headers["content-length"].isEmpty) XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(0)) @@ -193,7 +193,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init([("Content-Length", "1")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertEqual(headers["content-length"].first, "0") XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(0)) @@ -208,7 +208,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { var headers: HTTPHeaders = .init([("Content-Length", "1")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers["content-length"].first, "1") XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(1)) @@ -217,7 +217,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init([("Content-Length", "1")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers["content-length"].first, "1") XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(1)) @@ -232,7 +232,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertTrue(headers["content-length"].isEmpty) XCTAssertFalse(headers["transfer-encoding"].contains("chunked")) XCTAssertEqual(metadata?.body, .fixedSize(0)) @@ -241,7 +241,7 @@ class RequestValidationTests: XCTestCase { for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertEqual(headers["content-length"].first, "0") XCTAssertFalse(headers["transfer-encoding"].contains("chunked")) XCTAssertEqual(metadata?.body, .fixedSize(0)) @@ -255,13 +255,13 @@ class RequestValidationTests: XCTestCase { func testTransferEncodingHeaderHasBody() throws { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers, ["Content-Length": "1"]) } for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers, ["Content-Length": "1"]) } } @@ -273,13 +273,13 @@ class RequestValidationTests: XCTestCase { func testBothHeadersNoBody() throws { for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertEqual(headers, [:]) } for method: HTTPMethod in [.POST, .PUT] { var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 0))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) XCTAssertEqual(headers, ["Content-Length": "0"]) } } @@ -291,12 +291,12 @@ class RequestValidationTests: XCTestCase { func testBothHeadersHasBody() throws { for method: HTTPMethod in [.TRACE] { var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) } for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .POST, .PUT] { var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers, ["Content-Length": "1"]) } } @@ -323,14 +323,14 @@ class RequestValidationTests: XCTestCase { func testTraceMethodIsNotAllowedToHaveAFixedLengthBody() { var headers = HTTPHeaders() - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .fixed(length: 10))) { + XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .known(10))) { XCTAssertEqual($0 as? HTTPClientError, .traceRequestWithBody) } } func testTraceMethodIsNotAllowedToHaveADynamicLengthBody() { var headers = HTTPHeaders() - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .dynamic)) { + XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .unknown)) { XCTAssertEqual($0 as? HTTPClientError, .traceRequestWithBody) } } @@ -339,7 +339,7 @@ class RequestValidationTests: XCTestCase { var headers: HTTPHeaders = [ "Transfer-Encoding": "gzip, chunked", ] - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .POST, bodyLength: .fixed(length: 1))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .POST, bodyLength: .known(1))) XCTAssertEqual(headers, [ "Content-Length": "1", ]) @@ -349,7 +349,7 @@ class RequestValidationTests: XCTestCase { var headers: HTTPHeaders = [ "Transfer-Encoding": "gzip, chunked", ] - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .POST, bodyLength: .dynamic)) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .POST, bodyLength: .unknown)) XCTAssertEqual(headers, [ "Transfer-Encoding": "chunked", ]) diff --git a/Tests/AsyncHTTPClientTests/TransactionTests.swift b/Tests/AsyncHTTPClientTests/TransactionTests.swift index 8857253b6..7e2c62a0d 100644 --- a/Tests/AsyncHTTPClientTests/TransactionTests.swift +++ b/Tests/AsyncHTTPClientTests/TransactionTests.swift @@ -189,7 +189,7 @@ final class TransactionTests: XCTestCase { var request = HTTPClientRequest(url: "https://localhost/") request.method = .POST - request.body = .stream(length: nil, streamWriter) + request.body = .stream(streamWriter, length: .unknown) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) @@ -318,7 +318,7 @@ final class TransactionTests: XCTestCase { var request = HTTPClientRequest(url: "https://localhost/") request.method = .POST - request.body = .bytes(length: nil, "Hello world!".utf8) + request.body = .bytes("Hello world!".utf8, length: .unknown) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) guard let preparedRequest = maybePreparedRequest else { @@ -360,7 +360,7 @@ final class TransactionTests: XCTestCase { var request = HTTPClientRequest(url: "https://localhost/") request.method = .POST - request.body = .stream(length: nil, writer) + request.body = .stream(writer, length: .unknown) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) guard let preparedRequest = maybePreparedRequest else { @@ -486,7 +486,7 @@ final class TransactionTests: XCTestCase { var request = HTTPClientRequest(url: "https://localhost:\(httpBin.port)/") request.method = .POST request.headers = ["host": "localhost:\(httpBin.port)"] - request.body = .stream(length: 800, streamWriter) + request.body = .stream(streamWriter, length: .known(800)) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request))