Alamofire 4.5.0源码解析-NetworkReachabilityManager与ServerTrustPolicyManager

Alamofire 源码解析基本上按照文件的结构顺序进行分析,之前分析了三篇,本篇主要分析四个文件网络状态(NetworkReachabilityManager),安全协议(ServerTrustPolicyManager),时间轴(Timeline) ,校验(Validation).

NetworkReachabilityManager

NetworkReachabilityManager主要用于判断当前网络状态,Alamofire相对于AFNetWorking用了两个不同的枚举来表示:
<pre><code>` public enum NetworkReachabilityStatus {
case unknown
case notReachable
case reachable(ConnectionType)
}

/// Defines the various connection types detected by reachability flags.
///
/// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi.
/// - wwan:           The connection type is a WWAN connection.
public enum ConnectionType {
    case ethernetOrWiFi
    case wwan
}`</code></pre>

初始化代码:
<pre><code>` public convenience init?(host: String) {
guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
self.init(reachability: reachability)
}

/// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0.
///
/// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing
/// status of the device, both IPv4 and IPv6.
///
/// - returns: The new `NetworkReachabilityManager` instance.
public convenience init?() {
    var address = sockaddr_in()
    address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
    address.sin_family = sa_family_t(AF_INET)

    guard let reachability = withUnsafePointer(to: &address, { pointer in
        return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout<sockaddr>.size) {
            return SCNetworkReachabilityCreateWithAddress(nil, $0)
        }
    }) else { return nil }

    self.init(reachability: reachability)
}

private init(reachability: SCNetworkReachability) {
    self.reachability = reachability
    self.previousFlags = SCNetworkReachabilityFlags()
}`</code></pre>

代码中的MemoryLayout用来来实现原来sizeof的功能:
<pre><code>`public enum MemoryLayout<T> {

/// The contiguous memory footprint of `T`, in bytes.
///
/// A type's size does not include any dynamically allocated or out of line
/// storage. In particular, `MemoryLayout<T>.size`, when `T` is a class
/// type, is the same regardless of how many stored properties `T` has.
///
/// When allocating memory for multiple instances of `T` using an unsafe
/// pointer, use a multiple of the type's stride instead of its size.
///
/// - SeeAlso: `stride`
public static var size: Int { get }

/// The number of bytes from the start of one instance of `T` to the start of
/// the next when stored in contiguous memory or in an `Array<T>`.
///
/// This is the same as the number of bytes moved when an `UnsafePointer<T>`
/// instance is incremented. `T` may have a lower minimal alignment that
/// trades runtime performance for space efficiency. This value is always
/// positive.
public static var stride: Int { get }

/// The default memory alignment of `T`, in bytes.
///
/// Use the `alignment` property for a type when allocating memory using an
/// unsafe pointer. This value is always positive.
public static var alignment: Int { get }

/// Returns the contiguous memory footprint of the given instance.
///
/// The result does not include any dynamically allocated or out of line
/// storage. In particular, pointers and class instances all have the same
/// contiguous memory footprint, regardless of the size of the referenced
/// data.
///
/// When you have a type instead of an instance, use the
/// `MemoryLayout<T>.size` static property instead.
///
///     let x: Int = 100
///
///     // Finding the size of a value's type
///     let s = MemoryLayout.size(ofValue: x)
///     // s == 8
///
///     // Finding the size of a type directly
///     let t = MemoryLayout<Int>.size
///     // t == 8
///
/// - Parameter value: A value representative of the type to describe.
/// - Returns: The size, in bytes, of the given value's type.
///
/// - SeeAlso: `MemoryLayout.size`
public static func size(ofValue value: T) -> Int

/// Returns the number of bytes from the start of one instance of `T` to the
/// start of the next when stored in contiguous memory or in an `Array<T>`.
///
/// This is the same as the number of bytes moved when an `UnsafePointer<T>`
/// instance is incremented. `T` may have a lower minimal alignment that
/// trades runtime performance for space efficiency. The result is always
/// positive.
///
/// When you have a type instead of an instance, use the
/// `MemoryLayout<T>.stride` static property instead.
///
///     let x: Int = 100
///
///     // Finding the stride of a value's type
///     let s = MemoryLayout.stride(ofValue: x)
///     // s == 8
///
///     // Finding the stride of a type directly
///     let t = MemoryLayout<Int>.stride
///     // t == 8
///
/// - Parameter value: A value representative of the type to describe.
/// - Returns: The stride, in bytes, of the given value's type.
///
/// - SeeAlso: `MemoryLayout.stride`
public static func stride(ofValue value: T) -> Int

/// Returns the default memory alignment of `T`.
///
/// Use a type's alignment when allocating memory using an unsafe pointer.
///
/// When you have a type instead of an instance, use the
/// `MemoryLayout<T>.stride` static property instead.
///
///     let x: Int = 100
///
///     // Finding the alignment of a value's type
///     let s = MemoryLayout.alignment(ofValue: x)
///     // s == 8
///
///     // Finding the alignment of a type directly
///     let t = MemoryLayout<Int>.alignment
///     // t == 8
///
/// - Parameter value: A value representative of the type to describe.
/// - Returns: The default memory alignment, in bytes, of the given value's
///   type. This value is always positive.
///
/// - SeeAlso: `MemoryLayout.alignment`
public static func alignment(ofValue value: T) -> Int

}`</code></pre>

网络状态监听和取消监听:

<pre><code>` public func startListening() -> Bool {
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
context.info = Unmanaged.passUnretained(self).toOpaque()

    let callbackEnabled = SCNetworkReachabilitySetCallback(
        reachability,
        { (_, flags, info) in
            let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
            reachability.notifyListener(flags)
        },
        &context
    )

    let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)

    listenerQueue.async {
        self.previousFlags = SCNetworkReachabilityFlags()
        self.notifyListener(self.flags ?? SCNetworkReachabilityFlags())
    }

    return callbackEnabled && queueEnabled
}

/// Stops listening for changes in network reachability status.
public func stopListening() {
    SCNetworkReachabilitySetCallback(reachability, nil, nil)
    SCNetworkReachabilitySetDispatchQueue(reachability, nil)
}

`</code></pre>

ServerTrustPolicyManager

ServerTrustPolicyManager验证Https证书是否有效,相对于AFNetWorking多了一个证书注销的枚举:

performDefaultEvaluation:从客户端信任的CA列表中验证证书是否有效.

pinCertificates:客户端保存服务端证书的拷贝,首先验证域名/有效期信息,然后验证两个证书是否一致.

pinPublicKeys:客户端保存服务端证书的拷贝,验证服务端证书的公钥是否一致;

<pre><code>public enum ServerTrustPolicy { case performDefaultEvaluation(validateHost: Bool) case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags) case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) case disableEvaluation case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)</code></pre>

评估证书是否有效:

<pre><code>` public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool {
var serverTrustIsValid = false

    switch self {
    case let .performDefaultEvaluation(validateHost):
        let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
        SecTrustSetPolicies(serverTrust, policy)

        serverTrustIsValid = trustIsValid(serverTrust)
    case let .performRevokedEvaluation(validateHost, revocationFlags):
        let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
        let revokedPolicy = SecPolicyCreateRevocation(revocationFlags)
        SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef)

        serverTrustIsValid = trustIsValid(serverTrust)
    case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
        if validateCertificateChain {
            let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
            SecTrustSetPolicies(serverTrust, policy)

            SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray)
            SecTrustSetAnchorCertificatesOnly(serverTrust, true)

            serverTrustIsValid = trustIsValid(serverTrust)
        } else {
            let serverCertificatesDataArray = certificateData(for: serverTrust)
            let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates)

            outerLoop: for serverCertificateData in serverCertificatesDataArray {
                for pinnedCertificateData in pinnedCertificatesDataArray {
                    if serverCertificateData == pinnedCertificateData {
                        serverTrustIsValid = true
                        break outerLoop
                    }
                }
            }
        }
    case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost):
        var certificateChainEvaluationPassed = true

        if validateCertificateChain {
            let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
            SecTrustSetPolicies(serverTrust, policy)

            certificateChainEvaluationPassed = trustIsValid(serverTrust)
        }

        if certificateChainEvaluationPassed {
            outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] {
                for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
                    if serverPublicKey.isEqual(pinnedPublicKey) {
                        serverTrustIsValid = true
                        break outerLoop
                    }
                }
            }
        }
    case .disableEvaluation:
        serverTrustIsValid = true
    case let .customEvaluation(closure):
        serverTrustIsValid = closure(serverTrust, host)
    }

    return serverTrustIsValid
}

`</code></pre>

Timeline

相对于AFNetWorking时间轴的概念非常👍,关于网络请求中的开始时间,结束时间,序列化时间,总时间,整个请求到结束的时间都一目了然.
<pre><code>`

public struct Timeline {
/// The time the request was initialized.
public let requestStartTime: CFAbsoluteTime

/// The time the first bytes were received from or sent to the server.
public let initialResponseTime: CFAbsoluteTime

/// The time when the request was completed.
public let requestCompletedTime: CFAbsoluteTime

/// The time when the response serialization was completed.
public let serializationCompletedTime: CFAbsoluteTime

/// The time interval in seconds from the time the request started to the initial response from the server.
public let latency: TimeInterval

/// The time interval in seconds from the time the request started to the time the request completed.
public let requestDuration: TimeInterval

/// The time interval in seconds from the time the request completed to the time response serialization completed.
public let serializationDuration: TimeInterval

/// The time interval in seconds from the time the request started to the time response serialization completed.
public let totalDuration: TimeInterval

/// Creates a new `Timeline` instance with the specified request times.
///
/// - parameter requestStartTime:           The time the request was initialized. Defaults to `0.0`.
/// - parameter initialResponseTime:        The time the first bytes were received from or sent to the server.
///                                         Defaults to `0.0`.
/// - parameter requestCompletedTime:       The time when the request was completed. Defaults to `0.0`.
/// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults
///                                         to `0.0`.
///
/// - returns: The new `Timeline` instance.
public init(
    requestStartTime: CFAbsoluteTime = 0.0,
    initialResponseTime: CFAbsoluteTime = 0.0,
    requestCompletedTime: CFAbsoluteTime = 0.0,
    serializationCompletedTime: CFAbsoluteTime = 0.0)
{
    self.requestStartTime = requestStartTime
    self.initialResponseTime = initialResponseTime
    self.requestCompletedTime = requestCompletedTime
    self.serializationCompletedTime = serializationCompletedTime

    self.latency = initialResponseTime - requestStartTime
    self.requestDuration = requestCompletedTime - requestStartTime
    self.serializationDuration = serializationCompletedTime - requestCompletedTime
    self.totalDuration = serializationCompletedTime - requestStartTime
}

}

// MARK: - CustomStringConvertible

extension Timeline: CustomStringConvertible {
/// The textual representation used when written to an output stream, which includes the latency, the request
/// duration and the total duration.
public var description: String {
let latency = String(format: "%.3f", self.latency)
let requestDuration = String(format: "%.3f", self.requestDuration)
let serializationDuration = String(format: "%.3f", self.serializationDuration)
let totalDuration = String(format: "%.3f", self.totalDuration)

    // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
    // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
    let timings = [
        "\"Latency\": " + latency + " secs",
        "\"Request Duration\": " + requestDuration + " secs",
        "\"Serialization Duration\": " + serializationDuration + " secs",
        "\"Total Duration\": " + totalDuration + " secs"
    ]

    return "Timeline: { " + timings.joined(separator: ", ") + " }"
}

}

// MARK: - CustomDebugStringConvertible

extension Timeline: CustomDebugStringConvertible {
/// The textual representation used when written to an output stream, which includes the request start time, the
/// initial response time, the request completed time, the serialization completed time, the latency, the request
/// duration and the total duration.
public var debugDescription: String {
let requestStartTime = String(format: "%.3f", self.requestStartTime)
let initialResponseTime = String(format: "%.3f", self.initialResponseTime)
let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime)
let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime)
let latency = String(format: "%.3f", self.latency)
let requestDuration = String(format: "%.3f", self.requestDuration)
let serializationDuration = String(format: "%.3f", self.serializationDuration)
let totalDuration = String(format: "%.3f", self.totalDuration)

    // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
    // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
    let timings = [
        "\"Request Start Time\": " + requestStartTime,
        "\"Initial Response Time\": " + initialResponseTime,
        "\"Request Completed Time\": " + requestCompletedTime,
        "\"Serialization Completed Time\": " + serializationCompletedTime,
        "\"Latency\": " + latency + " secs",
        "\"Request Duration\": " + requestDuration + " secs",
        "\"Serialization Duration\": " + serializationDuration + " secs",
        "\"Total Duration\": " + totalDuration + " secs"
    ]

    return "Timeline: { " + timings.joined(separator: ", ") + " }"
}

}`</code></pre>

Validation

Validation是针对Request进行数据校验扩展,校验状态码,ContentType类型:

<pre><code>` fileprivate func validate<S: Sequence>(
statusCode acceptableStatusCodes: S,
response: HTTPURLResponse)
-> ValidationResult
where S.Iterator.Element == Int
{
if acceptableStatusCodes.contains(response.statusCode) {
return .success
} else {
let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
return .failure(AFError.responseValidationFailed(reason: reason))
}
}

// MARK: Content Type

fileprivate func validate<S: Sequence>(
    contentType acceptableContentTypes: S,
    response: HTTPURLResponse,
    data: Data?)
    -> ValidationResult
    where S.Iterator.Element == String
{
    guard let data = data, data.count > 0 else { return .success }

    guard
        let responseContentType = response.mimeType,
        let responseMIMEType = MIMEType(responseContentType)
    else {
        for contentType in acceptableContentTypes {
            if let mimeType = MIMEType(contentType), mimeType.isWildcard {
                return .success
            }
        }

        let error: AFError = {
            let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
            return AFError.responseValidationFailed(reason: reason)
        }()

        return .failure(error)
    }

    for contentType in acceptableContentTypes {
        if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
            return .success
        }
    }

    let error: AFError = {
        let reason: ErrorReason = .unacceptableContentType(
            acceptableContentTypes: Array(acceptableContentTypes),
            responseContentType: responseContentType
        )

        return AFError.responseValidationFailed(reason: reason)
    }()

    return .failure(error)
}

}`</code></pre>

Alamofire源码解析到此结束,Swift水平有限,欢迎多多指正~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容