Repository: Alamofire/Alamofire
File: Source/ResponseSerialization.swift (Swift)

Code before:
    /// - 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 {
    /// Closure type used to prepare `Data` for serialization.
    typealias DataPreprocessor = (Data) throws -> Data
    /// `DataPreprocessor` closure used to prepare incoming `Data` for serialization.
    var dataPreprocessor: DataPreprocessor { get }
    /// `HTTPMethod`s for which empty response bodies are considered appropriate.
    var emptyRequestMethods: Set<HTTPMethod> { get }
    /// HTTP response codes for which empty response bodies are considered appropriate.
    var emptyResponseCodes: Set<Int> { get }
}

extension ResponseSerializer {
    /// Default `DataPreprocessor` that merely passes `Data` through.
    public static var defaultDataPreprocessor: DataPreprocessor { return { $0 } }
    /// Default `HTTPMethod`s for which empty response bodies are considered appropriate. `[.head]` by default.
    public static var defaultEmptyRequestMethods: Set<HTTPMethod> { return [.head] }
    /// HTTP response codes for which empty response bodies are considered appropriate. `[204, 205]` by default.
    public static var defaultEmptyResponseCodes: Set<Int> { return [204, 205] }

    public var dataPreprocessor: DataPreprocessor { return Self.defaultDataPreprocessor }
    public var emptyRequestMethods: Set<HTTPMethod> { return Self.defaultEmptyRequestMethods }
    public var emptyResponseCodes: Set<Int> { return 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`.

Review comment (security):
I really like this change overall. It really gives you a lot of flexibility if you need it. I think for users that actually need this functionality though might be better served by this being a protocol rather than a closure. I think most people would want to define an explicit type to very easily test rather than having to implement it as a closure. I just think it could be some fairly complex logic that people may want to reuse more easily. They could pass functions off as the closure, but that doesn't seem ideal.

In the XSSI issue, it seems like it would be nice to be able to create an `XSSIDataPreprocessor: DataPreprocessor` that you could extensively unit test and leverage throughout your code. So I think I lean towards having `DataPreprocessor` be a protocol rather than a typealias'd closure. Either way, both solutions are solid.

Code after:
    /// - 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<HTTPMethod> { get }
    /// HTTP response codes for which empty response bodies are considered appropriate.
    var emptyResponseCodes: Set<Int> { 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 { return data }
}

/// `DataPreprocessor` that trims Google's typical `)]}',\n` XSSI JSON header.
public struct GoogleXSSIPreprocessor: DataPreprocessor {
    public init() { }

    public func preprocess(_ data: Data) throws -> Data {