Skip to content
This repository has been archived by the owner on Nov 16, 2020. It is now read-only.

Commit

Permalink
Merge pull request #8 from bre7/patch-1
Browse files Browse the repository at this point in the history
Handle percent encoded ampersand correctly in values
  • Loading branch information
tanner0101 authored May 9, 2018
2 parents 8448fa9 + 8e43c39 commit 0fe0255
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 8 deletions.
26 changes: 18 additions & 8 deletions Sources/URLEncodedForm/Data/URLEncodedFormParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ final class URLEncodedFormParser {
/// If empty values is false, `foo=` will resolve as `foo: true`
/// instead of `foo: ""`
func parse(percentEncoded: String, omitEmptyValues: Bool = false, omitFlags: Bool = false) throws -> [String: URLEncodedFormData] {
guard let decoded = percentEncoded.replacingOccurrences(of: "+", with: " ").removingPercentEncoding else {
throw URLEncodedFormError(identifier: "percentDecoding", reason: "Could not percent decode string: \(percentEncoded)")
}
return try parse(data: decoded, omitEmptyValues: omitEmptyValues, omitFlags: omitFlags)
let partiallyDecoded = percentEncoded.replacingOccurrences(of: "+", with: " ")
return try parse(data: partiallyDecoded, omitEmptyValues: omitEmptyValues, omitFlags: omitFlags)
}

/// Parses the data.
Expand All @@ -38,17 +36,28 @@ final class URLEncodedFormParser {
omittingEmptySubsequences: false
)

guard let decodedKey = try token.first?.utf8DecodedString().removingPercentEncoding else {
throw URLEncodedFormError(
identifier: "percentDecoding",
reason: "Could not percent decode string key: \(token[0])"
)
}
let decodedValue = try token.last?.utf8DecodedString().removingPercentEncoding

if token.count == 2 {
if omitEmptyValues && token[1].count == 0 {
continue
}
key = try parseKey(data: token[0])
data = try .str(token[1].utf8DecodedString())
guard let decodedValue = decodedValue else {
throw URLEncodedFormError(identifier: "percentDecoding", reason: "Could not percent decode string value: \(token[1])")
}
key = try parseKey(data: decodedKey)
data = .str(decodedValue)
} else if token.count == 1 {
if omitFlags {
continue
}
key = try parseKey(data: token[0])
key = try parseKey(data: decodedKey)
data = "true"
} else {
throw URLEncodedFormError(
Expand All @@ -74,7 +83,8 @@ final class URLEncodedFormParser {
}

/// Parses a `URLEncodedFormEncodedKey` from `Data`.
private func parseKey(data: Data) throws -> URLEncodedFormEncodedKey {
private func parseKey(data dataConvertible: LosslessDataConvertible) throws -> URLEncodedFormEncodedKey {
let data = dataConvertible.convertToData()
let stringData: Data
let subKeys: [URLEncodedFormEncodedSubKey]

Expand Down
7 changes: 7 additions & 0 deletions Tests/URLEncodedFormTests/URLEncodedFormParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class URLEncodedFormParserTests: XCTestCase {
let form = try URLEncodedFormParser.default.parse(data: data)
XCTAssertEqual(form, ["hello": "world", "foo": "bar"])
}

func testBasicWithAmpersand() throws {
let data = "hello=world&foo=bar%26bar".data(using: .utf8)!
let form = try URLEncodedFormParser.default.parse(data: data)
XCTAssertEqual(form, ["hello": "world", "foo": "bar&bar"])
}

func testDictionary() throws {
let data = "greeting[en]=hello&greeting[es]=hola".data(using: .utf8)!
Expand Down Expand Up @@ -47,6 +53,7 @@ class URLEncodedFormParserTests: XCTestCase {

static let allTests = [
("testBasic", testBasic),
("testBasicWithAmpersand", testBasicWithAmpersand),
("testDictionary", testDictionary),
("testArray", testArray),
("testOptions", testOptions),
Expand Down
7 changes: 7 additions & 0 deletions Tests/URLEncodedFormTests/URLEncodedFormSerializerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ class URLEncodedFormSerializerTests: XCTestCase {
XCTAssertEqual(String(data: data, encoding: .utf8)!, "aaa%5D=%2Bbbb%20%20ccc")
}

func testPercentEncodingWithAmpersand() throws {
let form: [String: URLEncodedFormData] = ["aaa": "b%26&b"]
let data = try URLEncodedFormSerializer.default.serialize(form)
XCTAssertEqual(String(data: data, encoding: .utf8)!, "aaa=b%2526&b")
}

func testNested() throws {
let form: [String: URLEncodedFormData] = ["a": ["b": ["c": ["d": ["hello": "world"]]]]]
let data = try URLEncodedFormSerializer.default.serialize(form)
Expand All @@ -16,6 +22,7 @@ class URLEncodedFormSerializerTests: XCTestCase {

static let allTests = [
("testPercentEncoding", testPercentEncoding),
("testPercentEncodingWithAmpersand", testPercentEncodingWithAmpersand),
("testNested", testNested),
]
}
Expand Down

0 comments on commit 0fe0255

Please sign in to comment.