Alamofire源码解析专栏文章地址
这篇文章我们来分析SessionDelegate相关的源码,主要涉及到Session中的一些属性类型源码的分析以及SessionDelegaet类中对于各种系统原生代理协议的实现
在文章的开头,我再将专栏的第一篇文章中放出来的Session类中的成员属性代码贴出来,因为该文章很大部分都是在分析Session类中的实例变量类型的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static let `default ` = Session ()public let session: URLSession public let delegate: SessionDelegate public let rootQueue: DispatchQueue public let startRequestsImmediately: Bool public let requestQueue: DispatchQueue public let serializationQueue: DispatchQueue public let interceptor: RequestInterceptor? public let serverTrustManager: ServerTrustManager? public let redirectHandler: RedirectHandler? public let cachedResponseHandler: CachedResponseHandler? public let eventMonitor: CompositeEventMonitor public let defaultEventMonitors: [EventMonitor ] = [AlamofireNotifications ()]var requestTaskMap = RequestTaskMap ()var activeRequests: Set <Request > = []var waitingCompletions: [URLSessionTask : () -> Void ] = [:]
事件监听中心CompositeEventMonitor 事件监听中心CompositeEventMonitor负责接收Alamofire系统中的各种通知,并将这些通知发送给全部的订阅者,监听中心给订阅者发送通知是通过调用函数的方式来发送的,所以在我们阅读CompositeEventMonitor源码之前,先看一下Alamofire中对订阅者的定义,Alamofre中使用一个协议EventMonitor来表示一个订阅者角色,协议中声明了各种通知对应的方法,如果监听中心想要发送一个通知给订阅者,就直接调用订阅者的对应方法完成通知的传递。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public protocol EventMonitor { var queue: DispatchQueue { get } func urlSession (_ session: URLSession, didBecomeInvalidWithError error: Error?) func urlSession (_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) func urlSession (_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) }
EventMonitor协议中定义了非常多的方法,一个方法对应一个通知事件,这些事件中一部分是与URLSessionDelegate或者URLSessionDataDelegate等一些系统原生网络代理中的方法相关,另外一部分是与我们上一篇文章中提到过的Reques他的生命周期相关。除了这个协议之外,因为该协议中定义的方法数量十分巨大,并且大多数情况下监听者只会同时关注其中的几个通知,所以为了便于实现协议,Alamofire还会协议中的每一个方法提供了一个默认实现,这些默认实现大部分由空实现构成。
1 2 3 4 5 6 7 8 9 10 11 12 extension EventMonitor { public var queue: DispatchQueue { .main } public func urlSession (_ session: URLSession, didBecomeInvalidWithError error: Error?) {} public func urlSession (_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) {} }
下面来看事件监听中心CompositeEventMonitor的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public final class CompositeEventMonitor : EventMonitor { public let queue = DispatchQueue (label: "org.alamofire.compositeEventMonitor" , qos: .background) let monitors: [EventMonitor ] init (monitors: [EventMonitor ]) { self .monitors = monitors } func performEvent (_ event: @escaping (EventMonitor) -> Void ) { queue.async { for monitor in self .monitors { monitor.queue.async { event(monitor) } } } } public func urlSession (_ session: URLSession, didBecomeInvalidWithError error: Error?) { performEvent { $0 .urlSession(session, didBecomeInvalidWithError: error) } } }
RequestTaskMap Alamofire中许多关于网络请求状态的更新都是基于原生URLSession的各种代理方法回调来完成的,原生的回调方法中会告诉你是哪一个Task的状态发生了改变,但是在Alamofire中将请求封装为了Request类,所以就需要有一个可以通过task找到与之对应的Request的方法。然后就有了RequestTaskMap,这个类中存储了Request与原生Task之间的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 struct RequestTaskMap { private typealias Events = (completed: Bool , metricsGathered: Bool ) private var tasksToRequests: [URLSessionTask : Request ] private var requestsToTasks: [Request : URLSessionTask ] private var taskEvents: [URLSessionTask : Events ] var requests: [Request ] { Array (tasksToRequests.values) } init (tasksToRequests: [URLSessionTask : Request ] = [:], requestsToTasks: [Request : URLSessionTask ] = [:], taskEvents: [URLSessionTask : (completed: Bool , metricsGathered: Bool )] = [:]) { self .tasksToRequests = tasksToRequests self .requestsToTasks = requestsToTasks self .taskEvents = taskEvents } subscript (_ request: Request ) -> URLSessionTask? { get { requestsToTasks[request] } set { guard let newValue = newValue else { guard let task = requestsToTasks[request] else { fatalError ("RequestTaskMap consistency error: no task corresponding to request found." ) } requestsToTasks.removeValue(forKey: request) tasksToRequests.removeValue(forKey: task) taskEvents.removeValue(forKey: task) return } requestsToTasks[request] = newValue tasksToRequests[newValue] = request taskEvents[newValue] = (completed: false , metricsGathered: false ) } } subscript (_ task: URLSessionTask ) -> Request? { get { tasksToRequests[task] } set { guard let newValue = newValue else { guard let request = tasksToRequests[task] else { fatalError ("RequestTaskMap consistency error: no request corresponding to task found." ) } tasksToRequests.removeValue(forKey: task) requestsToTasks.removeValue(forKey: request) taskEvents.removeValue(forKey: task) return } tasksToRequests[task] = newValue requestsToTasks[newValue] = task taskEvents[task] = (completed: false , metricsGathered: false ) } } }
RequestTaskMap除了上面源码中我们看到的存取Request与Task之间的映射关系的功能之外,还会额外记录每个task的完成状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 struct RequestTaskMap { private typealias Events = (completed: Bool , metricsGathered: Bool ) private var taskEvents: [URLSessionTask : Events ] mutating func disassociateIfNecessaryAfterGatheringMetricsForTask (_ task: URLSessionTask) -> Bool { guard let events = taskEvents[task] else { fatalError ("RequestTaskMap consistency error: no events corresponding to task found." ) } switch (events.completed, events.metricsGathered) { case (_ , true ): fatalError ("RequestTaskMap consistency error: duplicate metricsGatheredForTask call." ) case (false , false ): taskEvents[task] = (completed: false , metricsGathered: true ); return false case (true , false ): self [task] = nil ; return true } } mutating func disassociateIfNecessaryAfterCompletingTask (_ task: URLSessionTask) -> Bool { guard let events = taskEvents[task] else { fatalError ("RequestTaskMap consistency error: no events corresponding to task found." ) } switch (events.completed, events.metricsGathered) { case (true , _ ): fatalError ("RequestTaskMap consistency error: duplicate completionReceivedForTask call." ) #if os(watchOS) default : self [task] = nil ; return true #else case (false , false ): taskEvents[task] = (completed: true , metricsGathered: false ); return false case (false , true ): self [task] = nil ; return true #endif } } }
SessionDelegate类 由于要想通过代理处理原生URLSession中所有行为的回调是非常繁琐与复杂的,所以Alamofire新创建了一个类来实现原生Session的各种代理协议。Session是Alamofire中的核心管理类,SessionDelegate在处理代理回调的时候必然会涉及到需要与Session交互的逻辑。之前在分析Request时,Request也需要与Session进行交互,其做法时定义一个代理协议由Session本身实现,然后自身持有这个代理属性。现在这个SessionDelegate也使用了类似的方式处理这个问题。首先我们来看SessionStateProvider协议,虽然协议名称中没有包含delegate这个单词,但实际上从作用上来说,这就是一个代理协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protocol SessionStateProvider : AnyObject { var serverTrustManager: ServerTrustManager? { get } var redirectHandler: RedirectHandler? { get } var cachedResponseHandler: CachedResponseHandler? { get } func request (for task: URLSessionTask) -> Request? func didGatherMetricsForTask (_ task: URLSessionTask) func didCompleteTask (_ task: URLSessionTask, completion: @escaping () -> Void ) func credential (for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? func cancelRequestsForSessionInvalidation (with error: Error?) }
接下来我们来看一下Session类是如何实现这个协议的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 extension Session : SessionStateProvider { func request (for task: URLSessionTask) -> Request? { dispatchPrecondition(condition: .onQueue(rootQueue)) return requestTaskMap[task] } func didCompleteTask (_ task: URLSessionTask, completion: @escaping () -> Void ) { dispatchPrecondition(condition: .onQueue(rootQueue)) let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterCompletingTask(task) if didDisassociate { completion() } else { waitingCompletions[task] = completion } } func didGatherMetricsForTask (_ task: URLSessionTask) { dispatchPrecondition(condition: .onQueue(rootQueue)) let didDisassociate = requestTaskMap.disassociateIfNecessaryAfterGatheringMetricsForTask(task) if didDisassociate { waitingCompletions[task]?() waitingCompletions[task] = nil } } func credential (for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential? { dispatchPrecondition(condition: .onQueue(rootQueue)) return requestTaskMap[task]?.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for : protectionSpace) } func cancelRequestsForSessionInvalidation (with error: Error?) { dispatchPrecondition(condition: .onQueue(rootQueue)) requestTaskMap.requests.forEach { $0 .finish(error: AFError .sessionInvalidated(error: error)) } } }
接下来我们来看SessionDelegate中原生代理协议的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 extension SessionDelegate : URLSessionDataDelegate { open func urlSession (_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data) if let request = request(for : dataTask, as : DataRequest .self ) { request.didReceive(data: data) } else if let request = request(for : dataTask, as : DataStreamRequest .self ) { request.didReceive(data: data) } else { assertionFailure ("dataTask did not find DataRequest or DataStreamRequest in didReceive" ) return } } } extension SessionDelegate : URLSessionDownloadDelegate { open func urlSession (_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) { eventMonitor?.urlSession(session, downloadTask: downloadTask, didResumeAtOffset: fileOffset, expectedTotalBytes: expectedTotalBytes) guard let downloadRequest = request(for : downloadTask, as : DownloadRequest .self ) else { assertionFailure ("downloadTask did not find DownloadRequest." ) return } downloadRequest.updateDownloadProgress(bytesWritten: fileOffset, totalBytesExpectedToWrite: expectedTotalBytes) } }
上面我们只罗列出了SessionDelegate对原生代理协议的实现的一小部分,SessionDelegate中对于系统原生协议中的几乎所有方法都有对应的实现,但这些方法的基本套路都一样,都是根据通知传递过来的task找到与之对应的request,然后再对request做出某些状态更新的操作,所以这里就不一一列举了,如果读者对这些代码感兴趣,可以自行去源代码文件中查看,代码在SessionDelegate.swift
文件中。
SessionDelegate证书验证过程 在了解具体的流程之前,我们需要先了解几个类型。首先是Session类中的一个成员属性的类型:ServerTrustManager,该类型管理着Alamofire系统中所有与证书验证相关的证书验证器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 open class ServerTrustManager { public let allHostsMustBeEvaluated: Bool public let evaluators: [String : ServerTrustEvaluating ] public init (allHostsMustBeEvaluated: Bool = true , evaluators: [String : ServerTrustEvaluating ]) { self .allHostsMustBeEvaluated = allHostsMustBeEvaluated self .evaluators = evaluators } open func serverTrustEvaluator (forHost host: String) throws -> ServerTrustEvaluating? { guard let evaluator = evaluators[host] else { if allHostsMustBeEvaluated { throw AFError .serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host)) } return nil } return evaluator } }
接下来是关于证书验证器的定义,其在Alamofire中仅仅是一个简单的协议
1 2 3 4 5 6 public protocol ServerTrustEvaluating { func evaluate (_ trust: SecTrust, forHost host: String) throws }
然后我们来分析SeverDelegate类在证书验证流程中的一个关键helper方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func attemptServerTrustAuthentication (with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation { let host = challenge.protectionSpace.host guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust , let trust = challenge.protectionSpace.serverTrust else { return (.performDefaultHandling, nil , nil ) } do { guard let evaluator = try stateProvider?.serverTrustManager?.serverTrustEvaluator(forHost: host) else { return (.performDefaultHandling, nil , nil ) } try evaluator.evaluate(trust, forHost: host) return (.useCredential, URLCredential (trust: trust), nil ) } catch { return (.cancelAuthenticationChallenge, nil , error.asAFError(or: .serverTrustEvaluationFailed(reason: .customEvaluationFailed(error: error)))) } }
然后我们继续来分析SessionDelegate对于原生系统代理中的证书验证方法的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 open func urlSession (_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) { eventMonitor?.urlSession(session, task: task, didReceive: challenge) let evaluation: ChallengeEvaluation switch challenge.protectionSpace.authenticationMethod { case NSURLAuthenticationMethodServerTrust : evaluation = attemptServerTrustAuthentication(with: challenge) case NSURLAuthenticationMethodHTTPBasic , NSURLAuthenticationMethodHTTPDigest , NSURLAuthenticationMethodNTLM , NSURLAuthenticationMethodNegotiate , NSURLAuthenticationMethodClientCertificate : evaluation = attemptCredentialAuthentication(for : challenge, belongingTo: task) default : evaluation = (.performDefaultHandling, nil , nil ) } if let error = evaluation.error { stateProvider?.request(for : task)?.didFailTask(task, earlyWithError: error) } completionHandler(evaluation.disposition, evaluation.credential) }
ServerManage在接收到服务器发来的证书验证请求时,会将任务交给Session去执行,而Session会将任务交给其成员属性serverTrustManager去执行,最终serverTrustManager会在其内部寻找此次请求的Host字符串对应的一个证书验证器,并将任务交给证书验证器负责。
在使用Alamofire来进行网络请求时,如果遇到自签名证书或者单个证书服务于多个域名等需求时,需要你创建遵循ServerTrustEvaluating协议的证书验证器类,在func evaluate(_ trust: SecTrust, forHost host: String) throws
方法中,去验证每次请求的正确性。之后创建一个ServerTrustManager的实例,将一系列你创建的证书验证器与要验证的Host字符串的字典放入到你创建的serverTrustManager中去,最后将新创建的serverTrustManager通过实例化方法传入,构建出来一个新的Session,并在之后的网络请求中使用这个Session
好了本篇的内容到这里就结束了,系列专栏未完待续……