Created
February 26, 2015 22:53
-
-
Save KJTsanaktsidis/12ae946664fc3c1685d2 to your computer and use it in GitHub Desktop.
GCDWebServerAsyncPushResponse
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "GCDWebServerResponse.h" | |
#ifndef mobile_app_2_GCDWebServerAsyncPushResponse_h | |
#define mobile_app_2_GCDWebServerAsyncPushResponse_h | |
/** | |
* This response type lets you push data into it asynchronously | |
* and have it end up on the connection, as opposed to GCDWebServerStreamedResponse | |
* which requires you to provide data when asked | |
*/ | |
@interface GCDWebServerAsyncPushResponse : GCDWebServerResponse | |
/** | |
* The designated initialiser for the class | |
*/ | |
- (instancetype)initWithContentType:(NSString*)type; | |
/** | |
* This method is the way you push data into the response | |
* Unlike the other response types, calling it with empty data | |
* does nothing special; call the completeWithError: selector | |
* to signal completion | |
*/ | |
- (void)sendWithData:(NSData*)data; | |
/** | |
* And this method is called to signal completion | |
* Pass nil for the error if there are no problems | |
*/ | |
- (void)completeWithError:(NSError*)error; | |
@end | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "GCDWebServerAsyncPushResponse.h" | |
@interface GCDWebServerAsyncPushResponse () { | |
@private | |
NSMutableData* _buffer; | |
GCDWebServerBodyReaderCompletionBlock _pendingReadBlock; | |
NSError* _pendingError; | |
BOOL _haveFinished; | |
dispatch_queue_t _serialQueue; | |
} | |
@end | |
@implementation GCDWebServerAsyncPushResponse | |
- (instancetype)initWithContentType:(NSString *)type { | |
if ((self = [super init])) { | |
self.contentType = type; | |
_buffer = [[NSMutableData alloc] init]; | |
_serialQueue = dispatch_queue_create("GCDWebServerAsyncPushResponse", NULL); | |
_haveFinished = NO; | |
_pendingError = nil; | |
_pendingReadBlock = nil; | |
} | |
return self; | |
} | |
- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { | |
//The server has asked us for data | |
//Provide the contents of the buffer if we can, otherwise defer providing the server | |
//data until later | |
//Make sure all access to the internal state of the response is serialised | |
dispatch_sync(_serialQueue, ^{ | |
[self handleReadData:block]; | |
}); | |
} | |
- (void) handleReadData:(GCDWebServerBodyReaderCompletionBlock)block { | |
if (_pendingError != nil) { | |
//Case #1: we have a problem | |
block(nil, _pendingError); | |
_pendingReadBlock = nil; | |
} else if (_buffer.length > 0){ | |
//Case #2: we have data | |
block([_buffer copy], nil); | |
_buffer.length = 0; //clear the buffer | |
_pendingReadBlock = nil; | |
} else if (_haveFinished == YES) { | |
//Case #3: we are done, and the buffer is now empty | |
block([[NSData alloc] init], nil); | |
_pendingReadBlock = nil; | |
} else { | |
//Case #4: we have none of these things; deferr our response until later | |
_pendingReadBlock = block; | |
} | |
} | |
//This guy should only be called from the serial queue | |
- (void)pumpPendingRead { | |
if (_pendingReadBlock != nil) { | |
[self handleReadData:_pendingReadBlock]; | |
} | |
} | |
- (void)sendWithData:(NSData *)data { | |
dispatch_sync(_serialQueue, ^{ | |
[_buffer appendData:data]; | |
[self pumpPendingRead]; | |
}); | |
} | |
- (void)completeWithError:(NSError *)error { | |
dispatch_sync(_serialQueue, ^{ | |
if (error == nil) { | |
_haveFinished = YES; | |
} else { | |
_pendingError = error; | |
} | |
[self pumpPendingRead]; | |
}); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment