Skip to content

Commit 0e8f3b3

Browse files
committed
fix carriage returns
1 parent a9d6da6 commit 0e8f3b3

File tree

10 files changed

+77
-90
lines changed

10 files changed

+77
-90
lines changed

Sources/FormData/Field.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import Multipart
77
Headers and body reside in the Part.
88
*/
99
public struct Field {
10-
var name: String
11-
var filename: String?
12-
var part: Multipart.Part
10+
public var name: String
11+
public var filename: String?
12+
public var part: Multipart.Part
1313

1414
public init(name: String, filename: String?, part: Multipart.Part) {
1515
self.name = name

Sources/FormData/FormData+BytesConvertible.swift

Lines changed: 0 additions & 20 deletions
This file was deleted.

Sources/FormData/Parser.swift

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ import HTTP
88
parser.
99
*/
1010
public final class Parser {
11-
/// The multipart boundary being used.
12-
public let boundary: Bytes
13-
1411
/// The underlying multipart parser.
1512
/// Subscribe to preamble and epilogue events.
16-
public let multipartParser: Multipart.Parser
13+
public let multipart: Multipart.Parser
1714

1815
/// A callback type for handling parsed form-data fields.
1916
public typealias FieldCallback = (Field) -> ()
@@ -23,11 +20,10 @@ public final class Parser {
2320
public var onField: FieldCallback?
2421

2522
/// Create a new Form Data parser.
26-
public init(boundary: Bytes) {
27-
self.boundary = boundary
28-
multipartParser = Multipart.Parser(boundary: boundary)
23+
public init(multipart: Multipart.Parser) {
24+
self.multipart = multipart
2925

30-
multipartParser.onPart = { part in
26+
self.multipart.onPart = { part in
3127
if let contentDisposition = part.headers[.contentDisposition] {
3228
let parser = ContentDispositionParser()
3329

@@ -60,13 +56,4 @@ public final class Parser {
6056
}
6157
}
6258
}
63-
64-
/**
65-
The main method for passing bytes into the parser.
66-
67-
@see `Multipart.Parser.parse` for performance notes.
68-
*/
69-
public func parse(_ bytes: Bytes) throws {
70-
try multipartParser.parse(bytes)
71-
}
7259
}

Sources/FormData/Serializer.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,13 @@ import Multipart
66
suitable for an HTTP response or request body.
77
*/
88
public final class Serializer {
9-
/// The multipart boundary being used.
10-
public let boundary: Bytes
119

1210
/// The underlying multipart serializer.
1311
/// Use to serialize preamble and epilogue.
14-
public let multipartSerializer: Multipart.Serializer
12+
public let multipart: Multipart.Serializer
1513

16-
public init(boundary: Bytes) {
17-
self.boundary = boundary
18-
multipartSerializer = Multipart.Serializer(boundary: boundary)
14+
public init(multipart: Multipart.Serializer) {
15+
self.multipart = multipart
1916
}
2017

2118
/**
@@ -40,6 +37,6 @@ public final class Serializer {
4037
}
4138

4239
part.headers["Content-Disposition"] = contentDisposition
43-
try multipartSerializer.serialize(part)
40+
try multipart.serialize(part)
4441
}
4542
}

Sources/Multipart/HeaderParser.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ final class HeaderParser {
3434
func parse(_ byte: Byte) throws {
3535
main: switch state {
3636
case .none:
37-
if byte == .newLine {
37+
if byte == .newLine {
3838
break main
3939
}
4040

Sources/Multipart/Parser.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ public final class Parser {
121121
throw Error.hasAlreadyFinished
122122
}
123123

124+
// we only care about new lines
125+
// this requires that all line endings be `\n` or `\r\n`
126+
if byte == .carriageReturn {
127+
return
128+
}
129+
124130
switch state {
125131
case .preamble(let bodyEndIndex):
126132
try boundaryParser.parse(byte)

Sources/Multipart/Serializer.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,18 @@ public final class Serializer {
6161

6262
serialize([.hyphen, .hyphen])
6363
serialize(boundary)
64-
serialize(.newLine)
64+
serialize([.carriageReturn, .newLine])
6565
for (key, value) in part.headers {
6666
serialize(key.key.bytes)
6767
serialize(.colon)
6868
serialize(.space)
6969
serialize(value.bytes)
70-
serialize(.newLine)
70+
serialize([.carriageReturn, .newLine])
7171
}
72-
serialize(.newLine)
72+
serialize([.carriageReturn, .newLine])
7373

7474
serialize(part.body)
75-
serialize(.newLine)
75+
serialize([.carriageReturn, .newLine])
7676

7777
partsSerialized = true
7878
}
@@ -93,7 +93,7 @@ public final class Serializer {
9393
serialize([.hyphen, .hyphen])
9494
serialize(boundary)
9595
serialize([.hyphen, .hyphen])
96-
serialize(.newLine)
96+
serialize([.carriageReturn, .newLine])
9797
serialize(epilogue)
9898

9999
epilogueSerialized = true

Tests/FormDataTests/ParserTests.swift

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import Foundation
22
import XCTest
33
@testable import FormData
4+
import Multipart
45

56
class ParserTests: XCTestCase {
67
static var allTests = [
7-
("testInit", testInit),
88
("testFormData", testFormData),
9+
("testWebkit", testWebkit),
910
]
1011

11-
func testInit() throws {
12-
let parser = try Parser(boundary: "foo")
13-
XCTAssertEqual(parser.boundary, "foo".bytes)
14-
}
15-
1612
func testFormData() throws {
17-
let parser = try Parser(boundary: "---------------------------9051914041544843365972754266")
13+
let multipart = try Multipart.Parser(boundary: "---------------------------9051914041544843365972754266")
14+
let parser = FormData.Parser(multipart: multipart)
1815

1916
var message = ""
2017

@@ -42,7 +39,7 @@ class ParserTests: XCTestCase {
4239
fields[field.name] = field
4340
}
4441

45-
try parser.parse(message)
42+
try parser.multipart.parse(message)
4643

4744
XCTAssertEqual(fields.count, 3)
4845

@@ -54,4 +51,30 @@ class ParserTests: XCTestCase {
5451
XCTAssertEqual(fields["file1"]?.filename, "a.txt")
5552
XCTAssertEqual(fields["file2"]?.filename, "a.html")
5653
}
54+
55+
func testWebkit() throws {
56+
var message = ""
57+
58+
message += "------WebKitFormBoundaryezkRLRyEVe1aMUVZ\r\n"
59+
message += "Content-Disposition: form-data; name=\"file\"; filename=\"Screen Shot 2017-01-13 at 3.05.26 PM.png\"\r\n"
60+
message += "Content-Type: image/png\r\n"
61+
message += "\r\n"
62+
message += "PNG\n"
63+
message += "\n"
64+
message += "\n"
65+
message += "------WebKitFormBoundaryezkRLRyEVe1aMUVZ--\r\n"
66+
67+
let multipart = try Multipart.Parser(boundary: "----WebKitFormBoundaryezkRLRyEVe1aMUVZ")
68+
let parser = FormData.Parser(multipart: multipart)
69+
70+
var fields: [String: Field] = [:]
71+
72+
parser.onField = { field in
73+
fields[field.name] = field
74+
}
75+
76+
try parser.multipart.parse(message)
77+
78+
XCTAssertEqual(fields["file"]?.filename, "Screen Shot 2017-01-13 at 3.05.26 PM.png")
79+
}
5780
}

Tests/FormDataTests/SerializerTests.swift

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,8 @@ import HTTP
77

88
class SerializerTests: XCTestCase {
99
static var allTests = [
10-
("testInit", testInit),
1110
("testBasic", testBasic),
1211
]
13-
14-
15-
func testInit() throws {
16-
let serializer = try FormData.Serializer(boundary: "foo")
17-
XCTAssertEqual(serializer.boundary, "foo".bytes)
18-
}
1912

2013
public func testBasic() throws {
2114
let part1 = Part(headers: [
@@ -27,30 +20,31 @@ class SerializerTests: XCTestCase {
2720
let field1 = Field(name: "title", filename: nil, part: part1)
2821
let field2 = Field(name: "image", filename: "image.jpg", part: part2)
2922

30-
let serializer = try FormData.Serializer(boundary: "boundary42")
23+
let multipart = try Multipart.Serializer(boundary: "boundary42")
24+
let serializer = FormData.Serializer(multipart: multipart)
3125

3226
var serialized: Bytes = []
3327

34-
serializer.multipartSerializer.onSerialize = { bytes in
28+
serializer.multipart.onSerialize = { bytes in
3529
serialized += bytes
3630
}
3731

3832
try serializer.serialize(field1)
3933
try serializer.serialize(field2)
40-
try serializer.multipartSerializer.finish()
34+
try serializer.multipart.finish()
4135

4236
var expected = ""
4337

44-
expected += "--boundary42\n"
45-
expected += "Content-Disposition: form-data; name=\"title\"\n"
46-
expected += "Content-Type: text/plain; charset=us-ascii\n"
47-
expected += "\n"
48-
expected += "Systems should choose the 'best' type based on the local environment and references, in some cases even through user interaction.\n"
49-
expected += "--boundary42\n"
50-
expected += "Content-Disposition: form-data; name=\"image\"; filename=\"image.jpg\"\n"
51-
expected += "\n"
52-
expected += "Test123\n"
53-
expected += "--boundary42--\n"
38+
expected += "--boundary42\r\n"
39+
expected += "Content-Disposition: form-data; name=\"title\"\r\n"
40+
expected += "Content-Type: text/plain; charset=us-ascii\r\n"
41+
expected += "\r\n"
42+
expected += "Systems should choose the 'best' type based on the local environment and references, in some cases even through user interaction.\r\n"
43+
expected += "--boundary42\r\n"
44+
expected += "Content-Disposition: form-data; name=\"image\"; filename=\"image.jpg\"\r\n"
45+
expected += "\r\n"
46+
expected += "Test123\r\n"
47+
expected += "--boundary42--\r\n"
5448

5549
XCTAssertEqual(serialized.string, expected)
5650
}

Tests/MultipartTests/SerializerTests.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ class SerializerTests: XCTestCase {
3636

3737
var expected = ""
3838

39-
expected += "--boundary42\n"
40-
expected += "Content-Type: text/plain; charset=us-ascii\n"
41-
expected += "X-Test: 42\n"
42-
expected += "\n"
43-
expected += "Systems should choose the 'best' type based on the local environment and references, in some cases even through user interaction.\n"
44-
expected += "--boundary42\n"
45-
expected += "\n"
46-
expected += "Test123\n"
47-
expected += "--boundary42--\n"
39+
expected += "--boundary42\r\n"
40+
expected += "Content-Type: text/plain; charset=us-ascii\r\n"
41+
expected += "X-Test: 42\r\n"
42+
expected += "\r\n"
43+
expected += "Systems should choose the 'best' type based on the local environment and references, in some cases even through user interaction.\r\n"
44+
expected += "--boundary42\r\n"
45+
expected += "\r\n"
46+
expected += "Test123\r\n"
47+
expected += "--boundary42--\r\n"
4848

4949
XCTAssertEqual(serialized.string, expected)
5050
}

0 commit comments

Comments
 (0)