ParameterEncoder.swift 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. //
  2. // ParameterEncoder.swift
  3. //
  4. // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. import Foundation
  25. /// A type that can encode any `Encodable` type into a `URLRequest`.
  26. public protocol ParameterEncoder {
  27. /// Encode the provided `Encodable` parameters into `request`.
  28. ///
  29. /// - Parameters:
  30. /// - parameters: The `Encodable` parameter value.
  31. /// - request: The `URLRequest` into which to encode the parameters.
  32. /// - Returns: A `URLRequest` with the result of the encoding.
  33. /// - Throws: An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of
  34. /// `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`.
  35. func encode<Parameters: Encodable>(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest
  36. }
  37. /// A `ParameterEncoder` that encodes types as JSON body data.
  38. ///
  39. /// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`.
  40. open class JSONParameterEncoder: ParameterEncoder {
  41. /// Returns an encoder with default parameters.
  42. public static var `default`: JSONParameterEncoder { return JSONParameterEncoder() }
  43. /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`.
  44. public static var prettyPrinted: JSONParameterEncoder {
  45. let encoder = JSONEncoder()
  46. encoder.outputFormatting = .prettyPrinted
  47. return JSONParameterEncoder(encoder: encoder)
  48. }
  49. /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`.
  50. @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
  51. public static var sortedKeys: JSONParameterEncoder {
  52. let encoder = JSONEncoder()
  53. encoder.outputFormatting = .sortedKeys
  54. return JSONParameterEncoder(encoder: encoder)
  55. }
  56. let encoder: JSONEncoder
  57. /// Creates an instance with the provided `JSONEncoder`.
  58. ///
  59. /// - Parameter encoder: The `JSONEncoder`. Defaults to `JSONEncoder()`.
  60. public init(encoder: JSONEncoder = JSONEncoder()) {
  61. self.encoder = encoder
  62. }
  63. open func encode<Parameters: Encodable>(_ parameters: Parameters?,
  64. into request: URLRequest) throws -> URLRequest {
  65. guard let parameters = parameters else { return request }
  66. var request = request
  67. do {
  68. let data = try encoder.encode(parameters)
  69. request.httpBody = data
  70. if request.httpHeaders["Content-Type"] == nil {
  71. request.httpHeaders.update(.contentType("application/json"))
  72. }
  73. } catch {
  74. throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
  75. }
  76. return request
  77. }
  78. }
  79. /// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending
  80. /// on the `Destination` set.
  81. ///
  82. /// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to
  83. /// `application/x-www-form-urlencoded; charset=utf-8`.
  84. ///
  85. /// There is no published specification for how to encode collection types. By default, the convention of appending
  86. /// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
  87. /// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
  88. /// square brackets appended to array keys.
  89. ///
  90. /// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode
  91. /// `true` as 1 and `false` as 0.
  92. open class URLEncodedFormParameterEncoder: ParameterEncoder {
  93. /// Defines where the URL-encoded string should be set for each `URLRequest`.
  94. public enum Destination {
  95. /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request.
  96. /// Sets it to the `httpBody` for all other methods.
  97. case methodDependent
  98. /// Applies the encoded query string to any existing query string from the `URLRequest`.
  99. case queryString
  100. /// Applies the encoded query string to the `httpBody` of the `URLRequest`.
  101. case httpBody
  102. /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`.
  103. ///
  104. /// - Parameter method: The `HTTPMethod`.
  105. /// - Returns: Whether the URL-encoded string should be applied to a `URL`.
  106. func encodesParametersInURL(for method: HTTPMethod) -> Bool {
  107. switch self {
  108. case .methodDependent: return [.get, .head, .delete].contains(method)
  109. case .queryString: return true
  110. case .httpBody: return false
  111. }
  112. }
  113. }
  114. /// Returns an encoder with default parameters.
  115. public static var `default`: URLEncodedFormParameterEncoder { return URLEncodedFormParameterEncoder() }
  116. /// The `URLEncodedFormEncoder` to use.
  117. public let encoder: URLEncodedFormEncoder
  118. /// The `Destination` for the URL-encoded string.
  119. public let destination: Destination
  120. /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value.
  121. ///
  122. /// - Parameters:
  123. /// - encoder: The `URLEncodedFormEncoder`. Defaults to `URLEncodedFormEncoder()`.
  124. /// - destination: The `Destination`. Defaults to `.methodDependent`.
  125. public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) {
  126. self.encoder = encoder
  127. self.destination = destination
  128. }
  129. open func encode<Parameters: Encodable>(_ parameters: Parameters?,
  130. into request: URLRequest) throws -> URLRequest {
  131. guard let parameters = parameters else { return request }
  132. var request = request
  133. guard let url = request.url else {
  134. throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
  135. }
  136. guard let rawMethod = request.httpMethod, let method = HTTPMethod(rawValue: rawMethod) else {
  137. let rawValue = request.httpMethod ?? "nil"
  138. throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue)))
  139. }
  140. if destination.encodesParametersInURL(for: method),
  141. var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
  142. let query: String = try Result<String> { try encoder.encode(parameters) }
  143. .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.unwrap()
  144. let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands()
  145. components.percentEncodedQuery = newQueryString
  146. guard let newURL = components.url else {
  147. throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
  148. }
  149. request.url = newURL
  150. } else {
  151. if request.httpHeaders["Content-Type"] == nil {
  152. request.httpHeaders.update(.contentType("application/x-www-form-urlencoded; charset=utf-8"))
  153. }
  154. request.httpBody = try Result<Data> { try encoder.encode(parameters) }
  155. .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.unwrap()
  156. }
  157. return request
  158. }
  159. }
  160. /// An object that encodes instances into URL-encoded query strings.
  161. ///
  162. /// There is no published specification for how to encode collection types. By default, the convention of appending
  163. /// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
  164. /// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
  165. /// square brackets appended to array keys.
  166. ///
  167. /// `BoolEncoding` can be used to configure how `Bool` values are encoded. The default behavior is to encode
  168. /// `true` as 1 and `false` as 0.
  169. ///
  170. /// `SpaceEncoding` can be used to configure how spaces are encoded. Modern encodings use percent replacement (%20),
  171. /// while older encoding may expect spaces to be replaced with +.
  172. ///
  173. /// This type is largely based on Vapor's [`url-encoded-form`](https://github.com/vapor/url-encoded-form) project.
  174. public final class URLEncodedFormEncoder {
  175. /// Configures how `Bool` parameters are encoded.
  176. public enum BoolEncoding {
  177. /// Encodes `true` as `1`, `false` as `0`.
  178. case numeric
  179. /// Encodes `true` as "true", `false` as "false".
  180. case literal
  181. /// Encodes the given `Bool` as a `String`.
  182. ///
  183. /// - Parameter value: The `Bool` to encode.
  184. /// - Returns: The encoded `String`.
  185. func encode(_ value: Bool) -> String {
  186. switch self {
  187. case .numeric: return value ? "1" : "0"
  188. case .literal: return value ? "true" : "false"
  189. }
  190. }
  191. }
  192. /// Configures how `Array` parameters are encoded.
  193. public enum ArrayEncoding {
  194. /// An empty set of square brackets ("[]") are sppended to the key for every value.
  195. case brackets
  196. /// No brackets are appended to the key and the key is encoded as is.
  197. case noBrackets
  198. func encode(_ key: String) -> String {
  199. switch self {
  200. case .brackets: return "\(key)[]"
  201. case .noBrackets: return key
  202. }
  203. }
  204. }
  205. /// Configures how spaces are encoded.
  206. public enum SpaceEncoding {
  207. /// Encodes spaces according to normal percent escaping rules (%20).
  208. case percentEscaped
  209. /// Encodes spaces as `+`,
  210. case plusReplaced
  211. /// Encodes the string according to the encoding.
  212. ///
  213. /// - Parameter string: The `String` to encode.
  214. /// - Returns: The encoded `String`.
  215. func encode(_ string: String) -> String {
  216. switch self {
  217. case .percentEscaped: return string.replacingOccurrences(of: " ", with: "%20")
  218. case .plusReplaced: return string.replacingOccurrences(of: " ", with: "+")
  219. }
  220. }
  221. }
  222. /// `URLEncodedFormEncoder` error.
  223. public enum Error: Swift.Error {
  224. /// An invalid root object was created by the encoder. Only keyed values are valid.
  225. case invalidRootObject(String)
  226. var localizedDescription: String {
  227. switch self {
  228. case let .invalidRootObject(object): return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead."
  229. }
  230. }
  231. }
  232. /// The `ArrayEncoding` to use.
  233. public let arrayEncoding: ArrayEncoding
  234. /// The `BoolEncoding` to use.
  235. public let boolEncoding: BoolEncoding
  236. /// The `SpaceEncoding` to use.
  237. public let spaceEncoding: SpaceEncoding
  238. /// The `CharacterSet` of allowed characters.
  239. public var allowedCharacters: CharacterSet
  240. /// Creates an instance from the supplied parameters.
  241. ///
  242. /// - Parameters:
  243. /// - arrayEncoding: The `ArrayEncoding` instance. Defaults to `.brackets`.
  244. /// - boolEncoding: The `BoolEncoding` instance. Defaults to `.numeric`.
  245. /// - spaceEncoding: The `SpaceEncoding` instance. Defaults to `.percentEscaped`.
  246. /// - allowedCharacters: The `CharacterSet` of allowed (non-escaped) characters. Defaults to `.afURLQueryAllowed`.
  247. public init(arrayEncoding: ArrayEncoding = .brackets,
  248. boolEncoding: BoolEncoding = .numeric,
  249. spaceEncoding: SpaceEncoding = .percentEscaped,
  250. allowedCharacters: CharacterSet = .afURLQueryAllowed) {
  251. self.arrayEncoding = arrayEncoding
  252. self.boolEncoding = boolEncoding
  253. self.spaceEncoding = spaceEncoding
  254. self.allowedCharacters = allowedCharacters
  255. }
  256. func encode(_ value: Encodable) throws -> URLEncodedFormComponent {
  257. let context = URLEncodedFormContext(.object([:]))
  258. let encoder = _URLEncodedFormEncoder(context: context, boolEncoding: boolEncoding)
  259. try value.encode(to: encoder)
  260. return context.component
  261. }
  262. /// Encodes the `value` as a URL form encoded `String`.
  263. ///
  264. /// - Parameter value: The `Encodable` value.`
  265. /// - Returns: The encoded `String`.
  266. /// - Throws: An `Error` or `EncodingError` instance if encoding fails.
  267. public func encode(_ value: Encodable) throws -> String {
  268. let component: URLEncodedFormComponent = try encode(value)
  269. guard case let .object(object) = component else {
  270. throw Error.invalidRootObject("\(component)")
  271. }
  272. let serializer = URLEncodedFormSerializer(arrayEncoding: arrayEncoding,
  273. spaceEncoding: spaceEncoding,
  274. allowedCharacters: allowedCharacters)
  275. let query = serializer.serialize(object)
  276. return query
  277. }
  278. /// Encodes the value as `Data`. This is performed by first creating an encoded `String` and then returning the
  279. /// `.utf8` data.
  280. ///
  281. /// - Parameter value: The `Encodable` value.
  282. /// - Returns: The encoded `Data`.
  283. /// - Throws: An `Error` or `EncodingError` instance if encoding fails.
  284. public func encode(_ value: Encodable) throws -> Data {
  285. let string: String = try encode(value)
  286. return Data(string.utf8)
  287. }
  288. }
  289. final class _URLEncodedFormEncoder {
  290. var codingPath: [CodingKey]
  291. // Returns an empty dictionary, as this encoder doesn't support userInfo.
  292. var userInfo: [CodingUserInfoKey : Any] { return [:] }
  293. let context: URLEncodedFormContext
  294. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  295. public init(context: URLEncodedFormContext,
  296. codingPath: [CodingKey] = [],
  297. boolEncoding: URLEncodedFormEncoder.BoolEncoding) {
  298. self.context = context
  299. self.codingPath = codingPath
  300. self.boolEncoding = boolEncoding
  301. }
  302. }
  303. extension _URLEncodedFormEncoder: Encoder {
  304. func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
  305. let container = _URLEncodedFormEncoder.KeyedContainer<Key>(context: context,
  306. codingPath: codingPath,
  307. boolEncoding: boolEncoding)
  308. return KeyedEncodingContainer(container)
  309. }
  310. func unkeyedContainer() -> UnkeyedEncodingContainer {
  311. return _URLEncodedFormEncoder.UnkeyedContainer(context: context,
  312. codingPath: codingPath,
  313. boolEncoding: boolEncoding)
  314. }
  315. func singleValueContainer() -> SingleValueEncodingContainer {
  316. return _URLEncodedFormEncoder.SingleValueContainer(context: context,
  317. codingPath: codingPath,
  318. boolEncoding: boolEncoding)
  319. }
  320. }
  321. final class URLEncodedFormContext {
  322. var component: URLEncodedFormComponent
  323. init(_ component: URLEncodedFormComponent) {
  324. self.component = component
  325. }
  326. }
  327. enum URLEncodedFormComponent {
  328. case string(String)
  329. case array([URLEncodedFormComponent])
  330. case object([String: URLEncodedFormComponent])
  331. /// Converts self to an `[URLEncodedFormData]` or returns `nil` if not convertible.
  332. var array: [URLEncodedFormComponent]? {
  333. switch self {
  334. case let .array(array): return array
  335. default: return nil
  336. }
  337. }
  338. /// Converts self to an `[String: URLEncodedFormData]` or returns `nil` if not convertible.
  339. var object: [String: URLEncodedFormComponent]? {
  340. switch self {
  341. case let .object(object): return object
  342. default: return nil
  343. }
  344. }
  345. /// Sets self to the supplied value at a given path.
  346. ///
  347. /// data.set(to: "hello", at: ["path", "to", "value"])
  348. ///
  349. /// - parameters:
  350. /// - value: Value of `Self` to set at the supplied path.
  351. /// - path: `CodingKey` path to update with the supplied value.
  352. public mutating func set(to value: URLEncodedFormComponent, at path: [CodingKey]) {
  353. set(&self, to: value, at: path)
  354. }
  355. /// Recursive backing method to `set(to:at:)`.
  356. private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) {
  357. guard path.count >= 1 else {
  358. context = value
  359. return
  360. }
  361. let end = path[0]
  362. var child: URLEncodedFormComponent
  363. switch path.count {
  364. case 1:
  365. child = value
  366. case 2...:
  367. if let index = end.intValue {
  368. let array = context.array ?? []
  369. if array.count > index {
  370. child = array[index]
  371. } else {
  372. child = .array([])
  373. }
  374. set(&child, to: value, at: Array(path[1...]))
  375. } else {
  376. child = context.object?[end.stringValue] ?? .object([:])
  377. set(&child, to: value, at: Array(path[1...]))
  378. }
  379. default: fatalError("Unreachable")
  380. }
  381. if let index = end.intValue {
  382. if var array = context.array {
  383. if array.count > index {
  384. array[index] = child
  385. } else {
  386. array.append(child)
  387. }
  388. context = .array(array)
  389. } else {
  390. context = .array([child])
  391. }
  392. } else {
  393. if var object = context.object {
  394. object[end.stringValue] = child
  395. context = .object(object)
  396. } else {
  397. context = .object([end.stringValue: child])
  398. }
  399. }
  400. }
  401. }
  402. struct AnyCodingKey: CodingKey, Hashable {
  403. let stringValue: String
  404. let intValue: Int?
  405. init?(stringValue: String) {
  406. self.stringValue = stringValue
  407. intValue = nil
  408. }
  409. init?(intValue: Int) {
  410. stringValue = "\(intValue)"
  411. self.intValue = intValue
  412. }
  413. init<Key>(_ base: Key) where Key : CodingKey {
  414. if let intValue = base.intValue {
  415. self.init(intValue: intValue)!
  416. } else {
  417. self.init(stringValue: base.stringValue)!
  418. }
  419. }
  420. }
  421. extension _URLEncodedFormEncoder {
  422. final class KeyedContainer<Key> where Key: CodingKey {
  423. var codingPath: [CodingKey]
  424. private let context: URLEncodedFormContext
  425. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  426. init(context: URLEncodedFormContext,
  427. codingPath: [CodingKey],
  428. boolEncoding: URLEncodedFormEncoder.BoolEncoding) {
  429. self.context = context
  430. self.codingPath = codingPath
  431. self.boolEncoding = boolEncoding
  432. }
  433. private func nestedCodingPath(for key: CodingKey) -> [CodingKey] {
  434. return codingPath + [key]
  435. }
  436. }
  437. }
  438. extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
  439. func encodeNil(forKey key: Key) throws {
  440. let context = EncodingError.Context(codingPath: codingPath,
  441. debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
  442. throw EncodingError.invalidValue("\(key): nil", context)
  443. }
  444. func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable {
  445. var container = nestedSingleValueEncoder(for: key)
  446. try container.encode(value)
  447. }
  448. func nestedSingleValueEncoder(for key: Key) -> SingleValueEncodingContainer {
  449. let container = _URLEncodedFormEncoder.SingleValueContainer(context: context,
  450. codingPath: nestedCodingPath(for: key),
  451. boolEncoding: boolEncoding)
  452. return container
  453. }
  454. func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
  455. let container = _URLEncodedFormEncoder.UnkeyedContainer(context: context,
  456. codingPath: nestedCodingPath(for: key),
  457. boolEncoding: boolEncoding)
  458. return container
  459. }
  460. func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
  461. let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
  462. codingPath: nestedCodingPath(for: key),
  463. boolEncoding: boolEncoding)
  464. return KeyedEncodingContainer(container)
  465. }
  466. func superEncoder() -> Encoder {
  467. return _URLEncodedFormEncoder(context: context, codingPath: codingPath, boolEncoding: boolEncoding)
  468. }
  469. func superEncoder(forKey key: Key) -> Encoder {
  470. return _URLEncodedFormEncoder(context: context, codingPath: nestedCodingPath(for: key), boolEncoding: boolEncoding)
  471. }
  472. }
  473. extension _URLEncodedFormEncoder {
  474. final class SingleValueContainer {
  475. var codingPath: [CodingKey]
  476. private var canEncodeNewValue = true
  477. private let context: URLEncodedFormContext
  478. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  479. init(context: URLEncodedFormContext, codingPath: [CodingKey], boolEncoding: URLEncodedFormEncoder.BoolEncoding) {
  480. self.context = context
  481. self.codingPath = codingPath
  482. self.boolEncoding = boolEncoding
  483. }
  484. private func checkCanEncode(value: Any?) throws {
  485. guard canEncodeNewValue else {
  486. let context = EncodingError.Context(codingPath: codingPath,
  487. debugDescription: "Attempt to encode value through single value container when previously value already encoded.")
  488. throw EncodingError.invalidValue(value as Any, context)
  489. }
  490. }
  491. }
  492. }
  493. extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContainer {
  494. func encodeNil() throws {
  495. try checkCanEncode(value: nil)
  496. defer { canEncodeNewValue = false }
  497. let context = EncodingError.Context(codingPath: codingPath,
  498. debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
  499. throw EncodingError.invalidValue("nil", context)
  500. }
  501. func encode(_ value: Bool) throws {
  502. try encode(value, as: String(boolEncoding.encode(value)))
  503. }
  504. func encode(_ value: String) throws {
  505. try encode(value, as: value)
  506. }
  507. func encode(_ value: Double) throws {
  508. try encode(value, as: String(value))
  509. }
  510. func encode(_ value: Float) throws {
  511. try encode(value, as: String(value))
  512. }
  513. func encode(_ value: Int) throws {
  514. try encode(value, as: String(value))
  515. }
  516. func encode(_ value: Int8) throws {
  517. try encode(value, as: String(value))
  518. }
  519. func encode(_ value: Int16) throws {
  520. try encode(value, as: String(value))
  521. }
  522. func encode(_ value: Int32) throws {
  523. try encode(value, as: String(value))
  524. }
  525. func encode(_ value: Int64) throws {
  526. try encode(value, as: String(value))
  527. }
  528. func encode(_ value: UInt) throws {
  529. try encode(value, as: String(value))
  530. }
  531. func encode(_ value: UInt8) throws {
  532. try encode(value, as: String(value))
  533. }
  534. func encode(_ value: UInt16) throws {
  535. try encode(value, as: String(value))
  536. }
  537. func encode(_ value: UInt32) throws {
  538. try encode(value, as: String(value))
  539. }
  540. func encode(_ value: UInt64) throws {
  541. try encode(value, as: String(value))
  542. }
  543. private func encode<T>(_ value: T, as string: String) throws where T : Encodable {
  544. try checkCanEncode(value: value)
  545. defer { canEncodeNewValue = false }
  546. context.component.set(to: .string(string), at: codingPath)
  547. }
  548. func encode<T>(_ value: T) throws where T : Encodable {
  549. try checkCanEncode(value: value)
  550. defer { canEncodeNewValue = false }
  551. let encoder = _URLEncodedFormEncoder(context: context,
  552. codingPath: codingPath,
  553. boolEncoding: boolEncoding)
  554. try value.encode(to: encoder)
  555. }
  556. }
  557. extension _URLEncodedFormEncoder {
  558. final class UnkeyedContainer {
  559. var codingPath: [CodingKey]
  560. var count = 0
  561. var nestedCodingPath: [CodingKey] {
  562. return codingPath + [AnyCodingKey(intValue: count)!]
  563. }
  564. private let context: URLEncodedFormContext
  565. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  566. init(context: URLEncodedFormContext,
  567. codingPath: [CodingKey],
  568. boolEncoding: URLEncodedFormEncoder.BoolEncoding) {
  569. self.context = context
  570. self.codingPath = codingPath
  571. self.boolEncoding = boolEncoding
  572. }
  573. }
  574. }
  575. extension _URLEncodedFormEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
  576. func encodeNil() throws {
  577. let context = EncodingError.Context(codingPath: codingPath,
  578. debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
  579. throw EncodingError.invalidValue("nil", context)
  580. }
  581. func encode<T>(_ value: T) throws where T : Encodable {
  582. var container = nestedSingleValueContainer()
  583. try container.encode(value)
  584. }
  585. func nestedSingleValueContainer() -> SingleValueEncodingContainer {
  586. defer { count += 1 }
  587. return _URLEncodedFormEncoder.SingleValueContainer(context: context,
  588. codingPath: nestedCodingPath,
  589. boolEncoding: boolEncoding)
  590. }
  591. func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
  592. defer { count += 1 }
  593. let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
  594. codingPath: nestedCodingPath,
  595. boolEncoding: boolEncoding)
  596. return KeyedEncodingContainer(container)
  597. }
  598. func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
  599. defer { count += 1 }
  600. return _URLEncodedFormEncoder.UnkeyedContainer(context: context,
  601. codingPath: nestedCodingPath,
  602. boolEncoding: boolEncoding)
  603. }
  604. func superEncoder() -> Encoder {
  605. defer { count += 1 }
  606. return _URLEncodedFormEncoder(context: context, codingPath: codingPath, boolEncoding: boolEncoding)
  607. }
  608. }
  609. final class URLEncodedFormSerializer {
  610. let arrayEncoding: URLEncodedFormEncoder.ArrayEncoding
  611. let spaceEncoding: URLEncodedFormEncoder.SpaceEncoding
  612. let allowedCharacters: CharacterSet
  613. init(arrayEncoding: URLEncodedFormEncoder.ArrayEncoding,
  614. spaceEncoding: URLEncodedFormEncoder.SpaceEncoding,
  615. allowedCharacters: CharacterSet) {
  616. self.arrayEncoding = arrayEncoding
  617. self.spaceEncoding = spaceEncoding
  618. self.allowedCharacters = allowedCharacters
  619. }
  620. func serialize(_ object: [String: URLEncodedFormComponent]) -> String {
  621. var output: [String] = []
  622. for (key, component) in object {
  623. let value = serialize(component, forKey: key)
  624. output.append(value)
  625. }
  626. return output.joinedWithAmpersands()
  627. }
  628. func serialize(_ component: URLEncodedFormComponent, forKey key: String) -> String {
  629. switch component {
  630. case let .string(string): return "\(escape(key))=\(escape(string))"
  631. case let .array(array): return serialize(array, forKey: key)
  632. case let .object(dictionary): return serialize(dictionary, forKey: key)
  633. }
  634. }
  635. func serialize(_ object: [String: URLEncodedFormComponent], forKey key: String) -> String {
  636. let segments: [String] = object.map { (subKey, value) in
  637. let keyPath = "[\(subKey)]"
  638. return serialize(value, forKey: key + keyPath)
  639. }
  640. return segments.joinedWithAmpersands()
  641. }
  642. func serialize(_ array: [URLEncodedFormComponent], forKey key: String) -> String {
  643. let segments: [String] = array.map { (component) in
  644. let keyPath = arrayEncoding.encode(key)
  645. return serialize(component, forKey: keyPath)
  646. }
  647. return segments.joinedWithAmpersands()
  648. }
  649. func escape(_ query: String) -> String {
  650. var allowedCharactersWithSpace = allowedCharacters
  651. allowedCharactersWithSpace.insert(charactersIn: " ")
  652. let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query
  653. let spaceEncodedQuery = spaceEncoding.encode(escapedQuery)
  654. return spaceEncodedQuery
  655. }
  656. }
  657. extension Array where Element == String {
  658. func joinedWithAmpersands() -> String {
  659. return joined(separator: "&")
  660. }
  661. }
  662. extension CharacterSet {
  663. /// Creates a CharacterSet from RFC 3986 allowed characters.
  664. ///
  665. /// RFC 3986 states that the following characters are "reserved" characters.
  666. ///
  667. /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
  668. /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
  669. ///
  670. /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
  671. /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
  672. /// should be percent-escaped in the query string.
  673. public static let afURLQueryAllowed: CharacterSet = {
  674. let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
  675. let subDelimitersToEncode = "!$&'()*+,;="
  676. let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
  677. return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters)
  678. }()
  679. }