Merge branch '781-mime-stream-pair' of git://github.com/plausiblelabs/AFNetworking
This commit is contained in:
commit
f7d98aac9c
1 changed files with 109 additions and 82 deletions
|
|
@ -777,7 +777,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
maxLength:(NSUInteger)length;
|
maxLength:(NSUInteger)length;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
|
@interface AFMultipartBodyStreamProvider : NSObject
|
||||||
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
|
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
|
||||||
@property (nonatomic, assign) NSTimeInterval delay;
|
@property (nonatomic, assign) NSTimeInterval delay;
|
||||||
@property (nonatomic, readonly) unsigned long long contentLength;
|
@property (nonatomic, readonly) unsigned long long contentLength;
|
||||||
|
|
@ -786,13 +786,15 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
- (id)initWithStringEncoding:(NSStringEncoding)encoding;
|
- (id)initWithStringEncoding:(NSStringEncoding)encoding;
|
||||||
- (void)setInitialAndFinalBoundaries;
|
- (void)setInitialAndFinalBoundaries;
|
||||||
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
|
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
|
||||||
|
|
||||||
|
- (NSInputStream *)inputStream;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
@interface AFStreamingMultipartFormData ()
|
@interface AFStreamingMultipartFormData ()
|
||||||
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
|
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
|
||||||
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
|
@property (readwrite, nonatomic, strong) AFMultipartBodyStreamProvider *bodyStream;
|
||||||
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
|
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
@ -811,7 +813,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
|
|
||||||
self.request = urlRequest;
|
self.request = urlRequest;
|
||||||
self.stringEncoding = encoding;
|
self.stringEncoding = encoding;
|
||||||
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
|
self.bodyStream = [[AFMultipartBodyStreamProvider alloc] initWithStringEncoding:encoding];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
@ -931,7 +933,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
|
|
||||||
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"];
|
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"];
|
||||||
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
|
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
|
||||||
[self.request setHTTPBodyStream:self.bodyStream];
|
[self.request setHTTPBodyStream:self.bodyStream.inputStream];
|
||||||
|
|
||||||
return self.request;
|
return self.request;
|
||||||
}
|
}
|
||||||
|
|
@ -940,7 +942,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
@interface AFMultipartBodyStream () <NSCopying>
|
@interface AFMultipartBodyStreamProvider () <NSCopying, NSStreamDelegate>
|
||||||
@property (nonatomic, assign) NSStreamStatus streamStatus;
|
@property (nonatomic, assign) NSStreamStatus streamStatus;
|
||||||
@property (nonatomic, strong) NSError *streamError;
|
@property (nonatomic, strong) NSError *streamError;
|
||||||
|
|
||||||
|
|
@ -950,7 +952,15 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
@property (nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
|
@property (nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation AFMultipartBodyStream
|
static const NSUInteger AFMultipartBodyStreamProviderBufferSize = 4096;
|
||||||
|
|
||||||
|
@implementation AFMultipartBodyStreamProvider {
|
||||||
|
NSInputStream *_inputStream;
|
||||||
|
NSOutputStream *_outputStream;
|
||||||
|
NSMutableData *_buffer;
|
||||||
|
|
||||||
|
id _keepalive;
|
||||||
|
}
|
||||||
@synthesize streamStatus = _streamStatus;
|
@synthesize streamStatus = _streamStatus;
|
||||||
@synthesize streamError = _streamError;
|
@synthesize streamError = _streamError;
|
||||||
@synthesize stringEncoding = _stringEncoding;
|
@synthesize stringEncoding = _stringEncoding;
|
||||||
|
|
@ -970,9 +980,15 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
self.HTTPBodyParts = [NSMutableArray array];
|
self.HTTPBodyParts = [NSMutableArray array];
|
||||||
self.numberOfBytesInPacket = NSIntegerMax;
|
self.numberOfBytesInPacket = NSIntegerMax;
|
||||||
|
|
||||||
|
_buffer = [[NSMutableData alloc] init];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
_outputStream.delegate = nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setInitialAndFinalBoundaries {
|
- (void)setInitialAndFinalBoundaries {
|
||||||
if ([self.HTTPBodyParts count] > 0) {
|
if ([self.HTTPBodyParts count] > 0) {
|
||||||
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||||
|
|
@ -989,80 +1005,100 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
[self.HTTPBodyParts addObject:bodyPart];
|
[self.HTTPBodyParts addObject:bodyPart];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSInputStream *)inputStream {
|
||||||
|
if(_inputStream == nil) {
|
||||||
|
CFReadStreamRef readStream;
|
||||||
|
CFWriteStreamRef writeStream;
|
||||||
|
CFStreamCreateBoundPair(NULL, &readStream, &writeStream, AFMultipartBodyStreamProviderBufferSize);
|
||||||
|
_inputStream = CFBridgingRelease(readStream);
|
||||||
|
_outputStream = CFBridgingRelease(writeStream);
|
||||||
|
|
||||||
|
_outputStream.delegate = self;
|
||||||
|
if([NSThread isMainThread]) {
|
||||||
|
[_outputStream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||||
|
[_outputStream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
[_outputStream open];
|
||||||
|
_keepalive = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)isEmpty {
|
- (BOOL)isEmpty {
|
||||||
return [self.HTTPBodyParts count] == 0;
|
return [self.HTTPBodyParts count] == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - NSInputStream
|
#pragma mark - NSStreamDelegate
|
||||||
|
|
||||||
- (NSInteger)read:(uint8_t *)buffer
|
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
|
||||||
maxLength:(NSUInteger)length
|
if(eventCode & NSStreamEventHasSpaceAvailable) {
|
||||||
{
|
[self handleOutputStreamSpaceAvailable];
|
||||||
if ([self streamStatus] == NSStreamStatusClosed) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
NSInteger bytesRead = 0;
|
}
|
||||||
|
|
||||||
while ((NSUInteger)bytesRead < MIN(length, self.numberOfBytesInPacket)) {
|
- (void)handleOutputStreamSpaceAvailable {
|
||||||
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
|
while([_outputStream hasSpaceAvailable]) {
|
||||||
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
|
if([_buffer length] > 0) {
|
||||||
break;
|
NSInteger ret = [_outputStream write: [_buffer bytes] maxLength: [_buffer length]];
|
||||||
|
if(ret < 0) {
|
||||||
|
/* I don't think an error should ever actually happen with a bound pair.
|
||||||
|
* If it does, we'll just close the stream and give up. */
|
||||||
|
[self close];
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
/* Delete the written bytes from the buffer. */
|
||||||
|
[_buffer replaceBytesInRange: NSMakeRange(0, ret) withBytes: NULL length: 0];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bytesRead += [self.currentHTTPBodyPart read:&buffer[bytesRead] maxLength:(length - (NSUInteger)bytesRead)];
|
/* Refill the buffer. */
|
||||||
if (self.delay > 0.0f) {
|
|
||||||
[NSThread sleepForTimeInterval:self.delay];
|
/* Make sure the current body part is valid. */
|
||||||
|
if(self.currentHTTPBodyPart == nil) {
|
||||||
|
if(self.HTTPBodyPartEnumerator == nil) {
|
||||||
|
self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
|
||||||
|
}
|
||||||
|
self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the current part is still nil, then it's the end of the road: close the stream and bail. */
|
||||||
|
if(self.currentHTTPBodyPart == nil) {
|
||||||
|
[self close];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read some data. */
|
||||||
|
[_buffer setLength: AFMultipartBodyStreamProviderBufferSize];
|
||||||
|
NSInteger ret = [self.currentHTTPBodyPart read: [_buffer mutableBytes] maxLength: [_buffer length]];
|
||||||
|
if(ret < 0) {
|
||||||
|
/* Not sure how to handle an error currently. Close the output stream and bail out. */
|
||||||
|
[self close];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resize the buffer to how much was actually read. */
|
||||||
|
[_buffer setLength: ret];
|
||||||
|
|
||||||
|
/* If we hit EOF, invalidate the current body part so the next pass through will find a new one. */
|
||||||
|
if(ret == 0) {
|
||||||
|
self.currentHTTPBodyPart = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fall off the end. The next loop through will get data out of the buffer. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)getBuffer:(__unused uint8_t **)buffer
|
|
||||||
length:(__unused NSUInteger *)len
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)hasBytesAvailable {
|
|
||||||
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 {
|
- (void)close {
|
||||||
self.streamStatus = NSStreamStatusClosed;
|
[_outputStream close];
|
||||||
|
_outputStream.delegate = nil;
|
||||||
|
_keepalive = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)propertyForKey:(__unused NSString *)key {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)setProperty:(__unused id)property
|
|
||||||
forKey:(__unused NSString *)key
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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)contentLength {
|
||||||
unsigned long long length = 0;
|
unsigned long long length = 0;
|
||||||
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||||
|
|
@ -1072,26 +1108,10 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Undocumented CFReadStream Bridged Methods
|
|
||||||
|
|
||||||
- (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
|
#pragma mark - NSCopying
|
||||||
|
|
||||||
-(id)copyWithZone:(NSZone *)zone {
|
-(id)copyWithZone:(NSZone *)zone {
|
||||||
AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
|
AFMultipartBodyStreamProvider *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
|
||||||
|
|
||||||
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||||
[bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
|
[bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
|
||||||
|
|
@ -1107,10 +1127,12 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
AFInitialPhase = 0,
|
||||||
AFEncapsulationBoundaryPhase = 1,
|
AFEncapsulationBoundaryPhase = 1,
|
||||||
AFHeaderPhase = 2,
|
AFHeaderPhase = 2,
|
||||||
AFBodyPhase = 3,
|
AFBodyPhase = 3,
|
||||||
AFFinalBoundaryPhase = 4,
|
AFFinalBoundaryPhase = 4,
|
||||||
|
AFCompletedPhase = 5,
|
||||||
} AFHTTPBodyPartReadPhase;
|
} AFHTTPBodyPartReadPhase;
|
||||||
|
|
||||||
@interface AFHTTPBodyPart () <NSCopying> {
|
@interface AFHTTPBodyPart () <NSCopying> {
|
||||||
|
|
@ -1139,6 +1161,7 @@ typedef enum {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_phase = AFInitialPhase;
|
||||||
[self transitionToNextPhase];
|
[self transitionToNextPhase];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
@ -1273,6 +1296,9 @@ typedef enum {
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wcovered-switch-default"
|
#pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||||
switch (_phase) {
|
switch (_phase) {
|
||||||
|
case AFInitialPhase:
|
||||||
|
_phase = AFEncapsulationBoundaryPhase;
|
||||||
|
break;
|
||||||
case AFEncapsulationBoundaryPhase:
|
case AFEncapsulationBoundaryPhase:
|
||||||
_phase = AFHeaderPhase;
|
_phase = AFHeaderPhase;
|
||||||
break;
|
break;
|
||||||
|
|
@ -1286,8 +1312,9 @@ typedef enum {
|
||||||
_phase = AFFinalBoundaryPhase;
|
_phase = AFFinalBoundaryPhase;
|
||||||
break;
|
break;
|
||||||
case AFFinalBoundaryPhase:
|
case AFFinalBoundaryPhase:
|
||||||
|
case AFCompletedPhase:
|
||||||
default:
|
default:
|
||||||
_phase = AFEncapsulationBoundaryPhase;
|
_phase = AFCompletedPhase;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_phaseReadOffset = 0;
|
_phaseReadOffset = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue