From 76951d77a0609599d2dc233e7e40808a74767c6a Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 18 Jan 2024 18:35:32 +0100 Subject: [PATCH] Set content length in request/response bodies (#96) ### Motivation A surprising oversight, we were never setting the `content-length` header when sending out a known-length body. Some transports might be already doing this, but this change makes things more consistent. ### Modifications Add the `content-length` header when setting a body and we know the length from the `HTTPBody`. ### Result More consistent experience. ### Test Plan Adapted unit tests. --- .../Conversion/CurrencyExtensions.swift | 8 ++++++-- .../Conversion/Test_Converter+Client.swift | 14 +++++++------- .../Conversion/Test_Converter+Server.swift | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift b/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift index df6caf04..38a17115 100644 --- a/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift +++ b/Sources/OpenAPIRuntime/Conversion/CurrencyExtensions.swift @@ -435,8 +435,10 @@ extension Converter { contentType: String, convert: (T) throws -> HTTPBody ) rethrows -> HTTPBody { + let body = try convert(value) headerFields[.contentType] = contentType - return try convert(value) + if case let .known(length) = body.length { headerFields[.contentLength] = String(length) } + return body } /// Sets the provided request body and the appropriate content type. @@ -597,8 +599,10 @@ extension Converter { contentType: String, convert: (T) throws -> HTTPBody ) rethrows -> HTTPBody { + let body = try convert(value) headerFields[.contentType] = contentType - return try convert(value) + if case let .known(length) = body.length { headerFields[.contentLength] = String(length) } + return body } /// Returns a decoded value for the provided path parameter. diff --git a/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift b/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift index 57c11580..4a7b669c 100644 --- a/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift +++ b/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Client.swift @@ -95,7 +95,7 @@ final class Test_ClientConverterExtensions: Test_Runtime { contentType: "application/json" ) try await XCTAssertEqualStringifiedData(body, testStructPrettyString) - XCTAssertEqual(headerFields, [.contentType: "application/json"]) + XCTAssertEqual(headerFields, [.contentType: "application/json", .contentLength: "23"]) } func test_setOptionalRequestBodyAsJSON_codable_string() async throws { @@ -106,7 +106,7 @@ final class Test_ClientConverterExtensions: Test_Runtime { contentType: "application/json" ) try await XCTAssertEqualStringifiedData(body, testQuotedString) - XCTAssertEqual(headerFields, [.contentType: "application/json"]) + XCTAssertEqual(headerFields, [.contentType: "application/json", .contentLength: "7"]) } // | client | set | request body | JSON | required | setRequiredRequestBodyAsJSON | @@ -118,7 +118,7 @@ final class Test_ClientConverterExtensions: Test_Runtime { contentType: "application/json" ) try await XCTAssertEqualStringifiedData(body, testStructPrettyString) - XCTAssertEqual(headerFields, [.contentType: "application/json"]) + XCTAssertEqual(headerFields, [.contentType: "application/json", .contentLength: "23"]) } // | client | set | request body | urlEncodedForm | codable | optional | setRequiredRequestBodyAsURLEncodedForm | @@ -136,7 +136,7 @@ final class Test_ClientConverterExtensions: Test_Runtime { } try await XCTAssertEqualStringifiedData(body, testStructURLFormString) - XCTAssertEqual(headerFields, [.contentType: "application/x-www-form-urlencoded"]) + XCTAssertEqual(headerFields, [.contentType: "application/x-www-form-urlencoded", .contentLength: "41"]) } // | client | set | request body | urlEncodedForm | codable | required | setRequiredRequestBodyAsURLEncodedForm | @@ -148,7 +148,7 @@ final class Test_ClientConverterExtensions: Test_Runtime { contentType: "application/x-www-form-urlencoded" ) try await XCTAssertEqualStringifiedData(body, testStructURLFormString) - XCTAssertEqual(headerFields, [.contentType: "application/x-www-form-urlencoded"]) + XCTAssertEqual(headerFields, [.contentType: "application/x-www-form-urlencoded", .contentLength: "41"]) } // | client | set | request body | binary | optional | setOptionalRequestBodyAsBinary | @@ -160,7 +160,7 @@ final class Test_ClientConverterExtensions: Test_Runtime { contentType: "application/octet-stream" ) try await XCTAssertEqualStringifiedData(body, testString) - XCTAssertEqual(headerFields, [.contentType: "application/octet-stream"]) + XCTAssertEqual(headerFields, [.contentType: "application/octet-stream", .contentLength: "5"]) } // | client | set | request body | binary | required | setRequiredRequestBodyAsBinary | @@ -172,7 +172,7 @@ final class Test_ClientConverterExtensions: Test_Runtime { contentType: "application/octet-stream" ) try await XCTAssertEqualStringifiedData(body, testString) - XCTAssertEqual(headerFields, [.contentType: "application/octet-stream"]) + XCTAssertEqual(headerFields, [.contentType: "application/octet-stream", .contentLength: "5"]) } // | client | set | request body | multipart | required | setRequiredRequestBodyAsMultipart | diff --git a/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Server.swift b/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Server.swift index d70a58d7..632116b2 100644 --- a/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Server.swift +++ b/Tests/OpenAPIRuntimeTests/Conversion/Test_Converter+Server.swift @@ -316,7 +316,7 @@ final class Test_ServerConverterExtensions: Test_Runtime { contentType: "application/json" ) try await XCTAssertEqualStringifiedData(data, testStructPrettyString) - XCTAssertEqual(headers, [.contentType: "application/json"]) + XCTAssertEqual(headers, [.contentType: "application/json", .contentLength: "23"]) } // | server | set | response body | binary | required | setResponseBodyAsBinary | @@ -328,7 +328,7 @@ final class Test_ServerConverterExtensions: Test_Runtime { contentType: "application/octet-stream" ) try await XCTAssertEqualStringifiedData(data, testString) - XCTAssertEqual(headers, [.contentType: "application/octet-stream"]) + XCTAssertEqual(headers, [.contentType: "application/octet-stream", .contentLength: "5"]) } // | server | set | response body | multipart | required | setResponseBodyAsMultipart |