From 514c09dc3909dd0a10d03d256fa20ebbe8630d24 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Latour Date: Tue, 14 Oct 2014 12:40:51 -0700 Subject: [PATCH] Added truly asynchronous support to GCDWebServerStreamedResponse --- .../Responses/GCDWebServerStreamedResponse.h | 25 ++++++++++++++++++- .../Responses/GCDWebServerStreamedResponse.m | 20 ++++++++++++--- Mac/main.m | 20 ++++++++++++++- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/GCDWebServer/Responses/GCDWebServerStreamedResponse.h b/GCDWebServer/Responses/GCDWebServerStreamedResponse.h index b237fae..4325381 100644 --- a/GCDWebServer/Responses/GCDWebServerStreamedResponse.h +++ b/GCDWebServer/Responses/GCDWebServerStreamedResponse.h @@ -34,6 +34,19 @@ */ typedef NSData* (^GCDWebServerStreamBlock)(NSError** error); +/** + * The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock + * except the streamed data can be returned at a later time allowing for + * truly asynchronous generation of the data. + * + * The block must return empty NSData when done or nil on error and set the + * "error" argument which is guaranteed to be non-NULL. + * + * The block must eventually call "completionBlock" passing the streamed data + * if any and the error if applicable. + */ +typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock); + /** * The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams * the body of the HTTP response using a GCD block. @@ -46,8 +59,18 @@ typedef NSData* (^GCDWebServerStreamBlock)(NSError** error); + (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; /** - * This method is the designated initializer for the class. + * Creates a response with async streamed data and a given content type. + */ ++ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; + +/** + * Initializes a response with streamed data and a given content type. */ - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; +/** + * This method is the designated initializer for the class. + */ +- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; + @end diff --git a/GCDWebServer/Responses/GCDWebServerStreamedResponse.m b/GCDWebServer/Responses/GCDWebServerStreamedResponse.m index 2663816..fa3a941 100644 --- a/GCDWebServer/Responses/GCDWebServerStreamedResponse.m +++ b/GCDWebServer/Responses/GCDWebServerStreamedResponse.m @@ -29,7 +29,7 @@ @interface GCDWebServerStreamedResponse () { @private - GCDWebServerStreamBlock _block; + GCDWebServerAsyncStreamBlock _block; } @end @@ -39,7 +39,21 @@ return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]); } ++ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { + return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type asyncStreamBlock:block]); +} + - (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { + return [self initWithContentType:type asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { + + NSError* error = nil; + NSData* data = block(&error); + completionBlock(data, error); + + }]; +} + +- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { if ((self = [super init])) { _block = [block copy]; @@ -54,8 +68,8 @@ ARC_DEALLOC(super); } -- (NSData*)readData:(NSError**)error { - return _block(error); +- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { + _block(block); } - (NSString*)description { diff --git a/Mac/main.m b/Mac/main.m index baf297d..f0a5d97 100644 --- a/Mac/main.m +++ b/Mac/main.m @@ -311,7 +311,7 @@ int main(int argc, const char* argv[]) { fprintf(stdout, "Running in Streaming Response mode"); webServer = [[GCDWebServer alloc] init]; [webServer addHandlerForMethod:@"GET" - path:@"/" + path:@"/sync" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { @@ -327,6 +327,24 @@ int main(int argc, const char* argv[]) { }]; + }]; + [webServer addHandlerForMethod:@"GET" + path:@"/async" + requestClass:[GCDWebServerRequest class] + processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { + + __block int countDown = 10; + return [GCDWebServerStreamedResponse responseWithContentType:@"text/plain" asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + NSData* data = countDown ? [[NSString stringWithFormat:@"%i\n", countDown--] dataUsingEncoding:NSUTF8StringEncoding] : [NSData data]; + completionBlock(data, nil); + + }); + + }]; + }]; break; }