Alamofire 4.5.0源碼解析-NetworkReachabilityManager與ServerTrustPolicyManager

Alamofire 源碼解析基本上按照文件的結(jié)構(gòu)順序進(jìn)行分析,之前分析了三篇扁耐,本篇主要分析四個(gè)文件網(wǎng)絡(luò)狀態(tài)(NetworkReachabilityManager),安全協(xié)議(ServerTrustPolicyManager)卦方,時(shí)間軸(Timeline) 鳄哭,校驗(yàn)(Validation).

NetworkReachabilityManager

NetworkReachabilityManager主要用于判斷當(dāng)前網(wǎng)絡(luò)狀態(tài),Alamofire相對(duì)于AFNetWorking用了兩個(gè)不同的枚舉來表示:
<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用來來實(shí)現(xiàn)原來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>

網(wǎng)絡(luò)狀態(tài)監(jiān)聽和取消監(jiān)聽:

<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驗(yàn)證Https證書是否有效萎胰,相對(duì)于AFNetWorking多了一個(gè)證書注銷的枚舉:

performDefaultEvaluation:從客戶端信任的CA列表中驗(yàn)證證書是否有效.

pinCertificates:客戶端保存服務(wù)端證書的拷貝碾盟,首先驗(yàn)證域名/有效期信息,然后驗(yàn)證兩個(gè)證書是否一致.

pinPublicKeys:客戶端保存服務(wù)端證書的拷貝技竟,驗(yàn)證服務(wù)端證書的公鑰是否一致;

<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>

評(píng)估證書是否有效:

<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

相對(duì)于AFNetWorking時(shí)間軸的概念非常??冰肴,關(guān)于網(wǎng)絡(luò)請(qǐng)求中的開始時(shí)間,結(jié)束時(shí)間榔组,序列化時(shí)間熙尉,總時(shí)間,整個(gè)請(qǐng)求到結(jié)束的時(shí)間都一目了然.
<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是針對(duì)Request進(jìn)行數(shù)據(jù)校驗(yàn)擴(kuò)展瓷患,校驗(yàn)狀態(tài)碼骡尽,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源碼解析到此結(jié)束,Swift水平有限擅编,歡迎多多指正~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市箫踩,隨后出現(xiàn)的幾起案子爱态,更是在濱河造成了極大的恐慌,老刑警劉巖境钟,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锦担,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡慨削,警方通過查閱死者的電腦和手機(jī)洞渔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門套媚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人磁椒,你說我怎么就攤上這事堤瘤。” “怎么了浆熔?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵本辐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我医增,道長(zhǎng)慎皱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任叶骨,我火速辦了婚禮茫多,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忽刽。我一直安慰自己地梨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布缔恳。 她就那樣靜靜地躺著宝剖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歉甚。 梳的紋絲不亂的頭發(fā)上万细,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音纸泄,去河邊找鬼赖钞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛聘裁,可吹牛的內(nèi)容都是我干的雪营。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼衡便,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼献起!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起镣陕,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤谴餐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后呆抑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岂嗓,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年鹊碍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了厌殉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片食绿。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖公罕,靈堂內(nèi)的尸體忽然破棺而出器紧,到底是詐尸還是另有隱情,我是刑警寧澤熏兄,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布品洛,位于F島的核電站,受9級(jí)特大地震影響摩桶,放射性物質(zhì)發(fā)生泄漏桥状。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一硝清、第九天 我趴在偏房一處隱蔽的房頂上張望辅斟。 院中可真熱鬧,春花似錦芦拿、人聲如沸士飒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酵幕。三九已至,卻和暖如春缓苛,著一層夾襖步出監(jiān)牢的瞬間芳撒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工未桥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笔刹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓冬耿,卻偏偏與公主長(zhǎng)得像舌菜,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亦镶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Alamofire源碼解讀 AFNetworking的作者M(jìn)att Thompson 提出了一個(gè)新的類似AFNet...
    spicyShrimp閱讀 1,155評(píng)論 0 3
  • Alamofire 是一款 Swift 寫的 HTTP 網(wǎng)絡(luò)請(qǐng)求庫 前言 本篇內(nèi)容為 Alamofire 官方 R...
    zongmumask閱讀 20,844評(píng)論 6 66
  • 原文: Alamofire 4.0 Migration Guide作者: cnoon譯者: kemchenj 譯者...
    kemchenj閱讀 2,973評(píng)論 4 12
  • 本想在這篇文章中單獨(dú)寫AFNetworking 3.0中AFSecurityPolicy的源碼閱讀筆記的日月。但隨著源...
    WeiHing閱讀 2,575評(píng)論 1 13
  • 一、作用 不使用SSL/TLS的HTTP通信染乌,就是不加密的通信山孔。所有信息明文傳播,帶來了三大風(fēng)險(xiǎn)荷憋。 (1)竊聽風(fēng)險(xiǎn)...
    XLsn0w閱讀 10,555評(píng)論 2 44