From cf141359eed7931cfb40dbb77f50e54702b98fe5 Mon Sep 17 00:00:00 2001 From: rudyfremont <114568549+rudyfremont@users.noreply.github.com> Date: Mon, 20 Mar 2023 18:23:53 +0100 Subject: [PATCH] fix(Multipart): Create InputStream only when the body is filled (#23) --- .../MultipartForm/MultipartFormData.swift | 32 ++++++++++++------- .../MultipartFormDataEncoder.swift | 2 +- .../MultipartFormDataEncoderTests.swift | 14 ++++++++ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift b/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift index 780b725..3fe7e6b 100644 --- a/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift +++ b/Sources/SimpleHTTP/MultipartForm/MultipartFormData.swift @@ -48,15 +48,30 @@ enum Boundary { struct BodyPart { let headers: [Header] - let stream: InputStream let length: Int + let stream: () throws -> InputStream + var hasInitialBoundary = false var hasFinalBoundary = false - init(headers: [Header], stream: InputStream, length: Int) { + private init(headers: [Header], stream: @escaping () throws -> InputStream, length: Int) { self.headers = headers - self.stream = stream self.length = length + self.stream = stream + } + + init(headers: [Header], url: URL, length: Int) { + let stream: () throws -> InputStream = { + guard let stream = InputStream(url: url) else { + throw MultipartFormData.Error.inputStreamCreationFailed(url) + } + return stream + } + self.init(headers: headers, stream: stream, length: length) + } + + init(headers: [Header], data: Data) { + self.init(headers: headers, stream: { InputStream(data: data) }, length: data.count) } } @@ -126,12 +141,7 @@ public struct MultipartFormData { } let length = fileSize.intValue - - guard let stream = InputStream(url: url) else { - throw MultipartFormData.Error.inputStreamCreationFailed(url) - } - - bodyParts.append(BodyPart(headers: headers, stream: stream, length: length)) + bodyParts.append(BodyPart(headers: headers, url: url, length: length)) } /// Creates a body part from the data and add it to the instance. @@ -150,10 +160,8 @@ public struct MultipartFormData { /// - mimeType: MIME type associated to the data in the `Content-Type` HTTPHeader. public mutating func add(data: Data, name: String, fileName: String? = nil, mimeType: String? = nil) { let headers = defineBodyPartHeader(name: name, fileName: fileName, mimeType: mimeType) - let stream = InputStream(data: data) - let length = data.count - bodyParts.append(BodyPart(headers: headers, stream: stream, length: length)) + bodyParts.append(BodyPart(headers: headers, data: data)) } private func defineBodyPartHeader(name: String, fileName: String?, mimeType: String?) -> [Header] { diff --git a/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift b/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift index d74378c..67bd540 100644 --- a/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift +++ b/Sources/SimpleHTTP/MultipartForm/MultipartFormDataEncoder.swift @@ -46,7 +46,7 @@ struct MultipartFormDataEncoder { } encoded.append(try encodeBodyPart(headers: bodyPart.headers)) - encoded.append(try encodeBodyPart(stream: bodyPart.stream, length: bodyPart.length)) + encoded.append(try encodeBodyPart(stream: bodyPart.stream(), length: bodyPart.length)) if bodyPart.hasFinalBoundary { encoded.append(Boundary.data(for: .final, boundary: boundary)) diff --git a/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift b/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift index 2f37913..ba10610 100644 --- a/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift +++ b/Tests/SimpleHTTPTests/MultipartForm/MultipartFormDataEncoderTests.swift @@ -158,4 +158,18 @@ class MultipartFormDataEncoderTests: XCTestCase { XCTAssertEqual(encodedData, expectedData) } + func test_encode_encoreMultiPartFormDataTwice_notThrow() throws { + let boundary = "boundary" + var multipart = MultipartFormData(boundary: boundary) + + let data = "I'm pjechris, Nice to meet you" + let name1 = "data" + multipart.add(data: Data(data.utf8), name: name1) + + var encoder = MultipartFormDataEncoder(body: multipart) + _ = try encoder.encode() + + XCTAssertNoThrow(_ = try encoder.encode()) + } + }