AFNetworking2.0源码解析<二>

来源:互联网 发布:淘宝打猎气步枪价格 编辑:程序博客网 时间:2024/06/09 19:15

续AFNetworking2.0源码解析<一>

本篇我们继续来看看AFNetworking的下一个模块 — AFURLRequestSerialization。

AFURLRequestSerialization用于帮助构建NSURLRequest,主要做了两个事情:
1.构建普通请求:格式化请求参数,生成HTTP Header。
2.构建multipart请求。
分别看看它在这两点具体做了什么,怎么做的。

1.构建普通请求

A.格式化请求参数

一般我们请求都会按key=value的方式带上各种参数,GET方法参数直接加在URL上,POST方法放在body上,NSURLRequest没有封装好这个参数的解析,只能我们自己拼好字符串。AFNetworking提供了接口,让参数可以是NSDictionary, NSArray, NSSet这些类型,再由内部解析成字符串后赋给NSURLRequest。

转化过程大致是这样的:

@{     @"name" : @"bang",     @"phone": @{@"mobile": @"xx", @"home": @"xx"},     @"families": @[@"father", @"mother"],     @"nums": [NSSet setWithObjects:@"1", @"2", nil]}->@[     field: @"name", value: @"bang",     field: @"phone[mobile]", value: @"xx",     field: @"phone[home]", value: @"xx",     field: @"families[]", value: @"father",     field: @"families[]", value: @"mother",     field: @"nums", value: @"1",     field: @"nums", value: @"2",]->name=bang&phone[mobile]=xx&phone[home]=xx&families[]=father&families[]=mother&nums=1&num=2

第一部分是用户传进来的数据,支持包含NSArray,NSDictionary,NSSet这三种数据结构。
第二部分是转换成AFNetworking内自己的数据结构,每一个key-value对都用一个对象AFQueryStringPair表示,作用是最后可以根据不同的字符串编码生成各自的key=value字符串。主要函数是AFQueryStringPairsFromKeyAndValue,详见源码注释。
第三部分是最后生成NSURLRequest可用的字符串数据,并且对参数进行url编码,在AFQueryStringFromParametersWithEncoding这个函数里。

最后在把数据赋给NSURLRequest时根据不同的HTTP方法分别处理,对于GET/HEAD/DELETE方法,把参数加到URL后面,对于其他如POST/PUT方法,把数据加到body上,并设好HTTP头,告诉服务端字符串的编码。

B.HTTP Header

AFNetworking帮你组装好了一些HTTP请求头,包括语言Accept-Language,根据[NSLocale preferredLanguages]方法读取本地语言,告诉服务端自己能接受的语言。还有构建User-Agent,以及提供Basic Auth认证接口,帮你把用户名密码做base64编码后放入HTTP请求头。详见源码注释。

C.其他格式化方式

HTTP请求参数不一定是要key=value形式,可以是任何形式的数据,可以是json格式,苹果的plist格式,二进制protobuf格式等,AFNetworking提供了方法可以很容易扩展支持这些格式,默认就实现了json和plist格式。详见源码的类AFJSONRequestSerializer和AFPropertyListRequestSerializer。

2.构建multipart请求

构建Multipart请求是占篇幅很大的一个功能,AFURLRequestSerialization里2/3的代码都是在做这个事。

A.Multipart协议介绍

Multipart是HTTP协议为web表单新增的上传文件的协议,协议文档是rfc1867,它基于HTTP的POST方法,数据同样是放在body上,跟普通POST方法的区别是数据不是key=value形式,key=value形式难以表示文件实体,为此Multipart协议添加了分隔符,有自己的格式结构,大致如下:

—AaB03x
content-disposition: form-data; name=“name"

bang
–AaB03x
content-disposition: form-data; name=”pic”; filename=“content.txt”
Content-Type: text/plain

… contents of bang.txt …
–AaB03x–

以上表示数据name=bang以及一个文件,content.txt是文件名,… contents of bang.txt …是文件实体内容。分隔符—AaB03x是可以自定义的,写在HTTP头部里:

Content-type: multipart/form-data, boundary=AaB03x

每一个部分都有自己的头部,表明这部分的数据类型以及其他一些参数,例如文件名,普通字段的key。最后一个分隔符会多加两横,表示数据已经结束:—AaB03x—。

B.实现

接下来说说怎样构造Multipart里的数据,最简单的方式就是直接拼数据,要发送一个文件,就直接把文件所有内容读取出来,再按上述协议加上头部和分隔符,拼接好数据后扔给NSURLRequest的body就可以发送了,很简单。但这样做是不可用的,因为文件可能很大,这样拼数据把整个文件读进内存,很可能把内存撑爆了。

第二种方法是不把文件读出来,不在内存拼,而是新建一个临时文件,在这个文件上拼接数据,再把文件地址扔给NSURLRequest的bodyStream,这样上传的时候是分片读取这个文件,不会撑爆内存,但这样每次上传都需要新建个临时文件,对这个临时文件的管理也挺麻烦的。

第三种方法是构建自己的数据结构,只保存要上传的文件地址,边上传边拼数据,上传是分片的,拼数据也是分片的,拼到文件实体部分时直接从原来的文件分片读取。这方法没上述两种的问题,只是实现起来也没上述两种简单,AFNetworking就是实现这第三种方法,而且还更进一步,除了文件,还可以添加多个其他不同类型的数据,包括NSData,和InputStream。

AFNetworking里multipart请求的使用方式是这样:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];NSDictionary *parameters = @{@"foo": @"bar"};NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];[manager POST:@"http://example.com/resources.json" parameters:parameters constructingBodyWithBlock:^(id formData) {    [formData appendPartWithFileURL:filePath name:@"image" error:nil];} success:^(AFHTTPRequestOperation *operation, id responseObject) {    NSLog(@"Success: %@", responseObject);} failure:^(AFHTTPRequestOperation *operation, NSError *error) {    NSLog(@"Error: %@", error);}];

这里通过constructingBodyWithBlock向使用者提供了一个AFStreamingMultipartFormData对象,调这个对象的几种append方法就可以添加不同类型的数据,包括FileURL/NSData/NSInputStream,AFStreamingMultipartFormData内部把这些append的数据转成不同类型的AFHTTPBodyPart,添加到自定义的AFMultipartBodyStream里。最后把AFMultipartBodyStream赋给原来NSMutableURLRequest的bodyStream。NSURLConnection发送请求时会读取这个bodyStream,在读取数据时会调用这个bodyStream的-read:maxLength:方法,AFMultipartBodyStream重写了这个方法,不断读取之前append进来的AFHTTPBodyPart数据直到读完。

AFHTTPBodyPart封装了各部分数据的组装和读取,一个AFHTTPBodyPart就是一个数据块。实际上三种类型(FileURL/NSData/NSInputStream)的数据在AFHTTPBodyPart都转成NSInputStream,读取数据时只需读这个inputStream。inputStream只保存了数据的实体,没有包括分隔符和头部,AFHTTPBodyPart是边读取变拼接数据,用一个状态机确定现在数据读取到哪一部份,以及保存这个状态下已被读取的字节数,以此定位要读的数据位置,详见AFHTTPBodyPart的-read:maxLength:方法。

AFMultipartBodyStream封装了整个multipart数据的读取,主要是根据读取的位置确定现在要读哪一个AFHTTPBodyPart。AFStreamingMultipartFormData对外提供友好的append接口,并把构造好的AFMultipartBodyStream赋回给NSMutableURLRequest,关系大致如下图:

AFURLRequestSerialization

C.NSInputStream子类

NSURLRequest的setHTTPBodyStream接受的是一个NSInputStream*参数,那我们要自定义inputStream的话,创建一个NSInputStream的子类传给它是不是就可以了?实际上不行,这样做后用NSURLRequest发出请求会导致crash,提示[xx _scheduleInCFRunLoop:forMode:]: unrecognized selector。

这是因为NSURLRequest实际上接受的不是NSInputStream对象,而是CoreFoundation的CFReadStreamRef对象,因为CFReadStreamRef和NSInputStream是toll-free bridged,可以自由转换,但CFReadStreamRef会用到CFStreamScheduleWithRunLoop这个方法,当它调用到这个方法时,object-c的toll-free bridging机制会调用object-c对象NSInputStream的相应函数,这里就调用到了_scheduleInCFRunLoop:forMode:,若不实现这个方法就会crash。详见这篇文章。

3.源码注释

// AFSerialization.h//// Copyright (c) 2013-2014 AFNetworking (http://afnetworking.com)//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.#import "AFURLRequestSerialization.h"//兼容Mac和iOS引入不同的库#if __IPHONE_OS_VERSION_MIN_REQUIRED#import <MobileCoreServices/MobileCoreServices.h>#else#import <CoreServices/CoreServices.h>#endifNSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request";NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response";typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error);//base64编码实现,用于Basic Authstatic NSString * AFBase64EncodedStringFromString(NSString *string) {    NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];    NSUInteger length = [data length];    NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4];    uint8_t *input = (uint8_t *)[data bytes];    uint8_t *output = (uint8_t *)[mutableData mutableBytes];    for (NSUInteger i = 0; i < length; i += 3) {        NSUInteger value = 0;        for (NSUInteger j = i; j < (i + 3); j++) {            value <<= 8;            if (j < length) {                 value |= (0xFF & input[j]);             }         }                  static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";                  NSUInteger idx = (i / 3) * 4;         output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F];        output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F];        output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6)  & 0x3F] : '=';        output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0)  & 0x3F] : '=';    }    return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding];}//参数编码相关工具方法static NSString * const kAFCharactersToBeEscapedInQueryString = @":/?&=;+!@#$()',*";static NSString * AFPercentEscapedQueryStringKeyFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {    static NSString * const kAFCharactersToLeaveUnescapedInQueryStringPairKey = @"[].";return (__bridge_transfer  NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescapedInQueryStringPairKey, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));}static NSString * AFPercentEscapedQueryStringValueFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {return (__bridge_transfer  NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));}#pragma mark -//参数转化中间数据对象@interface AFQueryStringPair : NSObject@property (readwrite, nonatomic, strong) id field;@property (readwrite, nonatomic, strong) id value;- (id)initWithField:(id)field value:(id)value;- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding;@end@implementation AFQueryStringPair- (id)initWithField:(id)field value:(id)value {    self = [super init];    if (!self) {        return nil;    }    self.field = field;    self.value = value;    return self;}- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding {    if (!self.value || [self.value isEqual:[NSNull null]]) {        return AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding);    } else {        return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding), AFPercentEscapedQueryStringValueFromStringWithEncoding([self.value description], stringEncoding)];    }}@end#pragma mark -//转化参数相关方法extern NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary);extern NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value);static NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) {    NSMutableArray *mutablePairs = [NSMutableArray array];    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {        [mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]];    }    return [mutablePairs componentsJoinedByString:@"&"];}NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);}NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {    /*     转换效果:     @{         @"name" : @"bang",         @"phone": @{@"mobile": @"xx", @"home": @"xx"},         @"families": @[@"father", @"mother"],         @"nums": [NSSet setWithObjects:@"1", @"2", nil]     }     ->     @[         field: @"name", value: @"bang",         field: @"phone[mobile]", value: @"xx",         field: @"phone[home]", value: @"xx",         field: @"families[]", value: @"father",         field: @"families[]", value: @"mother",         field: @"nums", value: @"1",         field: @"nums", value: @"2",     ]     */    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];    //用NSSortDescriptor排序    //http://nshipster.com/nssortdescriptor/    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];    if ([value isKindOfClass:[NSDictionary class]]) {        //对应上述例子的 @"phone": @{@"mobile": @"xx", @"home": @"xx"}        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 objectForKey:nestedKey];            if (nestedValue) {                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];            }        }    } else if ([value isKindOfClass:[NSArray class]]) {        //对应上述例子的 @"families": @[@"father", @"mother"]        NSArray *array = value;        for (id nestedValue in array) {            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];        }    } else if ([value isKindOfClass:[NSSet class]]) {        //对应上述例子的 @"nums": [NSSet setWithObjects:@"1", @"2", nil]        NSSet *set = value;        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];        }    } else {        //其他类型,对应上述例子@"name" : @"bang"        //key和value都可以不是NSString类型,转成字符串时会调用这个对象的description方法        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];    }    return mutableQueryStringComponents;}#pragma mark -@interface AFStreamingMultipartFormData : NSObject- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest                    stringEncoding:(NSStringEncoding)encoding;- (NSMutableURLRequest *)requestByFinalizingMultipartFormData;@end#pragma mark -@interface AFHTTPRequestSerializer ()@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;@end@implementation AFHTTPRequestSerializer+ (instancetype)serializer {    return [[self alloc] init];}- (instancetype)init {    self = [super init];    if (!self) {        return nil;    }    self.stringEncoding = NSUTF8StringEncoding;    self.allowsCellularAccess = YES;    self.cachePolicy = NSURLRequestUseProtocolCachePolicy;    self.HTTPShouldHandleCookies = YES;    self.HTTPShouldUsePipelining = NO;    self.networkServiceType = NSURLNetworkServiceTypeDefault;    self.timeoutInterval = 60;    self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];    // Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4    NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];    [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {        float q = 1.0f - (idx * 0.1f);        [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];        *stop = q <= 0.5f;    }];    [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];    //分别为iOS Mac构建userAgent    //iOS的构建结果是    //AFNetworking iOS ProjectName/1.0.0 (iPhone 4; iOS 7.0.3; Scale/2.00)    NSString *userAgent = nil;#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wgnu"#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)    // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43    userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], (__bridge id)CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey) ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)    userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleExecutableKey] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleIdentifierKey], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] ?: [[[NSBundle mainBundle] infoDictionary] objectForKey:(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]];#endif#pragma clang diagnostic pop    if (userAgent) {        if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {            NSMutableString *mutableUserAgent = [userAgent mutableCopy];            if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {                userAgent = mutableUserAgent;            }        }        [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];    }    // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html    //需要把参数写在URL上的HTTP方法    self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];    return self;}#pragma mark -- (NSDictionary *)HTTPRequestHeaders {    return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];}- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {[self.mutableHTTPRequestHeaders setValue:value forKey:field];}- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username password:(NSString *)password {    //加basic认证HTTP头NSString *basicAuthCredentials = [NSString stringWithFormat:@"%@:%@", username, password];    [self setValue:[NSString stringWithFormat:@"Basic %@", AFBase64EncodedStringFromString(basicAuthCredentials)] forHTTPHeaderField:@"Authorization"];}- (void)setAuthorizationHeaderFieldWithToken:(NSString *)token {    [self setValue:[NSString stringWithFormat:@"Token token=\"%@\"", token] forHTTPHeaderField:@"Authorization"];}- (void)clearAuthorizationHeader {[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];}#pragma mark -- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style {    self.queryStringSerializationStyle = style;    self.queryStringSerialization = nil;}- (void)setQueryStringSerializationWithBlock:(NSString *(^)(NSURLRequest *, NSDictionary *, NSError *__autoreleasing *))block {    //让用户可以通过block自己组装请求参数    self.queryStringSerialization = block;}#pragma mark -- (NSMutableURLRequest *)requestWithMethod:(NSString *)method                                 URLString:(NSString *)URLString                                parameters:(id)parameters{    return [self requestWithMethod:method URLString:URLString parameters:parameters error:nil];}- (NSMutableURLRequest *)requestWithMethod:(NSString *)method                                 URLString:(NSString *)URLString                                parameters:(id)parameters                                     error:(NSError *__autoreleasing *)error{    NSParameterAssert(method);    NSParameterAssert(URLString);    NSURL *url = [NSURL URLWithString:URLString];    NSParameterAssert(url);    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];    mutableRequest.HTTPMethod = method;    mutableRequest.allowsCellularAccess = self.allowsCellularAccess;    mutableRequest.cachePolicy = self.cachePolicy;    mutableRequest.HTTPShouldHandleCookies = self.HTTPShouldHandleCookies;    mutableRequest.HTTPShouldUsePipelining = self.HTTPShouldUsePipelining;    mutableRequest.networkServiceType = self.networkServiceType;    mutableRequest.timeoutInterval = self.timeoutInterval;    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];return mutableRequest;}- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method                                              URLString:(NSString *)URLString                                             parameters:(NSDictionary *)parameters                              constructingBodyWithBlock:(void (^)(id  formData))block{    return [self multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:nil];}- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method                                              URLString:(NSString *)URLString                                             parameters:(NSDictionary *)parameters                              constructingBodyWithBlock:(void (^)(id  formData))block                                                  error:(NSError *__autoreleasing *)error{    NSParameterAssert(method);    //GET和HEAD方法不能用multipart传数据,一般都是POST方法    NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];    __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];    if (parameters) {        //把请求参数也放到multipart里        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {            NSData *data = nil;            if ([pair.value isKindOfClass:[NSData class]]) {                data = pair.value;            } else if ([pair.value isEqual:[NSNull null]]) {                data = [NSData data];            } else {                data = [[pair.value description] dataUsingEncoding:self.stringEncoding];            }            if (data) {                [formData appendPartWithFormData:data name:[pair.field description]];            }        }    }    //执行对外暴露的block接口,用户在这里append文件等数据    if (block) {        block(formData);    }    return [formData requestByFinalizingMultipartFormData];}//额外提供的一个方法,把request里的bodyStream写到文件//即可以把multipart发送的内容先生成好保存到文件- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request                             writingStreamContentsToFile:(NSURL *)fileURL                                       completionHandler:(void (^)(NSError *error))handler{    if (!request.HTTPBodyStream) {        return [request mutableCopy];    }    NSParameterAssert([fileURL isFileURL]);    NSInputStream *inputStream = request.HTTPBodyStream;    NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO];    __block NSError *error = nil;    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        //新开一条线程做这个事,stream的read和write是阻塞的        [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];        [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];        [inputStream open];        [outputStream open];        while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) {            uint8_t buffer[1024];            NSInteger bytesRead = [inputStream read:buffer maxLength:1024];            if (inputStream.streamError || bytesRead < 0) {                error = inputStream.streamError;                break;            }            NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead];            if (if (outputStream.streamError || bytesWritten < 0) {                error = outputStream.streamError;                break;            }            if (bytesRead == 0 && bytesWritten == 0) {                break;            }        }        [outputStream close];        [inputStream close];        if (handler) {            dispatch_async(dispatch_get_main_queue(), ^{                handler(error);            });        }    });    NSMutableURLRequest *mutableRequest = [request mutableCopy];    mutableRequest.HTTPBodyStream = nil;    return mutableRequest;}#pragma mark - AFURLRequestSerialization- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request                               withParameters:(id)parameters                                        error:(NSError *__autoreleasing *)error{    NSParameterAssert(request);    NSMutableURLRequest *mutableRequest = [request mutableCopy];    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {        //设好HTTP header,不覆盖原有的header        if (![request valueForHTTPHeaderField:field]) {            [mutableRequest setValue:value forHTTPHeaderField:field];        }    }];    if (parameters) {        NSString *query = nil;        if (self.queryStringSerialization) {            //用传进来的自定义block格式化请求参数            query = self.queryStringSerialization(request, parameters, error);        } else {            switch (self.queryStringSerializationStyle) {                case AFHTTPRequestQueryStringDefaultStyle:                    //默认的格式化方式                    query = AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding);                    break;            }        }        if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {            //参数加在URL上的HTTP方法,包括GET HEAD DELETE            mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];        } else {            //参数带在body上,大多是POST PUT            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {                //设置Content-Type HTTP头,告诉服务端body的参数编码类型                NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding));                [mutableRequest setValue:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forHTTPHeaderField:@"Content-Type"];            }            [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];        }    }    return mutableRequest;}#pragma mark - NSecureCoding+ (BOOL)supportsSecureCoding {    return YES;}- (id)initWithCoder:(NSCoder *)decoder {    self = [self init];    if (!self) {        return nil;    }    self.mutableHTTPRequestHeaders = [[decoder decodeObjectOfClass:[NSDictionary class] forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))] mutableCopy];    self.queryStringSerializationStyle = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))] unsignedIntegerValue];    return self;}- (void)encodeWithCoder:(NSCoder *)coder {    [coder encodeObject:self.mutableHTTPRequestHeaders forKey:NSStringFromSelector(@selector(mutableHTTPRequestHeaders))];    [coder encodeInteger:self.queryStringSerializationStyle forKey:NSStringFromSelector(@selector(queryStringSerializationStyle))];}#pragma mark - NSCopying- (id)copyWithZone:(NSZone *)zone {    AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];    serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];    serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;    serializer.queryStringSerialization = self.queryStringSerialization;    return serializer;}@end#pragma mark -/************* Multipart相关 ************/static NSString * AFCreateMultipartFormBoundary() {    //生成分隔符    return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];}static NSString * const kAFMultipartFormCRLF = @"\r\n";static inline NSString * AFMultipartFormInitialBoundary(NSString *boundary) {    //第一部分数据的分隔符    return [NSString stringWithFormat:@"--%@%@", boundary, kAFMultipartFormCRLF];}static inline NSString * AFMultipartFormEncapsulationBoundary(NSString *boundary) {    //中间部分数据的分隔符,前面要换行    return [NSString stringWithFormat:@"%@--%@%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];}static inline NSString * AFMultipartFormFinalBoundary(NSString *boundary) {    //最后一个分隔符,加--表示结束    return [NSString stringWithFormat:@"%@--%@--%@", kAFMultipartFormCRLF, boundary, kAFMultipartFormCRLF];}static inline NSString * AFContentTypeForPathExtension(NSString *extension) {    //通过系统CoreService库的方法根据文件后缀拿到mime类型#ifdef __UTTYPE__    NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);    NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);    if (!contentType) {        return @"application/octet-stream";    } else {        return contentType;    }#else#pragma unused (extension)    return @"application/octet-stream";#endif}//multipart上传时,3G网络建议的每个包的大小和读取延时NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16;NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;@interface AFHTTPBodyPart : NSObject@property (nonatomic, assign) NSStringEncoding stringEncoding;@property (nonatomic, strong) NSDictionary *headers;@property (nonatomic, copy) NSString *boundary;@property (nonatomic, strong) id body;@property (nonatomic, assign) unsigned long long bodyContentLength;@property (nonatomic, strong) NSInputStream *inputStream;@property (nonatomic, assign) BOOL hasInitialBoundary;@property (nonatomic, assign) BOOL hasFinalBoundary;@property (readonly, nonatomic, assign, getter = hasBytesAvailable) BOOL bytesAvailable;@property (readonly, nonatomic, assign) unsigned long long contentLength;- (NSInteger)read:(uint8_t *)buffer        maxLength:(NSUInteger)length;@end@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;@property (nonatomic, assign) NSTimeInterval delay;@property (nonatomic, strong) NSInputStream *inputStream;@property (readonly, nonatomic, assign) unsigned long long contentLength;@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;- (id)initWithStringEncoding:(NSStringEncoding)encoding;- (void)setInitialAndFinalBoundaries;- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;@end#pragma mark -//桥梁,对外提供接口,组装bodyStream给NSMutableURLRequest@interface AFStreamingMultipartFormData ()@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;@property (readwrite, nonatomic, copy) NSString *boundary;@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;@end@implementation AFStreamingMultipartFormData- (id)initWithURLRequest:(NSMutableURLRequest *)urlRequest          stringEncoding:(NSStringEncoding)encoding{    self = [super init];    if (!self) {        return nil;    }    self.request = urlRequest;    self.stringEncoding = encoding;    self.boundary = AFCreateMultipartFormBoundary();    self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];    return self;}- (BOOL)appendPartWithFileURL:(NSURL *)fileURL                         name:(NSString *)name                        error:(NSError * __autoreleasing *)error{    NSParameterAssert(fileURL);    NSParameterAssert(name);    //便捷接口,自动判断mimetype    NSString *fileName = [fileURL lastPathComponent];    NSString *mimeType = AFContentTypeForPathExtension([fileURL pathExtension]);    return [self appendPartWithFileURL:fileURL name:name fileName:fileName mimeType:mimeType error:error];}- (BOOL)appendPartWithFileURL:(NSURL *)fileURL                         name:(NSString *)name                     fileName:(NSString *)fileName                     mimeType:(NSString *)mimeType                        error:(NSError * __autoreleasing *)error{    NSParameterAssert(fileURL);    NSParameterAssert(name);    NSParameterAssert(fileName);    NSParameterAssert(mimeType);    //确保是能读到的文件路径    if (![fileURL isFileURL]) {        NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)};        if (error) {            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];        }        return NO;    } else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {        NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)};        if (error) {            *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];        }        return NO;    }    NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:error];    if (!fileAttributes) {        //文件属性拿不到就不知道文件大小,不给添加        return NO;    }    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];    bodyPart.stringEncoding = self.stringEncoding;    bodyPart.headers = mutableHeaders;    bodyPart.boundary = self.boundary;    //AFHTTPBodyPart会根据body的isKindOfClass:判断数据类型    bodyPart.body = fileURL;    bodyPart.bodyContentLength = [[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];    [self.bodyStream appendHTTPBodyPart:bodyPart];    return YES;}- (void)appendPartWithInputStream:(NSInputStream *)inputStream                             name:(NSString *)name                         fileName:(NSString *)fileName                           length:(int64_t)length                         mimeType:(NSString *)mimeType{    NSParameterAssert(name);    NSParameterAssert(fileName);    NSParameterAssert(mimeType);    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];    bodyPart.stringEncoding = self.stringEncoding;    bodyPart.headers = mutableHeaders;    bodyPart.boundary = self.boundary;    bodyPart.body = inputStream;    bodyPart.bodyContentLength = (unsigned long long)length;    [self.bodyStream appendHTTPBodyPart:bodyPart];}- (void)appendPartWithFileData:(NSData *)data                          name:(NSString *)name                      fileName:(NSString *)fileName                      mimeType:(NSString *)mimeType{    NSParameterAssert(name);    NSParameterAssert(fileName);    NSParameterAssert(mimeType);    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];    [mutableHeaders setValue:mimeType forKey:@"Content-Type"];    [self appendPartWithHeaders:mutableHeaders body:data];}- (void)appendPartWithFormData:(NSData *)data                          name:(NSString *)name{    NSParameterAssert(name);    NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary];    [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"];    [self appendPartWithHeaders:mutableHeaders body:data];}- (void)appendPartWithHeaders:(NSDictionary *)headers                         body:(NSData *)body{    NSParameterAssert(body);    AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];    bodyPart.stringEncoding = self.stringEncoding;    bodyPart.headers = headers;    bodyPart.boundary = self.boundary;    bodyPart.bodyContentLength = [body length];    bodyPart.body = body;    [self.bodyStream appendHTTPBodyPart:bodyPart];}- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes                                  delay:(NSTimeInterval)delay{    self.bodyStream.numberOfBytesInPacket = numberOfBytes;    self.bodyStream.delay = delay;}- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {    //bodyStream构造完成,数据都准备好了,把它赋给request    if ([self.bodyStream isEmpty]) {        return self.request;    }    // Reset the initial and final boundaries to ensure correct Content-Length    //下面会调用[self.bodyStream contentLength],这里需要先设好每个bodyPart的位置类型,才能正确算出整个bodyStream的长度    [self.bodyStream setInitialAndFinalBoundaries];    [self.request setHTTPBodyStream:self.bodyStream];    [self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];    [self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];    return self.request;}@end#pragma mark -@interface NSStream ()@property (readwrite) NSStreamStatus streamStatus;@property (readwrite, copy) NSError *streamError;@end@interface AFMultipartBodyStream () <NSCopying>@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;@property (readwrite, nonatomic, strong) NSOutputStream *outputStream;@property (readwrite, nonatomic, strong) NSMutableData *buffer;@end@implementation AFMultipartBodyStream#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wimplicit-atomic-properties"@synthesize streamStatus;@synthesize streamError;#pragma clang diagnostic pop- (id)initWithStringEncoding:(NSStringEncoding)encoding {    self = [super init];    if (!self) {        return nil;    }    self.stringEncoding = encoding;    self.HTTPBodyParts = [NSMutableArray array];    self.numberOfBytesInPacket = NSIntegerMax;    return self;}- (void)setInitialAndFinalBoundaries {    if ([self.HTTPBodyParts count] > 0) {        for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {            bodyPart.hasInitialBoundary = NO;            bodyPart.hasFinalBoundary = NO;        }        //标识第一个和最后一个bodyPart,因为需要添加的分隔符不一样        [[self.HTTPBodyParts objectAtIndex:0] setHasInitialBoundary:YES];        [[self.HTTPBodyParts lastObject] setHasFinalBoundary:YES];    }}- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {    [self.HTTPBodyParts addObject:bodyPart];}- (BOOL)isEmpty {    return [self.HTTPBodyParts count] == 0;}#pragma mark - NSInputStream- (NSInteger)read:(uint8_t *)buffer        maxLength:(NSUInteger)length{    if ([self streamStatus] == NSStreamStatusClosed) {        return 0;    }    NSInteger totalNumberOfBytesRead = 0;#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wgnu"    //一次读取的数据不超过系统指定的maxLength和用户指定的numberOfBytesInPacket中的最小值    //self.numberOfBytesInPacket用于3G网络请求优化,指定每次读取的数据包大小,有个建议值kAFUploadStream3GSuggestedPacketSize    while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {        //通过self.currentHTTPBodyPart和各个AFHTTPBodyPart里的游标决定读取数据的位置        if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {            //self.currentHTTPBodyPart已读完,看看还有没有下一个            if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {                //没有的话数据读取结束                //问题:NSURLRequest怎么知道数据读完了?                //应该是这个函数返回0时外部就知道stream已经读完,调用它的close方法。                //上面的-requestWithMultipartFormRequest:writingStreamContentsToFile:completionHandler:就是这样用的                break;            }        } else {            //这里maxLength是进入AFHTTPBodyPart读取的maxLength            //这里应该有bug,应该是            //NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead            NSUInteger maxLength = length - (NSUInteger)totalNumberOfBytesRead;            //从bodyPart里读数据到buffer            NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];            if (numberOfBytesRead == -1) {                self.streamError = self.currentHTTPBodyPart.inputStream.streamError;                break;            } else {                totalNumberOfBytesRead += numberOfBytesRead;                //self.delay用于3G网络请求优化,读取数据时延时,有个建议值kAFUploadStream3GSuggestedDelay                if (self.delay > 0.0f) {                    [NSThread sleepForTimeInterval:self.delay];                }            }        }    }#pragma clang diagnostic pop    return totalNumberOfBytesRead;}- (BOOL)getBuffer:(__unused uint8_t **)buffer           length:(__unused NSUInteger *)len{    return NO;}- (BOOL)hasBytesAvailable {    //这里估计是难以准确判断数据是否读完,简单做成只要是open状态就是有数据    //外面通过-read:maxLength读不到数据再close stream    return [self streamStatus] == NSStreamStatusOpen;}#pragma mark - NSStream- (void)open {    if (self.streamStatus == NSStreamStatusOpen) {        return;    }    self.streamStatus = NSStreamStatusOpen;    [self setInitialAndFinalBoundaries];    self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];}- (void)close {    self.streamStatus = NSStreamStatusClosed;}- (id)propertyForKey:(__unused NSString *)key {    return nil;}- (BOOL)setProperty:(__unused id)property             forKey:(__unused NSString *)key{    return NO;}//scheduleInRunLoop是为了delegate可以接收到stream状态改变的回调,估计是NSURLRequest没有用到delegate处理状态改变,这里简单置空了- (void)scheduleInRunLoop:(__unused NSRunLoop *)aRunLoop                  forMode:(__unused NSString *)mode{}- (void)removeFromRunLoop:(__unused NSRunLoop *)aRunLoop                  forMode:(__unused NSString *)mode{}- (unsigned long long)contentLength {    unsigned long long length = 0;    //bodyStream的文件长度等于所有bodyPart长度相加    //[bodyPart contentLength]已包含分隔符等数据的长度    for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {        length += [bodyPart contentLength];    }    return length;}#pragma mark - Undocumented CFReadStream Bridged Methods//NSURLRequest会调下面这三个方法,不定义会crash "unrecognized selector"//http://blog.octiplex.com/2011/06/how-to-implement-a-corefoundation-toll-free-bridged-nsinputstream-subclass/- (void)_scheduleInCFRunLoop:(__unused CFRunLoopRef)aRunLoop                     forMode:(__unused CFStringRef)aMode{}- (void)_unscheduleFromCFRunLoop:(__unused CFRunLoopRef)aRunLoop                         forMode:(__unused CFStringRef)aMode{}- (BOOL)_setCFClientFlags:(__unused CFOptionFlags)inFlags                 callback:(__unused CFReadStreamClientCallBack)inCallback                  context:(__unused CFStreamClientContext *)inContext {    return NO;}#pragma mark - NSCopying-(id)copyWithZone:(NSZone *)zone {    AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];    for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {        [bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];    }    [bodyStreamCopy setInitialAndFinalBoundaries];    return bodyStreamCopy;}@end#pragma mark -typedef enum {    AFEncapsulationBoundaryPhase = 1,    AFHeaderPhase                = 2,    AFBodyPhase                  = 3,    AFFinalBoundaryPhase         = 4,} AFHTTPBodyPartReadPhase;@interface AFHTTPBodyPart ()  {    AFHTTPBodyPartReadPhase _phase;    NSInputStream *_inputStream;    unsigned long long _phaseReadOffset;}- (BOOL)transitionToNextPhase;- (NSInteger)readData:(NSData *)data           intoBuffer:(uint8_t *)buffer            maxLength:(NSUInteger)length;@end@implementation AFHTTPBodyPart- (id)init {    self = [super init];    if (!self) {        return nil;    }    //进入AFEncapsulationBoundaryPhase状态    [self transitionToNextPhase];    return self;}- (void)dealloc {    if (_inputStream) {        [_inputStream close];        _inputStream = nil;    }}- (NSInputStream *)inputStream {    //数据源NSData/FileURL/NSInputStream都统一成NSInputStream    if (!_inputStream) {        if ([self.body isKindOfClass:[NSData class]]) {            _inputStream = [NSInputStream inputStreamWithData:self.body];        } else if ([self.body isKindOfClass:[NSURL class]]) {            _inputStream = [NSInputStream inputStreamWithURL:self.body];        } else if ([self.body isKindOfClass:[NSInputStream class]]) {            _inputStream = self.body;        }    }    return _inputStream;}- (NSString *)stringForHeaders {    //组装这一部分数据的头部    NSMutableString *headerString = [NSMutableString string];    for (NSString *field in [self.headers allKeys]) {        [headerString appendString:[NSString stringWithFormat:@"%@: %@%@", field, [self.headers valueForKey:field], kAFMultipartFormCRLF]];    }    [headerString appendString:kAFMultipartFormCRLF];    return [NSString stringWithString:headerString];}- (unsigned long long)contentLength {    unsigned long long length = 0;    //若是第一个part,加上开头分隔符(区别是前面没有换行),否则加上中间部分分隔符    NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];    length += [encapsulationBoundaryData length];    //算上这部分header数据的长度    NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];    length += [headersData length];    //在构造AFHTTPBodyPart的时候已经算好_bodyContentLength    length += _bodyContentLength;    //若是最后一个part,要加上结尾分隔符    NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);    length += [closingBoundaryData length];    return length;}- (BOOL)hasBytesAvailable {    // Allows `read:maxLength:` to be called again if `AFMultipartFormFinalBoundary` doesn't fit into the available buffer    if (_phase == AFFinalBoundaryPhase) {        return YES;    }#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wcovered-switch-default"    switch (self.inputStream.streamStatus) {        case NSStreamStatusNotOpen:        case NSStreamStatusOpening:        case NSStreamStatusOpen:        case NSStreamStatusReading:        case NSStreamStatusWriting:            return YES;        case NSStreamStatusAtEnd:        case NSStreamStatusClosed:        case NSStreamStatusError:        default:            return NO;    }#pragma clang diagnostic pop}- (NSInteger)read:(uint8_t *)buffer        maxLength:(NSUInteger)length{    //因为有可能跨几个阶段读取数据,需要一个变量记录总共被读取的数据长度    NSInteger totalNumberOfBytesRead = 0;    if (_phase == AFEncapsulationBoundaryPhase) {        //读取分隔符        NSData *encapsulationBoundaryData = [([self hasInitialBoundary] ? AFMultipartFormInitialBoundary(self.boundary) : AFMultipartFormEncapsulationBoundary(self.boundary)) dataUsingEncoding:self.stringEncoding];        totalNumberOfBytesRead += [self readData:encapsulationBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];    }    if (_phase == AFHeaderPhase) {        //读取header        NSData *headersData = [[self stringForHeaders] dataUsingEncoding:self.stringEncoding];        totalNumberOfBytesRead += [self readData:headersData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];    }    if (_phase == AFBodyPhase) {        //读取body        NSInteger numberOfBytesRead = 0;        numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];        if (numberOfBytesRead == -1) {            return -1;        } else {            totalNumberOfBytesRead += numberOfBytesRead;            if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {                [self transitionToNextPhase];            }        }    }    if (_phase == AFFinalBoundaryPhase) {        //若是最后一个部分,读取最后的分隔符        NSData *closingBoundaryData = ([self hasFinalBoundary] ? [AFMultipartFormFinalBoundary(self.boundary) dataUsingEncoding:self.stringEncoding] : [NSData data]);        totalNumberOfBytesRead += [self readData:closingBoundaryData intoBuffer:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];    }    return totalNumberOfBytesRead;}- (NSInteger)readData:(NSData *)data           intoBuffer:(uint8_t *)buffer            maxLength:(NSUInteger)length{#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wgnu"    //根据阶段和当前阶段已读取的字节数_phaseReadOffset,得到要读的数据范围,写入buffer    NSRange range = NSMakeRange((NSUInteger)_phaseReadOffset, MIN([data length] - ((NSUInteger)_phaseReadOffset), length));    [data getBytes:buffer range:range];#pragma clang diagnostic pop    //记录当前阶段已被读取的字节数    _phaseReadOffset += range.length;    if (((NSUInteger)_phaseReadOffset) >= [data length]) {        //当前阶段的数据读完了,进入下一阶段        [self transitionToNextPhase];    }    return (NSInteger)range.length;}- (BOOL)transitionToNextPhase {    if (![[NSThread currentThread] isMainThread]) {        [self performSelectorOnMainThread:@selector(transitionToNextPhase) withObject:nil waitUntilDone:YES];        return YES;    }    //状态机,切换到下一个阶段#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wcovered-switch-default"    switch (_phase) {        case AFEncapsulationBoundaryPhase:            //进入读取Header阶段            _phase = AFHeaderPhase;            break;        case AFHeaderPhase:            //进入读取body阶段,内容源inputStream要打开了            [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];            [self.inputStream open];            _phase = AFBodyPhase;            break;        case AFBodyPhase:            //读取结尾阶段,只有最后一个part有结尾            [self.inputStream close];            _phase = AFFinalBoundaryPhase;            break;        case AFFinalBoundaryPhase:        default:            //第一次进来时走这里,_phase的初始化在这里……,解析读取分隔符阶段            _phase = AFEncapsulationBoundaryPhase;            break;    }    //_phaseReadOffset记录当前状态已被读取的字节数,进入下一个阶段要清零    _phaseReadOffset = 0;#pragma clang diagnostic pop    return YES;}#pragma mark - NSCopying- (id)copyWithZone:(NSZone *)zone {    AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];    bodyPart.stringEncoding = self.stringEncoding;    bodyPart.headers = self.headers;    bodyPart.bodyContentLength = self.bodyContentLength;    bodyPart.body = self.body;    bodyPart.boundary = self.boundary;    return bodyPart;}@end#pragma mark -@implementation AFJSONRequestSerializer+ (instancetype)serializer {    return [self serializerWithWritingOptions:0];}+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions{    AFJSONRequestSerializer *serializer = [[self alloc] init];    serializer.writingOptions = writingOptions;    return serializer;}#pragma mark - AFURLRequestSerialization- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request                               withParameters:(id)parameters                                        error:(NSError *__autoreleasing *)error{    NSParameterAssert(request);    //参数带在URL的请求方法不处理,json只能放在body里    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {        return [super requestBySerializingRequest:request withParameters:parameters error:error];    }    NSMutableURLRequest *mutableRequest = [request mutableCopy];    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {        if (![request valueForHTTPHeaderField:field]) {            [mutableRequest setValue:value forHTTPHeaderField:field];        }    }];    if (parameters) {        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {            NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));            [mutableRequest setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"];        }        [mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];    }    return mutableRequest;}#pragma mark - NSecureCoding- (id)initWithCoder:(NSCoder *)decoder {    self = [super initWithCoder:decoder];    if (!self) {        return nil;    }    self.writingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writingOptions))] unsignedIntegerValue];    return self;}- (void)encodeWithCoder:(NSCoder *)coder {    [super encodeWithCoder:coder];    [coder encodeInteger:self.writingOptions forKey:NSStringFromSelector(@selector(writingOptions))];}#pragma mark - NSCopying- (id)copyWithZone:(NSZone *)zone {    AFJSONRequestSerializer *serializer = [super copyWithZone:zone];    serializer.writingOptions = self.writingOptions;    return serializer;}@end#pragma mark -@implementation AFPropertyListRequestSerializer+ (instancetype)serializer {    return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0];}+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format                        writeOptions:(NSPropertyListWriteOptions)writeOptions{    AFPropertyListRequestSerializer *serializer = [[self alloc] init];    serializer.format = format;    serializer.writeOptions = writeOptions;    return serializer;}#pragma mark - AFURLRequestSerializer- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request                               withParameters:(id)parameters                                        error:(NSError *__autoreleasing *)error{    NSParameterAssert(request);    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {        return [super requestBySerializingRequest:request withParameters:parameters error:error];    }    NSMutableURLRequest *mutableRequest = [request mutableCopy];    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {        if (![request valueForHTTPHeaderField:field]) {            [mutableRequest setValue:value forHTTPHeaderField:field];        }    }];    if (parameters) {        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {            NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));            [mutableRequest setValue:[NSString stringWithFormat:@"application/x-plist; charset=%@", charset] forHTTPHeaderField:@"Content-Type"];        }        [mutableRequest setHTTPBody:[NSPropertyListSerialization dataWithPropertyList:parameters format:self.format options:self.writeOptions error:error]];    }    return mutableRequest;}#pragma mark - NSecureCoding- (id)initWithCoder:(NSCoder *)decoder {    self = [super initWithCoder:decoder];    if (!self) {        return nil;    }    self.format = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue];    self.writeOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(writeOptions))] unsignedIntegerValue];    return self;}- (void)encodeWithCoder:(NSCoder *)coder {    [super encodeWithCoder:coder];    [coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))];    [coder encodeObject:@(self.writeOptions) forKey:NSStringFromSelector(@selector(writeOptions))];}#pragma mark - NSCopying- (id)copyWithZone:(NSZone *)zone {    AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone];    serializer.format = self.format;    serializer.writeOptions = self.writeOptions;    return serializer;}@end
0 0
原创粉丝点击