Refactoring upload input stream test and AFBufferedInputStreamProvider class
This commit is contained in:
parent
9f00255677
commit
f891264228
4 changed files with 92 additions and 118 deletions
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
|
|
@ -21,7 +21,85 @@
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#import "AFNetworkingTests.h"
|
#import "AFNetworkingTests.h"
|
||||||
#import "AFBufferedInputStreamProvider.h"
|
|
||||||
|
@interface AFBufferedInputStreamProvider : NSObject <NSStreamDelegate>
|
||||||
|
@property (nonatomic, strong) NSData *data;
|
||||||
|
@property (nonatomic, strong) NSInputStream *inputStream;
|
||||||
|
@property (nonatomic, strong) NSOutputStream *outputStream;
|
||||||
|
@property (nonatomic, readonly) NSUInteger bytesWritten;
|
||||||
|
|
||||||
|
- (id)initWithData:(NSData *)data;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AFBufferedInputStreamProvider
|
||||||
|
|
||||||
|
- (id)initWithData:(NSData *)data {
|
||||||
|
self = [super init];
|
||||||
|
if (!self) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data = 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];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
[self cleanup];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cleanup {
|
||||||
|
[self.outputStream close];
|
||||||
|
self.outputStream.delegate = nil;
|
||||||
|
[self.outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||||
|
_outputStream = nil;
|
||||||
|
|
||||||
|
[self.inputStream close];
|
||||||
|
_inputStream = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)writeBytesIfPossible {
|
||||||
|
[self.data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
|
||||||
|
NSInteger bytesWritten = [self.outputStream write:bytes maxLength:[self.data length] - self.bytesWritten];
|
||||||
|
if (bytesWritten < 0) {
|
||||||
|
*stop = YES;
|
||||||
|
} else {
|
||||||
|
_bytesWritten += bytesWritten;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (self.bytesWritten >= [self.data length]) {
|
||||||
|
[self cleanup];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSStreamDelegate
|
||||||
|
|
||||||
|
- (void)stream:(NSStream *)stream
|
||||||
|
handleEvent:(NSStreamEvent)eventCode
|
||||||
|
{
|
||||||
|
if (stream == self.outputStream && (eventCode & NSStreamEventHasSpaceAvailable)) {
|
||||||
|
[self writeBytesIfPossible];
|
||||||
|
} else if (eventCode & NSStreamEventErrorOccurred || eventCode & NSStreamEventEndEncountered) {
|
||||||
|
[self cleanup];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
@interface AFHTTPClientTests : SenTestCase
|
@interface AFHTTPClientTests : SenTestCase
|
||||||
@property (readwrite, nonatomic, strong) AFHTTPClient *client;
|
@property (readwrite, nonatomic, strong) AFHTTPClient *client;
|
||||||
|
|
@ -351,21 +429,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testMultipartUploadDoesNotPrematurelyCloseInputStream {
|
- (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];
|
NSMutableString *mutableString = [NSMutableString string];
|
||||||
NSInputStream *inputStream;
|
for (NSUInteger i = 0; i < 10; i++) {
|
||||||
__block AFBufferedInputStreamProvider *streamProvider = [[AFBufferedInputStreamProvider alloc] initWithData:data inputStream:&inputStream];
|
[mutableString appendString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData *data = [mutableString dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
__block AFBufferedInputStreamProvider *streamProvider = [[AFBufferedInputStreamProvider alloc] initWithData:data];
|
||||||
__block NSUInteger bytesWritten = 0;
|
__block NSUInteger bytesWritten = 0;
|
||||||
|
NSInputStream *inputStream = streamProvider.inputStream;
|
||||||
|
|
||||||
NSMutableURLRequest *request = [self.client multipartFormRequestWithMethod:@"POST" path:@"/post" parameters:@{ @"foo": @"bar" } constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
|
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"];
|
[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) {
|
|
||||||
|
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
|
||||||
|
operation.completionBlock = ^{
|
||||||
bytesWritten = streamProvider.bytesWritten;
|
bytesWritten = streamProvider.bytesWritten;
|
||||||
streamProvider = nil;
|
streamProvider = nil;
|
||||||
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
|
};
|
||||||
bytesWritten = streamProvider.bytesWritten;
|
|
||||||
streamProvider = nil;
|
|
||||||
}];
|
|
||||||
|
|
||||||
[self.client enqueueHTTPRequestOperation:operation];
|
[self.client enqueueHTTPRequestOperation:operation];
|
||||||
expect(operation.isFinished).will.beTruthy();
|
expect(operation.isFinished).will.beTruthy();
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@
|
||||||
29A9CE2117456336002360C8 /* AFJSONRequestOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */; };
|
29A9CE2117456336002360C8 /* AFJSONRequestOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */; };
|
||||||
29A9CE2217456336002360C8 /* 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 */; };
|
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 */; };
|
A7DC62A917592E4800EBEC2F /* AFURLConnectionOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DC62A817592E4800EBEC2F /* AFURLConnectionOperationTests.m */; };
|
||||||
A7DC62AA17592E4800EBEC2F /* 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 */; };
|
AC11A74923B64A3096ACADFC /* libPods-osx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 96A923755B00464187DEDBAF /* libPods-osx.a */; };
|
||||||
|
|
@ -70,8 +69,6 @@
|
||||||
55E73C267F33406A9F92476C /* libPods-ios.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ios.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
F8D62D39175ABF5E00C717C3 /* AFMockURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFMockURLProtocol.h; sourceTree = "<group>"; };
|
||||||
|
|
@ -174,8 +171,6 @@
|
||||||
29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */,
|
29A9CE2017456336002360C8 /* AFJSONRequestOperationTests.m */,
|
||||||
667B268017599C5800764906 /* AFImageRequestOperationTests.m */,
|
667B268017599C5800764906 /* AFImageRequestOperationTests.m */,
|
||||||
2580153A173EB3A70026AA6E /* AFHTTPClientTests.m */,
|
2580153A173EB3A70026AA6E /* AFHTTPClientTests.m */,
|
||||||
A13DC4CD1783470C00F146CE /* AFBufferedInputStreamProvider.h */,
|
|
||||||
A13DC4CE1783470C00F146CE /* AFBufferedInputStreamProvider.m */,
|
|
||||||
);
|
);
|
||||||
name = Tests;
|
name = Tests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -334,7 +329,6 @@
|
||||||
A7DC62A917592E4800EBEC2F /* AFURLConnectionOperationTests.m in Sources */,
|
A7DC62A917592E4800EBEC2F /* AFURLConnectionOperationTests.m in Sources */,
|
||||||
667B268117599C5800764906 /* AFImageRequestOperationTests.m in Sources */,
|
667B268117599C5800764906 /* AFImageRequestOperationTests.m in Sources */,
|
||||||
F8D62D3B175ABF5E00C717C3 /* AFMockURLProtocol.m in Sources */,
|
F8D62D3B175ABF5E00C717C3 /* AFMockURLProtocol.m in Sources */,
|
||||||
A13DC4CF1783470C00F146CE /* AFBufferedInputStreamProvider.m in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue