Swift DeveloperiOS开发iOS Developer

Alamofire 4.5.0源码解析-NetworkReach

2017-07-18  本文已影响196人  FlyElephant

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水平有限,欢迎多多指正~

上一篇下一篇

猜你喜欢

热点阅读