From 13ab70b06e6fbc73bc60752d3e38bd3fe214b85c Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Fri, 17 Dec 2021 16:55:33 +0100 Subject: [PATCH 01/12] Make all required types for async/await public and add nescary @inlinable/@usableFromInlinable for generic `HTTPClientReuqest.Body` builder metods. --- .../AsyncAwait/HTTPClient+execute.swift | 2 +- .../AsyncAwait/HTTPClientRequest.swift | 33 ++++++++++--------- .../AsyncAwait/HTTPClientResponse.swift | 25 +++++++------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift index f2680107a..930e68696 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift @@ -27,7 +27,7 @@ extension HTTPClient { /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. /// - Returns: The response to the request. Note that the `body` of the response may not yet have been fully received. - func execute( + public func execute( _ request: HTTPClientRequest, deadline: NIODeadline, logger: Logger diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index 89689225d..a2b391e63 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -17,14 +17,14 @@ import NIOCore import NIOHTTP1 @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -struct HTTPClientRequest { - var url: String - var method: HTTPMethod - var headers: HTTPHeaders +public struct HTTPClientRequest { + public var url: String + public var method: HTTPMethod + public var headers: HTTPHeaders - var body: Body? + public var body: Body? - init(url: String) { + public init(url: String) { self.url = url self.method = .GET self.headers = .init() @@ -34,16 +34,19 @@ struct HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest { - struct Body { + public struct Body { + @usableFromInline internal enum Mode { case asyncSequence(length: Int?, (ByteBufferAllocator) async throws -> ByteBuffer?) case sequence(length: Int?, 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,12 +54,12 @@ extension HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest.Body { - static func byteBuffer(_ byteBuffer: ByteBuffer) -> Self { + public static func byteBuffer(_ byteBuffer: ByteBuffer) -> Self { self.init(.byteBuffer(byteBuffer)) } @inlinable - static func bytes( + public static func bytes( length: Int?, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { @@ -71,7 +74,7 @@ extension HTTPClientRequest.Body { } @inlinable - static func bytes( + public static func bytes( length: Int?, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { @@ -86,7 +89,7 @@ extension HTTPClientRequest.Body { } @inlinable - static func bytes( + public static func bytes( _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { self.init(.sequence(length: bytes.count, canBeConsumedMultipleTimes: true) { allocator in @@ -100,7 +103,7 @@ extension HTTPClientRequest.Body { } @inlinable - static func stream( + public static func stream( length: Int?, _ sequenceOfBytes: SequenceOfBytes ) -> Self where SequenceOfBytes.Element == ByteBuffer { @@ -112,7 +115,7 @@ extension HTTPClientRequest.Body { } @inlinable - static func stream( + public static func stream( length: Int?, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift index 1ecfed9e5..5ffe3d4f4 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift @@ -17,13 +17,13 @@ import NIOCore import NIOHTTP1 @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -struct HTTPClientResponse { - var version: HTTPVersion - var status: HTTPResponseStatus - var headers: HTTPHeaders - var body: Body +public struct HTTPClientResponse { + public var version: HTTPVersion + public var status: HTTPResponseStatus + public var headers: HTTPHeaders + public var body: Body - struct Body { + public struct Body { private let bag: Transaction private let reference: ResponseRef @@ -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 + public typealias Element = AsyncIterator.Element + public struct AsyncIterator: AsyncIteratorProtocol { private let stream: IteratorStream fileprivate init(stream: IteratorStream) { self.stream = stream } - func next() async throws -> ByteBuffer? { + public func next() async throws -> ByteBuffer? { try await self.stream.next() } } - func makeAsyncIterator() -> Iterator { - Iterator(stream: IteratorStream(bag: self.bag)) + public func makeAsyncIterator() -> AsyncIterator { + AsyncIterator(stream: IteratorStream(bag: self.bag)) } } From 150e1f4eff9b265acf30c1b4a062d7a88035e724 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Fri, 17 Dec 2021 18:46:36 +0100 Subject: [PATCH 02/12] rename byteBuffer to bytes and remove Collection overload --- .../AsyncAwait/HTTPClientRequest.swift | 28 +++++-------------- .../AsyncAwaitEndToEndTests.swift | 2 +- .../HTTPClientRequestTests.swift | 4 +-- 3 files changed, 10 insertions(+), 24 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index a2b391e63..eed0dc035 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -54,16 +54,16 @@ extension HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest.Body { - public static func byteBuffer(_ byteBuffer: ByteBuffer) -> Self { + + public static func bytes(_ byteBuffer: ByteBuffer) -> Self { self.init(.byteBuffer(byteBuffer)) } @inlinable - public static func bytes( - length: Int?, + public static func bytes( _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: length, canBeConsumedMultipleTimes: false) { allocator in + self.init(.sequence(length: bytes.count, canBeConsumedMultipleTimes: true) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath return buffer @@ -72,27 +72,13 @@ extension HTTPClientRequest.Body { return allocator.buffer(bytes: bytes) }) } - + @inlinable - public static func bytes( + public static func bytes( length: Int?, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: length, canBeConsumedMultipleTimes: true) { allocator in - if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { - // fastpath - return buffer - } - // potentially really slow path - return allocator.buffer(bytes: bytes) - }) - } - - @inlinable - public static func bytes( - _ bytes: Bytes - ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: bytes.count, canBeConsumedMultipleTimes: true) { allocator in + self.init(.sequence(length: length, canBeConsumedMultipleTimes: bytes is Collection) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath return buffer diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift index 906bdaffa..f220b7f10 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) diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index f47b0ef93..de366b9b8 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 } From 11752a06ffdfe92f4fad1c6317729a04346c92d9 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Mon, 20 Dec 2021 14:20:15 +0100 Subject: [PATCH 03/12] use `RequestBodyLength` instead of optional `Int` to specify body length --- .../HTTPClientRequest+Prepared.swift | 10 ++-- .../AsyncAwait/HTTPClientRequest.swift | 21 ++++--- .../ConnectionPool/RequestBodyLength.swift | 6 +- Sources/AsyncHTTPClient/HTTPHandler.swift | 4 +- .../AsyncHTTPClient/RequestValidation.swift | 2 +- .../AsyncAwaitEndToEndTests.swift | 14 ++--- .../HTTPClientRequestTests.swift | 12 ++-- .../RequestValidationTests.swift | 56 +++++++++---------- .../TransactionTests.swift | 8 +-- 9 files changed, 68 insertions(+), 65 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift index bb7659a86..7b77ad224 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 = .fixed(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 = .fixed(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 eed0dc035..52be909fd 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -37,8 +37,8 @@ extension HTTPClientRequest { public 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) } @@ -54,7 +54,6 @@ extension HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest.Body { - public static func bytes(_ byteBuffer: ByteBuffer) -> Self { self.init(.byteBuffer(byteBuffer)) } @@ -63,7 +62,10 @@ extension HTTPClientRequest.Body { public static func bytes( _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: bytes.count, canBeConsumedMultipleTimes: true) { allocator in + self.init(.sequence( + length: .fixed(bytes.count), + canBeConsumedMultipleTimes: true + ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath return buffer @@ -75,10 +77,13 @@ extension HTTPClientRequest.Body { @inlinable public static func bytes( - length: Int?, + length: RequestBodyLength, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { - self.init(.sequence(length: length, canBeConsumedMultipleTimes: bytes is Collection) { allocator in + self.init(.sequence( + length: length, + canBeConsumedMultipleTimes: bytes is Collection + ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath return buffer @@ -90,7 +95,7 @@ extension HTTPClientRequest.Body { @inlinable public static func stream( - length: Int?, + length: RequestBodyLength, _ sequenceOfBytes: SequenceOfBytes ) -> Self where SequenceOfBytes.Element == ByteBuffer { var iterator = sequenceOfBytes.makeAsyncIterator() @@ -102,7 +107,7 @@ extension HTTPClientRequest.Body { @inlinable public static func stream( - length: Int?, + length: RequestBodyLength, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { var iterator = bytes.makeAsyncIterator() diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift index a963677dc..c0b114727 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -enum RequestBodyLength: Hashable { +public 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) + /// size of the request body is fixed and exactly `count` bytes + case fixed(_ count: Int) } diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 44ce2ecf5..8fde58a9b 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 = .fixed(0) return } guard let length = body.length else { self = .dynamic return } - self = .fixed(length: length) + self = .fixed(length) } } diff --git a/Sources/AsyncHTTPClient/RequestValidation.swift b/Sources/AsyncHTTPClient/RequestValidation.swift index ea0370484..64bdbd49d 100644 --- a/Sources/AsyncHTTPClient/RequestValidation.swift +++ b/Sources/AsyncHTTPClient/RequestValidation.swift @@ -24,7 +24,7 @@ extension HTTPHeaders { if case .TRACE = method { switch bodyLength { - case .fixed(length: 0): + case .fixed(0): break case .dynamic, .fixed: // A client MUST NOT send a message body in a TRACE request. diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift index f220b7f10..1fc515f9b 100644 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift @@ -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(length: .dynamic, AnySequence("1234".utf8)) 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(length: .dynamic, AnyCollection("1234".utf8)) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -190,7 +190,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, [ + request.body = .stream(length: .dynamic, [ ByteBuffer(string: "1"), ByteBuffer(string: "2"), ByteBuffer(string: "34"), @@ -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(length: .dynamic, "1234".utf8.asAsyncSequence()) 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(length: .dynamic, streamWriter) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -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(length: .dynamic, streamWriter) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -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(length: .dynamic, streamWriter) let task = Task { [request] in try await client.execute(request, deadline: .now() + .seconds(2), logger: logger) diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index de366b9b8..512dbd310 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -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(length: .dynamic, sequence) 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(length: .fixed(9), sequence) 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(length: .dynamic, asyncSequence) 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(length: .fixed(9), asyncSequence) 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 let .fixed(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 let .fixed(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..70f600a78 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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(1))) XCTAssertEqual(headers["content-length"].first, "1") XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(1)) @@ -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: .fixed(1))) XCTAssertEqual(headers["content-length"].first, "1") XCTAssertTrue(headers["transfer-encoding"].isEmpty) XCTAssertEqual(metadata?.body, .fixedSize(1)) @@ -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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(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: .fixed(1))) XCTAssertEqual(headers, ["Content-Length": "1"]) } } @@ -323,7 +323,7 @@ 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: .fixed(10))) { 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: .fixed(1))) XCTAssertEqual(headers, [ "Content-Length": "1", ]) diff --git a/Tests/AsyncHTTPClientTests/TransactionTests.swift b/Tests/AsyncHTTPClientTests/TransactionTests.swift index 8857253b6..607524a0a 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(length: .dynamic, streamWriter) 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(length: .dynamic, "Hello world!".utf8) 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(length: .dynamic, writer) 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(length: .fixed(800), streamWriter) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) From 1f2ede2688b1abe977a400e246780a2f7d9009e5 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Mon, 20 Dec 2021 16:18:57 +0100 Subject: [PATCH 04/12] add Collection overload and make RequestBodyLength a struct --- .../AsyncAwait/HTTPClientRequest.swift | 20 ++++++++++++++++++- .../ConnectionPool/RequestBodyLength.swift | 13 +++++++++--- .../AsyncHTTPClient/RequestValidation.swift | 6 +++--- .../HTTPClientRequestTests.swift | 4 ++-- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index 52be909fd..7a66a67af 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -82,7 +82,25 @@ extension HTTPClientRequest.Body { ) -> Self where Bytes.Element == UInt8 { self.init(.sequence( length: length, - canBeConsumedMultipleTimes: bytes is Collection + canBeConsumedMultipleTimes: false + ) { allocator in + if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { + // fastpath + return buffer + } + // potentially really slow path + return allocator.buffer(bytes: bytes) + }) + } + + @inlinable + public static func bytes( + length: RequestBodyLength, + _ bytes: Bytes + ) -> Self where Bytes.Element == UInt8 { + self.init(.sequence( + length: length, + canBeConsumedMultipleTimes: true ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { // fastpath diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift index c0b114727..237d23953 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift @@ -12,9 +12,16 @@ // //===----------------------------------------------------------------------===// -public enum RequestBodyLength: Hashable { +public struct RequestBodyLength { /// size of the request body is not known before starting the request - case dynamic + public static let dynamic: Self = .init(storage: .dynamic) /// size of the request body is fixed and exactly `count` bytes - case fixed(_ count: Int) + public static func fixed(_ count: Int) -> Self { + .init(storage: .fixed(count)) + } + internal enum Storage: Hashable { + case dynamic + case fixed(_ count: Int) + } + internal var storage: Storage } diff --git a/Sources/AsyncHTTPClient/RequestValidation.swift b/Sources/AsyncHTTPClient/RequestValidation.swift index 64bdbd49d..430a92ac0 100644 --- a/Sources/AsyncHTTPClient/RequestValidation.swift +++ b/Sources/AsyncHTTPClient/RequestValidation.swift @@ -23,7 +23,7 @@ extension HTTPHeaders { try self.validateFieldNames() if case .TRACE = method { - switch bodyLength { + switch bodyLength.storage { case .fixed(0): break case .dynamic, .fixed: @@ -36,7 +36,7 @@ extension HTTPHeaders { self.setTransportFraming(method: method, bodyLength: bodyLength) let connectionClose = self[canonicalForm: "connection"].lazy.map { $0.lowercased() }.contains("close") - switch bodyLength { + switch bodyLength.storage { case .dynamic: return .init(connectionClose: connectionClose, body: .stream) case .fixed(let length): @@ -87,7 +87,7 @@ extension HTTPHeaders { self.remove(name: "Content-Length") self.remove(name: "Transfer-Encoding") - switch bodyLength { + switch bodyLength.storage { case .fixed(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 diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index 512dbd310..302647965 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -500,7 +500,7 @@ extension Optional where Wrapped == HTTPClientRequest.Body { return buffer case .sequence(let announcedLength, _, let generate): let buffer = generate(ByteBufferAllocator()) - if case let .fixed(announcedLength) = announcedLength, + if case let .fixed(announcedLength) = announcedLength.storage, 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 case let .fixed(announcedLength) = announcedLength, + if case let .fixed(announcedLength) = announcedLength.storage, announcedLength != accumulatedBuffer.readableBytes { throw LengthMismatch(announcedLength: announcedLength, actualLength: accumulatedBuffer.readableBytes) } From 618e277b5794a8d3763a8bad6502ac5cbe669790 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Mon, 20 Dec 2021 17:32:50 +0100 Subject: [PATCH 05/12] only expose HTTPClientRequest.Body.Length publicy --- .../AsyncAwait/HTTPClientRequest.swift | 31 ++++++++++++++----- .../ConnectionPool/RequestBodyLength.swift | 16 ++++------ .../AsyncHTTPClient/RequestValidation.swift | 6 ++-- .../HTTPClientRequestTests.swift | 4 +-- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index 7a66a67af..0609ea361 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -77,11 +77,11 @@ extension HTTPClientRequest.Body { @inlinable public static func bytes( - length: RequestBodyLength, + length: Length, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { self.init(.sequence( - length: length, + length: length.storage, canBeConsumedMultipleTimes: false ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { @@ -95,11 +95,11 @@ extension HTTPClientRequest.Body { @inlinable public static func bytes( - length: RequestBodyLength, + length: Length, _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { self.init(.sequence( - length: length, + length: length.storage, canBeConsumedMultipleTimes: true ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { @@ -113,11 +113,11 @@ extension HTTPClientRequest.Body { @inlinable public static func stream( - length: RequestBodyLength, + length: Length, _ sequenceOfBytes: SequenceOfBytes ) -> 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 @@ -125,11 +125,11 @@ extension HTTPClientRequest.Body { @inlinable public static func stream( - length: RequestBodyLength, + length: Length, _ bytes: Bytes ) -> 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) @@ -155,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 { + public struct Length { + /// size of the request body is not known before starting the request + public static let dynamic: Self = .init(storage: .dynamic) + /// size of the request body is fixed and exactly `count` bytes + public static func fixed(_ count: Int) -> Self { + .init(storage: .fixed(count)) + } + + @usableFromInline + internal var storage: RequestBodyLength + } +} + #endif diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift index 237d23953..a72cd784b 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift @@ -12,16 +12,12 @@ // //===----------------------------------------------------------------------===// -public struct RequestBodyLength { + +/// - 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 - public static let dynamic: Self = .init(storage: .dynamic) + case dynamic /// size of the request body is fixed and exactly `count` bytes - public static func fixed(_ count: Int) -> Self { - .init(storage: .fixed(count)) - } - internal enum Storage: Hashable { - case dynamic - case fixed(_ count: Int) - } - internal var storage: Storage + case fixed(_ count: Int) } diff --git a/Sources/AsyncHTTPClient/RequestValidation.swift b/Sources/AsyncHTTPClient/RequestValidation.swift index 430a92ac0..64bdbd49d 100644 --- a/Sources/AsyncHTTPClient/RequestValidation.swift +++ b/Sources/AsyncHTTPClient/RequestValidation.swift @@ -23,7 +23,7 @@ extension HTTPHeaders { try self.validateFieldNames() if case .TRACE = method { - switch bodyLength.storage { + switch bodyLength { case .fixed(0): break case .dynamic, .fixed: @@ -36,7 +36,7 @@ extension HTTPHeaders { self.setTransportFraming(method: method, bodyLength: bodyLength) let connectionClose = self[canonicalForm: "connection"].lazy.map { $0.lowercased() }.contains("close") - switch bodyLength.storage { + switch bodyLength { case .dynamic: return .init(connectionClose: connectionClose, body: .stream) case .fixed(let length): @@ -87,7 +87,7 @@ extension HTTPHeaders { self.remove(name: "Content-Length") self.remove(name: "Transfer-Encoding") - switch bodyLength.storage { + switch bodyLength { case .fixed(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 diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index 302647965..512dbd310 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -500,7 +500,7 @@ extension Optional where Wrapped == HTTPClientRequest.Body { return buffer case .sequence(let announcedLength, _, let generate): let buffer = generate(ByteBufferAllocator()) - if case let .fixed(announcedLength) = announcedLength.storage, + if case let .fixed(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 case let .fixed(announcedLength) = announcedLength.storage, + if case let .fixed(announcedLength) = announcedLength, announcedLength != accumulatedBuffer.readableBytes { throw LengthMismatch(announcedLength: announcedLength, actualLength: accumulatedBuffer.readableBytes) } From 8b043d90a445d7e447b055cfc0221fd3d1752fc9 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Mon, 20 Dec 2021 17:38:31 +0100 Subject: [PATCH 06/12] make `next()` on response iterator mutating --- Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift index 5ffe3d4f4..52f03089b 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift @@ -57,7 +57,7 @@ extension HTTPClientResponse.Body: AsyncSequence { self.stream = stream } - public func next() async throws -> ByteBuffer? { + public mutating func next() async throws -> ByteBuffer? { try await self.stream.next() } } From f0ddb9cdb40206fa6bf5142b7cfd08c07be1d2bb Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Mon, 20 Dec 2021 17:42:49 +0100 Subject: [PATCH 07/12] SwiftFormat --- Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift | 6 +++--- .../AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift | 1 - Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift | 4 ++-- Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index 0609ea361..f19750cb1 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -74,7 +74,7 @@ extension HTTPClientRequest.Body { return allocator.buffer(bytes: bytes) }) } - + @inlinable public static func bytes( length: Length, @@ -92,7 +92,7 @@ extension HTTPClientRequest.Body { return allocator.buffer(bytes: bytes) }) } - + @inlinable public static func bytes( length: Length, @@ -164,7 +164,7 @@ extension HTTPClientRequest.Body { public static func fixed(_ count: Int) -> Self { .init(storage: .fixed(count)) } - + @usableFromInline internal var storage: RequestBodyLength } diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift index a72cd784b..0ddb876ec 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// - /// - Note: use `HTTPClientRequest.Body.Length` if you want to expose `RequestBodyLength` publicly @usableFromInline internal enum RequestBodyLength: Hashable { diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift index 1fc515f9b..76eec7480 100644 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift @@ -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( @@ -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( diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index 512dbd310..c4f6765b5 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -500,7 +500,7 @@ extension Optional where Wrapped == HTTPClientRequest.Body { return buffer case .sequence(let announcedLength, _, let generate): let buffer = generate(ByteBufferAllocator()) - if case let .fixed(announcedLength) = announcedLength, + if case .fixed(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 case let .fixed(announcedLength) = announcedLength, + if case .fixed(let announcedLength) = announcedLength, announcedLength != accumulatedBuffer.readableBytes { throw LengthMismatch(announcedLength: announcedLength, actualLength: accumulatedBuffer.readableBytes) } From 971b29236de6c43043fe9cbae6f7c7d2bc6b4c74 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Tue, 21 Dec 2021 09:54:45 +0100 Subject: [PATCH 08/12] fix race in tests --- .../AsyncAwaitEndToEndTests.swift | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift index 76eec7480..cea30e5e9 100644 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift @@ -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 From 5e57eb69e151d9d8adf044693dddf483b80edbd4 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Tue, 21 Dec 2021 09:57:31 +0100 Subject: [PATCH 09/12] rename .dynamic to .unkown --- .../AsyncAwait/HTTPClientRequest.swift | 2 +- .../ConnectionPool/RequestBodyLength.swift | 2 +- Sources/AsyncHTTPClient/HTTPHandler.swift | 2 +- Sources/AsyncHTTPClient/RequestValidation.swift | 6 +++--- .../AsyncAwaitEndToEndTests.swift | 14 +++++++------- .../HTTPClientRequestTests.swift | 4 ++-- .../RequestValidationTests.swift | 8 ++++---- Tests/AsyncHTTPClientTests/TransactionTests.swift | 6 +++--- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index f19750cb1..ebcd9d0ed 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -159,7 +159,7 @@ extension Optional where Wrapped == HTTPClientRequest.Body { extension HTTPClientRequest.Body { public struct Length { /// size of the request body is not known before starting the request - public static let dynamic: Self = .init(storage: .dynamic) + public static let unknown: Self = .init(storage: .unknown) /// size of the request body is fixed and exactly `count` bytes public static func fixed(_ count: Int) -> Self { .init(storage: .fixed(count)) diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift index 0ddb876ec..dc521ae97 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift @@ -16,7 +16,7 @@ @usableFromInline internal enum RequestBodyLength: Hashable { /// size of the request body is not known before starting the request - case dynamic + case unknown /// size of the request body is fixed and exactly `count` bytes case fixed(_ count: Int) } diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index 8fde58a9b..f04539983 100644 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ b/Sources/AsyncHTTPClient/HTTPHandler.swift @@ -694,7 +694,7 @@ extension RequestBodyLength { return } guard let length = body.length else { - self = .dynamic + self = .unknown return } self = .fixed(length) diff --git a/Sources/AsyncHTTPClient/RequestValidation.swift b/Sources/AsyncHTTPClient/RequestValidation.swift index 64bdbd49d..b8770df74 100644 --- a/Sources/AsyncHTTPClient/RequestValidation.swift +++ b/Sources/AsyncHTTPClient/RequestValidation.swift @@ -26,7 +26,7 @@ extension HTTPHeaders { switch bodyLength { case .fixed(0): break - case .dynamic, .fixed: + case .unknown, .fixed: // 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,7 +37,7 @@ 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): return .init(connectionClose: connectionClose, body: .fixedSize(length)) @@ -105,7 +105,7 @@ extension HTTPHeaders { } case .fixed(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 cea30e5e9..16d3a47a5 100644 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift @@ -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: .dynamic, AnySequence("1234".utf8)) + request.body = .bytes(length: .unknown, AnySequence("1234".utf8)) 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: .dynamic, AnyCollection("1234".utf8)) + request.body = .bytes(length: .unknown, AnyCollection("1234".utf8)) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -190,7 +190,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: .dynamic, [ + request.body = .stream(length: .unknown, [ ByteBuffer(string: "1"), ByteBuffer(string: "2"), ByteBuffer(string: "34"), @@ -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: .dynamic, "1234".utf8.asAsyncSequence()) + request.body = .stream(length: .unknown, "1234".utf8.asAsyncSequence()) 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: .dynamic, streamWriter) + request.body = .stream(length: .unknown, streamWriter) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -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: .dynamic, streamWriter) + request.body = .stream(length: .unknown, streamWriter) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -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: .dynamic, streamWriter) + request.body = .stream(length: .unknown, streamWriter) let task = Task { [request] in try await client.execute(request, deadline: .now() + .seconds(2), logger: logger) diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index c4f6765b5..a78f88e84 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -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: .dynamic, sequence) + request.body = .bytes(length: .unknown, sequence) 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: .dynamic, asyncSequence) + request.body = .stream(length: .unknown, asyncSequence) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } diff --git a/Tests/AsyncHTTPClientTests/RequestValidationTests.swift b/Tests/AsyncHTTPClientTests/RequestValidationTests.swift index 70f600a78..c1e27fd3d 100644 --- a/Tests/AsyncHTTPClientTests/RequestValidationTests.swift +++ b/Tests/AsyncHTTPClientTests/RequestValidationTests.swift @@ -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) @@ -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) @@ -330,7 +330,7 @@ class RequestValidationTests: XCTestCase { 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) } } @@ -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 607524a0a..0135fe388 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: .dynamic, streamWriter) + request.body = .stream(length: .unknown, streamWriter) 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: .dynamic, "Hello world!".utf8) + request.body = .bytes(length: .unknown, "Hello world!".utf8) 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: .dynamic, writer) + request.body = .stream(length: .unknown, writer) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) guard let preparedRequest = maybePreparedRequest else { From d8b41b86cab0c75b04717add1ba16736218ba8e8 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Tue, 21 Dec 2021 10:19:30 +0100 Subject: [PATCH 10/12] rename .fixed to .known --- .../HTTPClientRequest+Prepared.swift | 4 +- .../AsyncAwait/HTTPClientRequest.swift | 6 +- .../ConnectionPool/RequestBodyLength.swift | 2 +- Sources/AsyncHTTPClient/HTTPHandler.swift | 4 +- .../AsyncHTTPClient/RequestValidation.swift | 10 ++-- .../HTTPClientRequestTests.swift | 8 +-- .../RequestValidationTests.swift | 56 +++++++++---------- .../TransactionTests.swift | 2 +- 8 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift index 7b77ad224..de09df5b8 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift @@ -63,9 +63,9 @@ extension RequestBodyLength { init(_ body: HTTPClientRequest.Body?) { switch body?.mode { case .none: - self = .fixed(0) + self = .known(0) case .byteBuffer(let buffer): - self = .fixed(buffer.readableBytes) + 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 ebcd9d0ed..1fcb988e2 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -63,7 +63,7 @@ extension HTTPClientRequest.Body { _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { self.init(.sequence( - length: .fixed(bytes.count), + length: .known(bytes.count), canBeConsumedMultipleTimes: true ) { allocator in if let buffer = bytes.withContiguousStorageIfAvailable({ allocator.buffer(bytes: $0) }) { @@ -161,8 +161,8 @@ extension HTTPClientRequest.Body { /// size of the request body is not known before starting the request public static let unknown: Self = .init(storage: .unknown) /// size of the request body is fixed and exactly `count` bytes - public static func fixed(_ count: Int) -> Self { - .init(storage: .fixed(count)) + public static func known(_ count: Int) -> Self { + .init(storage: .known(count)) } @usableFromInline diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift index dc521ae97..38d90e057 100644 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift @@ -18,5 +18,5 @@ internal enum RequestBodyLength: Hashable { /// size of the request body is not known before starting the request case unknown /// size of the request body is fixed and exactly `count` bytes - case fixed(_ count: Int) + case known(_ count: Int) } diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift index f04539983..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(0) + self = .known(0) return } guard let length = body.length else { self = .unknown return } - self = .fixed(length) + self = .known(length) } } diff --git a/Sources/AsyncHTTPClient/RequestValidation.swift b/Sources/AsyncHTTPClient/RequestValidation.swift index b8770df74..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(0): + case .known(0): break - case .unknown, .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 @@ -39,7 +39,7 @@ extension HTTPHeaders { switch bodyLength { 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,7 +103,7 @@ 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 .unknown: self.add(name: "Transfer-Encoding", value: "chunked") diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index a78f88e84..19c4522cb 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -334,7 +334,7 @@ class HTTPClientRequestTests: XCTestCase { request.method = .POST let sequence = AnySequence(ByteBuffer(string: "post body").readableBytesView) - request.body = .bytes(length: .fixed(9), sequence) + request.body = .bytes(length: .known(9), sequence) 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: .fixed(9), asyncSequence) + request.body = .stream(length: .known(9), asyncSequence) 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 case .fixed(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 case .fixed(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 c1e27fd3d..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(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(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(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(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(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(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(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(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(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(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(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(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(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)) @@ -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(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)) @@ -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(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(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(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(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(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(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(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(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(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(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(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(1))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) XCTAssertEqual(headers, ["Content-Length": "1"]) } } @@ -323,7 +323,7 @@ class RequestValidationTests: XCTestCase { func testTraceMethodIsNotAllowedToHaveAFixedLengthBody() { var headers = HTTPHeaders() - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .fixed(10))) { + XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .known(10))) { 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(1))) + XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .POST, bodyLength: .known(1))) XCTAssertEqual(headers, [ "Content-Length": "1", ]) diff --git a/Tests/AsyncHTTPClientTests/TransactionTests.swift b/Tests/AsyncHTTPClientTests/TransactionTests.swift index 0135fe388..0385498f0 100644 --- a/Tests/AsyncHTTPClientTests/TransactionTests.swift +++ b/Tests/AsyncHTTPClientTests/TransactionTests.swift @@ -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: .fixed(800), streamWriter) + request.body = .stream(length: .known(800), streamWriter) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) From c5d33052a8692d6911dae0e9e5e8668a0560809d Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Tue, 21 Dec 2021 11:58:36 +0100 Subject: [PATCH 11/12] move `length` parameter to the end --- .../AsyncAwait/HTTPClientRequest.swift | 16 ++++++++-------- .../AsyncAwaitEndToEndTests.swift | 16 ++++++++-------- .../HTTPClientRequestTests.swift | 8 ++++---- .../AsyncHTTPClientTests/TransactionTests.swift | 8 ++++---- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index 1fcb988e2..cfab828a0 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -77,8 +77,8 @@ extension HTTPClientRequest.Body { @inlinable public static func bytes( - length: Length, - _ bytes: Bytes + _ bytes: Bytes, + length: Length ) -> Self where Bytes.Element == UInt8 { self.init(.sequence( length: length.storage, @@ -95,8 +95,8 @@ extension HTTPClientRequest.Body { @inlinable public static func bytes( - length: Length, - _ bytes: Bytes + _ bytes: Bytes, + length: Length ) -> Self where Bytes.Element == UInt8 { self.init(.sequence( length: length.storage, @@ -113,8 +113,8 @@ extension HTTPClientRequest.Body { @inlinable public static func stream( - length: Length, - _ sequenceOfBytes: SequenceOfBytes + _ sequenceOfBytes: SequenceOfBytes, + length: Length ) -> Self where SequenceOfBytes.Element == ByteBuffer { var iterator = sequenceOfBytes.makeAsyncIterator() let body = self.init(.asyncSequence(length: length.storage) { _ -> ByteBuffer? in @@ -125,8 +125,8 @@ extension HTTPClientRequest.Body { @inlinable public static func stream( - length: Length, - _ bytes: Bytes + _ bytes: Bytes, + length: Length ) -> Self where Bytes.Element == UInt8 { var iterator = bytes.makeAsyncIterator() let body = self.init(.asyncSequence(length: length.storage) { allocator -> ByteBuffer? in diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift index 16d3a47a5..08a39ca0e 100644 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift @@ -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: .unknown, 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: .unknown, 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: .unknown, [ + 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: .unknown, "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: .unknown, streamWriter) + request.body = .stream(streamWriter, length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -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: .unknown, streamWriter) + request.body = .stream(streamWriter, length: .unknown) guard let response = await XCTAssertNoThrowWithResult( try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) @@ -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: .unknown, streamWriter) + request.body = .stream(streamWriter, length: .unknown) let task = Task { [request] in try await client.execute(request, deadline: .now() + .seconds(2), logger: logger) diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift index 19c4522cb..1ebe7e939 100644 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift @@ -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: .unknown, 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: .known(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: .unknown, 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: .known(9), asyncSequence) + request.body = .stream(asyncSequence, length: .known(9)) var preparedRequest: PreparedRequest? XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) guard let preparedRequest = preparedRequest else { return } diff --git a/Tests/AsyncHTTPClientTests/TransactionTests.swift b/Tests/AsyncHTTPClientTests/TransactionTests.swift index 0385498f0..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: .unknown, 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: .unknown, "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: .unknown, 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: .known(800), streamWriter) + request.body = .stream(streamWriter, length: .known(800)) var maybePreparedRequest: PreparedRequest? XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) From 10b8bd2a44f3dfed9976575d9eab42bb61a79035 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Wed, 22 Dec 2021 11:04:00 +0100 Subject: [PATCH 12/12] make types internal again for now --- .../AsyncAwait/HTTPClient+execute.swift | 2 +- .../AsyncAwait/HTTPClientRequest.swift | 32 +++++++++---------- .../AsyncAwait/HTTPClientResponse.swift | 20 ++++++------ 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift index 930e68696..f2680107a 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift @@ -27,7 +27,7 @@ extension HTTPClient { /// - deadline: Point in time by which the request must complete. /// - logger: The logger to use for this request. /// - Returns: The response to the request. Note that the `body` of the response may not yet have been fully received. - public func execute( + func execute( _ request: HTTPClientRequest, deadline: NIODeadline, logger: Logger diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift index cfab828a0..193a1618d 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift @@ -17,14 +17,14 @@ import NIOCore import NIOHTTP1 @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -public struct HTTPClientRequest { - public var url: String - public var method: HTTPMethod - public var headers: HTTPHeaders +struct HTTPClientRequest { + var url: String + var method: HTTPMethod + var headers: HTTPHeaders - public var body: Body? + var body: Body? - public init(url: String) { + init(url: String) { self.url = url self.method = .GET self.headers = .init() @@ -34,7 +34,7 @@ public struct HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest { - public struct Body { + struct Body { @usableFromInline internal enum Mode { case asyncSequence(length: RequestBodyLength, (ByteBufferAllocator) async throws -> ByteBuffer?) @@ -54,12 +54,12 @@ extension HTTPClientRequest { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest.Body { - public static func bytes(_ byteBuffer: ByteBuffer) -> Self { + static func bytes(_ byteBuffer: ByteBuffer) -> Self { self.init(.byteBuffer(byteBuffer)) } @inlinable - public static func bytes( + static func bytes( _ bytes: Bytes ) -> Self where Bytes.Element == UInt8 { self.init(.sequence( @@ -76,7 +76,7 @@ extension HTTPClientRequest.Body { } @inlinable - public static func bytes( + static func bytes( _ bytes: Bytes, length: Length ) -> Self where Bytes.Element == UInt8 { @@ -94,7 +94,7 @@ extension HTTPClientRequest.Body { } @inlinable - public static func bytes( + static func bytes( _ bytes: Bytes, length: Length ) -> Self where Bytes.Element == UInt8 { @@ -112,7 +112,7 @@ extension HTTPClientRequest.Body { } @inlinable - public static func stream( + static func stream( _ sequenceOfBytes: SequenceOfBytes, length: Length ) -> Self where SequenceOfBytes.Element == ByteBuffer { @@ -124,7 +124,7 @@ extension HTTPClientRequest.Body { } @inlinable - public static func stream( + static func stream( _ bytes: Bytes, length: Length ) -> Self where Bytes.Element == UInt8 { @@ -157,11 +157,11 @@ extension Optional where Wrapped == HTTPClientRequest.Body { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientRequest.Body { - public struct Length { + struct Length { /// size of the request body is not known before starting the request - public static let unknown: Self = .init(storage: .unknown) + static let unknown: Self = .init(storage: .unknown) /// size of the request body is fixed and exactly `count` bytes - public static func known(_ count: Int) -> Self { + static func known(_ count: Int) -> Self { .init(storage: .known(count)) } diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift index 52f03089b..58f71979a 100644 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift +++ b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift @@ -17,13 +17,13 @@ import NIOCore import NIOHTTP1 @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -public struct HTTPClientResponse { - public var version: HTTPVersion - public var status: HTTPResponseStatus - public var headers: HTTPHeaders - public var body: Body +struct HTTPClientResponse { + var version: HTTPVersion + var status: HTTPResponseStatus + var headers: HTTPHeaders + var body: Body - public struct Body { + struct Body { private let bag: Transaction private let reference: ResponseRef @@ -48,21 +48,21 @@ public struct HTTPClientResponse { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTPClientResponse.Body: AsyncSequence { - public typealias Element = AsyncIterator.Element + typealias Element = AsyncIterator.Element - public struct AsyncIterator: AsyncIteratorProtocol { + struct AsyncIterator: AsyncIteratorProtocol { private let stream: IteratorStream fileprivate init(stream: IteratorStream) { self.stream = stream } - public mutating func next() async throws -> ByteBuffer? { + mutating func next() async throws -> ByteBuffer? { try await self.stream.next() } } - public func makeAsyncIterator() -> AsyncIterator { + func makeAsyncIterator() -> AsyncIterator { AsyncIterator(stream: IteratorStream(bag: self.bag)) } }