Fix AFHTTPBodyPart's read:maxLength: method to no longer treat a negative response from -[NSInputStream hasBytesAvailable] as an indication that the stream has come to an end, and instead examine the stream's state.

This commit is contained in:
Brian Croom 2013-07-02 16:02:16 -04:00
parent d492c4bff5
commit 1136c8bece
5 changed files with 133 additions and 4 deletions

View file

@ -1290,11 +1290,9 @@ typedef enum {
}
if (_phase == AFBodyPhase) {
if ([self.inputStream hasBytesAvailable]) {
bytesRead += [self.inputStream read:&buffer[bytesRead] maxLength:(length - (NSUInteger)bytesRead)];
}
bytesRead += [self.inputStream read:&buffer[bytesRead] maxLength:(length - (NSUInteger)bytesRead)];
if (![self.inputStream hasBytesAvailable]) {
if ([self.inputStream streamStatus] >= NSStreamStatusAtEnd) {
[self transitionToNextPhase];
}
}

View file

@ -0,0 +1,17 @@
//
// AFDelayingInputStreamProvider.h
// AFNetworking Tests
//
// Created by Dev Floater 53 on 2013-07-02.
// Copyright (c) 2013 AFNetworking. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface AFBufferedInputStreamProvider : NSObject
@property (nonatomic, readonly) NSUInteger bytesWritten;
- (id) initWithData:(NSData *)data inputStream:(NSInputStream *__autoreleasing *)outInputStream;
@end

View file

@ -0,0 +1,85 @@
//
// AFDelayingInputStreamProvider.m
// AFNetworking Tests
//
// Created by Dev Floater 53 on 2013-07-02.
// Copyright (c) 2013 AFNetworking. All rights reserved.
//
#import "AFBufferedInputStreamProvider.h"
@interface AFBufferedInputStreamProvider () <NSStreamDelegate>
@property (nonatomic, strong) NSData *sourceData;
@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@end
@implementation AFBufferedInputStreamProvider
- (id) initWithData:(NSData *)data inputStream:(NSInputStream *__autoreleasing *)outInputStream {
NSParameterAssert(outInputStream);
self = [super init];
if (!self) {
return nil;
}
self.sourceData = data;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreateBoundPair(NULL, &readStream, &writeStream, 16);
self.inputStream = CFBridgingRelease(readStream);
self.outputStream = CFBridgingRelease(writeStream);
self.outputStream.delegate = self;
[self.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[self.outputStream open];
*outInputStream = self.inputStream;
return self;
}
- (void)dealloc {
[self cleanup];
}
- (void) cleanup {
[self.outputStream close];
self.outputStream.delegate = nil;
[self.outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
self.outputStream = nil;
[self.inputStream close];
self.inputStream = nil;
}
- (void) writeBytesIfPossible {
while ([self.outputStream hasSpaceAvailable] && self.bytesWritten < [self.sourceData length]) {
const uint8_t *bytes = [self.sourceData bytes];
NSInteger res = [self.outputStream write:bytes+self.bytesWritten maxLength:[self.sourceData length]-self.bytesWritten];
if (res < 0) {
[self cleanup];
return;
}
else {
_bytesWritten += res;
}
}
if (self.bytesWritten >= [self.sourceData length]) {
[self cleanup];
}
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
if (aStream == self.outputStream && (eventCode & NSStreamEventHasSpaceAvailable)) {
[self writeBytesIfPossible];
}
else if (eventCode & NSStreamEventErrorOccurred || eventCode & NSStreamEventEndEncountered) {
[self cleanup];
}
}
@end

View file

@ -21,6 +21,7 @@
// THE SOFTWARE.
#import "AFNetworkingTests.h"
#import "AFBufferedInputStreamProvider.h"
@interface AFHTTPClientTests : SenTestCase
@property (readwrite, nonatomic, strong) AFHTTPClient *client;
@ -334,4 +335,26 @@
expect(operation.error).notTo.equal(NSURLErrorTimedOut);
}
- (void)testMultipartUploadDoesNotPrematurelyCloseInputStream {
NSData *data = [@"Here is some data. Its length is larger than that of the buffer size of the bound stream pair." dataUsingEncoding:NSUTF8StringEncoding];
NSInputStream *inputStream;
__block AFBufferedInputStreamProvider *streamProvider = [[AFBufferedInputStreamProvider alloc] initWithData:data inputStream:&inputStream];
__block NSUInteger bytesWritten = 0;
NSMutableURLRequest *request = [self.client multipartFormRequestWithMethod:@"POST" path:@"/post" parameters:@{ @"foo": @"bar" } constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithInputStream:inputStream name:@"data" fileName:@"string.txt" length:[data length] mimeType:@"text/plain"];
}];
AFHTTPRequestOperation *operation = [self.client HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
bytesWritten = streamProvider.bytesWritten;
streamProvider = nil;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
bytesWritten = streamProvider.bytesWritten;
streamProvider = nil;
}];
[self.client enqueueHTTPRequestOperation:operation];
expect(operation.isFinished).will.beTruthy();
expect(bytesWritten).will.equal([data length]);
}
@end

View file

@ -26,6 +26,7 @@
29A9CE2117456336002360C8 /* AFJSONRequestOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */; };
29A9CE2217456336002360C8 /* AFJSONRequestOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */; };
667B268117599C5800764906 /* AFImageRequestOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 667B268017599C5800764906 /* AFImageRequestOperationTests.m */; };
A13DC4CF1783470C00F146CE /* AFBufferedInputStreamProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A13DC4CE1783470C00F146CE /* AFBufferedInputStreamProvider.m */; };
A7DC62A917592E4800EBEC2F /* AFURLConnectionOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DC62A817592E4800EBEC2F /* AFURLConnectionOperationTests.m */; };
A7DC62AA17592E4800EBEC2F /* AFURLConnectionOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DC62A817592E4800EBEC2F /* AFURLConnectionOperationTests.m */; };
AC11A74923B64A3096ACADFC /* libPods-osx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 96A923755B00464187DEDBAF /* libPods-osx.a */; };
@ -69,6 +70,8 @@
55E73C267F33406A9F92476C /* libPods-ios.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios.a"; sourceTree = BUILT_PRODUCTS_DIR; };
667B268017599C5800764906 /* AFImageRequestOperationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFImageRequestOperationTests.m; sourceTree = "<group>"; };
96A923755B00464187DEDBAF /* libPods-osx.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-osx.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A13DC4CD1783470C00F146CE /* AFBufferedInputStreamProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFBufferedInputStreamProvider.h; sourceTree = "<group>"; };
A13DC4CE1783470C00F146CE /* AFBufferedInputStreamProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFBufferedInputStreamProvider.m; sourceTree = "<group>"; };
A7DC62A817592E4800EBEC2F /* AFURLConnectionOperationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLConnectionOperationTests.m; sourceTree = "<group>"; };
F8C6F281174D2C6200B154D5 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon.png; path = ../Example/Icon.png; sourceTree = "<group>"; };
F8D62D39175ABF5E00C717C3 /* AFMockURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFMockURLProtocol.h; sourceTree = "<group>"; };
@ -171,6 +174,8 @@
29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */,
667B268017599C5800764906 /* AFImageRequestOperationTests.m */,
2580153A173EB3A70026AA6E /* AFHTTPClientTests.m */,
A13DC4CD1783470C00F146CE /* AFBufferedInputStreamProvider.h */,
A13DC4CE1783470C00F146CE /* AFBufferedInputStreamProvider.m */,
);
name = Tests;
sourceTree = "<group>";
@ -329,6 +334,7 @@
A7DC62A917592E4800EBEC2F /* AFURLConnectionOperationTests.m in Sources */,
667B268117599C5800764906 /* AFImageRequestOperationTests.m in Sources */,
F8D62D3B175ABF5E00C717C3 /* AFMockURLProtocol.m in Sources */,
A13DC4CF1783470C00F146CE /* AFBufferedInputStreamProvider.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};