Skip to content

Commit

Permalink
Fix handling unicode characters in multipart filenames (#2826)
Browse files Browse the repository at this point in the history
Adds a case to ContentDisposition to be compliant with rfc-5987. This allows handling of unicode and other format characters in filenames.

closes #2802


* Add case to init of contentdisposition

* Add property for incoming UniCode characters to ContentDisposition

* Revert to earlier commit

* Add tests to decode and encode unicode
  • Loading branch information
BennyDeBock committed May 17, 2022
1 parent d005dec commit 21dde33
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ extension HTTPHeaders {
self.name = .init(parameter)
case "filename":
self.filename = .init(parameter)
case "filename*":
self.filename = .init(parameter)
default:
return nil
}
Expand Down
76 changes: 75 additions & 1 deletion Tests/VaporTests/ContentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ final class ContentTests: XCTestCase {
XCTAssertContains(res.body.string, "hi")
}
}

func testMultipartDecode() throws {
let data = """
--123\r
Expand Down Expand Up @@ -149,6 +149,52 @@ final class ContentTests: XCTestCase {
XCTAssertEqualJSON(res.body.string, expected)
}
}

func testMultipartDecodeUnicode() throws {
let data = """
--123\r
Content-Disposition: form-data; name="name"\r
\r
Vapor\r
--123\r
Content-Disposition: form-data; name="age"\r
\r
4\r
--123\r
Content-Disposition: form-data; name="image"; filename="她在吃水果.png"; filename*="UTF-8\'\'%E5%A5%B9%E5%9C%A8%E5%90%83%E6%B0%B4%E6%9E%9C.png"\r
\r
<contents of image>\r
--123--\r
"""
let expected = User(
name: "Vapor",
age: 4,
image: File(data: "<contents of image>", filename: "UTF-8\'\'%E5%A5%B9%E5%9C%A8%E5%90%83%E6%B0%B4%E6%9E%9C.png")
)

struct User: Content, Equatable {
var name: String
var age: Int
var image: File
}

let app = Application(.testing)
defer { app.shutdown() }

app.routes.get("multipart") { req -> User in
let decoded = try req.content.decode(User.self)
XCTAssertEqual(decoded, expected)
return decoded
}

try app.testable().test(.GET, "/multipart", headers: [
"Content-Type": "multipart/form-data; boundary=123"
], body: .init(string: data)) { res in
XCTAssertEqual(res.status, .ok)
XCTAssertEqualJSON(res.body.string, expected)
}
}

func testMultipartEncode() throws {
struct User: Content {
Expand Down Expand Up @@ -177,6 +223,34 @@ final class ContentTests: XCTestCase {
XCTAssertContains(res.body.string, "name=\"image\"")
}
}

func testMultiPartEncodeUnicode() throws {
struct User: Content {
static var defaultContentType: HTTPMediaType = .formData
var name: String
var age: Int
var image: File
}

let app = Application(.testing)
defer { app.shutdown() }

app.get("multipart") { req -> User in
return User(
name: "Vapor",
age: 4,
image: File(data: "<contents of image>", filename: "UTF-8\'\'%E5%A5%B9%E5%9C%A8%E5%90%83%E6%B0%B4%E6%9E%9C.png")
)
}
try app.testable().test(.GET, "/multipart") { res in
XCTAssertEqual(res.status, .ok)
let boundary = res.headers.contentType?.parameters["boundary"] ?? "none"
XCTAssertContains(res.body.string, "Content-Disposition: form-data; name=\"name\"")
XCTAssertContains(res.body.string, "--\(boundary)")
XCTAssertContains(res.body.string, "filename=UTF-8\'\'%E5%A5%B9%E5%9C%A8%E5%90%83%E6%B0%B4%E6%9E%9C.png")
XCTAssertContains(res.body.string, "name=\"image\"")
}
}

func testURLEncodedFormDecode() throws {
struct User: Content {
Expand Down

0 comments on commit 21dde33

Please sign in to comment.