فهرست منبع

[PR #1420] Switched User-Agent header to include version and build numbers.

Sergey Demchenko 9 سال پیش
والد
کامیت
e1cc66446c
1فایلهای تغییر یافته به همراه779 افزوده شده و 0 حذف شده
  1. 779 0
      Source/Manager.swift

+ 779 - 0
Source/Manager.swift

@@ -0,0 +1,779 @@
+//
+//  Manager.swift
+//
+//  Copyright (c) 2014-2016 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
+
+/**
+    Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`.
+*/
+public class Manager {
+
+    // MARK: - Properties
+
+    /**
+        A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly
+        for any ad hoc requests.
+    */
+    public static let sharedInstance: Manager = {
+        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
+        configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
+
+        return Manager(configuration: configuration)
+    }()
+
+    /**
+        Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
+    */
+    public static let defaultHTTPHeaders: [String: String] = {
+        // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3
+        let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
+
+        // Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
+        let acceptLanguage = NSLocale.preferredLanguages().prefix(6).enumerate().map { index, languageCode in
+            let quality = 1.0 - (Double(index) * 0.1)
+            return "\(languageCode);q=\(quality)"
+        }.joinWithSeparator(", ")
+
+        // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
+        let userAgent: String = {
+            if let info = NSBundle.mainBundle().infoDictionary {
+                let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
+                let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
+                let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
+                let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
+
+                let osNameVersion: String = {
+                    let versionString: String
+
+                    if #available(OSX 10.10, *) {
+                        let version = NSProcessInfo.processInfo().operatingSystemVersion
+                        versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
+                    } else {
+                        versionString = "10.9"
+                    }
+
+                    let osName: String = {
+                        #if os(iOS)
+                            return "iOS"
+                        #elseif os(watchOS)
+                            return "watchOS"
+                        #elseif os(tvOS)
+                            return "tvOS"
+                        #elseif os(OSX)
+                            return "OS X"
+                        #elseif os(Linux)
+                            return "Linux"
+                        #else
+                            return "Unknown"
+                        #endif
+                    }()
+
+                    return "\(osName) \(versionString)"
+                }()
+
+                return "\(executable)/\(bundle) (\(appVersion)/\(appBuild)); \(osNameVersion))"
+            }
+
+            return "Alamofire"
+        }()
+
+        return [
+            "Accept-Encoding": acceptEncoding,
+            "Accept-Language": acceptLanguage,
+            "User-Agent": userAgent
+        ]
+    }()
+
+    let queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
+
+    /// The underlying session.
+    public let session: NSURLSession
+
+    /// The session delegate handling all the task and session delegate callbacks.
+    public let delegate: SessionDelegate
+
+    /// Whether to start requests immediately after being constructed. `true` by default.
+    public var startRequestsImmediately: Bool = true
+
+    /**
+        The background completion handler closure provided by the UIApplicationDelegate
+        `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background
+        completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation
+        will automatically call the handler.
+
+        If you need to handle your own events before the handler is called, then you need to override the
+        SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished.
+
+        `nil` by default.
+    */
+    public var backgroundCompletionHandler: (() -> Void)?
+
+    // MARK: - Lifecycle
+
+    /**
+        Initializes the `Manager` instance with the specified configuration, delegate and server trust policy.
+
+        - parameter configuration:            The configuration used to construct the managed session.
+                                              `NSURLSessionConfiguration.defaultSessionConfiguration()` by default.
+        - parameter delegate:                 The delegate used when initializing the session. `SessionDelegate()` by
+                                              default.
+        - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
+                                              challenges. `nil` by default.
+
+        - returns: The new `Manager` instance.
+    */
+    public init(
+        configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(),
+        delegate: SessionDelegate = SessionDelegate(),
+        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
+    {
+        self.delegate = delegate
+        self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
+
+        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
+    }
+
+    /**
+        Initializes the `Manager` instance with the specified session, delegate and server trust policy.
+
+        - parameter session:                  The URL session.
+        - parameter delegate:                 The delegate of the URL session. Must equal the URL session's delegate.
+        - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
+                                              challenges. `nil` by default.
+
+        - returns: The new `Manager` instance if the URL session's delegate matches the delegate parameter.
+    */
+    public init?(
+        session: NSURLSession,
+        delegate: SessionDelegate,
+        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
+    {
+        guard delegate === session.delegate else { return nil }
+
+        self.delegate = delegate
+        self.session = session
+
+        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
+    }
+
+    private func commonInit(serverTrustPolicyManager serverTrustPolicyManager: ServerTrustPolicyManager?) {
+        session.serverTrustPolicyManager = serverTrustPolicyManager
+
+        delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
+            guard let strongSelf = self else { return }
+            dispatch_async(dispatch_get_main_queue()) { strongSelf.backgroundCompletionHandler?() }
+        }
+    }
+
+    deinit {
+        session.invalidateAndCancel()
+    }
+
+    // MARK: - Request
+
+    /**
+        Creates a request for the specified method, URL string, parameters, parameter encoding and headers.
+
+        - parameter method:     The HTTP method.
+        - parameter URLString:  The URL string.
+        - parameter parameters: The parameters. `nil` by default.
+        - parameter encoding:   The parameter encoding. `.URL` by default.
+        - parameter headers:    The HTTP headers. `nil` by default.
+
+        - returns: The created request.
+    */
+    public func request(
+        method: Method,
+        _ URLString: URLStringConvertible,
+        parameters: [String: AnyObject]? = nil,
+        encoding: ParameterEncoding = .URL,
+        headers: [String: String]? = nil)
+        -> Request
+    {
+        let mutableURLRequest = URLRequest(method, URLString, headers: headers)
+        let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
+        return request(encodedURLRequest)
+    }
+
+    /**
+        Creates a request for the specified URL request.
+
+        If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
+
+        - parameter URLRequest: The URL request
+
+        - returns: The created request.
+    */
+    public func request(URLRequest: URLRequestConvertible) -> Request {
+        var dataTask: NSURLSessionDataTask!
+        dispatch_sync(queue) { dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest) }
+
+        let request = Request(session: session, task: dataTask)
+        delegate[request.delegate.task] = request.delegate
+
+        if startRequestsImmediately {
+            request.resume()
+        }
+
+        return request
+    }
+
+    // MARK: - SessionDelegate
+
+    /**
+        Responsible for handling all delegate callbacks for the underlying session.
+    */
+    public class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
+        private var subdelegates: [Int: Request.TaskDelegate] = [:]
+        private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
+
+        /// Access the task delegate for the specified task in a thread-safe manner.
+        public subscript(task: NSURLSessionTask) -> Request.TaskDelegate? {
+            get {
+                var subdelegate: Request.TaskDelegate?
+                dispatch_sync(subdelegateQueue) { subdelegate = self.subdelegates[task.taskIdentifier] }
+
+                return subdelegate
+            }
+            set {
+                dispatch_barrier_async(subdelegateQueue) { self.subdelegates[task.taskIdentifier] = newValue }
+            }
+        }
+
+        /**
+            Initializes the `SessionDelegate` instance.
+
+            - returns: The new `SessionDelegate` instance.
+        */
+        public override init() {
+            super.init()
+        }
+
+        // MARK: - NSURLSessionDelegate
+
+        // MARK: Override Closures
+
+        /// Overrides default behavior for NSURLSessionDelegate method `URLSession:didBecomeInvalidWithError:`.
+        public var sessionDidBecomeInvalidWithError: ((NSURLSession, NSError?) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionDelegate method `URLSession:didReceiveChallenge:completionHandler:`.
+        public var sessionDidReceiveChallenge: ((NSURLSession, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
+
+        /// Overrides all behavior for NSURLSessionDelegate method `URLSession:didReceiveChallenge:completionHandler:` and requires the caller to call the `completionHandler`.
+        public var sessionDidReceiveChallengeWithCompletion: ((NSURLSession, NSURLAuthenticationChallenge, (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionDelegate method `URLSessionDidFinishEventsForBackgroundURLSession:`.
+        public var sessionDidFinishEventsForBackgroundURLSession: ((NSURLSession) -> Void)?
+
+        // MARK: Delegate Methods
+
+        /**
+            Tells the delegate that the session has been invalidated.
+
+            - parameter session: The session object that was invalidated.
+            - parameter error:   The error that caused invalidation, or nil if the invalidation was explicit.
+        */
+        public func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
+            sessionDidBecomeInvalidWithError?(session, error)
+        }
+
+        /**
+            Requests credentials from the delegate in response to a session-level authentication request from the remote server.
+
+            - parameter session:           The session containing the task that requested authentication.
+            - parameter challenge:         An object that contains the request for authentication.
+            - parameter completionHandler: A handler that your delegate method must call providing the disposition and credential.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            didReceiveChallenge challenge: NSURLAuthenticationChallenge,
+            completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void))
+        {
+            guard sessionDidReceiveChallengeWithCompletion == nil else {
+                sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
+                return
+            }
+
+            var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
+            var credential: NSURLCredential?
+
+            if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
+                (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
+            } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
+                let host = challenge.protectionSpace.host
+
+                if let
+                    serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicyForHost(host),
+                    serverTrust = challenge.protectionSpace.serverTrust
+                {
+                    if serverTrustPolicy.evaluateServerTrust(serverTrust, isValidForHost: host) {
+                        disposition = .UseCredential
+                        credential = NSURLCredential(forTrust: serverTrust)
+                    } else {
+                        disposition = .CancelAuthenticationChallenge
+                    }
+                }
+            }
+
+            completionHandler(disposition, credential)
+        }
+
+        /**
+            Tells the delegate that all messages enqueued for a session have been delivered.
+
+            - parameter session: The session that no longer has any outstanding requests.
+        */
+        public func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
+            sessionDidFinishEventsForBackgroundURLSession?(session)
+        }
+
+        // MARK: - NSURLSessionTaskDelegate
+
+        // MARK: Override Closures
+
+        /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:`.
+        public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest) -> NSURLRequest?)?
+
+        /// Overrides all behavior for NSURLSessionTaskDelegate method `URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:` and
+        /// requires the caller to call the `completionHandler`.
+        public var taskWillPerformHTTPRedirectionWithCompletion: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest, NSURLRequest? -> Void) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:`.
+        public var taskDidReceiveChallenge: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
+
+        /// Overrides all behavior for NSURLSessionTaskDelegate method `URLSession:task:didReceiveChallenge:completionHandler:` and
+        /// requires the caller to call the `completionHandler`.
+        public var taskDidReceiveChallengeWithCompletion: ((NSURLSession, NSURLSessionTask, NSURLAuthenticationChallenge, (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:`.
+        public var taskNeedNewBodyStream: ((NSURLSession, NSURLSessionTask) -> NSInputStream?)?
+
+        /// Overrides all behavior for NSURLSessionTaskDelegate method `URLSession:session:task:needNewBodyStream:` and
+        /// requires the caller to call the `completionHandler`.
+        public var taskNeedNewBodyStreamWithCompletion: ((NSURLSession, NSURLSessionTask, NSInputStream? -> Void) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
+        public var taskDidSendBodyData: ((NSURLSession, NSURLSessionTask, Int64, Int64, Int64) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionTaskDelegate method `URLSession:task:didCompleteWithError:`.
+        public var taskDidComplete: ((NSURLSession, NSURLSessionTask, NSError?) -> Void)?
+
+        // MARK: Delegate Methods
+
+        /**
+            Tells the delegate that the remote server requested an HTTP redirect.
+
+            - parameter session:           The session containing the task whose request resulted in a redirect.
+            - parameter task:              The task whose request resulted in a redirect.
+            - parameter response:          An object containing the server’s response to the original request.
+            - parameter request:           A URL request object filled out with the new location.
+            - parameter completionHandler: A closure that your handler should call with either the value of the request
+                                           parameter, a modified URL request object, or NULL to refuse the redirect and
+                                           return the body of the redirect response.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            task: NSURLSessionTask,
+            willPerformHTTPRedirection response: NSHTTPURLResponse,
+            newRequest request: NSURLRequest,
+            completionHandler: NSURLRequest? -> Void)
+        {
+            guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
+                taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
+                return
+            }
+
+            var redirectRequest: NSURLRequest? = request
+
+            if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
+                redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
+            }
+
+            completionHandler(redirectRequest)
+        }
+
+        /**
+            Requests credentials from the delegate in response to an authentication request from the remote server.
+
+            - parameter session:           The session containing the task whose request requires authentication.
+            - parameter task:              The task whose request requires authentication.
+            - parameter challenge:         An object that contains the request for authentication.
+            - parameter completionHandler: A handler that your delegate method must call providing the disposition and credential.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            task: NSURLSessionTask,
+            didReceiveChallenge challenge: NSURLAuthenticationChallenge,
+            completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)
+        {
+            guard taskDidReceiveChallengeWithCompletion == nil else {
+                taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
+                return
+            }
+
+            if let taskDidReceiveChallenge = taskDidReceiveChallenge {
+                let result = taskDidReceiveChallenge(session, task, challenge)
+                completionHandler(result.0, result.1)
+            } else if let delegate = self[task] {
+                delegate.URLSession(
+                    session,
+                    task: task,
+                    didReceiveChallenge: challenge,
+                    completionHandler: completionHandler
+                )
+            } else {
+                URLSession(session, didReceiveChallenge: challenge, completionHandler: completionHandler)
+            }
+        }
+
+        /**
+            Tells the delegate when a task requires a new request body stream to send to the remote server.
+
+            - parameter session:           The session containing the task that needs a new body stream.
+            - parameter task:              The task that needs a new body stream.
+            - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            task: NSURLSessionTask,
+            needNewBodyStream completionHandler: NSInputStream? -> Void)
+        {
+            guard taskNeedNewBodyStreamWithCompletion == nil else {
+                taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
+                return
+            }
+
+            if let taskNeedNewBodyStream = taskNeedNewBodyStream {
+                completionHandler(taskNeedNewBodyStream(session, task))
+            } else if let delegate = self[task] {
+                delegate.URLSession(session, task: task, needNewBodyStream: completionHandler)
+            }
+        }
+
+        /**
+            Periodically informs the delegate of the progress of sending body content to the server.
+
+            - parameter session:                  The session containing the data task.
+            - parameter task:                     The data task.
+            - parameter bytesSent:                The number of bytes sent since the last time this delegate method was called.
+            - parameter totalBytesSent:           The total number of bytes sent so far.
+            - parameter totalBytesExpectedToSend: The expected length of the body data.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            task: NSURLSessionTask,
+            didSendBodyData bytesSent: Int64,
+            totalBytesSent: Int64,
+            totalBytesExpectedToSend: Int64)
+        {
+            if let taskDidSendBodyData = taskDidSendBodyData {
+                taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
+            } else if let delegate = self[task] as? Request.UploadTaskDelegate {
+                delegate.URLSession(
+                    session,
+                    task: task,
+                    didSendBodyData: bytesSent,
+                    totalBytesSent: totalBytesSent,
+                    totalBytesExpectedToSend: totalBytesExpectedToSend
+                )
+            }
+        }
+
+        /**
+            Tells the delegate that the task finished transferring data.
+
+            - parameter session: The session containing the task whose request finished transferring data.
+            - parameter task:    The task whose request finished transferring data.
+            - parameter error:   If an error occurred, an error object indicating how the transfer failed, otherwise nil.
+        */
+        public func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
+            if let taskDidComplete = taskDidComplete {
+                taskDidComplete(session, task, error)
+            } else if let delegate = self[task] {
+                delegate.URLSession(session, task: task, didCompleteWithError: error)
+            }
+
+            NSNotificationCenter.defaultCenter().postNotificationName(Notifications.Task.DidComplete, object: task)
+
+            self[task] = nil
+        }
+
+        // MARK: - NSURLSessionDataDelegate
+
+        // MARK: Override Closures
+
+        /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:`.
+        public var dataTaskDidReceiveResponse: ((NSURLSession, NSURLSessionDataTask, NSURLResponse) -> NSURLSessionResponseDisposition)?
+
+        /// Overrides all behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveResponse:completionHandler:` and
+        /// requires caller to call the `completionHandler`.
+        public var dataTaskDidReceiveResponseWithCompletion: ((NSURLSession, NSURLSessionDataTask, NSURLResponse, NSURLSessionResponseDisposition -> Void) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didBecomeDownloadTask:`.
+        public var dataTaskDidBecomeDownloadTask: ((NSURLSession, NSURLSessionDataTask, NSURLSessionDownloadTask) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:didReceiveData:`.
+        public var dataTaskDidReceiveData: ((NSURLSession, NSURLSessionDataTask, NSData) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:`.
+        public var dataTaskWillCacheResponse: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse) -> NSCachedURLResponse?)?
+
+        /// Overrides all behavior for NSURLSessionDataDelegate method `URLSession:dataTask:willCacheResponse:completionHandler:` and
+        /// requires caller to call the `completionHandler`.
+        public var dataTaskWillCacheResponseWithCompletion: ((NSURLSession, NSURLSessionDataTask, NSCachedURLResponse, NSCachedURLResponse? -> Void) -> Void)?
+
+        // MARK: Delegate Methods
+
+        /**
+            Tells the delegate that the data task received the initial reply (headers) from the server.
+
+            - parameter session:           The session containing the data task that received an initial reply.
+            - parameter dataTask:          The data task that received an initial reply.
+            - parameter response:          A URL response object populated with headers.
+            - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
+                                           constant to indicate whether the transfer should continue as a data task or
+                                           should become a download task.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            dataTask: NSURLSessionDataTask,
+            didReceiveResponse response: NSURLResponse,
+            completionHandler: NSURLSessionResponseDisposition -> Void)
+        {
+            guard dataTaskDidReceiveResponseWithCompletion == nil else {
+                dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
+                return
+            }
+
+            var disposition: NSURLSessionResponseDisposition = .Allow
+
+            if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
+                disposition = dataTaskDidReceiveResponse(session, dataTask, response)
+            }
+
+            completionHandler(disposition)
+        }
+
+        /**
+            Tells the delegate that the data task was changed to a download task.
+
+            - parameter session:      The session containing the task that was replaced by a download task.
+            - parameter dataTask:     The data task that was replaced by a download task.
+            - parameter downloadTask: The new download task that replaced the data task.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            dataTask: NSURLSessionDataTask,
+            didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask)
+        {
+            if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
+                dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
+            } else {
+                let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask)
+                self[downloadTask] = downloadDelegate
+            }
+        }
+
+        /**
+            Tells the delegate that the data task has received some of the expected data.
+
+            - parameter session:  The session containing the data task that provided data.
+            - parameter dataTask: The data task that provided data.
+            - parameter data:     A data object containing the transferred data.
+        */
+        public func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
+            if let dataTaskDidReceiveData = dataTaskDidReceiveData {
+                dataTaskDidReceiveData(session, dataTask, data)
+            } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
+                delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
+            }
+        }
+
+        /**
+            Asks the delegate whether the data (or upload) task should store the response in the cache.
+
+            - parameter session:           The session containing the data (or upload) task.
+            - parameter dataTask:          The data (or upload) task.
+            - parameter proposedResponse:  The default caching behavior. This behavior is determined based on the current
+                                           caching policy and the values of certain received headers, such as the Pragma
+                                           and Cache-Control headers.
+            - parameter completionHandler: A block that your handler must call, providing either the original proposed
+                                           response, a modified version of that response, or NULL to prevent caching the
+                                           response. If your delegate implements this method, it must call this completion
+                                           handler; otherwise, your app leaks memory.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            dataTask: NSURLSessionDataTask,
+            willCacheResponse proposedResponse: NSCachedURLResponse,
+            completionHandler: NSCachedURLResponse? -> Void)
+        {
+            guard dataTaskWillCacheResponseWithCompletion == nil else {
+                dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
+                return
+            }
+
+            if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
+                completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
+            } else if let delegate = self[dataTask] as? Request.DataTaskDelegate {
+                delegate.URLSession(
+                    session,
+                    dataTask: dataTask,
+                    willCacheResponse: proposedResponse,
+                    completionHandler: completionHandler
+                )
+            } else {
+                completionHandler(proposedResponse)
+            }
+        }
+
+        // MARK: - NSURLSessionDownloadDelegate
+
+        // MARK: Override Closures
+
+        /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didFinishDownloadingToURL:`.
+        public var downloadTaskDidFinishDownloadingToURL: ((NSURLSession, NSURLSessionDownloadTask, NSURL) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:`.
+        public var downloadTaskDidWriteData: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
+
+        /// Overrides default behavior for NSURLSessionDownloadDelegate method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
+        public var downloadTaskDidResumeAtOffset: ((NSURLSession, NSURLSessionDownloadTask, Int64, Int64) -> Void)?
+
+        // MARK: Delegate Methods
+
+        /**
+            Tells the delegate that a download task has finished downloading.
+
+            - parameter session:      The session containing the download task that finished.
+            - parameter downloadTask: The download task that finished.
+            - parameter location:     A file URL for the temporary file. Because the file is temporary, you must either
+                                      open the file for reading or move it to a permanent location in your app’s sandbox
+                                      container directory before returning from this delegate method.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            downloadTask: NSURLSessionDownloadTask,
+            didFinishDownloadingToURL location: NSURL)
+        {
+            if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
+                downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
+            } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
+                delegate.URLSession(session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
+            }
+        }
+
+        /**
+            Periodically informs the delegate about the download’s progress.
+
+            - parameter session:                   The session containing the download task.
+            - parameter downloadTask:              The download task.
+            - parameter bytesWritten:              The number of bytes transferred since the last time this delegate
+                                                   method was called.
+            - parameter totalBytesWritten:         The total number of bytes transferred so far.
+            - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
+                                                   header. If this header was not provided, the value is
+                                                   `NSURLSessionTransferSizeUnknown`.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            downloadTask: NSURLSessionDownloadTask,
+            didWriteData bytesWritten: Int64,
+            totalBytesWritten: Int64,
+            totalBytesExpectedToWrite: Int64)
+        {
+            if let downloadTaskDidWriteData = downloadTaskDidWriteData {
+                downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
+            } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
+                delegate.URLSession(
+                    session,
+                    downloadTask: downloadTask,
+                    didWriteData: bytesWritten,
+                    totalBytesWritten: totalBytesWritten,
+                    totalBytesExpectedToWrite: totalBytesExpectedToWrite
+                )
+            }
+        }
+
+        /**
+            Tells the delegate that the download task has resumed downloading.
+
+            - parameter session:            The session containing the download task that finished.
+            - parameter downloadTask:       The download task that resumed. See explanation in the discussion.
+            - parameter fileOffset:         If the file's cache policy or last modified date prevents reuse of the
+                                            existing content, then this value is zero. Otherwise, this value is an
+                                            integer representing the number of bytes on disk that do not need to be
+                                            retrieved again.
+            - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
+                                            If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
+        */
+        public func URLSession(
+            session: NSURLSession,
+            downloadTask: NSURLSessionDownloadTask,
+            didResumeAtOffset fileOffset: Int64,
+            expectedTotalBytes: Int64)
+        {
+            if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
+                downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
+            } else if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
+                delegate.URLSession(
+                    session,
+                    downloadTask: downloadTask,
+                    didResumeAtOffset: fileOffset,
+                    expectedTotalBytes: expectedTotalBytes
+                )
+            }
+        }
+
+        // MARK: - NSURLSessionStreamDelegate
+
+        var _streamTaskReadClosed: Any?
+        var _streamTaskWriteClosed: Any?
+        var _streamTaskBetterRouteDiscovered: Any?
+        var _streamTaskDidBecomeInputStream: Any?
+
+        // MARK: - NSObject
+
+        public override func respondsToSelector(selector: Selector) -> Bool {
+            #if !os(OSX)
+                if selector == #selector(NSURLSessionDelegate.URLSessionDidFinishEventsForBackgroundURLSession(_:)) {
+                    return sessionDidFinishEventsForBackgroundURLSession != nil
+                }
+            #endif
+
+            switch selector {
+            case #selector(NSURLSessionDelegate.URLSession(_:didBecomeInvalidWithError:)):
+                return sessionDidBecomeInvalidWithError != nil
+            case #selector(NSURLSessionDelegate.URLSession(_:didReceiveChallenge:completionHandler:)):
+                return (sessionDidReceiveChallenge != nil  || sessionDidReceiveChallengeWithCompletion != nil)
+            case #selector(NSURLSessionTaskDelegate.URLSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
+                return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
+            case #selector(NSURLSessionDataDelegate.URLSession(_:dataTask:didReceiveResponse:completionHandler:)):
+                return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
+            default:
+                return self.dynamicType.instancesRespondToSelector(selector)
+            }
+        }
+    }
+}