ParameterEncoder.swift 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  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. /// `JSONEncoder` used to encode parameters.
  57. public let encoder: JSONEncoder
  58. /// Creates an instance with the provided `JSONEncoder`.
  59. ///
  60. /// - Parameter encoder: The `JSONEncoder`. Defaults to `JSONEncoder()`.
  61. public init(encoder: JSONEncoder = JSONEncoder()) {
  62. self.encoder = encoder
  63. }
  64. open func encode<Parameters: Encodable>(_ parameters: Parameters?,
  65. into request: URLRequest) throws -> URLRequest {
  66. guard let parameters = parameters else { return request }
  67. var request = request
  68. do {
  69. let data = try encoder.encode(parameters)
  70. request.httpBody = data
  71. if request.headers["Content-Type"] == nil {
  72. request.headers.update(.contentType("application/json"))
  73. }
  74. } catch {
  75. throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
  76. }
  77. return request
  78. }
  79. }
  80. /// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending
  81. /// on the `Destination` set.
  82. ///
  83. /// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to
  84. /// `application/x-www-form-urlencoded; charset=utf-8`.
  85. ///
  86. /// There is no published specification for how to encode collection types. By default, the convention of appending
  87. /// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
  88. /// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
  89. /// square brackets appended to array keys.
  90. ///
  91. /// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode
  92. /// `true` as 1 and `false` as 0.
  93. open class URLEncodedFormParameterEncoder: ParameterEncoder {
  94. /// Defines where the URL-encoded string should be set for each `URLRequest`.
  95. public enum Destination {
  96. /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request.
  97. /// Sets it to the `httpBody` for all other methods.
  98. case methodDependent
  99. /// Applies the encoded query string to any existing query string from the `URLRequest`.
  100. case queryString
  101. /// Applies the encoded query string to the `httpBody` of the `URLRequest`.
  102. case httpBody
  103. /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`.
  104. ///
  105. /// - Parameter method: The `HTTPMethod`.
  106. /// - Returns: Whether the URL-encoded string should be applied to a `URL`.
  107. func encodesParametersInURL(for method: HTTPMethod) -> Bool {
  108. switch self {
  109. case .methodDependent: return [.get, .head, .delete].contains(method)
  110. case .queryString: return true
  111. case .httpBody: return false
  112. }
  113. }
  114. }
  115. /// Returns an encoder with default parameters.
  116. public static var `default`: URLEncodedFormParameterEncoder { return URLEncodedFormParameterEncoder() }
  117. /// The `URLEncodedFormEncoder` to use.
  118. public let encoder: URLEncodedFormEncoder
  119. /// The `Destination` for the URL-encoded string.
  120. public let destination: Destination
  121. /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value.
  122. ///
  123. /// - Parameters:
  124. /// - encoder: The `URLEncodedFormEncoder`. Defaults to `URLEncodedFormEncoder()`.
  125. /// - destination: The `Destination`. Defaults to `.methodDependent`.
  126. public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) {
  127. self.encoder = encoder
  128. self.destination = destination
  129. }
  130. open func encode<Parameters: Encodable>(_ parameters: Parameters?,
  131. into request: URLRequest) throws -> URLRequest {
  132. guard let parameters = parameters else { return request }
  133. var request = request
  134. guard let url = request.url else {
  135. throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
  136. }
  137. guard let rawMethod = request.httpMethod, let method = HTTPMethod(rawValue: rawMethod) else {
  138. let rawValue = request.httpMethod ?? "nil"
  139. throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue)))
  140. }
  141. if destination.encodesParametersInURL(for: method),
  142. var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
  143. let query: String = try AFResult<String> { try encoder.encode(parameters) }
  144. .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
  145. let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands()
  146. components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString
  147. guard let newURL = components.url else {
  148. throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
  149. }
  150. request.url = newURL
  151. } else {
  152. if request.headers["Content-Type"] == nil {
  153. request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8"))
  154. }
  155. request.httpBody = try AFResult<Data> { try encoder.encode(parameters) }
  156. .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
  157. }
  158. return request
  159. }
  160. }
  161. /// An object that encodes instances into URL-encoded query strings.
  162. ///
  163. /// There is no published specification for how to encode collection types. By default, the convention of appending
  164. /// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
  165. /// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
  166. /// square brackets appended to array keys.
  167. ///
  168. /// `BoolEncoding` can be used to configure how `Bool` values are encoded. The default behavior is to encode
  169. /// `true` as 1 and `false` as 0.
  170. ///
  171. /// `DateEncoding` can be used to configure how `Date` values are encoded. By default, the `.deferredToDate`
  172. /// strategy is used, which formats dates from their structure.
  173. ///
  174. /// `SpaceEncoding` can be used to configure how spaces are encoded. Modern encodings use percent replacement (%20),
  175. /// while older encoding may expect spaces to be replaced with +.
  176. ///
  177. /// This type is largely based on Vapor's [`url-encoded-form`](https://github.com/vapor/url-encoded-form) project.
  178. public final class URLEncodedFormEncoder {
  179. /// Configures how `Bool` parameters are encoded.
  180. public enum BoolEncoding {
  181. /// Encodes `true` as `1`, `false` as `0`.
  182. case numeric
  183. /// Encodes `true` as "true", `false` as "false".
  184. case literal
  185. /// Encodes the given `Bool` as a `String`.
  186. ///
  187. /// - Parameter value: The `Bool` to encode.
  188. /// - Returns: The encoded `String`.
  189. func encode(_ value: Bool) -> String {
  190. switch self {
  191. case .numeric: return value ? "1" : "0"
  192. case .literal: return value ? "true" : "false"
  193. }
  194. }
  195. }
  196. /// Configures how `Date` parameters are encoded.
  197. public enum DateEncoding {
  198. /// Defers encoding to the `Date` type.
  199. case deferredToDate
  200. /// Encodes dates as seconds since midnight UTC on January 1, 1970.
  201. case secondsSince1970
  202. /// Encodes dates as milliseconds since midnight UTC on January 1, 1970.
  203. case millisecondsSince1970
  204. /// Encodes dates according to the ISO 8601 and RFC3339 standards.
  205. case iso8601
  206. /// Encodes dates using the given `DateFormatter`.
  207. case formatted(DateFormatter)
  208. /// Encodes dates using the given closure.
  209. case custom((Date) throws -> String)
  210. private static let iso8601Formatter: ISO8601DateFormatter = {
  211. let formatter = ISO8601DateFormatter()
  212. formatter.formatOptions = .withInternetDateTime
  213. return formatter
  214. }()
  215. /// Encodes the date according to the strategy.
  216. ///
  217. /// - Parameter string: The `Date` to encode.
  218. /// - Returns: The encoded `String` or `nil` if the date should be encoded as `Encodable` structure.
  219. func encode(_ value: Date) throws -> String? {
  220. switch self {
  221. case .deferredToDate:
  222. return nil
  223. case .secondsSince1970:
  224. return String(value.timeIntervalSince1970)
  225. case .millisecondsSince1970:
  226. return String(value.timeIntervalSince1970 * 1000.0)
  227. case .iso8601:
  228. return DateEncoding.iso8601Formatter.string(from: value)
  229. case let .formatted(formatter):
  230. return formatter.string(from: value)
  231. case let .custom(closure):
  232. return try closure(value)
  233. }
  234. }
  235. }
  236. /// Configures how `Array` parameters are encoded.
  237. public enum ArrayEncoding {
  238. /// An empty set of square brackets ("[]") are sppended to the key for every value.
  239. case brackets
  240. /// No brackets are appended to the key and the key is encoded as is.
  241. case noBrackets
  242. func encode(_ key: String) -> String {
  243. switch self {
  244. case .brackets: return "\(key)[]"
  245. case .noBrackets: return key
  246. }
  247. }
  248. }
  249. /// Configures how spaces are encoded.
  250. public enum SpaceEncoding {
  251. /// Encodes spaces according to normal percent escaping rules (%20).
  252. case percentEscaped
  253. /// Encodes spaces as `+`,
  254. case plusReplaced
  255. /// Encodes the string according to the encoding.
  256. ///
  257. /// - Parameter string: The `String` to encode.
  258. /// - Returns: The encoded `String`.
  259. func encode(_ string: String) -> String {
  260. switch self {
  261. case .percentEscaped: return string.replacingOccurrences(of: " ", with: "%20")
  262. case .plusReplaced: return string.replacingOccurrences(of: " ", with: "+")
  263. }
  264. }
  265. }
  266. /// `URLEncodedFormEncoder` error.
  267. public enum Error: Swift.Error {
  268. /// An invalid root object was created by the encoder. Only keyed values are valid.
  269. case invalidRootObject(String)
  270. var localizedDescription: String {
  271. switch self {
  272. case let .invalidRootObject(object): return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead."
  273. }
  274. }
  275. }
  276. /// The `ArrayEncoding` to use.
  277. public let arrayEncoding: ArrayEncoding
  278. /// The `BoolEncoding` to use.
  279. public let boolEncoding: BoolEncoding
  280. /// The `DateEncoding` to use.
  281. public let dateEncoding: DateEncoding
  282. /// The `SpaceEncoding` to use.
  283. public let spaceEncoding: SpaceEncoding
  284. /// The `CharacterSet` of allowed characters.
  285. public var allowedCharacters: CharacterSet
  286. /// Creates an instance from the supplied parameters.
  287. ///
  288. /// - Parameters:
  289. /// - arrayEncoding: The `ArrayEncoding` instance. Defaults to `.brackets`.
  290. /// - boolEncoding: The `BoolEncoding` instance. Defaults to `.numeric`.
  291. /// - dateEncoding: The `DateEncoding` instance. Defaults to `.deferredToDate`.
  292. /// - spaceEncoding: The `SpaceEncoding` instance. Defaults to `.percentEscaped`.
  293. /// - allowedCharacters: The `CharacterSet` of allowed (non-escaped) characters. Defaults to `.afURLQueryAllowed`.
  294. public init(arrayEncoding: ArrayEncoding = .brackets,
  295. boolEncoding: BoolEncoding = .numeric,
  296. dateEncoding: DateEncoding = .deferredToDate,
  297. spaceEncoding: SpaceEncoding = .percentEscaped,
  298. allowedCharacters: CharacterSet = .afURLQueryAllowed) {
  299. self.arrayEncoding = arrayEncoding
  300. self.boolEncoding = boolEncoding
  301. self.dateEncoding = dateEncoding
  302. self.spaceEncoding = spaceEncoding
  303. self.allowedCharacters = allowedCharacters
  304. }
  305. func encode(_ value: Encodable) throws -> URLEncodedFormComponent {
  306. let context = URLEncodedFormContext(.object([:]))
  307. let encoder = _URLEncodedFormEncoder(context: context,
  308. boolEncoding: boolEncoding,
  309. dateEncoding: dateEncoding)
  310. try value.encode(to: encoder)
  311. return context.component
  312. }
  313. /// Encodes the `value` as a URL form encoded `String`.
  314. ///
  315. /// - Parameter value: The `Encodable` value.`
  316. /// - Returns: The encoded `String`.
  317. /// - Throws: An `Error` or `EncodingError` instance if encoding fails.
  318. public func encode(_ value: Encodable) throws -> String {
  319. let component: URLEncodedFormComponent = try encode(value)
  320. guard case let .object(object) = component else {
  321. throw Error.invalidRootObject("\(component)")
  322. }
  323. let serializer = URLEncodedFormSerializer(arrayEncoding: arrayEncoding,
  324. spaceEncoding: spaceEncoding,
  325. allowedCharacters: allowedCharacters)
  326. let query = serializer.serialize(object)
  327. return query
  328. }
  329. /// Encodes the value as `Data`. This is performed by first creating an encoded `String` and then returning the
  330. /// `.utf8` data.
  331. ///
  332. /// - Parameter value: The `Encodable` value.
  333. /// - Returns: The encoded `Data`.
  334. /// - Throws: An `Error` or `EncodingError` instance if encoding fails.
  335. public func encode(_ value: Encodable) throws -> Data {
  336. let string: String = try encode(value)
  337. return Data(string.utf8)
  338. }
  339. }
  340. final class _URLEncodedFormEncoder {
  341. var codingPath: [CodingKey]
  342. // Returns an empty dictionary, as this encoder doesn't support userInfo.
  343. var userInfo: [CodingUserInfoKey : Any] { return [:] }
  344. let context: URLEncodedFormContext
  345. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  346. private let dateEncoding: URLEncodedFormEncoder.DateEncoding
  347. public init(context: URLEncodedFormContext,
  348. codingPath: [CodingKey] = [],
  349. boolEncoding: URLEncodedFormEncoder.BoolEncoding,
  350. dateEncoding: URLEncodedFormEncoder.DateEncoding) {
  351. self.context = context
  352. self.codingPath = codingPath
  353. self.boolEncoding = boolEncoding
  354. self.dateEncoding = dateEncoding
  355. }
  356. }
  357. extension _URLEncodedFormEncoder: Encoder {
  358. func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
  359. let container = _URLEncodedFormEncoder.KeyedContainer<Key>(context: context,
  360. codingPath: codingPath,
  361. boolEncoding: boolEncoding,
  362. dateEncoding: dateEncoding)
  363. return KeyedEncodingContainer(container)
  364. }
  365. func unkeyedContainer() -> UnkeyedEncodingContainer {
  366. return _URLEncodedFormEncoder.UnkeyedContainer(context: context,
  367. codingPath: codingPath,
  368. boolEncoding: boolEncoding,
  369. dateEncoding: dateEncoding)
  370. }
  371. func singleValueContainer() -> SingleValueEncodingContainer {
  372. return _URLEncodedFormEncoder.SingleValueContainer(context: context,
  373. codingPath: codingPath,
  374. boolEncoding: boolEncoding,
  375. dateEncoding: dateEncoding)
  376. }
  377. }
  378. final class URLEncodedFormContext {
  379. var component: URLEncodedFormComponent
  380. init(_ component: URLEncodedFormComponent) {
  381. self.component = component
  382. }
  383. }
  384. enum URLEncodedFormComponent {
  385. case string(String)
  386. case array([URLEncodedFormComponent])
  387. case object([String: URLEncodedFormComponent])
  388. /// Converts self to an `[URLEncodedFormData]` or returns `nil` if not convertible.
  389. var array: [URLEncodedFormComponent]? {
  390. switch self {
  391. case let .array(array): return array
  392. default: return nil
  393. }
  394. }
  395. /// Converts self to an `[String: URLEncodedFormData]` or returns `nil` if not convertible.
  396. var object: [String: URLEncodedFormComponent]? {
  397. switch self {
  398. case let .object(object): return object
  399. default: return nil
  400. }
  401. }
  402. /// Sets self to the supplied value at a given path.
  403. ///
  404. /// data.set(to: "hello", at: ["path", "to", "value"])
  405. ///
  406. /// - parameters:
  407. /// - value: Value of `Self` to set at the supplied path.
  408. /// - path: `CodingKey` path to update with the supplied value.
  409. public mutating func set(to value: URLEncodedFormComponent, at path: [CodingKey]) {
  410. set(&self, to: value, at: path)
  411. }
  412. /// Recursive backing method to `set(to:at:)`.
  413. private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) {
  414. guard path.count >= 1 else {
  415. context = value
  416. return
  417. }
  418. let end = path[0]
  419. var child: URLEncodedFormComponent
  420. switch path.count {
  421. case 1:
  422. child = value
  423. case 2...:
  424. if let index = end.intValue {
  425. let array = context.array ?? []
  426. if array.count > index {
  427. child = array[index]
  428. } else {
  429. child = .array([])
  430. }
  431. set(&child, to: value, at: Array(path[1...]))
  432. } else {
  433. child = context.object?[end.stringValue] ?? .object([:])
  434. set(&child, to: value, at: Array(path[1...]))
  435. }
  436. default: fatalError("Unreachable")
  437. }
  438. if let index = end.intValue {
  439. if var array = context.array {
  440. if array.count > index {
  441. array[index] = child
  442. } else {
  443. array.append(child)
  444. }
  445. context = .array(array)
  446. } else {
  447. context = .array([child])
  448. }
  449. } else {
  450. if var object = context.object {
  451. object[end.stringValue] = child
  452. context = .object(object)
  453. } else {
  454. context = .object([end.stringValue: child])
  455. }
  456. }
  457. }
  458. }
  459. struct AnyCodingKey: CodingKey, Hashable {
  460. let stringValue: String
  461. let intValue: Int?
  462. init?(stringValue: String) {
  463. self.stringValue = stringValue
  464. intValue = nil
  465. }
  466. init?(intValue: Int) {
  467. stringValue = "\(intValue)"
  468. self.intValue = intValue
  469. }
  470. init<Key>(_ base: Key) where Key : CodingKey {
  471. if let intValue = base.intValue {
  472. self.init(intValue: intValue)!
  473. } else {
  474. self.init(stringValue: base.stringValue)!
  475. }
  476. }
  477. }
  478. extension _URLEncodedFormEncoder {
  479. final class KeyedContainer<Key> where Key: CodingKey {
  480. var codingPath: [CodingKey]
  481. private let context: URLEncodedFormContext
  482. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  483. private let dateEncoding: URLEncodedFormEncoder.DateEncoding
  484. init(context: URLEncodedFormContext,
  485. codingPath: [CodingKey],
  486. boolEncoding: URLEncodedFormEncoder.BoolEncoding,
  487. dateEncoding: URLEncodedFormEncoder.DateEncoding) {
  488. self.context = context
  489. self.codingPath = codingPath
  490. self.boolEncoding = boolEncoding
  491. self.dateEncoding = dateEncoding
  492. }
  493. private func nestedCodingPath(for key: CodingKey) -> [CodingKey] {
  494. return codingPath + [key]
  495. }
  496. }
  497. }
  498. extension _URLEncodedFormEncoder.KeyedContainer: KeyedEncodingContainerProtocol {
  499. func encodeNil(forKey key: Key) throws {
  500. let context = EncodingError.Context(codingPath: codingPath,
  501. debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
  502. throw EncodingError.invalidValue("\(key): nil", context)
  503. }
  504. func encode<T>(_ value: T, forKey key: Key) throws where T : Encodable {
  505. var container = nestedSingleValueEncoder(for: key)
  506. try container.encode(value)
  507. }
  508. func nestedSingleValueEncoder(for key: Key) -> SingleValueEncodingContainer {
  509. let container = _URLEncodedFormEncoder.SingleValueContainer(context: context,
  510. codingPath: nestedCodingPath(for: key),
  511. boolEncoding: boolEncoding,
  512. dateEncoding: dateEncoding)
  513. return container
  514. }
  515. func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
  516. let container = _URLEncodedFormEncoder.UnkeyedContainer(context: context,
  517. codingPath: nestedCodingPath(for: key),
  518. boolEncoding: boolEncoding,
  519. dateEncoding: dateEncoding)
  520. return container
  521. }
  522. func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
  523. let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
  524. codingPath: nestedCodingPath(for: key),
  525. boolEncoding: boolEncoding,
  526. dateEncoding: dateEncoding)
  527. return KeyedEncodingContainer(container)
  528. }
  529. func superEncoder() -> Encoder {
  530. return _URLEncodedFormEncoder(context: context,
  531. codingPath: codingPath,
  532. boolEncoding: boolEncoding,
  533. dateEncoding: dateEncoding)
  534. }
  535. func superEncoder(forKey key: Key) -> Encoder {
  536. return _URLEncodedFormEncoder(context: context,
  537. codingPath: nestedCodingPath(for: key),
  538. boolEncoding: boolEncoding,
  539. dateEncoding: dateEncoding)
  540. }
  541. }
  542. extension _URLEncodedFormEncoder {
  543. final class SingleValueContainer {
  544. var codingPath: [CodingKey]
  545. private var canEncodeNewValue = true
  546. private let context: URLEncodedFormContext
  547. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  548. private let dateEncoding: URLEncodedFormEncoder.DateEncoding
  549. init(context: URLEncodedFormContext,
  550. codingPath: [CodingKey],
  551. boolEncoding: URLEncodedFormEncoder.BoolEncoding,
  552. dateEncoding: URLEncodedFormEncoder.DateEncoding) {
  553. self.context = context
  554. self.codingPath = codingPath
  555. self.boolEncoding = boolEncoding
  556. self.dateEncoding = dateEncoding
  557. }
  558. private func checkCanEncode(value: Any?) throws {
  559. guard canEncodeNewValue else {
  560. let context = EncodingError.Context(codingPath: codingPath,
  561. debugDescription: "Attempt to encode value through single value container when previously value already encoded.")
  562. throw EncodingError.invalidValue(value as Any, context)
  563. }
  564. }
  565. }
  566. }
  567. extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContainer {
  568. func encodeNil() throws {
  569. try checkCanEncode(value: nil)
  570. defer { canEncodeNewValue = false }
  571. let context = EncodingError.Context(codingPath: codingPath,
  572. debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
  573. throw EncodingError.invalidValue("nil", context)
  574. }
  575. func encode(_ value: Bool) throws {
  576. try encode(value, as: String(boolEncoding.encode(value)))
  577. }
  578. func encode(_ value: String) throws {
  579. try encode(value, as: value)
  580. }
  581. func encode(_ value: Double) throws {
  582. try encode(value, as: String(value))
  583. }
  584. func encode(_ value: Float) throws {
  585. try encode(value, as: String(value))
  586. }
  587. func encode(_ value: Int) throws {
  588. try encode(value, as: String(value))
  589. }
  590. func encode(_ value: Int8) throws {
  591. try encode(value, as: String(value))
  592. }
  593. func encode(_ value: Int16) throws {
  594. try encode(value, as: String(value))
  595. }
  596. func encode(_ value: Int32) throws {
  597. try encode(value, as: String(value))
  598. }
  599. func encode(_ value: Int64) throws {
  600. try encode(value, as: String(value))
  601. }
  602. func encode(_ value: UInt) throws {
  603. try encode(value, as: String(value))
  604. }
  605. func encode(_ value: UInt8) throws {
  606. try encode(value, as: String(value))
  607. }
  608. func encode(_ value: UInt16) throws {
  609. try encode(value, as: String(value))
  610. }
  611. func encode(_ value: UInt32) throws {
  612. try encode(value, as: String(value))
  613. }
  614. func encode(_ value: UInt64) throws {
  615. try encode(value, as: String(value))
  616. }
  617. private func encode<T>(_ value: T, as string: String) throws where T : Encodable {
  618. try checkCanEncode(value: value)
  619. defer { canEncodeNewValue = false }
  620. context.component.set(to: .string(string), at: codingPath)
  621. }
  622. func encode<T>(_ value: T) throws where T : Encodable {
  623. switch value {
  624. case let date as Date:
  625. guard let string = try dateEncoding.encode(date) else {
  626. fallthrough
  627. }
  628. try encode(value, as: string)
  629. default:
  630. try checkCanEncode(value: value)
  631. defer { canEncodeNewValue = false }
  632. let encoder = _URLEncodedFormEncoder(context: context,
  633. codingPath: codingPath,
  634. boolEncoding: boolEncoding,
  635. dateEncoding: dateEncoding)
  636. try value.encode(to: encoder)
  637. }
  638. }
  639. }
  640. extension _URLEncodedFormEncoder {
  641. final class UnkeyedContainer {
  642. var codingPath: [CodingKey]
  643. var count = 0
  644. var nestedCodingPath: [CodingKey] {
  645. return codingPath + [AnyCodingKey(intValue: count)!]
  646. }
  647. private let context: URLEncodedFormContext
  648. private let boolEncoding: URLEncodedFormEncoder.BoolEncoding
  649. private let dateEncoding: URLEncodedFormEncoder.DateEncoding
  650. init(context: URLEncodedFormContext,
  651. codingPath: [CodingKey],
  652. boolEncoding: URLEncodedFormEncoder.BoolEncoding,
  653. dateEncoding: URLEncodedFormEncoder.DateEncoding) {
  654. self.context = context
  655. self.codingPath = codingPath
  656. self.boolEncoding = boolEncoding
  657. self.dateEncoding = dateEncoding
  658. }
  659. }
  660. }
  661. extension _URLEncodedFormEncoder.UnkeyedContainer: UnkeyedEncodingContainer {
  662. func encodeNil() throws {
  663. let context = EncodingError.Context(codingPath: codingPath,
  664. debugDescription: "URLEncodedFormEncoder cannot encode nil values.")
  665. throw EncodingError.invalidValue("nil", context)
  666. }
  667. func encode<T>(_ value: T) throws where T : Encodable {
  668. var container = nestedSingleValueContainer()
  669. try container.encode(value)
  670. }
  671. func nestedSingleValueContainer() -> SingleValueEncodingContainer {
  672. defer { count += 1 }
  673. return _URLEncodedFormEncoder.SingleValueContainer(context: context,
  674. codingPath: nestedCodingPath,
  675. boolEncoding: boolEncoding,
  676. dateEncoding: dateEncoding)
  677. }
  678. func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
  679. defer { count += 1 }
  680. let container = _URLEncodedFormEncoder.KeyedContainer<NestedKey>(context: context,
  681. codingPath: nestedCodingPath,
  682. boolEncoding: boolEncoding,
  683. dateEncoding: dateEncoding)
  684. return KeyedEncodingContainer(container)
  685. }
  686. func nestedUnkeyedContainer() -> UnkeyedEncodingContainer {
  687. defer { count += 1 }
  688. return _URLEncodedFormEncoder.UnkeyedContainer(context: context,
  689. codingPath: nestedCodingPath,
  690. boolEncoding: boolEncoding,
  691. dateEncoding: dateEncoding)
  692. }
  693. func superEncoder() -> Encoder {
  694. defer { count += 1 }
  695. return _URLEncodedFormEncoder(context: context,
  696. codingPath: codingPath,
  697. boolEncoding: boolEncoding,
  698. dateEncoding: dateEncoding)
  699. }
  700. }
  701. final class URLEncodedFormSerializer {
  702. let arrayEncoding: URLEncodedFormEncoder.ArrayEncoding
  703. let spaceEncoding: URLEncodedFormEncoder.SpaceEncoding
  704. let allowedCharacters: CharacterSet
  705. init(arrayEncoding: URLEncodedFormEncoder.ArrayEncoding,
  706. spaceEncoding: URLEncodedFormEncoder.SpaceEncoding,
  707. allowedCharacters: CharacterSet) {
  708. self.arrayEncoding = arrayEncoding
  709. self.spaceEncoding = spaceEncoding
  710. self.allowedCharacters = allowedCharacters
  711. }
  712. func serialize(_ object: [String: URLEncodedFormComponent]) -> String {
  713. var output: [String] = []
  714. for (key, component) in object {
  715. let value = serialize(component, forKey: key)
  716. output.append(value)
  717. }
  718. return output.joinedWithAmpersands()
  719. }
  720. func serialize(_ component: URLEncodedFormComponent, forKey key: String) -> String {
  721. switch component {
  722. case let .string(string): return "\(escape(key))=\(escape(string))"
  723. case let .array(array): return serialize(array, forKey: key)
  724. case let .object(dictionary): return serialize(dictionary, forKey: key)
  725. }
  726. }
  727. func serialize(_ object: [String: URLEncodedFormComponent], forKey key: String) -> String {
  728. let segments: [String] = object.map { (subKey, value) in
  729. let keyPath = "[\(subKey)]"
  730. return serialize(value, forKey: key + keyPath)
  731. }
  732. return segments.joinedWithAmpersands()
  733. }
  734. func serialize(_ array: [URLEncodedFormComponent], forKey key: String) -> String {
  735. let segments: [String] = array.map { (component) in
  736. let keyPath = arrayEncoding.encode(key)
  737. return serialize(component, forKey: keyPath)
  738. }
  739. return segments.joinedWithAmpersands()
  740. }
  741. func escape(_ query: String) -> String {
  742. var allowedCharactersWithSpace = allowedCharacters
  743. allowedCharactersWithSpace.insert(charactersIn: " ")
  744. let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query
  745. let spaceEncodedQuery = spaceEncoding.encode(escapedQuery)
  746. return spaceEncodedQuery
  747. }
  748. }
  749. extension Array where Element == String {
  750. func joinedWithAmpersands() -> String {
  751. return joined(separator: "&")
  752. }
  753. }
  754. extension CharacterSet {
  755. /// Creates a CharacterSet from RFC 3986 allowed characters.
  756. ///
  757. /// RFC 3986 states that the following characters are "reserved" characters.
  758. ///
  759. /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
  760. /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
  761. ///
  762. /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
  763. /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
  764. /// should be percent-escaped in the query string.
  765. public static let afURLQueryAllowed: CharacterSet = {
  766. let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
  767. let subDelimitersToEncode = "!$&'()*+,;="
  768. let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
  769. return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters)
  770. }()
  771. }