From 72e6bf970261f52b8baa4c5489b7b4422d91bdbb Mon Sep 17 00:00:00 2001 From: Alex Reilly Date: Tue, 15 Oct 2019 17:19:42 -0700 Subject: [PATCH 1/4] project is building --- Package.swift | 9 +- .../include/multipartparser.h | 72 --- Sources/CMultipartParser/multipartparser.c | 342 ------------ .../Vapor/Content/ContentConfiguration.swift | 2 + Sources/Vapor/Multipart/FormDataDecoder.swift | 506 +++++++++--------- Sources/Vapor/Multipart/FormDataEncoder.swift | 396 +++++++------- Sources/Vapor/Multipart/MultipartError.swift | 34 +- Sources/Vapor/Multipart/MultipartParser.swift | 388 +++++++------- Sources/Vapor/Multipart/MultipartPart.swift | 208 +++---- .../Multipart/MultipartPartConvertible.swift | 272 +++++----- .../Vapor/Multipart/MultipartSerializer.swift | 88 +-- 11 files changed, 960 insertions(+), 1357 deletions(-) delete mode 100755 Sources/CMultipartParser/include/multipartparser.h delete mode 100755 Sources/CMultipartParser/multipartparser.c diff --git a/Package.swift b/Package.swift index 402932e86f..8d6e4dc44c 100644 --- a/Package.swift +++ b/Package.swift @@ -39,11 +39,13 @@ let package = Package( // WebSocket client library built on SwiftNIO .package(url: "https://github.com/vapor/websocket-kit.git", .branch("master")), + + .package(url: "https://github.com/vapor/multipart.git", .branch("VaporPort")) ], targets: [ // C helpers .target(name: "CBcrypt"), - .target(name: "CMultipartParser"), +// .target(name: "CMultipartParser"), .target(name: "COperatingSystem"), .target(name: "CURLParser"), @@ -51,7 +53,7 @@ let package = Package( .target(name: "Vapor", dependencies: [ "AsyncKit", "CBcrypt", - "CMultipartParser", +// "CMultipartParser", "COperatingSystem", "CURLParser", "ConsoleKit", @@ -67,7 +69,8 @@ let package = Package( "NIOWebSocket", "OpenCrypto", "RoutingKit", - "WebSocketKit" + "WebSocketKit", + "Multipart" ]), // Development diff --git a/Sources/CMultipartParser/include/multipartparser.h b/Sources/CMultipartParser/include/multipartparser.h deleted file mode 100755 index 73eb0b29b3..0000000000 --- a/Sources/CMultipartParser/include/multipartparser.h +++ /dev/null @@ -1,72 +0,0 @@ -// MIT License - -// Copyright (c) 2019 François Colas - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#ifndef MULTIPARTPARSER_H -#define MULTIPARTPARSER_H -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -typedef struct multipartparser multipartparser; -typedef struct multipartparser_callbacks multipartparser_callbacks; - -typedef int (*multipart_cb) (multipartparser*); -typedef int (*multipart_data_cb) (multipartparser*, const char* data, size_t size); - -struct multipartparser { - /** PRIVATE **/ - char boundary[70]; - int boundary_length; - int index; - uint16_t state; - - /** PUBLIC **/ - void* data; -}; - -struct multipartparser_callbacks { - multipart_cb on_body_begin; - multipart_cb on_part_begin; - multipart_data_cb on_header_field; - multipart_data_cb on_header_value; - multipart_cb on_headers_complete; - multipart_data_cb on_data; - multipart_cb on_part_end; - multipart_cb on_body_end; -}; - -void multipartparser_init(multipartparser* parser, const char* boundary); - -void multipartparser_callbacks_init(multipartparser_callbacks* callbacks); - -size_t multipartparser_execute(multipartparser* parser, - multipartparser_callbacks* callbacks, - const char* data, - size_t size); - -#ifdef __cplusplus -} -#endif -#endif // MULTIPARTPARSER_H diff --git a/Sources/CMultipartParser/multipartparser.c b/Sources/CMultipartParser/multipartparser.c deleted file mode 100755 index 0d1c71d5d6..0000000000 --- a/Sources/CMultipartParser/multipartparser.c +++ /dev/null @@ -1,342 +0,0 @@ -// MIT License - -// Copyright (c) 2019 François Colas - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#include "multipartparser.h" - -#include - -#define CR '\r' -#define LF '\n' -#define SP ' ' -#define HT '\t' -#define HYPHEN '-' - -#define CALLBACK_NOTIFY(NAME) \ - if (callbacks->on_##NAME != NULL) { \ - if (callbacks->on_##NAME(parser) != 0) \ - goto error; \ - } - -#define CALLBACK_DATA(NAME, P, S) \ - if (callbacks->on_##NAME != NULL) { \ - if (callbacks->on_##NAME(parser, P, S) != 0) \ - goto error; \ - } - -enum state { - s_preamble, - s_preamble_hy_hy, - s_first_boundary, - s_header_field_start, - s_header_field, - s_header_value_start, - s_header_value, - s_header_value_cr, - s_headers_done, - s_data, - s_data_cr, - s_data_cr_lf, - s_data_cr_lf_hy, - s_data_boundary_start, - s_data_boundary, - s_data_boundary_done, - s_data_boundary_done_cr_lf, - s_data_boundary_done_hy_hy, - s_epilogue, -}; - -/* Header field name as defined by rfc 2616. Also lowercases them. - * field-name = token - * token = 1* - * CTL = - * tspecials = "(" | ")" | "<" | ">" | "@" - * | "," | ";" | ":" | "\" | DQUOTE - * | "/" | "[" | "]" | "?" | "=" - * | "{" | "}" | SP | HT - * DQUOTE = - * SP = - * HT = - */ -static const char header_field_chars[256] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, '!', 0, '#', '$', '%', '&', '\'', -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 0, 0, '*', '+', 0, '-', '.', 0, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - '0', '1', '2', '3', '4', '5', '6', '7', -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - '8', '9', 0, 0, 0, 0, 0, 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 'X', 'Y', 'Z', 0, 0, 0, '^', '_', -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 'x', 'y', 'z', 0, '|', 0, '~', 0 -}; - -void multipartparser_init(multipartparser* parser, const char* boundary) -{ - memset(parser, 0, sizeof(*parser)); - - strncpy(parser->boundary, boundary, sizeof(parser->boundary)); - parser->boundary_length = strlen(parser->boundary); - - parser->state = s_preamble; -} - -void multipartparser_callbacks_init(multipartparser_callbacks* callbacks) -{ - memset(callbacks, 0, sizeof(*callbacks)); -} - -size_t multipartparser_execute(multipartparser* parser, - multipartparser_callbacks* callbacks, - const char* data, - size_t size) -{ - const char* mark; - const char* p; - unsigned char c; - - for (p = data; p < data + size; ++p) { - c = *p; - -reexecute: - switch (parser->state) { - - case s_preamble: - if (c == HYPHEN) - parser->state = s_preamble_hy_hy; - // else ignore everything before first boundary - break; - - case s_preamble_hy_hy: - if (c == HYPHEN) - parser->state = s_first_boundary; - else - parser->state = s_preamble; - break; - - case s_first_boundary: - if (parser->index == parser->boundary_length) { - if (c != CR) - goto error; - parser->index++; - break; - } - if (parser->index == parser->boundary_length + 1) { - if (c != LF) - goto error; - CALLBACK_NOTIFY(body_begin); - CALLBACK_NOTIFY(part_begin); - parser->index = 0; - parser->state = s_header_field_start; - break; - } - if (c == parser->boundary[parser->index]) { - parser->index++; - break; - } - goto error; - - case s_header_field_start: - if (c == CR) { - parser->state = s_headers_done; - break; - } - parser->state = s_header_field; - // fallthrough; - - case s_header_field: - mark = p; - while (p != data + size) { - c = *p; - if (header_field_chars[c] == 0) - break; - ++p; - } - if (p > mark) { - CALLBACK_DATA(header_field, mark, p - mark); - } - if (p == data + size) { - break; - } - if (c == ':') { - parser->state = s_header_value_start; - break; - } - goto error; - - case s_header_value_start: - if (c == SP || c == HT) { - break; - } - parser->state = s_header_value; - // fallthrough; - - case s_header_value: - mark = p; - while (p != data + size) { - c = *p; - if (c == CR) { - parser->state = s_header_value_cr; - break; - } - ++p; - } - if (p > mark) { - CALLBACK_DATA(header_value, mark, p - mark); - } - break; - - case s_header_value_cr: - if (c == LF) { - parser->state = s_header_field_start; - break; - } - goto error; - - case s_headers_done: - if (c == LF) { - CALLBACK_NOTIFY(headers_complete); - parser->state = s_data; - break; - } - goto error; - - case s_data: - mark = p; - while (p != data + size) { - c = *p; - if (c == CR) { - parser->state = s_data_cr; - break; - } - ++p; - } - if (p > mark) { - CALLBACK_DATA(data, mark, p - mark); - } - break; - - case s_data_cr: - if (c == LF) { - parser->state = s_data_cr_lf; - break; - } - CALLBACK_DATA(data, "\r", 1); - parser->state = s_data; - goto reexecute; - - case s_data_cr_lf: - if (c == HYPHEN) { - parser->state = s_data_cr_lf_hy; - break; - } - CALLBACK_DATA(data, "\r\n", 2); - parser->state = s_data; - goto reexecute; - - case s_data_cr_lf_hy: - if (c == HYPHEN) { - parser->state = s_data_boundary_start; - break; - } - CALLBACK_DATA(data, "\r\n-", 3); - parser->state = s_data; - goto reexecute; - - case s_data_boundary_start: - parser->index = 0; - parser->state = s_data_boundary; - // fallthrough; - - case s_data_boundary: - if (parser->index == parser->boundary_length) { - parser->index = 0; - parser->state = s_data_boundary_done; - goto reexecute; - } - if (c == parser->boundary[parser->index]) { - parser->index++; - break; - } - CALLBACK_DATA(data, parser->boundary, parser->index); - parser->state = s_data; - goto reexecute; - - case s_data_boundary_done: - if (c == CR) { - parser->state = s_data_boundary_done_cr_lf; - break; - } - if (c == HYPHEN) { - parser->state = s_data_boundary_done_hy_hy; - break; - } - goto error; - - case s_data_boundary_done_cr_lf: - if (c == LF) { - CALLBACK_NOTIFY(part_end); - CALLBACK_NOTIFY(part_begin); - parser->state = s_header_field_start; - break; - } - goto error; - - case s_data_boundary_done_hy_hy: - if (c == HYPHEN) { - CALLBACK_NOTIFY(part_end); - CALLBACK_NOTIFY(body_end); - parser->state = s_epilogue; - break; - } - goto error; - - case s_epilogue: - // Must be ignored according to rfc 1341. - break; - } - } - return size; - -error: - return p - data; -} diff --git a/Sources/Vapor/Content/ContentConfiguration.swift b/Sources/Vapor/Content/ContentConfiguration.swift index 244407912a..cbefb4bd09 100644 --- a/Sources/Vapor/Content/ContentConfiguration.swift +++ b/Sources/Vapor/Content/ContentConfiguration.swift @@ -1,3 +1,5 @@ +import Multipart + /// Configures which `Encoder`s and `Decoder`s to use when interacting with data in HTTP messages. /// /// var contentConfig = ContentConfig() diff --git a/Sources/Vapor/Multipart/FormDataDecoder.swift b/Sources/Vapor/Multipart/FormDataDecoder.swift index 207b1f80a0..8b7566ea2b 100644 --- a/Sources/Vapor/Multipart/FormDataDecoder.swift +++ b/Sources/Vapor/Multipart/FormDataDecoder.swift @@ -1,252 +1,258 @@ -/// Decodes `Decodable` types from `multipart/form-data` encoded `Data`. -/// -/// See [RFC#2388](https://tools.ietf.org/html/rfc2388) for more information about `multipart/form-data` encoding. -/// -/// Seealso `MultipartParser` for more information about the `multipart` encoding. -public struct FormDataDecoder: ContentDecoder { - /// Creates a new `FormDataDecoder`. - public init() { } - - /// `ContentDecoder` conformance. - public func decode(_ decodable: D.Type, from body: ByteBuffer, headers: HTTPHeaders) throws -> D - where D: Decodable - { - guard let boundary = headers.contentType?.parameters["boundary"] else { - throw Abort(.unsupportedMediaType) - } - return try self.decode(D.self, from: body, boundary: boundary) - } - - public func decode(_ decodable: D.Type, from data: String, boundary: String) throws -> D - where D: Decodable - { - var buffer = ByteBufferAllocator().buffer(capacity: data.utf8.count) - buffer.writeString(data) - return try self.decode(D.self, from: buffer, boundary: boundary) - } - - /// Decodes a `Decodable` item from `Data` using the supplied boundary. - /// - /// let foo = try FormDataDecoder().decode(Foo.self, from: data, boundary: "123") - /// - /// - parameters: - /// - encodable: Generic `Decodable` type. - /// - boundary: Multipart boundary to used in the encoding. - /// - throws: Any errors decoding the model with `Codable` or parsing the data. - /// - returns: An instance of the decoded type `D`. - public func decode(_ decodable: D.Type, from data: ByteBuffer, boundary: String) throws -> D - where D: Decodable - { - let parser = MultipartParser(boundary: boundary) - - var parts: [MultipartPart] = [] - var headers: [String: String] = [:] - var body: ByteBuffer? = nil - - parser.onHeader = { (field, value) in - headers[field] = value - } - parser.onBody = { new in - if var existing = body { - existing.writeBuffer(&new) - body = existing - } else { - body = new - } - } - parser.onPartComplete = { - let part = MultipartPart(headers: headers, body: body!) - headers = [:] - body = nil - parts.append(part) - } - - try parser.execute(data) - let multipart = FormDataDecoderContext(parts: parts) - let decoder = _FormDataDecoder(multipart: multipart, codingPath: []) - return try D(from: decoder) - } +import Multipart + +extension FormDataDecoder: ContentDecoder { + /// `ContentDecoder` conformance. + public func decode(_ decodable: D.Type, from body: ByteBuffer, headers: HTTPHeaders) throws -> D + where D: Decodable + { + guard let boundary = headers.contentType?.parameters["boundary"] else { + throw Abort(.unsupportedMediaType) + } + return try self.decode(D.self, from: body, boundary: boundary) + } } -// MARK: Private - -private final class FormDataDecoderContext { - var parts: [MultipartPart] - init(parts: [MultipartPart]) { - self.parts = parts - } - - func decode(_ decodable: D.Type, at codingPath: [CodingKey]) throws -> D where D: Decodable { - guard let convertible = D.self as? MultipartPartConvertible.Type else { - throw MultipartError(identifier: "convertible", reason: "`\(D.self)` is not `MultipartPartConvertible`.") - } - - let part: MultipartPart - switch codingPath.count { - case 1: - let name = codingPath[0].stringValue - guard let p = parts.firstPart(named: name) else { - throw MultipartError(identifier: "missingPart", reason: "No multipart part named '\(name)' was found.") - } - part = p - case 2: - let name = codingPath[0].stringValue + "[]" - guard let offset = codingPath[1].intValue else { - throw MultipartError(identifier: "arrayOffset", reason: "Nested form-data is not supported.") - } - part = parts.allParts(named: name)[offset] - default: throw MultipartError(identifier: "nested", reason: "Nested form-data is not supported.") - } - - return try convertible.convertFromMultipartPart(part) as! D - } -} - - -private struct _FormDataDecoder: Decoder { - var codingPath: [CodingKey] - var userInfo: [CodingUserInfoKey: Any] { - return [:] - } - let multipart: FormDataDecoderContext - - init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { - self.multipart = multipart - self.codingPath = codingPath - } - - func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { - return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath)) - } - - func unkeyedContainer() throws -> UnkeyedDecodingContainer { - return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath) - } - - func singleValueContainer() throws -> SingleValueDecodingContainer { - return _FormDataSingleValueDecoder(multipart: multipart, codingPath: codingPath) - } -} - -private struct _FormDataSingleValueDecoder: SingleValueDecodingContainer { - var codingPath: [CodingKey] - let multipart: FormDataDecoderContext - - init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { - self.multipart = multipart - self.codingPath = codingPath - } - - func decodeNil() -> Bool { - return false - } - - func decode(_ type: T.Type) throws -> T where T: Decodable { - return try multipart.decode(T.self, at: codingPath) - } -} - -private struct _FormDataKeyedDecoder: KeyedDecodingContainerProtocol where K: CodingKey { - var codingPath: [CodingKey] - var allKeys: [K] { - return multipart.parts - .compactMap { $0.name } - .compactMap { K(stringValue: $0) } - } - - let multipart: FormDataDecoderContext - - init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { - self.multipart = multipart - self.codingPath = codingPath - } - - func contains(_ key: K) -> Bool { - return multipart.parts.contains { $0.name == key.stringValue } - } - - func decodeNil(forKey key: K) throws -> Bool { - return false - } - - func decode(_ type: T.Type, forKey key: K) throws -> T where T : Decodable { - if T.self is MultipartPartConvertible.Type { - return try multipart.decode(T.self, at: codingPath + [key]) - } else { - let decoder = _FormDataDecoder(multipart: multipart, codingPath: codingPath + [key]) - return try T(from: decoder) - } - } - - func nestedContainer(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer where NestedKey : CodingKey { - return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath + [key])) - } - - func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { - return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath + [key]) - } - - func superDecoder() throws -> Decoder { - return _FormDataDecoder(multipart: multipart, codingPath: codingPath) - } - - func superDecoder(forKey key: K) throws -> Decoder { - return _FormDataDecoder(multipart: multipart, codingPath: codingPath + [key]) - } -} - -private struct _FormDataUnkeyedDecoder: UnkeyedDecodingContainer { - var codingPath: [CodingKey] - var count: Int? - var isAtEnd: Bool { - return currentIndex >= count! - } - var currentIndex: Int - var index: CodingKey { - return BasicCodingKey.index(self.currentIndex) - } - - let multipart: FormDataDecoderContext - - init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) throws { - self.multipart = multipart - self.codingPath = codingPath - - let name: String - switch codingPath.count { - case 1: name = codingPath[0].stringValue - default: throw MultipartError(identifier: "nesting", reason: "Nested form-data decoding is not supported.") - } - let parts = multipart.parts.allParts(named: name + "[]") - self.count = parts.count - self.currentIndex = 0 - } - - mutating func decodeNil() throws -> Bool { - return false - } - - mutating func decode(_ type: T.Type) throws -> T where T: Decodable { - defer { currentIndex += 1 } - if T.self is MultipartPartConvertible.Type { - return try multipart.decode(T.self, at: codingPath + [index]) - } else { - let decoder = _FormDataDecoder(multipart: multipart, codingPath: codingPath + [index]) - return try T(from: decoder) - } - } - - mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey : CodingKey { - return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath + [index])) - } - - mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { - return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath + [index]) - } - - mutating func superDecoder() throws -> Decoder { - return _FormDataDecoder(multipart: multipart, codingPath: codingPath + [index]) - } - - -} +///// Decodes `Decodable` types from `multipart/form-data` encoded `Data`. +///// +///// See [RFC#2388](https://tools.ietf.org/html/rfc2388) for more information about `multipart/form-data` encoding. +///// +///// Seealso `MultipartParser` for more information about the `multipart` encoding. +//public struct FormDataDecoder: ContentDecoder { +// /// Creates a new `FormDataDecoder`. +// public init() { } +// +// +// +// public func decode(_ decodable: D.Type, from data: String, boundary: String) throws -> D +// where D: Decodable +// { +// var buffer = ByteBufferAllocator().buffer(capacity: data.utf8.count) +// buffer.writeString(data) +// return try self.decode(D.self, from: buffer, boundary: boundary) +// } +// +// /// Decodes a `Decodable` item from `Data` using the supplied boundary. +// /// +// /// let foo = try FormDataDecoder().decode(Foo.self, from: data, boundary: "123") +// /// +// /// - parameters: +// /// - encodable: Generic `Decodable` type. +// /// - boundary: Multipart boundary to used in the encoding. +// /// - throws: Any errors decoding the model with `Codable` or parsing the data. +// /// - returns: An instance of the decoded type `D`. +// public func decode(_ decodable: D.Type, from data: ByteBuffer, boundary: String) throws -> D +// where D: Decodable +// { +// let parser = MultipartParser(boundary: boundary) +// +// var parts: [MultipartPart] = [] +// var headers: [String: String] = [:] +// var body: ByteBuffer? = nil +// +// parser.onHeader = { (field, value) in +// headers[field] = value +// } +// parser.onBody = { new in +// if var existing = body { +// existing.writeBuffer(&new) +// body = existing +// } else { +// body = new +// } +// } +// parser.onPartComplete = { +// let part = MultipartPart(headers: headers, body: body!) +// headers = [:] +// body = nil +// parts.append(part) +// } +// +// try parser.execute(data) +// let multipart = FormDataDecoderContext(parts: parts) +// let decoder = _FormDataDecoder(multipart: multipart, codingPath: []) +// return try D(from: decoder) +// } +//} +// +//// MARK: Private +// +//private final class FormDataDecoderContext { +// var parts: [MultipartPart] +// init(parts: [MultipartPart]) { +// self.parts = parts +// } +// +// func decode(_ decodable: D.Type, at codingPath: [CodingKey]) throws -> D where D: Decodable { +// guard let convertible = D.self as? MultipartPartConvertible.Type else { +// throw MultipartError(identifier: "convertible", reason: "`\(D.self)` is not `MultipartPartConvertible`.") +// } +// +// let part: MultipartPart +// switch codingPath.count { +// case 1: +// let name = codingPath[0].stringValue +// guard let p = parts.firstPart(named: name) else { +// throw MultipartError(identifier: "missingPart", reason: "No multipart part named '\(name)' was found.") +// } +// part = p +// case 2: +// let name = codingPath[0].stringValue + "[]" +// guard let offset = codingPath[1].intValue else { +// throw MultipartError(identifier: "arrayOffset", reason: "Nested form-data is not supported.") +// } +// part = parts.allParts(named: name)[offset] +// default: throw MultipartError(identifier: "nested", reason: "Nested form-data is not supported.") +// } +// +// return try convertible.convertFromMultipartPart(part) as! D +// } +//} +// +// +//private struct _FormDataDecoder: Decoder { +// var codingPath: [CodingKey] +// var userInfo: [CodingUserInfoKey: Any] { +// return [:] +// } +// let multipart: FormDataDecoderContext +// +// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { +// self.multipart = multipart +// self.codingPath = codingPath +// } +// +// func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { +// return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath)) +// } +// +// func unkeyedContainer() throws -> UnkeyedDecodingContainer { +// return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath) +// } +// +// func singleValueContainer() throws -> SingleValueDecodingContainer { +// return _FormDataSingleValueDecoder(multipart: multipart, codingPath: codingPath) +// } +//} +// +//private struct _FormDataSingleValueDecoder: SingleValueDecodingContainer { +// var codingPath: [CodingKey] +// let multipart: FormDataDecoderContext +// +// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { +// self.multipart = multipart +// self.codingPath = codingPath +// } +// +// func decodeNil() -> Bool { +// return false +// } +// +// func decode(_ type: T.Type) throws -> T where T: Decodable { +// return try multipart.decode(T.self, at: codingPath) +// } +//} +// +//private struct _FormDataKeyedDecoder: KeyedDecodingContainerProtocol where K: CodingKey { +// var codingPath: [CodingKey] +// var allKeys: [K] { +// return multipart.parts +// .compactMap { $0.name } +// .compactMap { K(stringValue: $0) } +// } +// +// let multipart: FormDataDecoderContext +// +// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { +// self.multipart = multipart +// self.codingPath = codingPath +// } +// +// func contains(_ key: K) -> Bool { +// return multipart.parts.contains { $0.name == key.stringValue } +// } +// +// func decodeNil(forKey key: K) throws -> Bool { +// return false +// } +// +// func decode(_ type: T.Type, forKey key: K) throws -> T where T : Decodable { +// if T.self is MultipartPartConvertible.Type { +// return try multipart.decode(T.self, at: codingPath + [key]) +// } else { +// let decoder = _FormDataDecoder(multipart: multipart, codingPath: codingPath + [key]) +// return try T(from: decoder) +// } +// } +// +// func nestedContainer(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer where NestedKey : CodingKey { +// return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath + [key])) +// } +// +// func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { +// return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath + [key]) +// } +// +// func superDecoder() throws -> Decoder { +// return _FormDataDecoder(multipart: multipart, codingPath: codingPath) +// } +// +// func superDecoder(forKey key: K) throws -> Decoder { +// return _FormDataDecoder(multipart: multipart, codingPath: codingPath + [key]) +// } +//} +// +//private struct _FormDataUnkeyedDecoder: UnkeyedDecodingContainer { +// var codingPath: [CodingKey] +// var count: Int? +// var isAtEnd: Bool { +// return currentIndex >= count! +// } +// var currentIndex: Int +// var index: CodingKey { +// return BasicCodingKey.index(self.currentIndex) +// } +// +// let multipart: FormDataDecoderContext +// +// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) throws { +// self.multipart = multipart +// self.codingPath = codingPath +// +// let name: String +// switch codingPath.count { +// case 1: name = codingPath[0].stringValue +// default: throw MultipartError(identifier: "nesting", reason: "Nested form-data decoding is not supported.") +// } +// let parts = multipart.parts.allParts(named: name + "[]") +// self.count = parts.count +// self.currentIndex = 0 +// } +// +// mutating func decodeNil() throws -> Bool { +// return false +// } +// +// mutating func decode(_ type: T.Type) throws -> T where T: Decodable { +// defer { currentIndex += 1 } +// if T.self is MultipartPartConvertible.Type { +// return try multipart.decode(T.self, at: codingPath + [index]) +// } else { +// let decoder = _FormDataDecoder(multipart: multipart, codingPath: codingPath + [index]) +// return try T(from: decoder) +// } +// } +// +// mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey : CodingKey { +// return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath + [index])) +// } +// +// mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { +// return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath + [index]) +// } +// +// mutating func superDecoder() throws -> Decoder { +// return _FormDataDecoder(multipart: multipart, codingPath: codingPath + [index]) +// } +// +// +//} diff --git a/Sources/Vapor/Multipart/FormDataEncoder.swift b/Sources/Vapor/Multipart/FormDataEncoder.swift index d4bac4dcd2..8f5be67fbd 100644 --- a/Sources/Vapor/Multipart/FormDataEncoder.swift +++ b/Sources/Vapor/Multipart/FormDataEncoder.swift @@ -1,49 +1,55 @@ -/// Encodes `Encodable` items to `multipart/form-data` encoded `Data`. -/// -/// See [RFC#2388](https://tools.ietf.org/html/rfc2388) for more information about `multipart/form-data` encoding. -/// -/// Seealso `MultipartParser` for more information about the `multipart` encoding. -public struct FormDataEncoder: ContentEncoder { - /// Creates a new `FormDataEncoder`. - public init() { } - - /// `ContentEncoder` conformance. - public func encode(_ encodable: E, to body: inout ByteBuffer, headers: inout HTTPHeaders) throws - where E: Encodable - { - let boundary = "----vaporBoundary\(randomBoundaryData())" - headers.contentType = HTTPMediaType(type: "multipart", subType: "form-data", parameters: ["boundary": boundary]) - try self.encode(encodable, boundary: boundary, into: &body) - } - - public func encode(_ encodable: E, boundary: String) throws -> String - where E: Encodable - { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - try self.encode(encodable, boundary: boundary, into: &buffer) - return buffer.readString(length: buffer.readableBytes)! - } - - /// Encodes an `Encodable` item to `Data` using the supplied boundary. - /// - /// let a = Foo(string: "a", int: 42, double: 3.14, array: [1, 2, 3]) - /// let data = try FormDataEncoder().encode(a, boundary: "123") - /// - /// - parameters: - /// - encodable: Generic `Encodable` item. - /// - boundary: Multipart boundary to use for encoding. This must not appear anywhere in the encoded data. - /// - throws: Any errors encoding the model with `Codable` or serializing the data. - /// - returns: `multipart/form-data`-encoded `Data`. - public func encode(_ encodable: E, boundary: String, into buffer: inout ByteBuffer) throws - where E: Encodable - { - let multipart = FormDataEncoderContext() - let encoder = _FormDataEncoder(multipart: multipart, codingPath: []) - try encodable.encode(to: encoder) - try MultipartSerializer().serialize(parts: multipart.parts, boundary: boundary, into: &buffer) - } +import Multipart + +extension FormDataEncoder: ContentEncoder { + /// `ContentEncoder` conformance. + public func encode(_ encodable: E, to body: inout ByteBuffer, headers: inout HTTPHeaders) throws + where E: Encodable + { + let boundary = "----vaporBoundary\(randomBoundaryData())" + headers.contentType = HTTPMediaType(type: "multipart", subType: "form-data", parameters: ["boundary": boundary]) + try self.encode(encodable, boundary: boundary, into: &body) + } } +///// Encodes `Encodable` items to `multipart/form-data` encoded `Data`. +///// +///// See [RFC#2388](https://tools.ietf.org/html/rfc2388) for more information about `multipart/form-data` encoding. +///// +///// Seealso `MultipartParser` for more information about the `multipart` encoding. +//public struct FormDataEncoder: ContentEncoder { +// /// Creates a new `FormDataEncoder`. +// public init() { } +// +// +// +// public func encode(_ encodable: E, boundary: String) throws -> String +// where E: Encodable +// { +// var buffer = ByteBufferAllocator().buffer(capacity: 0) +// try self.encode(encodable, boundary: boundary, into: &buffer) +// return buffer.readString(length: buffer.readableBytes)! +// } +// +// /// Encodes an `Encodable` item to `Data` using the supplied boundary. +// /// +// /// let a = Foo(string: "a", int: 42, double: 3.14, array: [1, 2, 3]) +// /// let data = try FormDataEncoder().encode(a, boundary: "123") +// /// +// /// - parameters: +// /// - encodable: Generic `Encodable` item. +// /// - boundary: Multipart boundary to use for encoding. This must not appear anywhere in the encoded data. +// /// - throws: Any errors encoding the model with `Codable` or serializing the data. +// /// - returns: `multipart/form-data`-encoded `Data`. +// public func encode(_ encodable: E, boundary: String, into buffer: inout ByteBuffer) throws +// where E: Encodable +// { +// let multipart = FormDataEncoderContext() +// let encoder = _FormDataEncoder(multipart: multipart, codingPath: []) +// try encodable.encode(to: encoder) +// try MultipartSerializer().serialize(parts: multipart.parts, boundary: boundary, into: &buffer) +// } +//} +// // MARK: Private private let chars = "abcdefghijklmnopqrstuvwxyz0123456789" @@ -55,154 +61,154 @@ private func randomBoundaryData() -> String { } return string } - -private final class FormDataEncoderContext { - var parts: [MultipartPart] - init() { - self.parts = [] - } - - func encode(_ encodable: E, at codingPath: [CodingKey]) throws where E: Encodable { - guard let convertible = encodable as? MultipartPartConvertible else { - throw MultipartError(identifier: "convertible", reason: "`\(E.self)` is not `MultipartPartConvertible`.") - } - - var part = try convertible.convertToMultipartPart() - switch codingPath.count { - case 1: part.name = codingPath[0].stringValue - case 2: - guard codingPath[1].intValue != nil else { - throw MultipartError(identifier: "nestedEncode", reason: "Nesting is not supported when encoding multipart data.") - } - part.name = codingPath[0].stringValue + "[]" - default: throw MultipartError(identifier: "nestedEncode", reason: "Nesting is not supported when encoding multipart data.") - } - parts.append(part) - } - - func encode(_ files: [File], at codingPath: [CodingKey]) throws { - for file in files { - try encode(file, at: codingPath) - } - } -} - -private struct _FormDataEncoder: Encoder { - let codingPath: [CodingKey] - let multipart: FormDataEncoderContext - var userInfo: [CodingUserInfoKey: Any] { - return [:] - } - - init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { - self.multipart = multipart - self.codingPath = codingPath - } - - func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { - return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath)) - } - - func unkeyedContainer() -> UnkeyedEncodingContainer { - return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath) - } - - func singleValueContainer() -> SingleValueEncodingContainer { - return _FormDataSingleValueEncoder(multipart: multipart, codingPath: codingPath) - } -} - -private struct _FormDataSingleValueEncoder: SingleValueEncodingContainer { - let multipart: FormDataEncoderContext - var codingPath: [CodingKey] - - init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { - self.multipart = multipart - self.codingPath = codingPath - } - - mutating func encodeNil() throws { - // do nothing - } - - mutating func encode(_ value: T) throws where T : Encodable { - try multipart.encode(value, at: codingPath) - } -} - -private struct _FormDataKeyedEncoder: KeyedEncodingContainerProtocol where K: CodingKey { - let multipart: FormDataEncoderContext - var codingPath: [CodingKey] - - init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { - self.multipart = multipart - self.codingPath = codingPath - } - - mutating func encodeNil(forKey key: K) throws { - // ignore - } - - mutating func encode(_ value: T, forKey key: K) throws where T : Encodable { - if value is MultipartPartConvertible { - try multipart.encode(value, at: codingPath + [key]) - } else if let value = value as? [File] { - try multipart.encode(value, at: codingPath + [key]) - } else { - let encoder = _FormDataEncoder(multipart: multipart, codingPath: codingPath + [key]) - try value.encode(to: encoder) - } - } - - mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer where NestedKey: CodingKey { - return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath + [key])) - } - - mutating func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer { - return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath + [key]) - } - - mutating func superEncoder() -> Encoder { - return _FormDataEncoder(multipart: multipart, codingPath: codingPath) - } - - mutating func superEncoder(forKey key: K) -> Encoder { - return _FormDataEncoder(multipart: multipart, codingPath: codingPath + [key]) - } -} - -private struct _FormDataUnkeyedEncoder: UnkeyedEncodingContainer { - var count: Int - let multipart: FormDataEncoderContext - var codingPath: [CodingKey] - var index: CodingKey { - return BasicCodingKey.index(0) - } - - init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { - self.multipart = multipart - self.codingPath = codingPath - self.count = 0 - } - - mutating func encodeNil() throws { - // ignore - } - - mutating func encode(_ value: T) throws where T : Encodable { - let encoder = _FormDataEncoder(multipart: multipart, codingPath: codingPath + [index]) - try value.encode(to: encoder) - } - - mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { - return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath + [index])) - } - - mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { - return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath + [index]) - } - - mutating func superEncoder() -> Encoder { - return _FormDataEncoder(multipart: multipart, codingPath: codingPath + [index]) - } -} +// +//private final class FormDataEncoderContext { +// var parts: [MultipartPart] +// init() { +// self.parts = [] +// } +// +// func encode(_ encodable: E, at codingPath: [CodingKey]) throws where E: Encodable { +// guard let convertible = encodable as? MultipartPartConvertible else { +// throw MultipartError(identifier: "convertible", reason: "`\(E.self)` is not `MultipartPartConvertible`.") +// } +// +// var part = try convertible.convertToMultipartPart() +// switch codingPath.count { +// case 1: part.name = codingPath[0].stringValue +// case 2: +// guard codingPath[1].intValue != nil else { +// throw MultipartError(identifier: "nestedEncode", reason: "Nesting is not supported when encoding multipart data.") +// } +// part.name = codingPath[0].stringValue + "[]" +// default: throw MultipartError(identifier: "nestedEncode", reason: "Nesting is not supported when encoding multipart data.") +// } +// parts.append(part) +// } +// +// func encode(_ files: [File], at codingPath: [CodingKey]) throws { +// for file in files { +// try encode(file, at: codingPath) +// } +// } +//} +// +//private struct _FormDataEncoder: Encoder { +// let codingPath: [CodingKey] +// let multipart: FormDataEncoderContext +// var userInfo: [CodingUserInfoKey: Any] { +// return [:] +// } +// +// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { +// self.multipart = multipart +// self.codingPath = codingPath +// } +// +// func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { +// return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath)) +// } +// +// func unkeyedContainer() -> UnkeyedEncodingContainer { +// return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath) +// } +// +// func singleValueContainer() -> SingleValueEncodingContainer { +// return _FormDataSingleValueEncoder(multipart: multipart, codingPath: codingPath) +// } +//} +// +//private struct _FormDataSingleValueEncoder: SingleValueEncodingContainer { +// let multipart: FormDataEncoderContext +// var codingPath: [CodingKey] +// +// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { +// self.multipart = multipart +// self.codingPath = codingPath +// } +// +// mutating func encodeNil() throws { +// // do nothing +// } +// +// mutating func encode(_ value: T) throws where T : Encodable { +// try multipart.encode(value, at: codingPath) +// } +//} +// +//private struct _FormDataKeyedEncoder: KeyedEncodingContainerProtocol where K: CodingKey { +// let multipart: FormDataEncoderContext +// var codingPath: [CodingKey] +// +// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { +// self.multipart = multipart +// self.codingPath = codingPath +// } +// +// mutating func encodeNil(forKey key: K) throws { +// // ignore +// } +// +// mutating func encode(_ value: T, forKey key: K) throws where T : Encodable { +// if value is MultipartPartConvertible { +// try multipart.encode(value, at: codingPath + [key]) +// } else if let value = value as? [File] { +// try multipart.encode(value, at: codingPath + [key]) +// } else { +// let encoder = _FormDataEncoder(multipart: multipart, codingPath: codingPath + [key]) +// try value.encode(to: encoder) +// } +// } +// +// mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer where NestedKey: CodingKey { +// return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath + [key])) +// } +// +// mutating func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer { +// return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath + [key]) +// } +// +// mutating func superEncoder() -> Encoder { +// return _FormDataEncoder(multipart: multipart, codingPath: codingPath) +// } +// +// mutating func superEncoder(forKey key: K) -> Encoder { +// return _FormDataEncoder(multipart: multipart, codingPath: codingPath + [key]) +// } +//} +// +//private struct _FormDataUnkeyedEncoder: UnkeyedEncodingContainer { +// var count: Int +// let multipart: FormDataEncoderContext +// var codingPath: [CodingKey] +// var index: CodingKey { +// return BasicCodingKey.index(0) +// } +// +// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { +// self.multipart = multipart +// self.codingPath = codingPath +// self.count = 0 +// } +// +// mutating func encodeNil() throws { +// // ignore +// } +// +// mutating func encode(_ value: T) throws where T : Encodable { +// let encoder = _FormDataEncoder(multipart: multipart, codingPath: codingPath + [index]) +// try value.encode(to: encoder) +// } +// +// mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { +// return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath + [index])) +// } +// +// mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { +// return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath + [index]) +// } +// +// mutating func superEncoder() -> Encoder { +// return _FormDataEncoder(multipart: multipart, codingPath: codingPath + [index]) +// } +//} diff --git a/Sources/Vapor/Multipart/MultipartError.swift b/Sources/Vapor/Multipart/MultipartError.swift index 8d4d5927e5..9d18c9575d 100644 --- a/Sources/Vapor/Multipart/MultipartError.swift +++ b/Sources/Vapor/Multipart/MultipartError.swift @@ -1,17 +1,17 @@ -/// Errors that can be thrown while working with Multipart. -public struct MultipartError: Error { - /// See `Debuggable`. - public static let readableName = "Multipart Error" - - /// See `Debuggable`. - public let identifier: String - - /// See `Debuggable`. - public var reason: String - - /// Creates a new `MultipartError`. - public init(identifier: String, reason: String) { - self.identifier = identifier - self.reason = reason - } -} +///// Errors that can be thrown while working with Multipart. +//public struct MultipartError: Error { +// /// See `Debuggable`. +// public static let readableName = "Multipart Error" +// +// /// See `Debuggable`. +// public let identifier: String +// +// /// See `Debuggable`. +// public var reason: String +// +// /// Creates a new `MultipartError`. +// public init(identifier: String, reason: String) { +// self.identifier = identifier +// self.reason = reason +// } +//} diff --git a/Sources/Vapor/Multipart/MultipartParser.swift b/Sources/Vapor/Multipart/MultipartParser.swift index 310171d12b..d80319cc66 100644 --- a/Sources/Vapor/Multipart/MultipartParser.swift +++ b/Sources/Vapor/Multipart/MultipartParser.swift @@ -1,194 +1,194 @@ -import CMultipartParser - -/// Parses multipart-encoded `Data` into `MultipartPart`s. Multipart encoding is a widely-used format for encoding/// web-form data that includes rich content like files. It allows for arbitrary data to be encoded -/// in each part thanks to a unique delimiter "boundary" that is defined separately. This -/// boundary is guaranteed by the client to not appear anywhere in the data. -/// -/// `multipart/form-data` is a special case of `multipart` encoding where each part contains a `Content-Disposition` -/// header and name. This is used by the `FormDataEncoder` and `FormDataDecoder` to convert `Codable` types to/from -/// multipart data. -/// -/// See [Wikipedia](https://en.wikipedia.org/wiki/MIME#Multipart_messages) for more information. -/// -/// Seealso `form-urlencoded` encoding where delimiter boundaries are not required. -public final class MultipartParser { - public var onHeader: (String, String) -> () - public var onBody: (inout ByteBuffer) -> () - public var onPartComplete: () -> () - - private var callbacks: multipartparser_callbacks - private var parser: multipartparser - - private enum HeaderState { - case ready - case field(field: String) - case value(field: String, value: String) - } - - private var headerState: HeaderState - - /// Creates a new `MultipartParser`. - public init(boundary: String) { - self.onHeader = { _, _ in } - self.onBody = { _ in } - self.onPartComplete = { } - - var parser = multipartparser() - multipartparser_init(&parser, boundary) - var callbacks = multipartparser_callbacks() - multipartparser_callbacks_init(&callbacks) - self.callbacks = callbacks - self.parser = parser - self.headerState = .ready - self.callbacks.on_header_field = { parser, data, size in - guard let context = Context.from(parser) else { - return 1 - } - let string = String(cPointer: data, count: size) - context.parser.handleHeaderField(string) - return 0 - } - self.callbacks.on_header_value = { parser, data, size in - guard let context = Context.from(parser) else { - return 1 - } - let string = String(cPointer: data, count: size) - context.parser.handleHeaderValue(string) - return 0 - } - self.callbacks.on_data = { parser, data, size in - guard let context = Context.from(parser) else { - return 1 - } - var buffer = context.slice(at: data, count: size) - context.parser.handleData(&buffer) - return 0 - } - self.callbacks.on_body_begin = { parser in - return 0 - } - self.callbacks.on_headers_complete = { parser in - guard let context = Context.from(parser) else { - return 1 - } - context.parser.handleHeadersComplete() - return 0 - } - self.callbacks.on_part_end = { parser in - guard let context = Context.from(parser) else { - return 1 - } - context.parser.handlePartEnd() - return 0 - } - self.callbacks.on_body_end = { parser in - return 0 - } - } - - struct Context { - static func from(_ pointer: UnsafeMutablePointer?) -> Context? { - guard let parser = pointer?.pointee else { - return nil - } - return parser.data.assumingMemoryBound(to: MultipartParser.Context.self).pointee - } - - unowned let parser: MultipartParser - let unsafeBuffer: UnsafeRawBufferPointer - let buffer: ByteBuffer - - func slice(at pointer: UnsafePointer?, count: Int) -> ByteBuffer { - guard let pointer = pointer else { - fatalError("no data pointer") - } - guard let unsafeBufferStart = unsafeBuffer.baseAddress?.assumingMemoryBound(to: Int8.self) else { - fatalError("no base address") - } - let unsafeBufferEnd = unsafeBufferStart.advanced(by: unsafeBuffer.count) - if pointer >= unsafeBufferStart && pointer <= unsafeBufferEnd { - // we were given back a pointer inside our buffer, we can be efficient - let offset = unsafeBufferStart.distance(to: pointer) - guard let buffer = self.buffer.getSlice(at: offset, length: count) else { - fatalError("invalid offset") - } - return buffer - } else { - // the buffer is to somewhere else, like a statically allocated string - // let's create a new buffer - let bytes = UnsafeRawBufferPointer(start: UnsafeRawPointer(pointer), count: count) - var buffer = ByteBufferAllocator().buffer(capacity: 0) - buffer.writeBytes(bytes) - return buffer - } - } - } - - public func execute(_ string: String) throws { - var buffer = ByteBufferAllocator().buffer(capacity: string.utf8.count) - buffer.writeString(string) - return try self.execute(buffer) - } - - public func execute(_ buffer: ByteBuffer) throws { - let result = buffer.withUnsafeReadableBytes { (unsafeBuffer: UnsafeRawBufferPointer) -> Int in - var context = Context(parser: self, unsafeBuffer: unsafeBuffer, buffer: buffer) - return withUnsafeMutablePointer(to: &context) { (contextPointer: UnsafeMutablePointer) -> Int in - self.parser.data = .init(contextPointer) - return multipartparser_execute(&self.parser, &self.callbacks, unsafeBuffer.baseAddress?.assumingMemoryBound(to: Int8.self), unsafeBuffer.count) - } - } - guard result == buffer.readableBytes else { - throw Abort(.unprocessableEntity) - } - } - - // MARK: Private - - private func handleHeaderField(_ new: String) { - switch self.headerState { - case .ready: - self.headerState = .field(field: new) - case .field(let existing): - self.headerState = .field(field: existing + new) - case .value(let field, let value): - self.onHeader(field, value) - self.headerState = .field(field: new) - } - } - - private func handleHeaderValue(_ new: String) { - switch self.headerState { - case .field(let name): - self.headerState = .value(field: name, value: new) - case .value(let name, let existing): - self.headerState = .value(field: name, value: existing + new) - default: fatalError() - } - } - - private func handleHeadersComplete() { - switch self.headerState { - case .value(let field, let value): - self.onHeader(field, value) - self.headerState = .ready - case .ready: break - default: fatalError() - } - } - - private func handleData(_ data: inout ByteBuffer) { - self.onBody(&data) - } - - private func handlePartEnd() { - self.onPartComplete() - } -} - -private extension String { - init(cPointer: UnsafePointer?, count: Int) { - let pointer = UnsafeRawPointer(cPointer)?.assumingMemoryBound(to: UInt8.self) - self.init(decoding: UnsafeBufferPointer(start: pointer, count: count), as: UTF8.self) - } -} +//import CMultipartParser +// +///// Parses multipart-encoded `Data` into `MultipartPart`s. Multipart encoding is a widely-used format for encoding/// web-form data that includes rich content like files. It allows for arbitrary data to be encoded +///// in each part thanks to a unique delimiter "boundary" that is defined separately. This +///// boundary is guaranteed by the client to not appear anywhere in the data. +///// +///// `multipart/form-data` is a special case of `multipart` encoding where each part contains a `Content-Disposition` +///// header and name. This is used by the `FormDataEncoder` and `FormDataDecoder` to convert `Codable` types to/from +///// multipart data. +///// +///// See [Wikipedia](https://en.wikipedia.org/wiki/MIME#Multipart_messages) for more information. +///// +///// Seealso `form-urlencoded` encoding where delimiter boundaries are not required. +//public final class MultipartParser { +// public var onHeader: (String, String) -> () +// public var onBody: (inout ByteBuffer) -> () +// public var onPartComplete: () -> () +// +// private var callbacks: multipartparser_callbacks +// private var parser: multipartparser +// +// private enum HeaderState { +// case ready +// case field(field: String) +// case value(field: String, value: String) +// } +// +// private var headerState: HeaderState +// +// /// Creates a new `MultipartParser`. +// public init(boundary: String) { +// self.onHeader = { _, _ in } +// self.onBody = { _ in } +// self.onPartComplete = { } +// +// var parser = multipartparser() +// multipartparser_init(&parser, boundary) +// var callbacks = multipartparser_callbacks() +// multipartparser_callbacks_init(&callbacks) +// self.callbacks = callbacks +// self.parser = parser +// self.headerState = .ready +// self.callbacks.on_header_field = { parser, data, size in +// guard let context = Context.from(parser) else { +// return 1 +// } +// let string = String(cPointer: data, count: size) +// context.parser.handleHeaderField(string) +// return 0 +// } +// self.callbacks.on_header_value = { parser, data, size in +// guard let context = Context.from(parser) else { +// return 1 +// } +// let string = String(cPointer: data, count: size) +// context.parser.handleHeaderValue(string) +// return 0 +// } +// self.callbacks.on_data = { parser, data, size in +// guard let context = Context.from(parser) else { +// return 1 +// } +// var buffer = context.slice(at: data, count: size) +// context.parser.handleData(&buffer) +// return 0 +// } +// self.callbacks.on_body_begin = { parser in +// return 0 +// } +// self.callbacks.on_headers_complete = { parser in +// guard let context = Context.from(parser) else { +// return 1 +// } +// context.parser.handleHeadersComplete() +// return 0 +// } +// self.callbacks.on_part_end = { parser in +// guard let context = Context.from(parser) else { +// return 1 +// } +// context.parser.handlePartEnd() +// return 0 +// } +// self.callbacks.on_body_end = { parser in +// return 0 +// } +// } +// +// struct Context { +// static func from(_ pointer: UnsafeMutablePointer?) -> Context? { +// guard let parser = pointer?.pointee else { +// return nil +// } +// return parser.data.assumingMemoryBound(to: MultipartParser.Context.self).pointee +// } +// +// unowned let parser: MultipartParser +// let unsafeBuffer: UnsafeRawBufferPointer +// let buffer: ByteBuffer +// +// func slice(at pointer: UnsafePointer?, count: Int) -> ByteBuffer { +// guard let pointer = pointer else { +// fatalError("no data pointer") +// } +// guard let unsafeBufferStart = unsafeBuffer.baseAddress?.assumingMemoryBound(to: Int8.self) else { +// fatalError("no base address") +// } +// let unsafeBufferEnd = unsafeBufferStart.advanced(by: unsafeBuffer.count) +// if pointer >= unsafeBufferStart && pointer <= unsafeBufferEnd { +// // we were given back a pointer inside our buffer, we can be efficient +// let offset = unsafeBufferStart.distance(to: pointer) +// guard let buffer = self.buffer.getSlice(at: offset, length: count) else { +// fatalError("invalid offset") +// } +// return buffer +// } else { +// // the buffer is to somewhere else, like a statically allocated string +// // let's create a new buffer +// let bytes = UnsafeRawBufferPointer(start: UnsafeRawPointer(pointer), count: count) +// var buffer = ByteBufferAllocator().buffer(capacity: 0) +// buffer.writeBytes(bytes) +// return buffer +// } +// } +// } +// +// public func execute(_ string: String) throws { +// var buffer = ByteBufferAllocator().buffer(capacity: string.utf8.count) +// buffer.writeString(string) +// return try self.execute(buffer) +// } +// +// public func execute(_ buffer: ByteBuffer) throws { +// let result = buffer.withUnsafeReadableBytes { (unsafeBuffer: UnsafeRawBufferPointer) -> Int in +// var context = Context(parser: self, unsafeBuffer: unsafeBuffer, buffer: buffer) +// return withUnsafeMutablePointer(to: &context) { (contextPointer: UnsafeMutablePointer) -> Int in +// self.parser.data = .init(contextPointer) +// return multipartparser_execute(&self.parser, &self.callbacks, unsafeBuffer.baseAddress?.assumingMemoryBound(to: Int8.self), unsafeBuffer.count) +// } +// } +// guard result == buffer.readableBytes else { +// throw Abort(.unprocessableEntity) +// } +// } +// +// // MARK: Private +// +// private func handleHeaderField(_ new: String) { +// switch self.headerState { +// case .ready: +// self.headerState = .field(field: new) +// case .field(let existing): +// self.headerState = .field(field: existing + new) +// case .value(let field, let value): +// self.onHeader(field, value) +// self.headerState = .field(field: new) +// } +// } +// +// private func handleHeaderValue(_ new: String) { +// switch self.headerState { +// case .field(let name): +// self.headerState = .value(field: name, value: new) +// case .value(let name, let existing): +// self.headerState = .value(field: name, value: existing + new) +// default: fatalError() +// } +// } +// +// private func handleHeadersComplete() { +// switch self.headerState { +// case .value(let field, let value): +// self.onHeader(field, value) +// self.headerState = .ready +// case .ready: break +// default: fatalError() +// } +// } +// +// private func handleData(_ data: inout ByteBuffer) { +// self.onBody(&data) +// } +// +// private func handlePartEnd() { +// self.onPartComplete() +// } +//} +// +//private extension String { +// init(cPointer: UnsafePointer?, count: Int) { +// let pointer = UnsafeRawPointer(cPointer)?.assumingMemoryBound(to: UInt8.self) +// self.init(decoding: UnsafeBufferPointer(start: pointer, count: count), as: UTF8.self) +// } +//} diff --git a/Sources/Vapor/Multipart/MultipartPart.swift b/Sources/Vapor/Multipart/MultipartPart.swift index f145e41b0c..c4d5bbc456 100644 --- a/Sources/Vapor/Multipart/MultipartPart.swift +++ b/Sources/Vapor/Multipart/MultipartPart.swift @@ -1,104 +1,104 @@ -/// A single part of a `multipart`-encoded message. -public struct MultipartPart: Equatable { - /// The part's headers. - public var headers: [String: String] - - /// The part's raw data. - public var body: ByteBuffer - - /// Gets or sets the `filename` attribute from the part's `"Content-Disposition"` header. - public var filename: String? { - get { return contentDisposition?.parameters["filename"] } - set { - var value: HTTPHeaderValue - if let existing = contentDisposition { - value = existing - } else { - value = HTTPHeaderValue("form-data") - } - value.parameters["filename"] = newValue - contentDisposition = value - } - } - - /// Gets or sets the `name` attribute from the part's `"Content-Disposition"` header. - public var name: String? { - get { return contentDisposition?.parameters["name"] } - set { - var value: HTTPHeaderValue - if let existing = contentDisposition { - value = existing - } else { - value = HTTPHeaderValue("form-data") - } - value.parameters["name"] = newValue - contentDisposition = value - } - } - - /// Gets or sets the part's `"Content-Disposition"` header. - public var contentDisposition: HTTPHeaderValue? { - get { return headers["Content-Disposition"].flatMap { HTTPHeaderValue.parse($0) } } - set { headers["Content-Disposition"] = newValue?.serialize() } - } - - /// Gets or sets the part's `"Content-Type"` header. - public var contentType: HTTPMediaType? { - get { return headers["Content-Type"].flatMap { HTTPMediaType.parse($0) } } - set { headers["Content-Type"] = newValue?.serialize() } - } - - /// Creates a new `MultipartPart`. - /// - /// let part = MultipartPart(headers: ["Content-Type": "text/plain"], body: "hello") - /// - /// - parameters: - /// - headers: The part's headers. - /// - body: The part's data. - public init(headers: [String: String] = [:], body: String) { - var buffer = ByteBufferAllocator().buffer(capacity: body.utf8.count) - buffer.writeString(body) - self.init(headers: headers, body: buffer) - } - - /// Creates a new `MultipartPart`. - /// - /// let part = MultipartPart(headers: ["Content-Type": "text/plain"], body: "hello") - /// - /// - parameters: - /// - headers: The part's headers. - /// - body: The part's data. - public init(headers: [String: String] = [:], body: ByteBuffer) { - self.headers = headers - self.body = body - } -} - -// MARK: Array Extensions - -extension Array where Element == MultipartPart { - /// Returns the first `MultipartPart` with matching name attribute in `"Content-Disposition"` header. - public func firstPart(named name: String) -> MultipartPart? { - for el in self { - if el.name == name { - return el - } - } - return nil - } - - /// Returns all `MultipartPart`s with matching name attribute in `"Content-Disposition"` header. - public func allParts(named name: String) -> [MultipartPart] { - return filter { $0.name == name } - } - - /// Returns the first `MultipartPart` with matching filename attribute in `"Content-Disposition"` header. - public func firstFile(filename: String) -> MultipartPart? { - for el in self { - if el.filename == filename { - return el - } - } - return nil - } -} +///// A single part of a `multipart`-encoded message. +//public struct MultipartPart: Equatable { +// /// The part's headers. +// public var headers: [String: String] +// +// /// The part's raw data. +// public var body: ByteBuffer +// +// /// Gets or sets the `filename` attribute from the part's `"Content-Disposition"` header. +// public var filename: String? { +// get { return contentDisposition?.parameters["filename"] } +// set { +// var value: HTTPHeaderValue +// if let existing = contentDisposition { +// value = existing +// } else { +// value = HTTPHeaderValue("form-data") +// } +// value.parameters["filename"] = newValue +// contentDisposition = value +// } +// } +// +// /// Gets or sets the `name` attribute from the part's `"Content-Disposition"` header. +// public var name: String? { +// get { return contentDisposition?.parameters["name"] } +// set { +// var value: HTTPHeaderValue +// if let existing = contentDisposition { +// value = existing +// } else { +// value = HTTPHeaderValue("form-data") +// } +// value.parameters["name"] = newValue +// contentDisposition = value +// } +// } +// +// /// Gets or sets the part's `"Content-Disposition"` header. +// public var contentDisposition: HTTPHeaderValue? { +// get { return headers["Content-Disposition"].flatMap { HTTPHeaderValue.parse($0) } } +// set { headers["Content-Disposition"] = newValue?.serialize() } +// } +// +// /// Gets or sets the part's `"Content-Type"` header. +// public var contentType: HTTPMediaType? { +// get { return headers["Content-Type"].flatMap { HTTPMediaType.parse($0) } } +// set { headers["Content-Type"] = newValue?.serialize() } +// } +// +// /// Creates a new `MultipartPart`. +// /// +// /// let part = MultipartPart(headers: ["Content-Type": "text/plain"], body: "hello") +// /// +// /// - parameters: +// /// - headers: The part's headers. +// /// - body: The part's data. +// public init(headers: [String: String] = [:], body: String) { +// var buffer = ByteBufferAllocator().buffer(capacity: body.utf8.count) +// buffer.writeString(body) +// self.init(headers: headers, body: buffer) +// } +// +// /// Creates a new `MultipartPart`. +// /// +// /// let part = MultipartPart(headers: ["Content-Type": "text/plain"], body: "hello") +// /// +// /// - parameters: +// /// - headers: The part's headers. +// /// - body: The part's data. +// public init(headers: [String: String] = [:], body: ByteBuffer) { +// self.headers = headers +// self.body = body +// } +//} +// +//// MARK: Array Extensions +// +//extension Array where Element == MultipartPart { +// /// Returns the first `MultipartPart` with matching name attribute in `"Content-Disposition"` header. +// public func firstPart(named name: String) -> MultipartPart? { +// for el in self { +// if el.name == name { +// return el +// } +// } +// return nil +// } +// +// /// Returns all `MultipartPart`s with matching name attribute in `"Content-Disposition"` header. +// public func allParts(named name: String) -> [MultipartPart] { +// return filter { $0.name == name } +// } +// +// /// Returns the first `MultipartPart` with matching filename attribute in `"Content-Disposition"` header. +// public func firstFile(filename: String) -> MultipartPart? { +// for el in self { +// if el.filename == filename { +// return el +// } +// } +// return nil +// } +//} diff --git a/Sources/Vapor/Multipart/MultipartPartConvertible.swift b/Sources/Vapor/Multipart/MultipartPartConvertible.swift index 9d5e1c77d6..e9049d0694 100644 --- a/Sources/Vapor/Multipart/MultipartPartConvertible.swift +++ b/Sources/Vapor/Multipart/MultipartPartConvertible.swift @@ -1,136 +1,136 @@ -import struct Foundation.Data - -/// Supports converting to / from a `MultipartPart`. -public protocol MultipartPartConvertible { - /// Converts `self` to `MultipartPart`. - func convertToMultipartPart() throws -> MultipartPart - - /// Converts a `MultipartPart` to `Self`. - static func convertFromMultipartPart(_ part: MultipartPart) throws -> Self -} - -extension MultipartPart: MultipartPartConvertible { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { return self } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> MultipartPart { return part } -} - -extension String: MultipartPartConvertible { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { - return MultipartPart(body: self) - } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> String { - var buffer = part.body - return buffer.readString(length: buffer.readableBytes)! - } -} - -extension FixedWidthInteger { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { - return MultipartPart(headers: [:], body: self.description) - } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Self { - guard let fwi = try Self(String.convertFromMultipartPart(part)) else { - throw MultipartError(identifier: "int", reason: "Could not convert `Data` to `\(Self.self)`.") - } - return fwi - } -} - -extension Int: MultipartPartConvertible { } -extension Int8: MultipartPartConvertible { } -extension Int16: MultipartPartConvertible { } -extension Int32: MultipartPartConvertible { } -extension Int64: MultipartPartConvertible { } -extension UInt: MultipartPartConvertible { } -extension UInt8: MultipartPartConvertible { } -extension UInt16: MultipartPartConvertible { } -extension UInt32: MultipartPartConvertible { } -extension UInt64: MultipartPartConvertible { } - - -extension Float: MultipartPartConvertible { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { - return MultipartPart(body: description) - } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Float { - guard let float = try Float(String.convertFromMultipartPart(part)) else { - throw MultipartError(identifier: "float", reason: "Could not convert `Data` to `\(Float.self)`.") - } - return float - } -} - -extension Double: MultipartPartConvertible { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { - return MultipartPart(body: description) - } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Double { - guard let double = try Double(String.convertFromMultipartPart(part)) else { - throw MultipartError(identifier: "double", reason: "Could not convert `Data` to `\(Double.self)`.") - } - return double - } -} - -extension Bool: MultipartPartConvertible { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { - return MultipartPart(body: description) - } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Bool { - guard let option = try Bool(String.convertFromMultipartPart(part)) else { - throw MultipartError(identifier: "boolean", reason: "Could not convert `Data` to `Bool`. Must be one of: [true, false]") - } - return option - } -} - -extension File: MultipartPartConvertible { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { - var part = MultipartPart(body: data) - part.filename = filename - part.contentType = contentType - return part - } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> File { - guard let filename = part.filename else { - throw MultipartError(identifier: "filename", reason: "Multipart part missing a filename.") - } - return File(data: part.body, filename: filename) - } -} - -extension Data: MultipartPartConvertible { - /// See `MultipartPartConvertible`. - public func convertToMultipartPart() throws -> MultipartPart { - var buffer = ByteBufferAllocator().buffer(capacity: self.count) - buffer.writeBytes(self) - return MultipartPart(body: buffer) - } - - /// See `MultipartPartConvertible`. - public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Data { - var buffer = part.body - return buffer.readData(length: buffer.readableBytes)! - } -} +//import struct Foundation.Data +// +///// Supports converting to / from a `MultipartPart`. +//public protocol MultipartPartConvertible { +// /// Converts `self` to `MultipartPart`. +// func convertToMultipartPart() throws -> MultipartPart +// +// /// Converts a `MultipartPart` to `Self`. +// static func convertFromMultipartPart(_ part: MultipartPart) throws -> Self +//} +// +//extension MultipartPart: MultipartPartConvertible { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { return self } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> MultipartPart { return part } +//} +// +//extension String: MultipartPartConvertible { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { +// return MultipartPart(body: self) +// } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> String { +// var buffer = part.body +// return buffer.readString(length: buffer.readableBytes)! +// } +//} +// +//extension FixedWidthInteger { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { +// return MultipartPart(headers: [:], body: self.description) +// } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Self { +// guard let fwi = try Self(String.convertFromMultipartPart(part)) else { +// throw MultipartError(identifier: "int", reason: "Could not convert `Data` to `\(Self.self)`.") +// } +// return fwi +// } +//} +// +//extension Int: MultipartPartConvertible { } +//extension Int8: MultipartPartConvertible { } +//extension Int16: MultipartPartConvertible { } +//extension Int32: MultipartPartConvertible { } +//extension Int64: MultipartPartConvertible { } +//extension UInt: MultipartPartConvertible { } +//extension UInt8: MultipartPartConvertible { } +//extension UInt16: MultipartPartConvertible { } +//extension UInt32: MultipartPartConvertible { } +//extension UInt64: MultipartPartConvertible { } +// +// +//extension Float: MultipartPartConvertible { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { +// return MultipartPart(body: description) +// } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Float { +// guard let float = try Float(String.convertFromMultipartPart(part)) else { +// throw MultipartError(identifier: "float", reason: "Could not convert `Data` to `\(Float.self)`.") +// } +// return float +// } +//} +// +//extension Double: MultipartPartConvertible { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { +// return MultipartPart(body: description) +// } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Double { +// guard let double = try Double(String.convertFromMultipartPart(part)) else { +// throw MultipartError(identifier: "double", reason: "Could not convert `Data` to `\(Double.self)`.") +// } +// return double +// } +//} +// +//extension Bool: MultipartPartConvertible { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { +// return MultipartPart(body: description) +// } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Bool { +// guard let option = try Bool(String.convertFromMultipartPart(part)) else { +// throw MultipartError(identifier: "boolean", reason: "Could not convert `Data` to `Bool`. Must be one of: [true, false]") +// } +// return option +// } +//} +// +//extension File: MultipartPartConvertible { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { +// var part = MultipartPart(body: data) +// part.filename = filename +// part.contentType = contentType +// return part +// } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> File { +// guard let filename = part.filename else { +// throw MultipartError(identifier: "filename", reason: "Multipart part missing a filename.") +// } +// return File(data: part.body, filename: filename) +// } +//} +// +//extension Data: MultipartPartConvertible { +// /// See `MultipartPartConvertible`. +// public func convertToMultipartPart() throws -> MultipartPart { +// var buffer = ByteBufferAllocator().buffer(capacity: self.count) +// buffer.writeBytes(self) +// return MultipartPart(body: buffer) +// } +// +// /// See `MultipartPartConvertible`. +// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Data { +// var buffer = part.body +// return buffer.readData(length: buffer.readableBytes)! +// } +//} diff --git a/Sources/Vapor/Multipart/MultipartSerializer.swift b/Sources/Vapor/Multipart/MultipartSerializer.swift index 429ff2e1e0..848512bbdb 100644 --- a/Sources/Vapor/Multipart/MultipartSerializer.swift +++ b/Sources/Vapor/Multipart/MultipartSerializer.swift @@ -1,44 +1,44 @@ -/// Serializes `MultipartForm`s to `Data`. -/// -/// See `MultipartParser` for more information about the multipart encoding. -public final class MultipartSerializer { - /// Creates a new `MultipartSerializer`. - public init() { } - - public func serialize(parts: [MultipartPart], boundary: String) throws -> String { - var buffer = ByteBufferAllocator().buffer(capacity: 0) - try self.serialize(parts: parts, boundary: boundary, into: &buffer) - return buffer.readString(length: buffer.readableBytes)! - } - - /// Serializes the `MultipartForm` to data. - /// - /// let data = try MultipartSerializer().serialize(parts: [part], boundary: "123") - /// print(data) // multipart-encoded - /// - /// - parameters: - /// - parts: One or more `MultipartPart`s to serialize into `Data`. - /// - boundary: Multipart boundary to use for encoding. This must not appear anywhere in the encoded data. - /// - throws: Any errors that may occur during serialization. - /// - returns: `multipart`-encoded `Data`. - public func serialize(parts: [MultipartPart], boundary: String, into buffer: inout ByteBuffer) throws { - for part in parts { - buffer.writeString("--") - buffer.writeString(boundary) - buffer.writeString("\r\n") - for (key, val) in part.headers { - buffer.writeString(key) - buffer.writeString(": ") - buffer.writeString(val) - buffer.writeString("\r\n") - } - buffer.writeString("\r\n") - var body = part.body - buffer.writeBuffer(&body) - buffer.writeString("\r\n") - } - buffer.writeString("--") - buffer.writeString(boundary) - buffer.writeString("--\r\n") - } -} +///// Serializes `MultipartForm`s to `Data`. +///// +///// See `MultipartParser` for more information about the multipart encoding. +//public final class MultipartSerializer { +// /// Creates a new `MultipartSerializer`. +// public init() { } +// +// public func serialize(parts: [MultipartPart], boundary: String) throws -> String { +// var buffer = ByteBufferAllocator().buffer(capacity: 0) +// try self.serialize(parts: parts, boundary: boundary, into: &buffer) +// return buffer.readString(length: buffer.readableBytes)! +// } +// +// /// Serializes the `MultipartForm` to data. +// /// +// /// let data = try MultipartSerializer().serialize(parts: [part], boundary: "123") +// /// print(data) // multipart-encoded +// /// +// /// - parameters: +// /// - parts: One or more `MultipartPart`s to serialize into `Data`. +// /// - boundary: Multipart boundary to use for encoding. This must not appear anywhere in the encoded data. +// /// - throws: Any errors that may occur during serialization. +// /// - returns: `multipart`-encoded `Data`. +// public func serialize(parts: [MultipartPart], boundary: String, into buffer: inout ByteBuffer) throws { +// for part in parts { +// buffer.writeString("--") +// buffer.writeString(boundary) +// buffer.writeString("\r\n") +// for (key, val) in part.headers { +// buffer.writeString(key) +// buffer.writeString(": ") +// buffer.writeString(val) +// buffer.writeString("\r\n") +// } +// buffer.writeString("\r\n") +// var body = part.body +// buffer.writeBuffer(&body) +// buffer.writeString("\r\n") +// } +// buffer.writeString("--") +// buffer.writeString(boundary) +// buffer.writeString("--\r\n") +// } +//} From bda59f6d4fd051e65dedfc2bdda899cc9b847c5f Mon Sep 17 00:00:00 2001 From: Alex Reilly Date: Tue, 15 Oct 2019 17:21:12 -0700 Subject: [PATCH 2/4] removed multipart tests --- Tests/VaporTests/MultipartTests.swift | 399 -------------------------- 1 file changed, 399 deletions(-) delete mode 100644 Tests/VaporTests/MultipartTests.swift diff --git a/Tests/VaporTests/MultipartTests.swift b/Tests/VaporTests/MultipartTests.swift deleted file mode 100644 index c7664f8288..0000000000 --- a/Tests/VaporTests/MultipartTests.swift +++ /dev/null @@ -1,399 +0,0 @@ -import Vapor -import XCTest - -class MultipartTests: XCTestCase { - let named = """ - test123 - aijdisadi>SDASDdwekqie4u219034u129e0wque90qjsd90asffs - - - SDASD [SubSequence] { - precondition(maxLength > 0, "groups must be greater than zero") - var start = startIndex - return stride(from: 0, to: count, by: maxLength).map { _ in - let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex - defer { start = end } - return self[start.." - } -} From ffb897f07ea86d10b81afb040669fbd63c908ac2 Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 22 Oct 2019 14:03:27 -0400 Subject: [PATCH 3/4] multipart updates --- Package.swift | 17 +- .../Vapor/Content/ContentConfiguration.swift | 2 - Sources/Vapor/Exports.swift | 2 + Sources/Vapor/Multipart/File+Multipart.swift | 57 ++++ Sources/Vapor/Multipart/FormDataDecoder.swift | 267 +----------------- Sources/Vapor/Multipart/FormDataEncoder.swift | 208 +------------- Sources/Vapor/Multipart/MultipartError.swift | 17 -- Sources/Vapor/Multipart/MultipartParser.swift | 194 ------------- Sources/Vapor/Multipart/MultipartPart.swift | 104 ------- .../Multipart/MultipartPartConvertible.swift | 136 --------- .../Vapor/Multipart/MultipartSerializer.swift | 44 --- Tests/VaporTests/ApplicationTests.swift | 14 +- 12 files changed, 97 insertions(+), 965 deletions(-) create mode 100644 Sources/Vapor/Multipart/File+Multipart.swift delete mode 100644 Sources/Vapor/Multipart/MultipartError.swift delete mode 100644 Sources/Vapor/Multipart/MultipartParser.swift delete mode 100644 Sources/Vapor/Multipart/MultipartPart.swift delete mode 100644 Sources/Vapor/Multipart/MultipartPartConvertible.swift delete mode 100644 Sources/Vapor/Multipart/MultipartSerializer.swift diff --git a/Package.swift b/Package.swift index 82051dafe7..b785611f77 100644 --- a/Package.swift +++ b/Package.swift @@ -11,12 +11,18 @@ let package = Package( .library(name: "XCTVapor", targets: ["XCTVapor"]) ], dependencies: [ + // HTTP client library built on SwiftNIO + .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.0.0-alpha.1"), + // Sugary extensions for the SwiftNIO library .package(url: "https://github.com/vapor/async-kit.git", .branch("master")), // 💻 APIs for creating interactive CLI tools. .package(url: "https://github.com/vapor/console-kit.git", .branch("master")), + // Parses and serializes multipart-encoded data with Codable support. + .package(url: "https://github.com/vapor/multipart-kit.git", .branch("master")), + // 🔑 Hashing (BCrypt, SHA2, HMAC), encryption (AES), public-key (RSA), and random data generation. .package(url: "https://github.com/vapor/open-crypto.git", from: "4.0.0-alpha.2"), @@ -38,43 +44,36 @@ let package = Package( // Swift logging API .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"), - // HTTP client library built on SwiftNIO - .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.0.0-alpha.1"), - // WebSocket client library built on SwiftNIO .package(url: "https://github.com/vapor/websocket-kit.git", .branch("master")), - - .package(url: "https://github.com/vapor/multipart.git", .branch("VaporPort")) ], targets: [ // C helpers .target(name: "CBcrypt"), -// .target(name: "CMultipartParser"), .target(name: "COperatingSystem"), .target(name: "CURLParser"), // Vapor .target(name: "Vapor", dependencies: [ + "AsyncHTTPClient", "AsyncKit", "CBcrypt", -// "CMultipartParser", "COperatingSystem", "CURLParser", "ConsoleKit", "Logging", + "MultipartKit", "NIO", "NIOExtras", "NIOFoundationCompat", "NIOHTTPCompression", "NIOHTTP1", "NIOHTTP2", - "AsyncHTTPClient", "NIOSSL", "NIOWebSocket", "OpenCrypto", "RoutingKit", "WebSocketKit", - "Multipart" ]), // Development diff --git a/Sources/Vapor/Content/ContentConfiguration.swift b/Sources/Vapor/Content/ContentConfiguration.swift index cbefb4bd09..244407912a 100644 --- a/Sources/Vapor/Content/ContentConfiguration.swift +++ b/Sources/Vapor/Content/ContentConfiguration.swift @@ -1,5 +1,3 @@ -import Multipart - /// Configures which `Encoder`s and `Decoder`s to use when interacting with data in HTTP messages. /// /// var contentConfig = ContentConfig() diff --git a/Sources/Vapor/Exports.swift b/Sources/Vapor/Exports.swift index 8f032318f8..d99d03e3a6 100644 --- a/Sources/Vapor/Exports.swift +++ b/Sources/Vapor/Exports.swift @@ -9,6 +9,8 @@ @_exported import Logging +@_exported import MultipartKit + @_exported import struct NIO.ByteBuffer @_exported import struct NIO.ByteBufferAllocator @_exported import protocol NIO.Channel diff --git a/Sources/Vapor/Multipart/File+Multipart.swift b/Sources/Vapor/Multipart/File+Multipart.swift new file mode 100644 index 0000000000..b0cdfee6b3 --- /dev/null +++ b/Sources/Vapor/Multipart/File+Multipart.swift @@ -0,0 +1,57 @@ +extension File: MultipartPartConvertible { + public var multipart: MultipartPart? { + var part = MultipartPart(headers: [:], body: .init(self.data.readableBytesView)) + part.contentType = self.extension + .flatMap { HTTPMediaType.fileExtension($0) } + .flatMap { $0.serialize() } + part.filename = self.filename + return part + } + + public init?(multipart: MultipartPart) { + guard let filename = multipart.filename else { + return nil + } + self.init(data: multipart.body, filename: filename) + } +} + +extension MultipartPart { + public var contentType: String? { + get { return self.headers.firstValue(name: .contentType) } + set { + if let value = newValue { + self.headers.replaceOrAdd(name: .contentType, value: value) + } else { + self.headers.remove(name: .contentType) + } + } + } + + public var filename: String? { + get { + return self.contentDisposition?.parameters["filename"] + } + set { + var disposition: HTTPHeaderValue + if let existing = self.contentDisposition { + disposition = existing + } else { + disposition = HTTPHeaderValue("form-data") + } + disposition.parameters["filename"] = newValue + self.contentDisposition = disposition + } + } + + public var contentDisposition: HTTPHeaderValue? { + get { self.headers.firstValue(name: .contentDisposition).flatMap { .parse($0) } } + set { + if let value = newValue { + self.headers.replaceOrAdd(name: .contentDisposition, value: value.serialize()) + } else { + self.headers.remove(name: .contentDisposition) + } + } + } +} diff --git a/Sources/Vapor/Multipart/FormDataDecoder.swift b/Sources/Vapor/Multipart/FormDataDecoder.swift index 8b7566ea2b..ad32d2342d 100644 --- a/Sources/Vapor/Multipart/FormDataDecoder.swift +++ b/Sources/Vapor/Multipart/FormDataDecoder.swift @@ -1,258 +1,13 @@ -import Multipart - extension FormDataDecoder: ContentDecoder { - /// `ContentDecoder` conformance. - public func decode(_ decodable: D.Type, from body: ByteBuffer, headers: HTTPHeaders) throws -> D - where D: Decodable - { - guard let boundary = headers.contentType?.parameters["boundary"] else { - throw Abort(.unsupportedMediaType) - } - return try self.decode(D.self, from: body, boundary: boundary) - } + /// `ContentDecoder` conformance. + public func decode(_ decodable: D.Type, from body: ByteBuffer, headers: HTTPHeaders) throws -> D + where D: Decodable + { + guard let boundary = headers.contentType?.parameters["boundary"] else { + throw Abort(.unsupportedMediaType) + } + var body = body + let buffer = body.readBytes(length: body.readableBytes) ?? [] + return try self.decode(D.self, from: buffer, boundary: boundary) + } } - -///// Decodes `Decodable` types from `multipart/form-data` encoded `Data`. -///// -///// See [RFC#2388](https://tools.ietf.org/html/rfc2388) for more information about `multipart/form-data` encoding. -///// -///// Seealso `MultipartParser` for more information about the `multipart` encoding. -//public struct FormDataDecoder: ContentDecoder { -// /// Creates a new `FormDataDecoder`. -// public init() { } -// -// -// -// public func decode(_ decodable: D.Type, from data: String, boundary: String) throws -> D -// where D: Decodable -// { -// var buffer = ByteBufferAllocator().buffer(capacity: data.utf8.count) -// buffer.writeString(data) -// return try self.decode(D.self, from: buffer, boundary: boundary) -// } -// -// /// Decodes a `Decodable` item from `Data` using the supplied boundary. -// /// -// /// let foo = try FormDataDecoder().decode(Foo.self, from: data, boundary: "123") -// /// -// /// - parameters: -// /// - encodable: Generic `Decodable` type. -// /// - boundary: Multipart boundary to used in the encoding. -// /// - throws: Any errors decoding the model with `Codable` or parsing the data. -// /// - returns: An instance of the decoded type `D`. -// public func decode(_ decodable: D.Type, from data: ByteBuffer, boundary: String) throws -> D -// where D: Decodable -// { -// let parser = MultipartParser(boundary: boundary) -// -// var parts: [MultipartPart] = [] -// var headers: [String: String] = [:] -// var body: ByteBuffer? = nil -// -// parser.onHeader = { (field, value) in -// headers[field] = value -// } -// parser.onBody = { new in -// if var existing = body { -// existing.writeBuffer(&new) -// body = existing -// } else { -// body = new -// } -// } -// parser.onPartComplete = { -// let part = MultipartPart(headers: headers, body: body!) -// headers = [:] -// body = nil -// parts.append(part) -// } -// -// try parser.execute(data) -// let multipart = FormDataDecoderContext(parts: parts) -// let decoder = _FormDataDecoder(multipart: multipart, codingPath: []) -// return try D(from: decoder) -// } -//} -// -//// MARK: Private -// -//private final class FormDataDecoderContext { -// var parts: [MultipartPart] -// init(parts: [MultipartPart]) { -// self.parts = parts -// } -// -// func decode(_ decodable: D.Type, at codingPath: [CodingKey]) throws -> D where D: Decodable { -// guard let convertible = D.self as? MultipartPartConvertible.Type else { -// throw MultipartError(identifier: "convertible", reason: "`\(D.self)` is not `MultipartPartConvertible`.") -// } -// -// let part: MultipartPart -// switch codingPath.count { -// case 1: -// let name = codingPath[0].stringValue -// guard let p = parts.firstPart(named: name) else { -// throw MultipartError(identifier: "missingPart", reason: "No multipart part named '\(name)' was found.") -// } -// part = p -// case 2: -// let name = codingPath[0].stringValue + "[]" -// guard let offset = codingPath[1].intValue else { -// throw MultipartError(identifier: "arrayOffset", reason: "Nested form-data is not supported.") -// } -// part = parts.allParts(named: name)[offset] -// default: throw MultipartError(identifier: "nested", reason: "Nested form-data is not supported.") -// } -// -// return try convertible.convertFromMultipartPart(part) as! D -// } -//} -// -// -//private struct _FormDataDecoder: Decoder { -// var codingPath: [CodingKey] -// var userInfo: [CodingUserInfoKey: Any] { -// return [:] -// } -// let multipart: FormDataDecoderContext -// -// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { -// self.multipart = multipart -// self.codingPath = codingPath -// } -// -// func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { -// return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath)) -// } -// -// func unkeyedContainer() throws -> UnkeyedDecodingContainer { -// return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath) -// } -// -// func singleValueContainer() throws -> SingleValueDecodingContainer { -// return _FormDataSingleValueDecoder(multipart: multipart, codingPath: codingPath) -// } -//} -// -//private struct _FormDataSingleValueDecoder: SingleValueDecodingContainer { -// var codingPath: [CodingKey] -// let multipart: FormDataDecoderContext -// -// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { -// self.multipart = multipart -// self.codingPath = codingPath -// } -// -// func decodeNil() -> Bool { -// return false -// } -// -// func decode(_ type: T.Type) throws -> T where T: Decodable { -// return try multipart.decode(T.self, at: codingPath) -// } -//} -// -//private struct _FormDataKeyedDecoder: KeyedDecodingContainerProtocol where K: CodingKey { -// var codingPath: [CodingKey] -// var allKeys: [K] { -// return multipart.parts -// .compactMap { $0.name } -// .compactMap { K(stringValue: $0) } -// } -// -// let multipart: FormDataDecoderContext -// -// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) { -// self.multipart = multipart -// self.codingPath = codingPath -// } -// -// func contains(_ key: K) -> Bool { -// return multipart.parts.contains { $0.name == key.stringValue } -// } -// -// func decodeNil(forKey key: K) throws -> Bool { -// return false -// } -// -// func decode(_ type: T.Type, forKey key: K) throws -> T where T : Decodable { -// if T.self is MultipartPartConvertible.Type { -// return try multipart.decode(T.self, at: codingPath + [key]) -// } else { -// let decoder = _FormDataDecoder(multipart: multipart, codingPath: codingPath + [key]) -// return try T(from: decoder) -// } -// } -// -// func nestedContainer(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer where NestedKey : CodingKey { -// return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath + [key])) -// } -// -// func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { -// return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath + [key]) -// } -// -// func superDecoder() throws -> Decoder { -// return _FormDataDecoder(multipart: multipart, codingPath: codingPath) -// } -// -// func superDecoder(forKey key: K) throws -> Decoder { -// return _FormDataDecoder(multipart: multipart, codingPath: codingPath + [key]) -// } -//} -// -//private struct _FormDataUnkeyedDecoder: UnkeyedDecodingContainer { -// var codingPath: [CodingKey] -// var count: Int? -// var isAtEnd: Bool { -// return currentIndex >= count! -// } -// var currentIndex: Int -// var index: CodingKey { -// return BasicCodingKey.index(self.currentIndex) -// } -// -// let multipart: FormDataDecoderContext -// -// init(multipart: FormDataDecoderContext, codingPath: [CodingKey]) throws { -// self.multipart = multipart -// self.codingPath = codingPath -// -// let name: String -// switch codingPath.count { -// case 1: name = codingPath[0].stringValue -// default: throw MultipartError(identifier: "nesting", reason: "Nested form-data decoding is not supported.") -// } -// let parts = multipart.parts.allParts(named: name + "[]") -// self.count = parts.count -// self.currentIndex = 0 -// } -// -// mutating func decodeNil() throws -> Bool { -// return false -// } -// -// mutating func decode(_ type: T.Type) throws -> T where T: Decodable { -// defer { currentIndex += 1 } -// if T.self is MultipartPartConvertible.Type { -// return try multipart.decode(T.self, at: codingPath + [index]) -// } else { -// let decoder = _FormDataDecoder(multipart: multipart, codingPath: codingPath + [index]) -// return try T(from: decoder) -// } -// } -// -// mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey : CodingKey { -// return KeyedDecodingContainer(_FormDataKeyedDecoder(multipart: multipart, codingPath: codingPath + [index])) -// } -// -// mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { -// return try _FormDataUnkeyedDecoder(multipart: multipart, codingPath: codingPath + [index]) -// } -// -// mutating func superDecoder() throws -> Decoder { -// return _FormDataDecoder(multipart: multipart, codingPath: codingPath + [index]) -// } -// -// -//} diff --git a/Sources/Vapor/Multipart/FormDataEncoder.swift b/Sources/Vapor/Multipart/FormDataEncoder.swift index 8f5be67fbd..d34a55fb64 100644 --- a/Sources/Vapor/Multipart/FormDataEncoder.swift +++ b/Sources/Vapor/Multipart/FormDataEncoder.swift @@ -1,55 +1,14 @@ -import Multipart - extension FormDataEncoder: ContentEncoder { - /// `ContentEncoder` conformance. - public func encode(_ encodable: E, to body: inout ByteBuffer, headers: inout HTTPHeaders) throws - where E: Encodable - { - let boundary = "----vaporBoundary\(randomBoundaryData())" - headers.contentType = HTTPMediaType(type: "multipart", subType: "form-data", parameters: ["boundary": boundary]) - try self.encode(encodable, boundary: boundary, into: &body) - } + /// `ContentEncoder` conformance. + public func encode(_ encodable: E, to body: inout ByteBuffer, headers: inout HTTPHeaders) throws + where E: Encodable + { + let boundary = "----vaporBoundary\(randomBoundaryData())" + headers.contentType = HTTPMediaType(type: "multipart", subType: "form-data", parameters: ["boundary": boundary]) + try self.encode(encodable, boundary: boundary, into: &body) + } } -///// Encodes `Encodable` items to `multipart/form-data` encoded `Data`. -///// -///// See [RFC#2388](https://tools.ietf.org/html/rfc2388) for more information about `multipart/form-data` encoding. -///// -///// Seealso `MultipartParser` for more information about the `multipart` encoding. -//public struct FormDataEncoder: ContentEncoder { -// /// Creates a new `FormDataEncoder`. -// public init() { } -// -// -// -// public func encode(_ encodable: E, boundary: String) throws -> String -// where E: Encodable -// { -// var buffer = ByteBufferAllocator().buffer(capacity: 0) -// try self.encode(encodable, boundary: boundary, into: &buffer) -// return buffer.readString(length: buffer.readableBytes)! -// } -// -// /// Encodes an `Encodable` item to `Data` using the supplied boundary. -// /// -// /// let a = Foo(string: "a", int: 42, double: 3.14, array: [1, 2, 3]) -// /// let data = try FormDataEncoder().encode(a, boundary: "123") -// /// -// /// - parameters: -// /// - encodable: Generic `Encodable` item. -// /// - boundary: Multipart boundary to use for encoding. This must not appear anywhere in the encoded data. -// /// - throws: Any errors encoding the model with `Codable` or serializing the data. -// /// - returns: `multipart/form-data`-encoded `Data`. -// public func encode(_ encodable: E, boundary: String, into buffer: inout ByteBuffer) throws -// where E: Encodable -// { -// let multipart = FormDataEncoderContext() -// let encoder = _FormDataEncoder(multipart: multipart, codingPath: []) -// try encodable.encode(to: encoder) -// try MultipartSerializer().serialize(parts: multipart.parts, boundary: boundary, into: &buffer) -// } -//} -// // MARK: Private private let chars = "abcdefghijklmnopqrstuvwxyz0123456789" @@ -61,154 +20,3 @@ private func randomBoundaryData() -> String { } return string } -// -//private final class FormDataEncoderContext { -// var parts: [MultipartPart] -// init() { -// self.parts = [] -// } -// -// func encode(_ encodable: E, at codingPath: [CodingKey]) throws where E: Encodable { -// guard let convertible = encodable as? MultipartPartConvertible else { -// throw MultipartError(identifier: "convertible", reason: "`\(E.self)` is not `MultipartPartConvertible`.") -// } -// -// var part = try convertible.convertToMultipartPart() -// switch codingPath.count { -// case 1: part.name = codingPath[0].stringValue -// case 2: -// guard codingPath[1].intValue != nil else { -// throw MultipartError(identifier: "nestedEncode", reason: "Nesting is not supported when encoding multipart data.") -// } -// part.name = codingPath[0].stringValue + "[]" -// default: throw MultipartError(identifier: "nestedEncode", reason: "Nesting is not supported when encoding multipart data.") -// } -// parts.append(part) -// } -// -// func encode(_ files: [File], at codingPath: [CodingKey]) throws { -// for file in files { -// try encode(file, at: codingPath) -// } -// } -//} -// -//private struct _FormDataEncoder: Encoder { -// let codingPath: [CodingKey] -// let multipart: FormDataEncoderContext -// var userInfo: [CodingUserInfoKey: Any] { -// return [:] -// } -// -// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { -// self.multipart = multipart -// self.codingPath = codingPath -// } -// -// func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey { -// return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath)) -// } -// -// func unkeyedContainer() -> UnkeyedEncodingContainer { -// return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath) -// } -// -// func singleValueContainer() -> SingleValueEncodingContainer { -// return _FormDataSingleValueEncoder(multipart: multipart, codingPath: codingPath) -// } -//} -// -//private struct _FormDataSingleValueEncoder: SingleValueEncodingContainer { -// let multipart: FormDataEncoderContext -// var codingPath: [CodingKey] -// -// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { -// self.multipart = multipart -// self.codingPath = codingPath -// } -// -// mutating func encodeNil() throws { -// // do nothing -// } -// -// mutating func encode(_ value: T) throws where T : Encodable { -// try multipart.encode(value, at: codingPath) -// } -//} -// -//private struct _FormDataKeyedEncoder: KeyedEncodingContainerProtocol where K: CodingKey { -// let multipart: FormDataEncoderContext -// var codingPath: [CodingKey] -// -// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { -// self.multipart = multipart -// self.codingPath = codingPath -// } -// -// mutating func encodeNil(forKey key: K) throws { -// // ignore -// } -// -// mutating func encode(_ value: T, forKey key: K) throws where T : Encodable { -// if value is MultipartPartConvertible { -// try multipart.encode(value, at: codingPath + [key]) -// } else if let value = value as? [File] { -// try multipart.encode(value, at: codingPath + [key]) -// } else { -// let encoder = _FormDataEncoder(multipart: multipart, codingPath: codingPath + [key]) -// try value.encode(to: encoder) -// } -// } -// -// mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer where NestedKey: CodingKey { -// return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath + [key])) -// } -// -// mutating func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer { -// return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath + [key]) -// } -// -// mutating func superEncoder() -> Encoder { -// return _FormDataEncoder(multipart: multipart, codingPath: codingPath) -// } -// -// mutating func superEncoder(forKey key: K) -> Encoder { -// return _FormDataEncoder(multipart: multipart, codingPath: codingPath + [key]) -// } -//} -// -//private struct _FormDataUnkeyedEncoder: UnkeyedEncodingContainer { -// var count: Int -// let multipart: FormDataEncoderContext -// var codingPath: [CodingKey] -// var index: CodingKey { -// return BasicCodingKey.index(0) -// } -// -// init(multipart: FormDataEncoderContext, codingPath: [CodingKey]) { -// self.multipart = multipart -// self.codingPath = codingPath -// self.count = 0 -// } -// -// mutating func encodeNil() throws { -// // ignore -// } -// -// mutating func encode(_ value: T) throws where T : Encodable { -// let encoder = _FormDataEncoder(multipart: multipart, codingPath: codingPath + [index]) -// try value.encode(to: encoder) -// } -// -// mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer where NestedKey : CodingKey { -// return KeyedEncodingContainer(_FormDataKeyedEncoder(multipart: multipart, codingPath: codingPath + [index])) -// } -// -// mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { -// return _FormDataUnkeyedEncoder(multipart: multipart, codingPath: codingPath + [index]) -// } -// -// mutating func superEncoder() -> Encoder { -// return _FormDataEncoder(multipart: multipart, codingPath: codingPath + [index]) -// } -//} diff --git a/Sources/Vapor/Multipart/MultipartError.swift b/Sources/Vapor/Multipart/MultipartError.swift deleted file mode 100644 index 9d18c9575d..0000000000 --- a/Sources/Vapor/Multipart/MultipartError.swift +++ /dev/null @@ -1,17 +0,0 @@ -///// Errors that can be thrown while working with Multipart. -//public struct MultipartError: Error { -// /// See `Debuggable`. -// public static let readableName = "Multipart Error" -// -// /// See `Debuggable`. -// public let identifier: String -// -// /// See `Debuggable`. -// public var reason: String -// -// /// Creates a new `MultipartError`. -// public init(identifier: String, reason: String) { -// self.identifier = identifier -// self.reason = reason -// } -//} diff --git a/Sources/Vapor/Multipart/MultipartParser.swift b/Sources/Vapor/Multipart/MultipartParser.swift deleted file mode 100644 index d80319cc66..0000000000 --- a/Sources/Vapor/Multipart/MultipartParser.swift +++ /dev/null @@ -1,194 +0,0 @@ -//import CMultipartParser -// -///// Parses multipart-encoded `Data` into `MultipartPart`s. Multipart encoding is a widely-used format for encoding/// web-form data that includes rich content like files. It allows for arbitrary data to be encoded -///// in each part thanks to a unique delimiter "boundary" that is defined separately. This -///// boundary is guaranteed by the client to not appear anywhere in the data. -///// -///// `multipart/form-data` is a special case of `multipart` encoding where each part contains a `Content-Disposition` -///// header and name. This is used by the `FormDataEncoder` and `FormDataDecoder` to convert `Codable` types to/from -///// multipart data. -///// -///// See [Wikipedia](https://en.wikipedia.org/wiki/MIME#Multipart_messages) for more information. -///// -///// Seealso `form-urlencoded` encoding where delimiter boundaries are not required. -//public final class MultipartParser { -// public var onHeader: (String, String) -> () -// public var onBody: (inout ByteBuffer) -> () -// public var onPartComplete: () -> () -// -// private var callbacks: multipartparser_callbacks -// private var parser: multipartparser -// -// private enum HeaderState { -// case ready -// case field(field: String) -// case value(field: String, value: String) -// } -// -// private var headerState: HeaderState -// -// /// Creates a new `MultipartParser`. -// public init(boundary: String) { -// self.onHeader = { _, _ in } -// self.onBody = { _ in } -// self.onPartComplete = { } -// -// var parser = multipartparser() -// multipartparser_init(&parser, boundary) -// var callbacks = multipartparser_callbacks() -// multipartparser_callbacks_init(&callbacks) -// self.callbacks = callbacks -// self.parser = parser -// self.headerState = .ready -// self.callbacks.on_header_field = { parser, data, size in -// guard let context = Context.from(parser) else { -// return 1 -// } -// let string = String(cPointer: data, count: size) -// context.parser.handleHeaderField(string) -// return 0 -// } -// self.callbacks.on_header_value = { parser, data, size in -// guard let context = Context.from(parser) else { -// return 1 -// } -// let string = String(cPointer: data, count: size) -// context.parser.handleHeaderValue(string) -// return 0 -// } -// self.callbacks.on_data = { parser, data, size in -// guard let context = Context.from(parser) else { -// return 1 -// } -// var buffer = context.slice(at: data, count: size) -// context.parser.handleData(&buffer) -// return 0 -// } -// self.callbacks.on_body_begin = { parser in -// return 0 -// } -// self.callbacks.on_headers_complete = { parser in -// guard let context = Context.from(parser) else { -// return 1 -// } -// context.parser.handleHeadersComplete() -// return 0 -// } -// self.callbacks.on_part_end = { parser in -// guard let context = Context.from(parser) else { -// return 1 -// } -// context.parser.handlePartEnd() -// return 0 -// } -// self.callbacks.on_body_end = { parser in -// return 0 -// } -// } -// -// struct Context { -// static func from(_ pointer: UnsafeMutablePointer?) -> Context? { -// guard let parser = pointer?.pointee else { -// return nil -// } -// return parser.data.assumingMemoryBound(to: MultipartParser.Context.self).pointee -// } -// -// unowned let parser: MultipartParser -// let unsafeBuffer: UnsafeRawBufferPointer -// let buffer: ByteBuffer -// -// func slice(at pointer: UnsafePointer?, count: Int) -> ByteBuffer { -// guard let pointer = pointer else { -// fatalError("no data pointer") -// } -// guard let unsafeBufferStart = unsafeBuffer.baseAddress?.assumingMemoryBound(to: Int8.self) else { -// fatalError("no base address") -// } -// let unsafeBufferEnd = unsafeBufferStart.advanced(by: unsafeBuffer.count) -// if pointer >= unsafeBufferStart && pointer <= unsafeBufferEnd { -// // we were given back a pointer inside our buffer, we can be efficient -// let offset = unsafeBufferStart.distance(to: pointer) -// guard let buffer = self.buffer.getSlice(at: offset, length: count) else { -// fatalError("invalid offset") -// } -// return buffer -// } else { -// // the buffer is to somewhere else, like a statically allocated string -// // let's create a new buffer -// let bytes = UnsafeRawBufferPointer(start: UnsafeRawPointer(pointer), count: count) -// var buffer = ByteBufferAllocator().buffer(capacity: 0) -// buffer.writeBytes(bytes) -// return buffer -// } -// } -// } -// -// public func execute(_ string: String) throws { -// var buffer = ByteBufferAllocator().buffer(capacity: string.utf8.count) -// buffer.writeString(string) -// return try self.execute(buffer) -// } -// -// public func execute(_ buffer: ByteBuffer) throws { -// let result = buffer.withUnsafeReadableBytes { (unsafeBuffer: UnsafeRawBufferPointer) -> Int in -// var context = Context(parser: self, unsafeBuffer: unsafeBuffer, buffer: buffer) -// return withUnsafeMutablePointer(to: &context) { (contextPointer: UnsafeMutablePointer) -> Int in -// self.parser.data = .init(contextPointer) -// return multipartparser_execute(&self.parser, &self.callbacks, unsafeBuffer.baseAddress?.assumingMemoryBound(to: Int8.self), unsafeBuffer.count) -// } -// } -// guard result == buffer.readableBytes else { -// throw Abort(.unprocessableEntity) -// } -// } -// -// // MARK: Private -// -// private func handleHeaderField(_ new: String) { -// switch self.headerState { -// case .ready: -// self.headerState = .field(field: new) -// case .field(let existing): -// self.headerState = .field(field: existing + new) -// case .value(let field, let value): -// self.onHeader(field, value) -// self.headerState = .field(field: new) -// } -// } -// -// private func handleHeaderValue(_ new: String) { -// switch self.headerState { -// case .field(let name): -// self.headerState = .value(field: name, value: new) -// case .value(let name, let existing): -// self.headerState = .value(field: name, value: existing + new) -// default: fatalError() -// } -// } -// -// private func handleHeadersComplete() { -// switch self.headerState { -// case .value(let field, let value): -// self.onHeader(field, value) -// self.headerState = .ready -// case .ready: break -// default: fatalError() -// } -// } -// -// private func handleData(_ data: inout ByteBuffer) { -// self.onBody(&data) -// } -// -// private func handlePartEnd() { -// self.onPartComplete() -// } -//} -// -//private extension String { -// init(cPointer: UnsafePointer?, count: Int) { -// let pointer = UnsafeRawPointer(cPointer)?.assumingMemoryBound(to: UInt8.self) -// self.init(decoding: UnsafeBufferPointer(start: pointer, count: count), as: UTF8.self) -// } -//} diff --git a/Sources/Vapor/Multipart/MultipartPart.swift b/Sources/Vapor/Multipart/MultipartPart.swift deleted file mode 100644 index c4d5bbc456..0000000000 --- a/Sources/Vapor/Multipart/MultipartPart.swift +++ /dev/null @@ -1,104 +0,0 @@ -///// A single part of a `multipart`-encoded message. -//public struct MultipartPart: Equatable { -// /// The part's headers. -// public var headers: [String: String] -// -// /// The part's raw data. -// public var body: ByteBuffer -// -// /// Gets or sets the `filename` attribute from the part's `"Content-Disposition"` header. -// public var filename: String? { -// get { return contentDisposition?.parameters["filename"] } -// set { -// var value: HTTPHeaderValue -// if let existing = contentDisposition { -// value = existing -// } else { -// value = HTTPHeaderValue("form-data") -// } -// value.parameters["filename"] = newValue -// contentDisposition = value -// } -// } -// -// /// Gets or sets the `name` attribute from the part's `"Content-Disposition"` header. -// public var name: String? { -// get { return contentDisposition?.parameters["name"] } -// set { -// var value: HTTPHeaderValue -// if let existing = contentDisposition { -// value = existing -// } else { -// value = HTTPHeaderValue("form-data") -// } -// value.parameters["name"] = newValue -// contentDisposition = value -// } -// } -// -// /// Gets or sets the part's `"Content-Disposition"` header. -// public var contentDisposition: HTTPHeaderValue? { -// get { return headers["Content-Disposition"].flatMap { HTTPHeaderValue.parse($0) } } -// set { headers["Content-Disposition"] = newValue?.serialize() } -// } -// -// /// Gets or sets the part's `"Content-Type"` header. -// public var contentType: HTTPMediaType? { -// get { return headers["Content-Type"].flatMap { HTTPMediaType.parse($0) } } -// set { headers["Content-Type"] = newValue?.serialize() } -// } -// -// /// Creates a new `MultipartPart`. -// /// -// /// let part = MultipartPart(headers: ["Content-Type": "text/plain"], body: "hello") -// /// -// /// - parameters: -// /// - headers: The part's headers. -// /// - body: The part's data. -// public init(headers: [String: String] = [:], body: String) { -// var buffer = ByteBufferAllocator().buffer(capacity: body.utf8.count) -// buffer.writeString(body) -// self.init(headers: headers, body: buffer) -// } -// -// /// Creates a new `MultipartPart`. -// /// -// /// let part = MultipartPart(headers: ["Content-Type": "text/plain"], body: "hello") -// /// -// /// - parameters: -// /// - headers: The part's headers. -// /// - body: The part's data. -// public init(headers: [String: String] = [:], body: ByteBuffer) { -// self.headers = headers -// self.body = body -// } -//} -// -//// MARK: Array Extensions -// -//extension Array where Element == MultipartPart { -// /// Returns the first `MultipartPart` with matching name attribute in `"Content-Disposition"` header. -// public func firstPart(named name: String) -> MultipartPart? { -// for el in self { -// if el.name == name { -// return el -// } -// } -// return nil -// } -// -// /// Returns all `MultipartPart`s with matching name attribute in `"Content-Disposition"` header. -// public func allParts(named name: String) -> [MultipartPart] { -// return filter { $0.name == name } -// } -// -// /// Returns the first `MultipartPart` with matching filename attribute in `"Content-Disposition"` header. -// public func firstFile(filename: String) -> MultipartPart? { -// for el in self { -// if el.filename == filename { -// return el -// } -// } -// return nil -// } -//} diff --git a/Sources/Vapor/Multipart/MultipartPartConvertible.swift b/Sources/Vapor/Multipart/MultipartPartConvertible.swift deleted file mode 100644 index e9049d0694..0000000000 --- a/Sources/Vapor/Multipart/MultipartPartConvertible.swift +++ /dev/null @@ -1,136 +0,0 @@ -//import struct Foundation.Data -// -///// Supports converting to / from a `MultipartPart`. -//public protocol MultipartPartConvertible { -// /// Converts `self` to `MultipartPart`. -// func convertToMultipartPart() throws -> MultipartPart -// -// /// Converts a `MultipartPart` to `Self`. -// static func convertFromMultipartPart(_ part: MultipartPart) throws -> Self -//} -// -//extension MultipartPart: MultipartPartConvertible { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { return self } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> MultipartPart { return part } -//} -// -//extension String: MultipartPartConvertible { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { -// return MultipartPart(body: self) -// } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> String { -// var buffer = part.body -// return buffer.readString(length: buffer.readableBytes)! -// } -//} -// -//extension FixedWidthInteger { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { -// return MultipartPart(headers: [:], body: self.description) -// } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Self { -// guard let fwi = try Self(String.convertFromMultipartPart(part)) else { -// throw MultipartError(identifier: "int", reason: "Could not convert `Data` to `\(Self.self)`.") -// } -// return fwi -// } -//} -// -//extension Int: MultipartPartConvertible { } -//extension Int8: MultipartPartConvertible { } -//extension Int16: MultipartPartConvertible { } -//extension Int32: MultipartPartConvertible { } -//extension Int64: MultipartPartConvertible { } -//extension UInt: MultipartPartConvertible { } -//extension UInt8: MultipartPartConvertible { } -//extension UInt16: MultipartPartConvertible { } -//extension UInt32: MultipartPartConvertible { } -//extension UInt64: MultipartPartConvertible { } -// -// -//extension Float: MultipartPartConvertible { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { -// return MultipartPart(body: description) -// } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Float { -// guard let float = try Float(String.convertFromMultipartPart(part)) else { -// throw MultipartError(identifier: "float", reason: "Could not convert `Data` to `\(Float.self)`.") -// } -// return float -// } -//} -// -//extension Double: MultipartPartConvertible { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { -// return MultipartPart(body: description) -// } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Double { -// guard let double = try Double(String.convertFromMultipartPart(part)) else { -// throw MultipartError(identifier: "double", reason: "Could not convert `Data` to `\(Double.self)`.") -// } -// return double -// } -//} -// -//extension Bool: MultipartPartConvertible { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { -// return MultipartPart(body: description) -// } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Bool { -// guard let option = try Bool(String.convertFromMultipartPart(part)) else { -// throw MultipartError(identifier: "boolean", reason: "Could not convert `Data` to `Bool`. Must be one of: [true, false]") -// } -// return option -// } -//} -// -//extension File: MultipartPartConvertible { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { -// var part = MultipartPart(body: data) -// part.filename = filename -// part.contentType = contentType -// return part -// } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> File { -// guard let filename = part.filename else { -// throw MultipartError(identifier: "filename", reason: "Multipart part missing a filename.") -// } -// return File(data: part.body, filename: filename) -// } -//} -// -//extension Data: MultipartPartConvertible { -// /// See `MultipartPartConvertible`. -// public func convertToMultipartPart() throws -> MultipartPart { -// var buffer = ByteBufferAllocator().buffer(capacity: self.count) -// buffer.writeBytes(self) -// return MultipartPart(body: buffer) -// } -// -// /// See `MultipartPartConvertible`. -// public static func convertFromMultipartPart(_ part: MultipartPart) throws -> Data { -// var buffer = part.body -// return buffer.readData(length: buffer.readableBytes)! -// } -//} diff --git a/Sources/Vapor/Multipart/MultipartSerializer.swift b/Sources/Vapor/Multipart/MultipartSerializer.swift deleted file mode 100644 index 848512bbdb..0000000000 --- a/Sources/Vapor/Multipart/MultipartSerializer.swift +++ /dev/null @@ -1,44 +0,0 @@ -///// Serializes `MultipartForm`s to `Data`. -///// -///// See `MultipartParser` for more information about the multipart encoding. -//public final class MultipartSerializer { -// /// Creates a new `MultipartSerializer`. -// public init() { } -// -// public func serialize(parts: [MultipartPart], boundary: String) throws -> String { -// var buffer = ByteBufferAllocator().buffer(capacity: 0) -// try self.serialize(parts: parts, boundary: boundary, into: &buffer) -// return buffer.readString(length: buffer.readableBytes)! -// } -// -// /// Serializes the `MultipartForm` to data. -// /// -// /// let data = try MultipartSerializer().serialize(parts: [part], boundary: "123") -// /// print(data) // multipart-encoded -// /// -// /// - parameters: -// /// - parts: One or more `MultipartPart`s to serialize into `Data`. -// /// - boundary: Multipart boundary to use for encoding. This must not appear anywhere in the encoded data. -// /// - throws: Any errors that may occur during serialization. -// /// - returns: `multipart`-encoded `Data`. -// public func serialize(parts: [MultipartPart], boundary: String, into buffer: inout ByteBuffer) throws { -// for part in parts { -// buffer.writeString("--") -// buffer.writeString(boundary) -// buffer.writeString("\r\n") -// for (key, val) in part.headers { -// buffer.writeString(key) -// buffer.writeString(": ") -// buffer.writeString(val) -// buffer.writeString("\r\n") -// } -// buffer.writeString("\r\n") -// var body = part.body -// buffer.writeBuffer(&body) -// buffer.writeString("\r\n") -// } -// buffer.writeString("--") -// buffer.writeString(boundary) -// buffer.writeString("--\r\n") -// } -//} diff --git a/Tests/VaporTests/ApplicationTests.swift b/Tests/VaporTests/ApplicationTests.swift index 7be90ef93e..5b38c882cf 100644 --- a/Tests/VaporTests/ApplicationTests.swift +++ b/Tests/VaporTests/ApplicationTests.swift @@ -276,7 +276,7 @@ final class ApplicationTests: XCTestCase { --123\r Content-Disposition: form-data; name="age"\r \r - 3\r + 4\r --123\r Content-Disposition: form-data; name="image"; filename="droplet.png"\r \r @@ -284,7 +284,11 @@ final class ApplicationTests: XCTestCase { --123--\r """ - let expected = User(name: "Vapor", age: 3, image: File(data: "", filename: "droplet.png")) + let expected = User( + name: "Vapor", + age: 4, + image: File(data: "", filename: "droplet.png") + ) struct User: Content, Equatable { var name: String @@ -321,7 +325,11 @@ final class ApplicationTests: XCTestCase { defer { app.shutdown() } app.get("multipart") { req -> User in - return User(name: "Vapor", age: 3, image: File(data: "", filename: "droplet.png")) + return User( + name: "Vapor", + age: 4, + image: File(data: "", filename: "droplet.png") + ) } try app.testable().test(.GET, "/multipart") { res in From e456704ea9de22420098bbcbb27da3852011ef6d Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 22 Oct 2019 14:10:02 -0400 Subject: [PATCH 4/4] regen linuxmain --- Tests/VaporTests/XCTestManifests.swift | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Tests/VaporTests/XCTestManifests.swift b/Tests/VaporTests/XCTestManifests.swift index 1f0db8a04a..a592406689 100644 --- a/Tests/VaporTests/XCTestManifests.swift +++ b/Tests/VaporTests/XCTestManifests.swift @@ -83,23 +83,6 @@ extension BCryptTests { ] } -extension MultipartTests { - // DO NOT MODIFY: This is autogenerated, use: - // `swift test --generate-linuxmain` - // to regenerate. - static let __allTests__MultipartTests = [ - ("testBasics", testBasics), - ("testDocBlocks", testDocBlocks), - ("testFormDataDecoderFile", testFormDataDecoderFile), - ("testFormDataDecoderMultiple", testFormDataDecoderMultiple), - ("testFormDataDecoderW3", testFormDataDecoderW3), - ("testFormDataDecoderW3Streaming", testFormDataDecoderW3Streaming), - ("testFormDataEncoder", testFormDataEncoder), - ("testMultifile", testMultifile), - ("testMultipleFile", testMultipleFile), - ] -} - extension URLEncodedFormTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` @@ -147,7 +130,6 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(ApplicationTests.__allTests__ApplicationTests), testCase(AuthenticationTests.__allTests__AuthenticationTests), testCase(BCryptTests.__allTests__BCryptTests), - testCase(MultipartTests.__allTests__MultipartTests), testCase(URLEncodedFormTests.__allTests__URLEncodedFormTests), testCase(ValidationTests.__allTests__ValidationTests), ]