Skip to content

Instantly share code, notes, and snippets.

@KJTsanaktsidis
Created February 26, 2015 22:53
Show Gist options
  • Save KJTsanaktsidis/12ae946664fc3c1685d2 to your computer and use it in GitHub Desktop.
Save KJTsanaktsidis/12ae946664fc3c1685d2 to your computer and use it in GitHub Desktop.
GCDWebServerAsyncPushResponse
#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
#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