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;
@end
@interface AFMultipartBodyStreamProvider : NSObject
@property (nonatomic, assign) NSUInteger bufferLength;
@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
@property (nonatomic, assign) NSTimeInterval delay;
@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, readonly) unsigned long long contentLength;
@ -817,7 +817,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@interface AFStreamingMultipartFormData ()
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
@property (readwrite, nonatomic, strong) AFMultipartBodyStreamProvider *bodyStream;
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
@end
@ -836,7 +836,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
self.request = urlRequest;
self.stringEncoding = encoding;
self.bodyStream = [[AFMultipartBodyStreamProvider alloc] initWithStringEncoding:encoding];
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
return self;
}
@ -968,7 +968,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
delay:(NSTimeInterval)delay
{
self.bodyStream.bufferLength = numberOfBytes;
self.bodyStream.numberOfBytesInPacket = numberOfBytes;
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:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
[self.request setHTTPBodyStream:self.bodyStream.inputStream];
[self.request setHTTPBodyStream:self.bodyStream];
return self.request;
}
@ -991,7 +991,9 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
#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, strong) NSMutableArray *HTTPBodyParts;
@property (nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
@ -1000,13 +1002,9 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
@property (nonatomic, strong) NSMutableData *buffer;
@end
static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
@implementation AFMultipartBodyStreamProvider {
@private
// Workaround for stream delegates being weakly referenced, but otherwise unowned
__strong id _self;
}
@implementation AFMultipartBodyStream
@synthesize streamStatus = _streamStatus;
@synthesize streamError = _streamError;
@synthesize stringEncoding = _stringEncoding;
@synthesize HTTPBodyParts = _HTTPBodyParts;
@synthesize HTTPBodyPartEnumerator = _HTTPBodyPartEnumerator;
@ -1014,7 +1012,7 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
@synthesize inputStream = _inputStream;
@synthesize outputStream = _outputStream;
@synthesize buffer = _buffer;
@synthesize bufferLength = _numberOfBytesInPacket;
@synthesize numberOfBytesInPacket = _numberOfBytesInPacket;
@synthesize delay = _delay;
- (id)initWithStringEncoding:(NSStringEncoding)encoding {
@ -1025,16 +1023,9 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
self.stringEncoding = encoding;
self.HTTPBodyParts = [NSMutableArray array];
self.bufferLength = NSIntegerMax;
self.buffer = [[NSMutableData alloc] init];
self.bufferLength = AFMultipartBodyStreamProviderDefaultBufferLength;
return self;
}
self.numberOfBytesInPacket = NSIntegerMax;
- (void)dealloc {
_outputStream.delegate = nil;
return self;
}
- (void)setInitialAndFinalBoundaries {
@ -1053,116 +1044,80 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
[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 {
return [self.HTTPBodyParts count] == 0;
}
#pragma mark - NSStreamDelegate
#pragma mark - NSInputStream
// 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.
// 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
- (void)retryWrite:(NSStream *)stream {
[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)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
if ([self streamStatus] == NSStreamStatusClosed) {
return 0;
}
}
NSInteger bytesRead = 0;
- (void)handleOutputStreamSpaceAvailable {
while ([_outputStream hasSpaceAvailable]) {
if ([_buffer length] > 0) {
NSInteger numberOfBytesWritten = [_outputStream write:(uint8_t const *)[_buffer bytes] maxLength:[_buffer length]];
if (numberOfBytesWritten < 0) {
[self close];
return;
while ((NSUInteger)bytesRead < MIN(length, self.numberOfBytesInPacket)) {
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
break;
}
[_buffer replaceBytesInRange:NSMakeRange(0, (NSUInteger)numberOfBytesWritten) withBytes:NULL length:0];
} else {
if (!self.currentHTTPBodyPart) {
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;
}
bytesRead += [self.currentHTTPBodyPart read:&buffer[bytesRead] maxLength:(length - (NSUInteger)bytesRead)];
if (self.delay > 0.0f) {
[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 {
NSOutputStream *outputStream = self.outputStream;
[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;
self.streamStatus = NSStreamStatusClosed;
}
- (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 length = 0;
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
@ -1172,10 +1127,26 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
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
- (id)copyWithZone:(NSZone *)zone {
AFMultipartBodyStreamProvider *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
-(id)copyWithZone:(NSZone *)zone {
AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
[bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
@ -1191,12 +1162,10 @@ static const NSUInteger AFMultipartBodyStreamProviderDefaultBufferLength = 4096;
#pragma mark -
typedef enum {
AFInitialPhase = 0,
AFEncapsulationBoundaryPhase = 1,
AFHeaderPhase = 2,
AFBodyPhase = 3,
AFFinalBoundaryPhase = 4,
AFCompletedPhase = 5,
} AFHTTPBodyPartReadPhase;
@interface AFHTTPBodyPart () <NSCopying> {
@ -1301,7 +1270,6 @@ typedef enum {
return NO;
}
#pragma clang diagnostic pop
}
- (NSInteger)read:(uint8_t *)buffer
@ -1365,9 +1333,6 @@ typedef enum {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcovered-switch-default"
switch (_phase) {
case AFInitialPhase:
_phase = AFEncapsulationBoundaryPhase;
break;
case AFEncapsulationBoundaryPhase:
_phase = AFHeaderPhase;
break;
@ -1381,9 +1346,8 @@ typedef enum {
_phase = AFFinalBoundaryPhase;
break;
case AFFinalBoundaryPhase:
case AFCompletedPhase:
default:
_phase = AFCompletedPhase;
_phase = AFEncapsulationBoundaryPhase;
break;
}
_phaseReadOffset = 0;