// // ResponseSerialization.swift // // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // import Foundation // MARK: Protocols /// The type to which all data response serializers must conform in order to serialize a response. public protocol DataResponseSerializerProtocol { /// The type of serialized object to be created. associatedtype SerializedObject /// Serialize the response `Data` into the provided type.. /// /// - Parameters: /// - request: `URLRequest` which was used to perform the request, if any. /// - response: `HTTPURLResponse` received from the server, if any. /// - data: `Data` returned from the server, if any. /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. /// /// - Returns: The `SerializedObject`. /// - Throws: Any `Error` produced during serialization. func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> SerializedObject } /// The type to which all download response serializers must conform in order to serialize a response. public protocol DownloadResponseSerializerProtocol { /// The type of serialized object to be created. associatedtype SerializedObject /// Serialize the downloaded response `Data` from disk into the provided type.. /// /// - Parameters: /// - request: `URLRequest` which was used to perform the request, if any. /// - response: `HTTPURLResponse` received from the server, if any. /// - fileURL: File `URL` to which the response data was downloaded. /// - error: `Error` produced by Alamofire or the underlying `URLSession` during the request. /// /// - Returns: The `SerializedObject`. /// - Throws: Any `Error` produced during serialization. func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> SerializedObject } /// A serializer that can handle both data and download responses. public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol { /// `DataPreprocessor` used to prepare incoming `Data` for serialization. var dataPreprocessor: DataPreprocessor { get } /// `HTTPMethod`s for which empty response bodies are considered appropriate. var emptyRequestMethods: Set { get } /// HTTP response codes for which empty response bodies are considered appropriate. var emptyResponseCodes: Set { get } } /// Type used to preprocess `Data` before it handled by a serializer. public protocol DataPreprocessor { /// Process `Data` before it's handled by a serializer. /// - Parameter data: The raw `Data` to process. func preprocess(_ data: Data) throws -> Data } /// `DataPreprocessor` that returns passed `Data` without any transform. public struct PassthroughPreprocessor: DataPreprocessor { public init() {} public func preprocess(_ data: Data) throws -> Data { data } } /// `DataPreprocessor` that trims Google's typical `)]}',\n` XSSI JSON header. public struct GoogleXSSIPreprocessor: DataPreprocessor { public init() {} public func preprocess(_ data: Data) throws -> Data { (data.prefix(6) == Data(")]}',\n".utf8)) ? data.dropFirst(6) : data } } extension ResponseSerializer { /// Default `DataPreprocessor`. `PassthroughPreprocessor` by default. public static var defaultDataPreprocessor: DataPreprocessor { PassthroughPreprocessor() } /// Default `HTTPMethod`s for which empty response bodies are considered appropriate. `[.head]` by default. public static var defaultEmptyRequestMethods: Set { [.head] } /// HTTP response codes for which empty response bodies are considered appropriate. `[204, 205]` by default. public static var defaultEmptyResponseCodes: Set { [204, 205] } public var dataPreprocessor: DataPreprocessor { Self.defaultDataPreprocessor } public var emptyRequestMethods: Set { Self.defaultEmptyRequestMethods } public var emptyResponseCodes: Set { Self.defaultEmptyResponseCodes } /// Determines whether the `request` allows empty response bodies, if `request` exists. /// /// - Parameter request: `URLRequest` to evaluate. /// /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `request` was `nil`. public func requestAllowsEmptyResponseData(_ request: URLRequest?) -> Bool? { request.flatMap { $0.httpMethod } .flatMap(HTTPMethod.init) .map { emptyRequestMethods.contains($0) } } /// Determines whether the `response` allows empty response bodies, if `response` exists`. /// /// - Parameter response: `HTTPURLResponse` to evaluate. /// /// - Returns: `Bool` representing the outcome of the evaluation, or `nil` if `response` was `nil`. public func responseAllowsEmptyResponseData(_ response: HTTPURLResponse?) -> Bool? { response.flatMap { $0.statusCode } .map { emptyResponseCodes.contains($0) } } /// Determines whether `request` and `response` allow empty response bodies. /// /// - Parameters: /// - request: `URLRequest` to evaluate. /// - response: `HTTPURLResponse` to evaluate. /// /// - Returns: `true` if `request` or `response` allow empty bodies, `false` otherwise. public func emptyResponseAllowed(forRequest request: URLRequest?, response: HTTPURLResponse?) -> Bool { (requestAllowsEmptyResponseData(request) == true) || (responseAllowsEmptyResponseData(response) == true) } } /// By default, any serializer declared to conform to both types will get file serialization for free, as it just feeds /// the data read from disk into the data response serializer. public extension DownloadResponseSerializerProtocol where Self: DataResponseSerializerProtocol { func serializeDownload(request: URLRequest?, response: HTTPURLResponse?, fileURL: URL?, error: Error?) throws -> Self.SerializedObject { guard error == nil else { throw error! } guard let fileURL = fileURL else { throw AFError.responseSerializationFailed(reason: .inputFileNil) } let data: Data do { data = try Data(contentsOf: fileURL) } catch { throw AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)) } do { return try serialize(request: request, response: response, data: data, error: error) } catch { throw error } } } // MARK: - Default extension DataRequest { /// Adds a handler to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - completionHandler: The code to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { appendResponseSerializer { // Start work that should be on the serialization queue. let result = AFResult(value: self.data, error: self.error) // End work that should be on the serialization queue. self.underlyingQueue.async { let response = DataResponse(request: self.request, response: self.response, data: self.data, metrics: self.metrics, serializationDuration: 0, result: result) self.eventMonitor?.request(self, didParseResponse: response) self.responseSerializerDidComplete { queue.async { completionHandler(response) } } } } return self } /// Adds a handler to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default /// - responseSerializer: The response serializer responsible for serializing the request, response, and data. /// - completionHandler: The code to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func response(queue: DispatchQueue = .main, responseSerializer: Serializer, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { appendResponseSerializer { // Start work that should be on the serialization queue. let start = ProcessInfo.processInfo.systemUptime let result: AFResult = Result { try responseSerializer.serialize(request: self.request, response: self.response, data: self.data, error: self.error) }.mapError { error in error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) } let end = ProcessInfo.processInfo.systemUptime // End work that should be on the serialization queue. self.underlyingQueue.async { let response = DataResponse(request: self.request, response: self.response, data: self.data, metrics: self.metrics, serializationDuration: end - start, result: result) self.eventMonitor?.request(self, didParseResponse: response) guard let serializerError = result.failure, let delegate = self.delegate else { self.responseSerializerDidComplete { queue.async { completionHandler(response) } } return } delegate.retryResult(for: self, dueTo: serializerError) { retryResult in var didComplete: (() -> Void)? defer { if let didComplete = didComplete { self.responseSerializerDidComplete { queue.async { didComplete() } } } } switch retryResult { case .doNotRetry: didComplete = { completionHandler(response) } case let .doNotRetryWithError(retryError): let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) let response = DataResponse(request: self.request, response: self.response, data: self.data, metrics: self.metrics, serializationDuration: end - start, result: result) didComplete = { completionHandler(response) } case .retry, .retryWithDelay: delegate.retryRequest(self, withDelay: retryResult.delay) } } } } return self } } extension DownloadRequest { /// Adds a handler to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - completionHandler: The code to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { appendResponseSerializer { // Start work that should be on the serialization queue. let result = AFResult(value: self.fileURL, error: self.error) // End work that should be on the serialization queue. self.underlyingQueue.async { let response = DownloadResponse(request: self.request, response: self.response, fileURL: self.fileURL, resumeData: self.resumeData, metrics: self.metrics, serializationDuration: 0, result: result) self.eventMonitor?.request(self, didParseResponse: response) self.responseSerializerDidComplete { queue.async { completionHandler(response) } } } } return self } /// Adds a handler to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - responseSerializer: The response serializer responsible for serializing the request, response, and data /// contained in the destination `URL`. /// - completionHandler: The code to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func response(queue: DispatchQueue = .main, responseSerializer: Serializer, completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { appendResponseSerializer { // Start work that should be on the serialization queue. let start = ProcessInfo.processInfo.systemUptime let result: AFResult = Result { try responseSerializer.serializeDownload(request: self.request, response: self.response, fileURL: self.fileURL, error: self.error) }.mapError { error in error.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: error))) } let end = ProcessInfo.processInfo.systemUptime // End work that should be on the serialization queue. self.underlyingQueue.async { let response = DownloadResponse(request: self.request, response: self.response, fileURL: self.fileURL, resumeData: self.resumeData, metrics: self.metrics, serializationDuration: end - start, result: result) self.eventMonitor?.request(self, didParseResponse: response) guard let serializerError = result.failure, let delegate = self.delegate else { self.responseSerializerDidComplete { queue.async { completionHandler(response) } } return } delegate.retryResult(for: self, dueTo: serializerError) { retryResult in var didComplete: (() -> Void)? defer { if let didComplete = didComplete { self.responseSerializerDidComplete { queue.async { didComplete() } } } } switch retryResult { case .doNotRetry: didComplete = { completionHandler(response) } case let .doNotRetryWithError(retryError): let result: AFResult = .failure(retryError.asAFError(orFailWith: "Received retryError was not already AFError")) let response = DownloadResponse(request: self.request, response: self.response, fileURL: self.fileURL, resumeData: self.resumeData, metrics: self.metrics, serializationDuration: end - start, result: result) didComplete = { completionHandler(response) } case .retry, .retryWithDelay: delegate.retryRequest(self, withDelay: retryResult.delay) } } } } return self } } // MARK: - Data /// A `ResponseSerializer` that performs minimal response checking and returns any response `Data` as-is. By default, a /// request returning `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the /// response has an HTTP status code valid for empty responses, then an empty `Data` value is returned. public final class DataResponseSerializer: ResponseSerializer { public let dataPreprocessor: DataPreprocessor public let emptyResponseCodes: Set public let emptyRequestMethods: Set /// Creates an instance using the provided values. /// /// - Parameters: /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. public init(dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods) { self.dataPreprocessor = dataPreprocessor self.emptyResponseCodes = emptyResponseCodes self.emptyRequestMethods = emptyRequestMethods } public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Data { guard error == nil else { throw error! } guard var data = data, !data.isEmpty else { guard emptyResponseAllowed(forRequest: request, response: response) else { throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) } return Data() } data = try dataPreprocessor.preprocess(data) return data } } extension DataRequest { /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is called. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseData(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { response(queue: queue, responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods), completionHandler: completionHandler) } } extension DownloadRequest { /// Adds a handler using a `DataResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is called. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseData(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set = DataResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = DataResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { response(queue: queue, responseSerializer: DataResponseSerializer(dataPreprocessor: dataPreprocessor, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods), completionHandler: completionHandler) } } // MARK: - String /// A `ResponseSerializer` that decodes the response data as a `String`. By default, a request returning `nil` or no /// data is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code /// valid for empty responses, then an empty `String` is returned. public final class StringResponseSerializer: ResponseSerializer { public let dataPreprocessor: DataPreprocessor /// Optional string encoding used to validate the response. public let encoding: String.Encoding? public let emptyResponseCodes: Set public let emptyRequestMethods: Set /// Creates an instance with the provided values. /// /// - Parameters: /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. /// - encoding: A string encoding. Defaults to `nil`, in which case the encoding will be determined /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. public init(dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, encoding: String.Encoding? = nil, emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods) { self.dataPreprocessor = dataPreprocessor self.encoding = encoding self.emptyResponseCodes = emptyResponseCodes self.emptyRequestMethods = emptyRequestMethods } public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> String { guard error == nil else { throw error! } guard var data = data, !data.isEmpty else { guard emptyResponseAllowed(forRequest: request, response: response) else { throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) } return "" } data = try dataPreprocessor.preprocess(data) var convertedEncoding = encoding if let encodingName = response?.textEncodingName, convertedEncoding == nil { convertedEncoding = String.Encoding(ianaCharsetName: encodingName) } let actualEncoding = convertedEncoding ?? .isoLatin1 guard let string = String(data: data, encoding: actualEncoding) else { throw AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)) } return string } } extension DataRequest { /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseString(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, encoding: String.Encoding? = nil, emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { response(queue: queue, responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, encoding: encoding, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods), completionHandler: completionHandler) } } extension DownloadRequest { /// Adds a handler using a `StringResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseString(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, encoding: String.Encoding? = nil, emptyResponseCodes: Set = StringResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = StringResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { response(queue: queue, responseSerializer: StringResponseSerializer(dataPreprocessor: dataPreprocessor, encoding: encoding, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods), completionHandler: completionHandler) } } // MARK: - JSON /// A `ResponseSerializer` that decodes the response data using `JSONSerialization`. By default, a request returning /// `nil` or no data is considered an error. However, if the request has an `HTTPMethod` or the response has an /// HTTP status code valid for empty responses, then an `NSNull` value is returned. public final class JSONResponseSerializer: ResponseSerializer { public let dataPreprocessor: DataPreprocessor public let emptyResponseCodes: Set public let emptyRequestMethods: Set /// `JSONSerialization.ReadingOptions` used when serializing a response. public let options: JSONSerialization.ReadingOptions /// Creates an instance with the provided values. /// /// - Parameters: /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. /// - options: The options to use. `.allowFragments` by default. public init(dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, options: JSONSerialization.ReadingOptions = .allowFragments) { self.dataPreprocessor = dataPreprocessor self.emptyResponseCodes = emptyResponseCodes self.emptyRequestMethods = emptyRequestMethods self.options = options } public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Any { guard error == nil else { throw error! } guard var data = data, !data.isEmpty else { guard emptyResponseAllowed(forRequest: request, response: response) else { throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) } return NSNull() } data = try dataPreprocessor.preprocess(data) do { return try JSONSerialization.jsonObject(with: data, options: options) } catch { throw AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)) } } } extension DataRequest { /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` /// by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseJSON(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { response(queue: queue, responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods, options: options), completionHandler: completionHandler) } } extension DownloadRequest { /// Adds a handler using a `JSONResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` /// by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseJSON(queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = JSONResponseSerializer.defaultDataPreprocessor, emptyResponseCodes: Set = JSONResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = JSONResponseSerializer.defaultEmptyRequestMethods, options: JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { response(queue: queue, responseSerializer: JSONResponseSerializer(dataPreprocessor: dataPreprocessor, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods, options: options), completionHandler: completionHandler) } } // MARK: - Empty /// Protocol representing an empty response. Use `T.emptyValue()` to get an instance. public protocol EmptyResponse { /// Empty value for the conforming type. /// /// - Returns: Value of `Self` to use for empty values. static func emptyValue() -> Self } /// Type representing an empty value. Use `Empty.value` to get the static instance. public struct Empty: Codable { /// Static `Empty` instance used for all `Empty` responses. public static let value = Empty() } extension Empty: EmptyResponse { public static func emptyValue() -> Empty { value } } // MARK: - DataDecoder Protocol /// Any type which can decode `Data` into a `Decodable` type. public protocol DataDecoder { /// Decode `Data` into the provided type. /// /// - Parameters: /// - type: The `Type` to be decoded. /// - data: The `Data` to be decoded. /// /// - Returns: The decoded value of type `D`. /// - Throws: Any error that occurs during decode. func decode(_ type: D.Type, from data: Data) throws -> D } /// `JSONDecoder` automatically conforms to `DataDecoder`. extension JSONDecoder: DataDecoder {} /// `PropertyListDecoder` automatically conforms to `DataDecoder`. extension PropertyListDecoder: DataDecoder {} // MARK: - Decodable /// A `ResponseSerializer` that decodes the response data as a generic value using any type that conforms to /// `DataDecoder`. By default, this is an instance of `JSONDecoder`. Additionally, a request returning `nil` or no data /// is considered an error. However, if the request has an `HTTPMethod` or the response has an HTTP status code valid /// for empty responses then an empty value will be returned. If the decoded type conforms to `EmptyResponse`, the /// type's `emptyValue()` will be returned. If the decoded type is `Empty`, the `.value` instance is returned. If the /// decoded type *does not* conform to `EmptyResponse` and isn't `Empty`, an error will be produced. public final class DecodableResponseSerializer: ResponseSerializer { public let dataPreprocessor: DataPreprocessor /// The `DataDecoder` instance used to decode responses. public let decoder: DataDecoder public let emptyResponseCodes: Set public let emptyRequestMethods: Set /// Creates an instance using the values provided. /// /// - Parameters: /// - dataPreprocessor: `DataPreprocessor` used to prepare the received `Data` for serialization. /// - decoder: The `DataDecoder`. `JSONDecoder()` by default. /// - emptyResponseCodes: The HTTP response codes for which empty responses are allowed. `[204, 205]` by default. /// - emptyRequestMethods: The HTTP request methods for which empty responses are allowed. `[.head]` by default. public init(dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, decoder: DataDecoder = JSONDecoder(), emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods) { self.dataPreprocessor = dataPreprocessor self.decoder = decoder self.emptyResponseCodes = emptyResponseCodes self.emptyRequestMethods = emptyRequestMethods } public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> T { guard error == nil else { throw error! } guard var data = data, !data.isEmpty else { guard emptyResponseAllowed(forRequest: request, response: response) else { throw AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength) } guard let emptyResponseType = T.self as? EmptyResponse.Type, let emptyValue = emptyResponseType.emptyValue() as? T else { throw AFError.responseSerializationFailed(reason: .invalidEmptyResponse(type: "\(T.self)")) } return emptyValue } data = try dataPreprocessor.preprocess(data) do { return try decoder.decode(T.self, from: data) } catch { throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) } } } extension DataRequest { /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - type: `Decodable` type to decode from response data. /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` /// by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseDecodable(of type: T.Type = T.self, queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, decoder: DataDecoder = JSONDecoder(), emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDataResponse) -> Void) -> Self { response(queue: queue, responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, decoder: decoder, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods), completionHandler: completionHandler) } } extension DownloadRequest { /// Adds a handler using a `DecodableResponseSerializer` to be called once the request has finished. /// /// - Parameters: /// - type: `Decodable` type to decode from response data. /// - queue: The queue on which the completion handler is dispatched. `.main` by default. /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the /// `completionHandler`. `PassthroughPreprocessor()` by default. /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default. /// - encoding: The string encoding. Defaults to `nil`, in which case the encoding will be determined /// from the server response, falling back to the default HTTP character set, `ISO-8859-1`. /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default. /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default. /// - options: `JSONSerialization.ReadingOptions` used when parsing the response. `.allowFragments` /// by default. /// - completionHandler: A closure to be executed once the request has finished. /// /// - Returns: The request. @discardableResult public func responseDecodable(of type: T.Type = T.self, queue: DispatchQueue = .main, dataPreprocessor: DataPreprocessor = DecodableResponseSerializer.defaultDataPreprocessor, decoder: DataDecoder = JSONDecoder(), emptyResponseCodes: Set = DecodableResponseSerializer.defaultEmptyResponseCodes, emptyRequestMethods: Set = DecodableResponseSerializer.defaultEmptyRequestMethods, completionHandler: @escaping (AFDownloadResponse) -> Void) -> Self { response(queue: queue, responseSerializer: DecodableResponseSerializer(dataPreprocessor: dataPreprocessor, decoder: decoder, emptyResponseCodes: emptyResponseCodes, emptyRequestMethods: emptyRequestMethods), completionHandler: completionHandler) } } // MARK: - DataStreamRequest /// A type which can serialize incoming `Data`. public protocol DataStreamSerializer { /// Type produced from the serialized `Data`. associatedtype SerializedObject /// Serializes incoming `Data` into a `SerializedObject` value. /// /// - Parameter data: `Data` to be serialized. /// /// - Throws: Any error produced during serialization. func serialize(_ data: Data) throws -> SerializedObject } /// `DataStreamSerializer` which uses the provided `DataPreprocessor` and `DataDecoder` to serialize the incoming `Data`. public struct DecodableStreamSerializer: DataStreamSerializer { /// `DataDecoder` used to decode incoming `Data`. public let decoder: DataDecoder /// `DataPreprocessor` incoming `Data` is passed through before being passed to the `DataDecoder`. public let dataPreprocessor: DataPreprocessor /// Creates an instance with the provided `DataDecoder` and `DataPreprocessor`. /// - Parameters: /// - decoder: ` DataDecoder` used to decode incoming `Data`. /// - dataPreprocessor: `DataPreprocessor` used to process incoming `Data` before it's passed through the `decoder`. public init(decoder: DataDecoder = JSONDecoder(), dataPreprocessor: DataPreprocessor = PassthroughPreprocessor()) { self.decoder = decoder self.dataPreprocessor = dataPreprocessor } public func serialize(_ data: Data) throws -> T { let processedData = try dataPreprocessor.preprocess(data) do { return try decoder.decode(T.self, from: processedData) } catch { throw AFError.responseSerializationFailed(reason: .decodingFailed(error: error)) } } } /// `DataStreamSerializer` which performs no serialization on incoming `Data`. public struct PassthroughStreamSerializer: DataStreamSerializer { public func serialize(_ data: Data) throws -> Data { data } } /// `DataStreamSerializer` which serializes incoming stream `Data` into `UTF8`-decoded `String` values. public struct StringStreamSerializer: DataStreamSerializer { public func serialize(_ data: Data) throws -> String { String(decoding: data, as: UTF8.self) } } extension DataStreamRequest { /// Adds a `StreamHandler` which performs no parsing on incoming `Data`. /// /// - Parameters: /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. /// /// - Returns: The `DataStreamRequest`. @discardableResult public func responseStream(on queue: DispatchQueue = .main, stream: @escaping Handler) -> Self { let parser = { [unowned self] (data: Data) in queue.async { self.capturingError { try stream(.init(event: .stream(.success(data)), token: .init(self))) } self.updateAndCompleteIfPossible() } } $streamMutableState.write { $0.streams.append(parser) } appendStreamCompletion(on: queue, stream: stream) return self } /// Adds a `StreamHandler` which uses the provided `DataStreamSerializer` to process incoming `Data`. /// /// - Parameters: /// - serializer: `DataStreamSerializer` used to process incoming `Data`. Its work is done on the `serializationQueue`. /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. /// /// - Returns: The `DataStreamRequest`. @discardableResult public func responseStream(using serializer: Serializer, on queue: DispatchQueue = .main, stream: @escaping Handler) -> Self { let parser = { [unowned self] (data: Data) in self.serializationQueue.async { // Start work on serialization queue. let result = Result { try serializer.serialize(data) } .mapError { $0.asAFError(or: .responseSerializationFailed(reason: .customSerializationFailed(error: $0))) } // End work on serialization queue. self.underlyingQueue.async { self.eventMonitor?.request(self, didParseStream: result) if result.isFailure, self.automaticallyCancelOnStreamError { self.cancel() } queue.async { self.capturingError { try stream(.init(event: .stream(result), token: .init(self))) } self.updateAndCompleteIfPossible() } } } } $streamMutableState.write { $0.streams.append(parser) } appendStreamCompletion(on: queue, stream: stream) return self } /// Adds a `StreamHandler` which parses incoming `Data` as a UTF8 `String`. /// /// - Parameters: /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. /// /// - Returns: The `DataStreamRequest`. @discardableResult public func responseStreamString(on queue: DispatchQueue = .main, stream: @escaping Handler) -> Self { let parser = { [unowned self] (data: Data) in self.serializationQueue.async { // Start work on serialization queue. let string = String(decoding: data, as: UTF8.self) // End work on serialization queue. self.underlyingQueue.async { self.eventMonitor?.request(self, didParseStream: .success(string)) queue.async { self.capturingError { try stream(.init(event: .stream(.success(string)), token: .init(self))) } self.updateAndCompleteIfPossible() } } } } $streamMutableState.write { $0.streams.append(parser) } appendStreamCompletion(on: queue, stream: stream) return self } private func updateAndCompleteIfPossible() { $streamMutableState.write { state in state.numberOfExecutingStreams -= 1 guard state.numberOfExecutingStreams == 0, !state.enqueuedCompletionEvents.isEmpty else { return } let completionEvents = state.enqueuedCompletionEvents self.underlyingQueue.async { completionEvents.forEach { $0() } } state.enqueuedCompletionEvents.removeAll() } } /// Adds a `StreamHandler` which parses incoming `Data` using the provided `DataDecoder`. /// /// - Parameters: /// - type: `Decodable` type to parse incoming `Data` into. /// - queue: `DispatchQueue` on which to perform `StreamHandler` closure. /// - decoder: `DataDecoder` used to decode the incoming `Data`. /// - preprocessor: `DataPreprocessor` used to process the incoming `Data` before it's passed to the `decoder`. /// - stream: `StreamHandler` closure called as `Data` is received. May be called multiple times. /// /// - Returns: The `DataStreamRequest`. @discardableResult public func responseStreamDecodable(of type: T.Type = T.self, on queue: DispatchQueue = .main, using decoder: DataDecoder = JSONDecoder(), preprocessor: DataPreprocessor = PassthroughPreprocessor(), stream: @escaping Handler) -> Self { responseStream(using: DecodableStreamSerializer(decoder: decoder, dataPreprocessor: preprocessor), stream: stream) } }