0001    // ParameterEncoding.swift
0002    //
0003    // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/)
0004    //
0005    // Permission is hereby granted, free of charge, to any person obtaining a copy
0006    // of this software and associated documentation files (the "Software"), to deal
0007    // in the Software without restriction, including without limitation the rights
0008    // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0009    // copies of the Software, and to permit persons to whom the Software is
0010    // furnished to do so, subject to the following conditions:
0011    //
0012    // The above copyright notice and this permission notice shall be included in
0013    // all copies or substantial portions of the Software.
0014    //
0015    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016    // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017    // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0018    // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019    // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0020    // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0021    // THE SOFTWARE.
0022    
0023    import Foundation
0024    
0025    /**
0026        HTTP method definitions.
0027    
0028        See https://tools.ietf.org/html/rfc7231#section-4.3
0029    */
0030    public enum Method
Alamofire.swift:87
    method: Method,
Alamofire.swift:119
    method: Method,
Alamofire.swift:163
    method: Method,
Alamofire.swift:197
    method: Method,
Alamofire.swift:231
    method: Method,
Alamofire.swift:266
    method: Method,
Alamofire.swift:323
    method: Method,
Download.swift:80
        method: Method,
Manager.swift:181
        method: Method,
ParameterEncoding.swift:102
            func encodesParametersInURL(method: Method) -> Bool {
ParameterEncoding.swift:118
            if let method = Method(rawValue: mutableURLRequest.HTTPMethod) where encodesParametersInURL(method) {
Upload.swift:99
        method: Method,
Upload.swift:138
        method: Method,
Upload.swift:178
        method: Method,
Upload.swift:235
        method: Method,
: String { 0031 case OPTIONS, GET
ParameterEncoding.swift:111
                case .GET, .HEAD, .DELETE:
, HEAD
ParameterEncoding.swift:111
                case .GET, .HEAD, .DELETE:
, POST, PUT, PATCH, DELETE
ParameterEncoding.swift:111
                case .GET, .HEAD, .DELETE:
, TRACE, CONNECT 0032 } 0033 0034 // MARK: ParameterEncoding 0035 0036 /** 0037 Used to specify the way in which a set of parameters are applied to a URL request. 0038 0039 - `URL`: Creates a query string to be set as or appended to any existing URL query for `GET`, `HEAD`, 0040 and `DELETE` requests, or set as the body for requests with any other HTTP method. The 0041 `Content-Type` HTTP header field of an encoded request with HTTP body is set to 0042 `application/x-www-form-urlencoded; charset=utf-8`. Since there is no published specification 0043 for how to encode collection types, the convention of appending `[]` to the key for array 0044 values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested 0045 dictionary values (`foo[bar]=baz`). 0046 0047 - `URLEncodedInURL`: Creates query string to be set as or appended to any existing URL query. Uses the same 0048 implementation as the `.URL` case, but always applies the encoded result to the URL. 0049 0050 - `JSON`: Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is 0051 set as the body of the request. The `Content-Type` HTTP header field of an encoded request is 0052 set to `application/json`. 0053 0054 - `PropertyList`: Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, 0055 according to the associated format and write options values, which is set as the body of the 0056 request. The `Content-Type` HTTP header field of an encoded request is set to 0057 `application/x-plist`. 0058 0059 - `Custom`: Uses the associated closure value to construct a new request given an existing request and 0060 parameters. 0061 */ 0062 public enum ParameterEncoding
Alamofire.swift:122
    encoding: ParameterEncoding = .URL,
Alamofire.swift:326
    encoding: ParameterEncoding = .URL,
Download.swift:83
        encoding: ParameterEncoding = .URL,
Manager.swift:184
        encoding: ParameterEncoding = .URL,
{ 0063 case URL
Alamofire.swift:122
    encoding: ParameterEncoding = .URL,
Alamofire.swift:326
    encoding: ParameterEncoding = .URL,
Download.swift:83
        encoding: ParameterEncoding = .URL,
Manager.swift:184
        encoding: ParameterEncoding = .URL,
ParameterEncoding.swift:90
        case .URL, .URLEncodedInURL:
0064 case URLEncodedInURL
ParameterEncoding.swift:90
        case .URL, .URLEncodedInURL:
ParameterEncoding.swift:104
                case .URLEncodedInURL:
0065 case JSON
ParameterEncoding.swift:140
        case .JSON:
0066 case PropertyList
ParameterEncoding.swift:150
        case .PropertyList(let format, let options):
(NSPropertyListFormat, NSPropertyListWriteOptions) 0067 case Custom
ParameterEncoding.swift:162
        case .Custom(let closure):
((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?)) 0068 0069 /** 0070 Creates a URL request by encoding parameters and applying them onto an existing request. 0071 0072 - parameter URLRequest: The request to have parameters applied. 0073 - parameter parameters: The parameters to apply. 0074 0075 - returns: A tuple containing the constructed request and the error that occurred during parameter encoding, 0076 if any. 0077 */ 0078 public func encode
Download.swift:89
        let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
Manager.swift:189
        let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
( 0079 URLRequest: URLRequestConvertible, 0080 parameters: [String: AnyObject]?) 0081 -> (NSMutableURLRequest, NSError?) 0082 { 0083 var mutableURLRequest = URLRequest.URLRequest 0084 0085 guard let parameters = parameters else { return (mutableURLRequest, nil) } 0086 0087 var encodingError: NSError? = nil 0088 0089 switch self { 0090 case .URL, .URLEncodedInURL: 0091 func query(parameters: [String: AnyObject]) -> String { 0092 var components: [(String, String)] = [] 0093 0094 for key in parameters.keys.sort(<) { 0095 let value = parameters[key]! 0096 components += queryComponents(key, value) 0097 } 0098 0099 return (components.map { "\($0)=\($1)" } as [String]).joinWithSeparator("&") 0100 } 0101 0102 func encodesParametersInURL(method: Method) -> Bool { 0103 switch self { 0104 case .URLEncodedInURL: 0105 return true 0106 default: 0107 break 0108 } 0109 0110 switch method { 0111 case .GET, .HEAD, .DELETE: 0112 return true 0113 default: 0114 return false 0115 } 0116 } 0117 0118 if let method = Method(rawValue: mutableURLRequest.HTTPMethod) where encodesParametersInURL(method) { 0119 if let 0120 URLComponents = NSURLComponents(URL: mutableURLRequest.URL!, resolvingAgainstBaseURL: false) 0121 where !parameters.isEmpty 0122 { 0123 let percentEncodedQuery = (URLComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters) 0124 URLComponents.percentEncodedQuery = percentEncodedQuery 0125 mutableURLRequest.URL = URLComponents.URL 0126 } 0127 } else { 0128 if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil { 0129 mutableURLRequest.setValue( 0130 "application/x-www-form-urlencoded; charset=utf-8", 0131 forHTTPHeaderField: "Content-Type" 0132 ) 0133 } 0134 0135 mutableURLRequest.HTTPBody = query(parameters).dataUsingEncoding( 0136 NSUTF8StringEncoding, 0137 allowLossyConversion: false 0138 ) 0139 } 0140 case .JSON: 0141 do { 0142 let options = NSJSONWritingOptions() 0143 let data = try NSJSONSerialization.dataWithJSONObject(parameters, options: options) 0144 0145 mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") 0146 mutableURLRequest.HTTPBody = data 0147 } catch { 0148 encodingError = error as NSError 0149 } 0150 case .PropertyList(let format, let options): 0151 do { 0152 let data = try NSPropertyListSerialization.dataWithPropertyList( 0153 parameters, 0154 format: format, 0155 options: options 0156 ) 0157 mutableURLRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type") 0158 mutableURLRequest.HTTPBody = data 0159 } catch { 0160 encodingError = error as NSError 0161 } 0162 case .Custom(let closure): 0163 (mutableURLRequest, encodingError) = closure(mutableURLRequest, parameters) 0164 } 0165 0166 return (mutableURLRequest, encodingError) 0167 } 0168 0169 /** 0170 Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion. 0171 0172 - parameter key: The key of the query component. 0173 - parameter value: The value of the query component. 0174 0175 - returns: The percent-escaped, URL encoded query string components. 0176 */ 0177 public func queryComponents
ParameterEncoding.swift:96
                    components += queryComponents(key, value)
ParameterEncoding.swift:182
                components += queryComponents("\(key)[\(nestedKey)]", value)
ParameterEncoding.swift:186
                components += queryComponents("\(key)[]", value)
(key: String, _ value: AnyObject) -> [(String, String)] { 0178 var components: [(String, String)] = [] 0179 0180 if let dictionary = value as? [String: AnyObject] { 0181 for (nestedKey, value) in dictionary { 0182 components += queryComponents("\(key)[\(nestedKey)]", value) 0183 } 0184 } else if let array = value as? [AnyObject] { 0185 for value in array { 0186 components += queryComponents("\(key)[]", value) 0187 } 0188 } else { 0189 components.append((escape(key), escape("\(value)"))) 0190 } 0191 0192 return components 0193 } 0194 0195 /** 0196 Returns a percent-escaped string following RFC 3986 for a query string key or value. 0197 0198 RFC 3986 states that the following characters are "reserved" characters. 0199 0200 - General Delimiters: ":", "#", "[", "]", "@", "?", "/" 0201 - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" 0202 0203 In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow 0204 query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" 0205 should be percent-escaped in the query string. 0206 0207 - parameter string: The string to be percent-escaped. 0208 0209 - returns: The percent-escaped string. 0210 */ 0211 public func escape
ParameterEncoding.swift:189
            components.append((escape(key), escape("\(value)")))
ParameterEncoding.swift:189
            components.append((escape(key), escape("\(value)")))
(string: String) -> String { 0212 let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 0213 let subDelimitersToEncode = "!$&'()*+,;=" 0214 0215 let allowedCharacterSet = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet 0216 allowedCharacterSet.removeCharactersInString(generalDelimitersToEncode + subDelimitersToEncode) 0217 0218 var escaped = "" 0219 0220 //========================================================================================================== 0221 // 0222 // Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few 0223 // hundred Chinense characters causes various malloc error crashes. To avoid this issue until iOS 8 is no 0224 // longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more 0225 // info, please refer to: 0226 // 0227 // - https://github.com/Alamofire/Alamofire/issues/206 0228 // 0229 //========================================================================================================== 0230 0231 if #available(iOS 8.3, OSX 10.10, *) { 0232 escaped = string.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? string 0233 } else { 0234 let batchSize = 50 0235 var index = string.startIndex 0236 0237 while index != string.endIndex { 0238 let startIndex = index 0239 let endIndex = index.advancedBy(batchSize, limit: string.endIndex) 0240 let range = Range(start: startIndex, end: endIndex) 0241 0242 let substring = string.substringWithRange(range) 0243 0244 escaped += substring.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet) ?? substring 0245 0246 index = endIndex 0247 } 0248 } 0249 0250 return escaped 0251 } 0252 } 0253