Added support for asynchronous reading in GCDWebServerBodyReader

This commit is contained in:
Pierre-Olivier Latour
2014-10-14 12:40:19 -07:00
parent a933b2126e
commit c4bf7b11e2
4 changed files with 71 additions and 45 deletions
+43 -41
View File
@@ -280,54 +280,56 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { - (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
GWS_DCHECK([_response hasBody]); GWS_DCHECK([_response hasBody]);
NSError* error = nil; [_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
NSData* data = [_response performReadData:&error];
if (data) { if (data) {
if (data.length) { if (data.length) {
if (_response.usesChunkedTransferEncoding) { if (_response.usesChunkedTransferEncoding) {
const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String]; const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
size_t hexLength = strlen(hexString); size_t hexLength = strlen(hexString);
NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)]; NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
if (chunk == nil) { if (chunk == nil) {
GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error); GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error);
block(NO); block(NO);
return; return;
}
char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
bcopy(hexString, ptr, hexLength);
ptr += hexLength;
*ptr++ = '\r';
*ptr++ = '\n';
bcopy(data.bytes, ptr, data.length);
ptr += data.length;
*ptr++ = '\r';
*ptr = '\n';
data = chunk;
} }
char* ptr = (char*)[(NSMutableData*)chunk mutableBytes]; [self _writeData:data withCompletionBlock:^(BOOL success) {
bcopy(hexString, ptr, hexLength);
ptr += hexLength;
*ptr++ = '\r';
*ptr++ = '\n';
bcopy(data.bytes, ptr, data.length);
ptr += data.length;
*ptr++ = '\r';
*ptr = '\n';
data = chunk;
}
[self _writeData:data withCompletionBlock:^(BOOL success) {
if (success) { if (success) {
[self _writeBodyWithCompletionBlock:block]; [self _writeBodyWithCompletionBlock:block];
} else { } else {
block(NO); block(NO);
} }
}];
} else {
if (_response.usesChunkedTransferEncoding) {
[self _writeData:_lastChunkData withCompletionBlock:^(BOOL success) {
block(success);
}]; }];
} else { } else {
block(YES); if (_response.usesChunkedTransferEncoding) {
[self _writeData:_lastChunkData withCompletionBlock:^(BOOL success) {
block(success);
}];
} else {
block(YES);
}
} }
} else {
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
block(NO);
} }
} else {
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error); }];
block(NO);
}
} }
@end @end
+1 -1
View File
@@ -249,6 +249,6 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
- (void)prepareForReading; - (void)prepareForReading;
- (BOOL)performOpen:(NSError**)error; - (BOOL)performOpen:(NSError**)error;
- (NSData*)performReadData:(NSError**)error; - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
- (void)performClose; - (void)performClose;
@end @end
+19
View File
@@ -27,6 +27,12 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
/**
* The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the
* GCDWebServerBodyReader object when reading data from it asynchronously.
*/
typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* error);
/** /**
* This protocol is used by the GCDWebServerConnection to communicate with * This protocol is used by the GCDWebServerConnection to communicate with
* the GCDWebServerResponse and read the HTTP body data to send. * the GCDWebServerResponse and read the HTTP body data to send.
@@ -39,6 +45,8 @@
*/ */
@protocol GCDWebServerBodyReader <NSObject> @protocol GCDWebServerBodyReader <NSObject>
@required
/** /**
* This method is called before any body data is sent. * This method is called before any body data is sent.
* *
@@ -61,6 +69,17 @@
*/ */
- (void)close; - (void)close;
@optional
/**
* If this method is implemented, it will be preferred over -readData:.
*
* It must call the passed block when data is available, passing a non-empty
* NSData if there is body data available, or an empty NSData there is no more
* body data, or nil on error and pass an NSError along.
*/
- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
@end @end
/** /**
+8 -3
View File
@@ -244,9 +244,14 @@
return [_reader open:error]; return [_reader open:error];
} }
- (NSData*)performReadData:(NSError**)error { - (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
GWS_DCHECK(_opened); if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) {
return [_reader readData:error]; [_reader asyncReadDataWithCompletion:block];
} else {
NSError* error = nil;
NSData* data = [_reader readData:&error];
block(data, error);
}
} }
- (void)performClose { - (void)performClose {