瀏覽代碼

Added percent escaping tests around reserved / unreserved / illegal characters.

Christian Noon 10 年之前
父節點
當前提交
357b34e323
共有 2 個文件被更改,包括 108 次插入16 次删除
  1. 33 1
      Source/ParameterEncoding.swift
  2. 75 15
      Tests/ParameterEncodingTests.swift

+ 33 - 1
Source/ParameterEncoding.swift

@@ -150,8 +150,40 @@ public enum ParameterEncoding {
         return components
     }
 
+    /**
+        Returns a percent escaped string following RFC 3986 for query string formatting.
+
+        RFC 3986 states that the following characters are "reserved" characters.
+
+        - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
+        - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
+
+        Core Foundation interprets RFC 3986 in terms of legal and illegal characters.
+
+        - Legal Numbers: "0123456789"
+        - Legal Letters: "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        - Legal Characters: "!", "$", "&", "'", "(", ")", "*", "+", ",", "-",
+                            ".", "/", ":", ";", "=", "?", "@", "_", "~", "\""
+        - Illegal Characters: All characters not listed as Legal
+
+        While the Core Foundation `CFURLCreateStringByAddingPercentEscapes` documentation states
+        that it follows RFC 3986, the headers actually point out that it follows RFC 2396. This
+        explains why it does not consider "[", "]" and "#" to be "legal" characters even though 
+        they are specified as "reserved" characters in RFC 3986. The following rdar has been filed
+        to hopefully get the documentation updated.
+
+        - https://openradar.appspot.com/radar?id=5058257274011648
+
+        In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
+        query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
+        should be percent escaped in the query string.
+
+        :param: string The string to be percent escaped.
+
+        :returns: The percent escaped string.
+    */
     func escape(string: String) -> String {
-        let generalDelimiters = ":#[]@" // dropping "?" and "/" due to RFC 3986 - Section 3.4
+        let generalDelimiters = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
         let subDelimiters = "!$&'()*+,;="
 
         let legalURLCharactersToBeEscaped: CFStringRef = generalDelimiters + subDelimiters

+ 75 - 15
Tests/ParameterEncodingTests.swift

@@ -30,15 +30,12 @@ class ParameterEncodingTestCase: BaseTestCase {
 
 // MARK: -
 
-/**
-    The URL parameter encoding tests cover a variety of cases for encoding query parameters in addition to percent escaping reserved characters. The percent escaping implementation follows RFC 3986 - Sections 2.2, 2.4 and 3.4. All reserved characters are percent encoded with the exception of the "?" and "/" characters. This exception was made to allow other URIs to be included as query parameters without issue. See RFC 3986 - Section 3.4 for more details.
-*/
 class URLParameterEncodingTestCase: ParameterEncodingTestCase {
     // MARK: Properties
 
     let encoding: ParameterEncoding = .URL
 
-    // MARK: Tests
+    // MARK: Tests - Parameter Types
 
     func testURLParameterEncodeNilParameters() {
         // Given
@@ -164,6 +161,78 @@ class URLParameterEncodingTestCase: ParameterEncodingTestCase {
         XCTAssertEqual(URLRequest.URL?.query ?? "", "foo%5Bbar%5D%5Bbaz%5D%5B%5D=a&foo%5Bbar%5D%5Bbaz%5D%5B%5D=1&foo%5Bbar%5D%5Bbaz%5D%5B%5D=1", "query is incorrect")
     }
 
+    // MARK: Tests - All Reserved / Unreserved / Illegal Characters According to RFC 3986
+
+    func testThatReservedCharactersArePercentEscapedMinusQuestionMarkAndForwardSlash() {
+        // Given
+        let generalDelimiters = ":#[]@"
+        let subDelimiters = "!$&'()*+,;="
+        let parameters = ["reserved": "\(generalDelimiters)\(subDelimiters)"]
+
+        // When
+        let (URLRequest, error) = self.encoding.encode(self.URLRequest, parameters: parameters)
+
+        // Then
+        XCTAssertEqual(URLRequest.URL?.query ?? "", "reserved=%3A%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D", "query is incorrect")
+    }
+
+    func testThatReservedCharactersQuestionMarkAndForwardSlashAreNotPercentEscaped() {
+        // Given
+        let parameters = ["reserved": "?/"]
+
+        // When
+        let (URLRequest, error) = self.encoding.encode(self.URLRequest, parameters: parameters)
+
+        // Then
+        XCTAssertEqual(URLRequest.URL?.query ?? "", "reserved=?/", "query is incorrect")
+    }
+
+    func testThatUnreservedNumericCharactersAreNotPercentEscaped() {
+        // Given
+        let parameters = ["numbers": "0123456789"]
+
+        // When
+        let (URLRequest, error) = self.encoding.encode(self.URLRequest, parameters: parameters)
+
+        // Then
+        XCTAssertEqual(URLRequest.URL?.query ?? "", "numbers=0123456789", "query is incorrect")
+    }
+
+    func testThatUnreservedLowercaseCharactersAreNotPercentEscaped() {
+        // Given
+        let parameters = ["lowercase": "abcdefghijklmnopqrstuvwxyz"]
+
+        // When
+        let (URLRequest, error) = self.encoding.encode(self.URLRequest, parameters: parameters)
+
+        // Then
+        XCTAssertEqual(URLRequest.URL?.query ?? "", "lowercase=abcdefghijklmnopqrstuvwxyz", "query is incorrect")
+    }
+
+    func testThatUnreservedUppercaseCharactersAreNotPercentEscaped() {
+        // Given
+        let parameters = ["uppercase": "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
+
+        // When
+        let (URLRequest, error) = self.encoding.encode(self.URLRequest, parameters: parameters)
+
+        // Then
+        XCTAssertEqual(URLRequest.URL?.query ?? "", "uppercase=ABCDEFGHIJKLMNOPQRSTUVWXYZ", "query is incorrect")
+    }
+
+    func testThatIllegalASCIICharactersArePercentEscaped() {
+        // Given
+        let parameters = ["illegal": " \"#%<>[]\\^`{}|"]
+
+        // When
+        let (URLRequest, error) = self.encoding.encode(self.URLRequest, parameters: parameters)
+
+        // Then
+        XCTAssertEqual(URLRequest.URL?.query ?? "", "illegal=%20%22%23%25%3C%3E%5B%5D%5C%5E%60%7B%7D%7C", "query is incorrect")
+    }
+
+    // MARK: Tests - Special Character Queries
+
     func testURLParameterEncodeStringWithAmpersandKeyStringWithAmpersandValueParameter() {
         // Given
         let parameters = ["foo&bar": "baz&qux", "foobar": "bazqux"]
@@ -219,17 +288,6 @@ class URLParameterEncodingTestCase: ParameterEncodingTestCase {
         XCTAssertEqual(URLRequest.URL?.query ?? "", "%2Bfoo%2B=%2Bbar%2B", "query is incorrect")
     }
 
-    func testURLParameterEncodeStringKeyAllowedCharactersStringValueParameter() {
-        // Given
-        let parameters = ["allowed": " =\"#%<>@\\^`{}[]|&"]
-
-        // When
-        let (URLRequest, error) = self.encoding.encode(self.URLRequest, parameters: parameters)
-
-        // Then
-        XCTAssertEqual(URLRequest.URL?.query ?? "", "allowed=%20%3D%22%23%25%3C%3E%40%5C%5E%60%7B%7D%5B%5D%7C%26", "query is incorrect")
-    }
-
     func testURLParameterEncodeStringKeyPercentEncodedStringValueParameter() {
         // Given
         let parameters = ["percent": "%25"]
@@ -281,6 +339,8 @@ class URLParameterEncodingTestCase: ParameterEncodingTestCase {
         XCTAssertEqual(URLRequest.URL?.query ?? "", "hd=%5B1%5D&%2Bfoo%2B=%2Bbar%2B", "query is incorrect")
     }
 
+    // MARK: Tests - Varying HTTP Methods
+
     func testURLParameterEncodeGETParametersInURL() {
         // Given
         var mutableURLRequest = self.URLRequest.mutableCopy() as! NSMutableURLRequest