NSURLSession
AFNHTTPSessionManager
AFHTTPSessionManager
is a subclass of AFURLSessionManager
with convenience methods for making HTTP requests. When a baseURL
is provided, requests made with the GET
/ POST
/ et al. convenience methods can be made with relative paths.
在.h文件中
遵守NSSecureCoding, NSCopying协议
定义属性
baseURL(NSURL),requestSerializer(AFHTTPRequestSerializer),responseSerializer(AFHTTPResponseSerializer)。
1.初始化方法:
+ (instancetype)manager;
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
- (instancetype)initWithBaseURL:(nullable NSURL *)url sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
2.创建HTTP请求
主要是发送GET,POST(有无下载,downloadProgress是在Session queue中,不在主队列),HEAD,PUT,PATCH,DELETE。
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(nullable id)parameters progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
只有POST请求有多种,需要加constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
将一个参数和拼接数据到HTTP主体。该block参数是采用AFMultipartFormData
协议的对象。
在.m文件中
主要是方法:
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
所有的网络请求都调用dataTaskWithHTTPMethod这个方法来实现。
实现了:
- NSObject中的description方法来打印出BaseURL,session,和operationQueue。
- NSSecureCoding中的supportsSecureCoding,initWithCoder,encodeWithCoder方法。
- NSCopying中的copyWithZone方法(HTTPClient)
AFURLSessionManager
AFURLSessionManager
创建和管理一个 NSURLSession
对象根据一个特殊的 NSURLSessionConfiguration
对象, 它符合<NSURLSessionTaskDelegate>
, <NSURLSessionDataDelegate>
, <NSURLSessionDownloadDelegate>
, and <NSURLSessionDelegate>
.
NSURLSession & NSURLSessionTask Delegate Methods:
NSURLSessionDelegate
- URLSession:didBecomeInvalidWithError:
- URLSession:didReceiveChallenge:completionHandler:
- URLSessionDidFinishEventsForBackgroundURLSession:
NSURLSessionTaskDelegate
-URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
- URLSession:task:didReceiveChallenge:completionHandler:
-URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
- URLSession:task:needNewBodyStream:
- URLSession:task:didCompleteWithError:
NSURLSessionDataDelegate
URLSession:dataTask:didReceiveResponse:completionHandler:
URLSession:dataTask:didBecomeDownloadTask:
URLSession:dataTask:didReceiveData:
URLSession:dataTask:willCacheResponse:completionHandler:
NSURLSessionDownloadDelegate
-
URLSession:downloadTask:didFinishDownloadingToURL:
-URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
在.h文件中
遵守协议protocol为 <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>,对应相应的代理方法。
定义属性
1.session(NSURLSession),operationQueue(NSOperationQueue).responseSerializer(AFURLResponseSerialization)。
2.管理安全的策略:securityPolicy(AFSecurityPolicy).
3.管理检测网络的连通性:reachabilityManager(AFNetworkReachabilityManager).
4.获得 Session Tasks:tasks,dataTasks,uploadTasks,downloadTasks.
5.管理回调的队列:
completionQueue(dispatch_queue_t),completionGroup(dispatch_group_t).
定义方法:
- 初始化方法:
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration;
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
2.运行Data Tasks:
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
3.运行上传任务:
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
4.运行下载任务:
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
还有就是把NSURLRequest替换成NSData,
5.获取任务的进程(Getting Progress for Tasks):
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
还有downloadProgressForTask
6.设置Session的代理回调
- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
- 设置Task的代理回调
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;
- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
8.设置Data Task 的代理回调
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
9.设置Download Task的回调方法
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
10.设置通知Notification
在.m 文件中
主要有是三个@interface和@implement。
一个是AFURLSessionManagerTaskDelegate。
一个是_AFURLSessionTaskSwizzling。
一个是AFURLSessionManager。
- AFURLSessionManagerTaskDelegate
遵守协议<NSURLSessionTaskDelegate, NSURLSessionDataDelegate,NSURLSessionDownloadDelegate>,
NSProgress Tracking
- (void)setupProgressForTask:(NSURLSessionTask *)task
一部分是对代理持有的两个属性 uploadProgress和 downloadProgress设置回调
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
[self.uploadProgress setCancellable:YES];
[self.uploadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
[self.uploadProgress setPausable:YES];
[self.uploadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.uploadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}
设置上传的取消暂停,下载一样的道理。
另一个部分是对 task 和 NSProgress的uploadProgress和downloadPreogress属性进行键值观测。
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task
主要是移除download和upload的observer。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
主要是上面第一个函数中的实现NSURLSessionTask和NSURLSessionDownloadTask的类中下载和上传的的接收和期望接收的字节数,发送和期望发送的字节数。改变进度,并调用 block
NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
如果遇到error,dispatch_group_async,如果当前 manager持有 completionGroup或者 completionQueue就在主线程中调用 completionHandler并发送通知(在主线程中)
如果在执行当前 task 时没有遇到错误,那么先对数据进行序列化,然后同样调用 block 并发送通知。
NSURLSessionDataTaskDelegate与NSURLSessionDownloadTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
在收到数据后调用。并拼接data.
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
在下载完成对应文件后调用,并处理下载文件。如果fileManagerError,则发出通知。
2._AFURLSessionTaskSwizzling
主要用到runtime的知识,来交换方法。修改 NSURLSessionTask的 resume和 suspend 方法。这样做的目的是为了在方法 resume或者 suspend被调用时发出通知。具体方法调剂过程是在 + load方法中进行的.a.首先用 NSClassFromString(@"NSURLSessionTask")
判断当前部署的 iOS 版本是否含有类 NSURLSessionTask。b.因为 iOS7 和 iOS8 上对于 NSURLSessionTask的实现不同,所以会通过 - [NSURLSession dataTaskWithURL:]方法返回一个 NSURLSessionTask 实例。c .取得当前类 _AFURLSessionTaskSwizzling 中的实现 af_resume。d.如果当前类 currentClass有 resume。
方法真:使用 swizzleResumeAndSuspendMethodForClass:调剂该类的 resume 和 suspend方法
假:currentClass = [currentClass superclass]
3.AFURLSessionManager(最重要的地方)
a.初始化在- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration
中,初始化会话配置,设置为默认的defaultSessionConfiguration,初始化会话session,并设置会话的代理和代理队列,初始化响应序列化responseSerializer,安全认证securityPolicy,和监控网络状态reachabilityManager。
b. 两通知:- (void)taskDidResume:(NSNotification *)notification
与- (void)taskDidSuspend:(NSNotification *)notification
c.然后为NSURLSessionTask设置AFURLSessionManagerTaskDelegate,
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task{
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
} ```
AFURLSessionManager就是通过字典 mutableTaskDelegatesKeyedByTaskIdentifier来存储并管理每一个 NSURLSessionTask,它以 taskIdentifier为键存储 task。该方法使用 NSLock
来保证不同线程使用 mutableTaskDelegatesKeyedByTaskIdentifier时,不会出现**线程竞争**的问题。
d.为DataTask加代理。
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler```
为uploadTask加代理,为downloadTask加代理。移除代理方法
- (void)removeDelegateForTask:(NSURLSessionTask *)task
.
用KVO检测
- (NSArray *)tasksForKeyPath:(NSString *)keyPath
如果无效session,则取消任务:
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks
设置response的序列
- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer
为任务添加(add)和移除(remove)通知观察者.
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task
管理NSURLSessionTask
先调用
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler```
方法传入NSURLRequest,
-
(NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}```
返回一个 AFURLSessionManagerTaskDelegate对象,将 completionHandler的
uploadProgressBlock和 downloadProgressBlock 传入该对象并在相应事件发生时进行回调
然后
Reachability
AFNetworkReachabilityManager
主要是网络的可用性,类似于苹果文档的Reachability
AFNetworkReachabilityStatusUnknow= -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
在.h文件中
先是初始化:
+ (instancetype)sharedManager;
+ (instancetype)manager;
+ (instancetype)managerForDomain:(NSString *)domain;
+ (instancetype)managerForAddress:(const void *)address;
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
然后开始或停止可用性的监控:
- (void)startMonitoring;
- (void)stopMonitoring;
- (NSString *)localizedNetworkReachabilityStatusString;
再设置网络可用性来改变回调:
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
最后是一些常量,通知和功能。
在.m文件中:
初始化:
+ (instancetype)sharedManager
中用dispatch_once,调用+ (instancetype)manager
,
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}```
1. 使用 SCNetworkReachabilityCreateWithAddress或者 SCNetworkReachabilityCreateWithName生成一个 SCNetworkReachabilityRef的引用。
2. 两个方法会通过一个**域名**或者一个 sockaddr_in的指针生个 SCNetworkReachabilityRef。
3. 调用 `- [AFNetworkReachabilityManager initWithReachability:] `将生成的 SCNetworkReachabilityRef引用传给networkReachability.
4. 当调用 CFBridgingRelease(reachability)后,会把 reachability桥接成一个 NSObject 对象赋值给self.networkReachability,然后释放原来的 CoreFoundation 对象。
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
监控网络状态:
- (void)startMonitoring {
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
} - (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
1. 先调用 - stopMonitoring方法,如果之前设置过对网络状态的监听,使用SCNetworkReachabilityUnscheduleFromRunLoop方法取消之前在 Main Runloop 中的监听.
2. 创建一个在每次网络状态改变时的回调block,每次回调被调用时,重新设置networkReachabilityStatus的属性,调用networkReachabilityStatus,
3. 创建一个SCNetworkReachabilityContext,其中的 callback就是上一步中的创建的 block 对象。这里的 AFNetworkReachabilityRetainCallback 和 AFNetworkReachabilityReleaseCallback
都是非常简单的 block,在回调被调用时,只是使用 Block_copy和 Block_release
这样的宏。传入的 info
会以参数的形式在 AFNetworkReachabilityCallback
执行时传入
static const void * AFNetworkReachabilityRetainCallback(const void *info) { return Block_copy(info); }
static void AFNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); } }
4. 当目标的网络状态改变时,会调用传入的回调
5. 在 Main Runloop 中对应的模式开始监控网络状态。
6. 取当前的网络状态,调用 callback
其中` SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);`回调了AFNetworkReachabilityManager的AFNetworkReachabilityCallback。
总结:
AFNetworkReachabilityManager实际上只是一个对底层 SystemConfiguration库中的 C 函数封装的类,它为我们隐藏了 C 语言的实现,提供了统一的 Objective-C 语言接口。
它是 AFNetworking中一个即插即用的模块
# Security
## AFSecurityPolicy
为了阻止中间人攻击,以及其它漏洞的工具。
用枚举设置了验证服务器被信任的方式SSL的类型有None,PublicKey,还有Certificate。
AFSSLPinningModeNone是默认的认证方式,只会在系统的信任的证书列表中对服务端返回的证书进行验证
AFSSLPinningModeCertificate需要客户端预先保存服务端的证书
AFSSLPinningModePublicKey也需要预先保存服务端发送的证书,但是这里只会验证证书中的公钥是否正确
遵守<NSSecureCoding, NSCopying>。
### 从bundle获取证书,并返回
`+ (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle;`
### 获取特殊的安全策略
`+ (instancetype)defaultPolicy;`
### 初始化
`+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;`
`+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates;`
初始化方法时,主要目的是设置**验证服务器是否受信任的方式**。
### 验证服务端是否信任
-
(BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);if (!AFServerTrustIsValid(serverTrust)) { return NO; } // obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA) NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) { if ([self.pinnedCertificates containsObject:trustChainCertificate]) { return YES; } } return NO; } case AFSSLPinningModePublicKey: { NSUInteger trustedPublicKeyCount = 0; NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); for (id trustChainPublicKey in publicKeys) { for (id pinnedPublicKey in self.pinnedPublicKeys) { if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { trustedPublicKeyCount += 1; } } } return trustedPublicKeyCount > 0; }
}
return NO;
}```
- 所以如果没有提供证书或者不验证证书,并且还设置 allowInvalidCertificates为真,满足上面的所有条件,说明这次的验证是不安全的,会直接返回 NO.
- 设置 policy:如果要验证域名的话,就以域名为参数创建一个 SecPolicyRef,否则会创建一个符合 X509 标准的默认SecPolicyRef对象
- 验证证书的有效性:如果只根据信任列表中的证书进行验证,即 self.SSLPinningMode == AFSSLPinningModeNone
。如果允许无效的证书的就会直接返回 YES
。不允许就会对服务端信任进行验证。
如果服务器信任无效,并且不允许无效证书,就会返回 NO. - 根据 SSLPinningMode对服务器信任进行验证
AFSSLPinningModeNone直接返回 NO
AFSSLPinningModeCertificate
AFSSLPinningModePublicKey
Serialization
们对发出请求以及接收响应的过程进行序列化,这涉及到两个模块:
AFURLRequestSerialization
修改请求(主要是 HTTP 请求)的头部,提供了一些语义明确的接口设置 HTTP 头部字段。
主要用于 AFHTTPSessionManager中,因为它主要用于修改 HTTP 头部。
AFURLResponseSerialization(重要)
处理响应的模块,将请求返回的数据解析成对应的格式.这个模块使用在 AFURLSessionManager
也就是核心类。其实也只是个协议
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
遵循这个协议的类同时也要遵循 NSObject、NSSecureCoding 和 NSCopying 这三个协议,实现安全编码、拷贝以及 Objective-C 对象的基本行为。
这个协议只有一个必须实现的方法:
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;```
在.h文件中中有7个类:
第一个
`@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>`剩下的六个AFJSONResponseSerializer,AFXMLParserResponseSerializer,AFXMLDocumentResponseSerializer,AFPropertyListResponseSerializer,AFImageResponseSerializer,AFCompoundResponseSerializer都继承自AFHTTPResponseSerializer。
### AFHTTPResponseSerializer
先看一下AFHTTPResponseSerializer的实现:
- (instancetype)serializer {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.stringEncoding = NSUTF8StringEncoding;
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;
return self;
}
因为是对 HTTP 响应进行序列化,所以这里设置了 stringEncoding为 NSUTF8StringEncoding而且没有对接收的内容类型加以限制。
将 acceptableStatusCodes设置为从 200 到 299 之间的状态码, 因为只有这些状态码表示**获得了有效的响应**
验证响应的有效性:
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
if ([data length] > 0 && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL], AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}```
这个方法根据在初始化方法中初始化的属性 acceptableContentTypes 和 acceptableStatusCodes来判断当前响应是否有效。
AFURLResponseSerialization协议的实现
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}```
主要是调用上面的方法对响应进行验证,然后返回数据,实在是没什么难度。
1. NSSecureCoding
对安全协议的实现,就是支持安全编码,根据属性acceptableStatusCodes,acceptableContentTypes然后初始化,然后encode
2. NSCopying
实现copy属性
### AFJSONResponseSerializer
初始化方法只是在调用父类的初始化方法之后更新了 acceptableContentTypes
属性:
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}```
这个类中与父类差别最大的就是对 AFURLResponseSerialization协议的实现。
- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(
NSError *__autoreleasing *)error
{
#1: 验证请求
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
#2: 解决一个由只包含一个空格的响应引起的 bug, 略
#3: 序列化 JSON
id responseObject = nil;
NSError *serializationError = nil;
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
#4: 移除 JSON 中的 null
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error); }
return responseObject;
}```
然后实现NSSecureCoding,NSCopying与上面类似
其他几个子类实现差不多。
## AFURLRequestSerialization
AFURLRequestSerialization的主要工作是对发出的 HTTP 请求进行处理,它有几部分的工作需要完成。
而这个文件中的大部分类都是为 AFHTTPRequestSerializer服务的:
1. 处理查询的 URL 参数
2. 设置 HTTP 头部字段
3. 设置请求的属性
4. 分块上传
### 处理查询参数
处理查询参数这部分主要是通过 AFQueryStringPair还有一些 C 函数来完成的,这个类有两个属性 field和 value对应 HTTP 请求的查询 URL 中的参数。在.m文件中,先是它的实现。
@interface AFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
- (instancetype)initWithField:(id)field value:(id)value;
- (NSString *)URLEncodedStringValue;
@end
其中URLEncodedStringValue方法会返回
`return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];`
key=value这种格式,同时使用 AFPercentEscapedStringFromString函数来对 field和 value进行处理,将其中的 :#[]@!$&'()*+,;=等字符转换为百分号表示的形式。
这一部分代码还负责返回查询参数,将 AFQueryStringPair中key和value
转换为以下这种形式:
username=tom&password=123456&hello[world]=helloworld
下面是用FOUNDATION_EXPORT定义的NSArray的数组,有AFQueryStringPairsFromDictionary,AFQueryStringPairsFromKeyAndValue,AFQueryStringFromParameters,AFQueryStringPairsFromDictionary,AFQueryStringPairsFromKeyAndValue。
它的实现主要依赖于一个递归函数 AFQueryStringPairsFromKeyAndValue,如果当前的 value
是一个集合类型的话,那么它就会不断地递归调用自己。
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
返回一个数组
[ username=tom, password=123456, hello[world]=helloworld]
得到这个数组之后就会调用 AFQueryStringFromParameters使用 &来拼接它们。
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}```
- 设置请求的属性
这个下面有一个AFStreamingMultipartFormData的声明,是多形式的数据流。
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}```
在这些属性被设置时,会触发 KVO,然后将新的属性存储在一个名为 mutableObservedChangedKeyPaths的字典中:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}```
然后会在生成NSURLRequest的时候设置这些属性:
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
- 设置HTTP头部字段
在AFHTTPRequestSerializer的interface文件中提供了一些属性方便我们设置 HTTP 头部字段
mutableHTTPRequestHeaders.
- (NSDictionary *)HTTPRequestHeaders {
//在设置 HTTP 头部字段时,都会存储到这个可变字典中。而当真正使用时,用这个方法,来获取对应版本的不可变字典。
return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
}
- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
}
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
return [self.mutableHTTPRequestHeaders valueForKey:field];
}
这个类是如何设置一些我们平时常用的头部字段的。首先是 User-Agent,AFHTTPRequestSerializer刚刚初始化时,就会根据当前编译的平台生成一个 userAgent。在iOS,ios_watch还有iOS_VERSION_MIN的平台下生成不同。
之后有方法设置授权头部用用户名和密码,还有清除方法:
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password
{
NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
[self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}
- (void)clearAuthorizationHeader {
[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
}```
设置了AFHTTPBodyPart,AFMultipartBodyStream的@interface。
字符串:
#UIKit+AFNetworking
参考:
[源码解析](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/AFNetworking/%E5%A4%84%E7%90%86%E8%AF%B7%E6%B1%82%E5%92%8C%E5%93%8D%E5%BA%94%20AFURLSerialization%EF%BC%88%E4%B8%89%EF%BC%89.md)