diff --git a/GCDWebServer/Core/GCDWebServerConnection.m b/GCDWebServer/Core/GCDWebServerConnection.m index 263719d..7c08f00 100644 --- a/GCDWebServer/Core/GCDWebServerConnection.m +++ b/GCDWebServer/Core/GCDWebServerConnection.m @@ -280,54 +280,56 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { - (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { GWS_DCHECK([_response hasBody]); - NSError* error = nil; - NSData* data = [_response performReadData:&error]; - if (data) { - if (data.length) { - if (_response.usesChunkedTransferEncoding) { - const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String]; - size_t hexLength = strlen(hexString); - NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)]; - if (chunk == nil) { - GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error); - block(NO); - return; + [_response performReadDataWithCompletion:^(NSData* data, NSError* error) { + + if (data) { + if (data.length) { + if (_response.usesChunkedTransferEncoding) { + const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String]; + size_t hexLength = strlen(hexString); + NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)]; + if (chunk == nil) { + GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error); + block(NO); + 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]; - 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) { - [self _writeBodyWithCompletionBlock:block]; - } else { - block(NO); - } - - }]; - } else { - if (_response.usesChunkedTransferEncoding) { - [self _writeData:_lastChunkData withCompletionBlock:^(BOOL success) { + [self _writeData:data withCompletionBlock:^(BOOL success) { - block(success); + if (success) { + [self _writeBodyWithCompletionBlock:block]; + } else { + block(NO); + } }]; } 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 diff --git a/GCDWebServer/Core/GCDWebServerPrivate.h b/GCDWebServer/Core/GCDWebServerPrivate.h index 3fa5eea..5bb9761 100644 --- a/GCDWebServer/Core/GCDWebServerPrivate.h +++ b/GCDWebServer/Core/GCDWebServerPrivate.h @@ -249,6 +249,6 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; - (void)prepareForReading; - (BOOL)performOpen:(NSError**)error; -- (NSData*)performReadData:(NSError**)error; +- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; - (void)performClose; @end diff --git a/GCDWebServer/Core/GCDWebServerResponse.h b/GCDWebServer/Core/GCDWebServerResponse.h index ee626a6..01ef2ab 100644 --- a/GCDWebServer/Core/GCDWebServerResponse.h +++ b/GCDWebServer/Core/GCDWebServerResponse.h @@ -27,6 +27,12 @@ #import +/** + * 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 * the GCDWebServerResponse and read the HTTP body data to send. @@ -39,6 +45,8 @@ */ @protocol GCDWebServerBodyReader +@required + /** * This method is called before any body data is sent. * @@ -61,6 +69,17 @@ */ - (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 /** diff --git a/GCDWebServer/Core/GCDWebServerResponse.m b/GCDWebServer/Core/GCDWebServerResponse.m index 381f19d..8b4fc4a 100644 --- a/GCDWebServer/Core/GCDWebServerResponse.m +++ b/GCDWebServer/Core/GCDWebServerResponse.m @@ -244,9 +244,14 @@ return [_reader open:error]; } -- (NSData*)performReadData:(NSError**)error { - GWS_DCHECK(_opened); - return [_reader readData:error]; +- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { + if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) { + [_reader asyncReadDataWithCompletion:block]; + } else { + NSError* error = nil; + NSData* data = [_reader readData:&error]; + block(data, error); + } } - (void)performClose {