Merge pull request #1044 from AFNetworking/experimental-revert-multipart-stream-provider

Revert Multipart Form Stream Provider
This commit is contained in:
Mattt Thompson 2013-06-01 06:40:03 -07:00
commit c15fae4d30

View file

@ -801,8 +801,8 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
maxLength:(NSUInteger)length; maxLength:(NSUInteger)length;
@end @end
@interface AFMultipartBodyStreamProvider : NSObject @interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
@property (nonatomic, assign) NSUInteger bufferLength; @property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
@property (nonatomic, assign) NSTimeInterval delay; @property (nonatomic, assign) NSTimeInterval delay;
@property (nonatomic, strong) NSInputStream *inputStream; @property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, readonly) unsigned long long contentLength; @property (nonatomic, readonly) unsigned long long contentLength;
@ -817,7 +817,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@interface AFStreamingMultipartFormData () @interface AFStreamingMultipartFormData ()
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request; @property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
@property (readwrite, nonatomic, strong) AFMultipartBodyStreamProvider *bodyStream; @property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
@end @end
@ -836,7 +836,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
self.request = urlRequest; self.request = urlRequest;
self.stringEncoding = encoding; self.stringEncoding = encoding;
self.bodyStream = [[AFMultipartBodyStreamProvider alloc] initWithStringEncoding:encoding]; self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
return self; return self;
} }
@ -968,7 +968,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes - (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
delay:(NSTimeInterval)delay delay:(NSTimeInterval)delay
{ {
self.bodyStream.bufferLength = numberOfBytes; self.bodyStream.numberOfBytesInPacket = numberOfBytes;
self.bodyStream.delay = delay; self.bodyStream.delay = delay;
} }
@ -982,7 +982,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.inputStream]; [self.request setHTTPBodyStream:self.bodyStream];
return self.request; return self.request;
} }
@ -991,7 +991,9 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
#pragma mark - #pragma mark -
@interface AFMultipartBodyStreamProvider () <NSCopying, NSStreamDelegate> @interface AFMultipartBodyStream () <NSCopying>
@property (nonatomic, assign) NSStreamStatus streamStatus;
@property (nonatomic, strong) NSError *streamError;
@property (nonatomic, assign) NSStringEncoding stringEncoding; @property (nonatomic, assign) NSStringEncoding stringEncoding;
@property (nonatomic, strong) NSMutableArray *HTTPBodyParts; @property (nonatomic, strong) NSMutableArray *HTTPBodyParts;
@property (nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator; @property (nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
@ -1000,13 +1002,9 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@property (nonatomic, strong) NSMutableData *buffer; @property (nonatomic, strong) NSMutableData *buffer;
@end @end
static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096; @implementation AFMultipartBodyStream
@synthesize streamStatus = _streamStatus;
@implementation AFMultipartBodyStreamProvider { @synthesize streamError = _streamError;
@private
// Workaround for stream delegates being weakly referenced, but otherwise unowned
__strong id _self;
}
@synthesize stringEncoding = _stringEncoding; @synthesize stringEncoding = _stringEncoding;
@synthesize HTTPBodyParts = _HTTPBodyParts; @synthesize HTTPBodyParts = _HTTPBodyParts;
@synthesize HTTPBodyPartEnumerator = _HTTPBodyPartEnumerator; @synthesize HTTPBodyPartEnumerator = _HTTPBodyPartEnumerator;
@ -1014,7 +1012,7 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
@synthesize inputStream = _inputStream; @synthesize inputStream = _inputStream;
@synthesize outputStream = _outputStream; @synthesize outputStream = _outputStream;
@synthesize buffer = _buffer; @synthesize buffer = _buffer;
@synthesize bufferLength = _numberOfBytesInPacket; @synthesize numberOfBytesInPacket = _numberOfBytesInPacket;
@synthesize delay = _delay; @synthesize delay = _delay;
- (id)initWithStringEncoding:(NSStringEncoding)encoding { - (id)initWithStringEncoding:(NSStringEncoding)encoding {
@ -1025,16 +1023,9 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
self.stringEncoding = encoding; self.stringEncoding = encoding;
self.HTTPBodyParts = [NSMutableArray array]; self.HTTPBodyParts = [NSMutableArray array];
self.bufferLength = NSIntegerMax; self.numberOfBytesInPacket = NSIntegerMax;
self.buffer = [[NSMutableData alloc] init];
self.bufferLength = AFMultipartBodyStreamProviderDefaultBufferLength;
return self;
}
- (void)dealloc { return self;
_outputStream.delegate = nil;
} }
- (void)setInitialAndFinalBoundaries { - (void)setInitialAndFinalBoundaries {
@ -1053,116 +1044,80 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
[self.HTTPBodyParts addObject:bodyPart]; [self.HTTPBodyParts addObject:bodyPart];
} }
- (NSInputStream *)inputStream {
if (_inputStream == nil) {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreateBoundPair(NULL, &readStream, &writeStream, (NSInteger)self.bufferLength);
_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];
_self = self;
}
return _inputStream;
}
- (BOOL)isEmpty { - (BOOL)isEmpty {
return [self.HTTPBodyParts count] == 0; return [self.HTTPBodyParts count] == 0;
} }
#pragma mark - NSStreamDelegate #pragma mark - NSInputStream
- (NSInteger)read:(uint8_t *)buffer
// This retry works around a nasty problem in which mutli-part uploads will fail due to the stream delegate being sent a `NSStreamEventHasSpaceAvailable` event before the input stream has finished opening. maxLength:(NSUInteger)length
// This workaround simply replays the event after allowing the run-loop to cycle, providing enough time for the input stream to finish opening. It appears that this bug is in the CFNetwork layer. {
// See: https://github.com/AFNetworking/AFNetworking/issues/948 if ([self streamStatus] == NSStreamStatusClosed) {
- (void)retryWrite:(NSStream *)stream { return 0;
[self stream:stream handleEvent:NSStreamEventHasSpaceAvailable];
}
- (void)stream:(NSStream *)stream
handleEvent:(NSStreamEvent)eventCode {
if (eventCode & NSStreamEventHasSpaceAvailable) {
if (self.inputStream.streamStatus < NSStreamStatusOpen) {
[self performSelector:@selector(retryWrite:) withObject:stream afterDelay:0.1];
} else {
[self handleOutputStreamSpaceAvailable];
}
} }
} NSInteger bytesRead = 0;
- (void)handleOutputStreamSpaceAvailable { while ((NSUInteger)bytesRead < MIN(length, self.numberOfBytesInPacket)) {
while ([_outputStream hasSpaceAvailable]) { if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
if ([_buffer length] > 0) { if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
NSInteger numberOfBytesWritten = [_outputStream write:(uint8_t const *)[_buffer bytes] maxLength:[_buffer length]]; break;
if (numberOfBytesWritten < 0) {
[self close];
return;
} }
[_buffer replaceBytesInRange:NSMakeRange(0, (NSUInteger)numberOfBytesWritten) withBytes:NULL length:0];
} else { } else {
if (!self.currentHTTPBodyPart) { bytesRead += [self.currentHTTPBodyPart read:&buffer[bytesRead] maxLength:(length - (NSUInteger)bytesRead)];
if (!self.HTTPBodyPartEnumerator) {
self.HTTPBodyPartEnumerator = [self.HTTPBodyParts objectEnumerator];
}
self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject];
}
if (!self.currentHTTPBodyPart) {
[self close];
return;
}
[_buffer setLength:self.bufferLength];
NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:(uint8_t *)[_buffer mutableBytes] maxLength:[_buffer length]];
if (numberOfBytesRead < 0) {
[self close];
return;
}
[_buffer setLength:(NSUInteger)numberOfBytesRead];
if (numberOfBytesRead == 0) {
self.currentHTTPBodyPart = nil;
}
if (self.delay > 0.0f) { if (self.delay > 0.0f) {
[NSThread sleepForTimeInterval:self.delay]; [NSThread sleepForTimeInterval:self.delay];
} }
} }
} }
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 {
NSOutputStream *outputStream = self.outputStream; self.streamStatus = NSStreamStatusClosed;
[outputStream close];
outputStream.delegate = nil;
// Workaround for a race condition in CFStream _CFStreamCopyRunLoopsAndModes. This outputstream needs to be retained just a little longer.
// See: https://github.com/AFNetworking/AFNetworking/issues/907
NSTimeInterval delay = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
outputStream.delegate = nil;
});
_self = 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) {
@ -1172,10 +1127,26 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
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 {
AFMultipartBodyStreamProvider *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding]; AFMultipartBodyStream *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]];
@ -1191,12 +1162,10 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
#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> {
@ -1301,7 +1270,6 @@ typedef enum {
return NO; return NO;
} }
#pragma clang diagnostic pop #pragma clang diagnostic pop
} }
- (NSInteger)read:(uint8_t *)buffer - (NSInteger)read:(uint8_t *)buffer
@ -1365,9 +1333,6 @@ 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;
@ -1381,9 +1346,8 @@ typedef enum {
_phase = AFFinalBoundaryPhase; _phase = AFFinalBoundaryPhase;
break; break;
case AFFinalBoundaryPhase: case AFFinalBoundaryPhase:
case AFCompletedPhase:
default: default:
_phase = AFCompletedPhase; _phase = AFEncapsulationBoundaryPhase;
break; break;
} }
_phaseReadOffset = 0; _phaseReadOffset = 0;