Added connected state to GCDWebServer

This commit is contained in:
Pierre-Olivier Latour
2014-04-16 00:50:35 -03:00
parent c5ca0f7cee
commit 2d8996b91e
5 changed files with 173 additions and 6 deletions
+4
View File
@@ -48,6 +48,8 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
@protocol GCDWebServerDelegate <NSObject>
@optional
- (void)webServerDidStart:(GCDWebServer*)server;
- (void)webServerDidConnect:(GCDWebServer*)server;
- (void)webServerDidDisconnect:(GCDWebServer*)server;
- (void)webServerDidStop:(GCDWebServer*)server;
@end
@@ -56,6 +58,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
@property(nonatomic, readonly, getter=isRunning) BOOL running;
@property(nonatomic, readonly) NSUInteger port;
@property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active
@property(nonatomic, readonly, getter=isConnected) BOOL connected;
- (instancetype)init;
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
- (void)removeAllHandlers;
@@ -70,6 +73,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
+ (Class)connectionClass; // Default is GCDWebServerConnection
+ (NSString*)serverName; // Default is class name
+ (BOOL)shouldAutomaticallyMapHEADToGET; // Default is YES which means HEAD requests are mapped to GET requests with the response body being discarded
+ (NSTimeInterval)connectedStateCoalescingInterval; // Allows coalescing of fast sequences of -webServerDidConnect: / -webServerDidDisconnect: - Default is 1.0 seconds (set to 0.0 to disable)
@end
@interface GCDWebServer (Extensions)
+84 -1
View File
@@ -44,7 +44,11 @@
@interface GCDWebServer () {
@private
id<GCDWebServerDelegate> __unsafe_unretained _delegate;
dispatch_queue_t _syncQueue;
NSMutableArray* _handlers;
NSInteger _activeConnections; // Accessed only with _syncQueue
BOOL _connected;
CFRunLoopTimerRef _connectedTimer;
NSUInteger _port;
dispatch_source_t _source;
@@ -120,7 +124,7 @@ static void _SignalHandler(int signal) {
@implementation GCDWebServer
@synthesize delegate=_delegate, handlers=_handlers, port=_port;
@synthesize delegate=_delegate, handlers=_handlers, port=_port, connected=_connected;
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
@@ -137,24 +141,99 @@ static void _SignalHandler(int signal) {
GCDWebServerInitializeFunctions();
}
static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
@autoreleasepool {
[(ARC_BRIDGE GCDWebServer*)info _didDisconnect];
}
}
- (instancetype)init {
if ((self = [super init])) {
_syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
_handlers = [[NSMutableArray alloc] init];
CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
if ([[self class] connectedStateCoalescingInterval] > 0.0) {
_connectedTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _ConnectedTimerCallBack, &context);
CFRunLoopAddTimer(CFRunLoopGetMain(), _connectedTimer, kCFRunLoopCommonModes);
}
}
return self;
}
- (void)dealloc {
DCHECK(_connected == NO);
DCHECK(_activeConnections == 0);
_delegate = nil;
if (_source) {
[self stop];
}
if (_connectedTimer) {
CFRunLoopTimerInvalidate(_connectedTimer);
CFRelease(_connectedTimer);
}
ARC_RELEASE(_handlers);
ARC_DISPATCH_RELEASE(_syncQueue);
ARC_DEALLOC(super);
}
- (void)_didConnect {
DCHECK(_connected == NO);
_connected = YES;
LOG_DEBUG(@"Did connect");
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
[_delegate webServerDidConnect:self];
}
}
// Called from any thread
- (void)willStartConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{
DCHECK(_activeConnections >= 0);
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (_connectedTimer) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, HUGE_VAL);
}
if (_connected == NO) {
[self _didConnect];
}
});
}
_activeConnections += 1;
});
}
- (void)_didDisconnect {
DCHECK(_connected == YES);
_connected = NO;
LOG_DEBUG(@"Did disconnect");
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
[_delegate webServerDidDisconnect:self];
}
}
// Called from any thread
- (void)didEndConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{
DCHECK(_activeConnections > 0);
_activeConnections -= 1;
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (_connectedTimer) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, CFAbsoluteTimeGetCurrent() + [[self class] connectedStateCoalescingInterval]);
} else {
[self _didDisconnect];
}
});
}
});
}
- (NSString*)bonjourName {
CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL;
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
@@ -346,6 +425,10 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
return YES;
}
+ (NSTimeInterval)connectedStateCoalescingInterval {
return 1.0;
}
@end
@implementation GCDWebServer (Extensions)
+9 -5
View File
@@ -584,6 +584,9 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
_localAddress = ARC_RETAIN(localAddress);
_remoteAddress = ARC_RETAIN(remoteAddress);
_socket = socket;
LOG_DEBUG(@"Did open connection on socket %i", _socket);
[_server willStartConnection:self];
if (![self open]) {
close(_socket);
@@ -592,7 +595,6 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
}
_opened = YES;
LOG_DEBUG(@"Did open connection on socket %i", _socket);
[self _readRequestHeaders];
}
return self;
@@ -620,10 +622,6 @@ static NSString* _StringFromAddressData(NSData* data) {
}
- (void)dealloc {
if (_opened) {
[self close];
}
int result = close(_socket);
if (result != 0) {
LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno);
@@ -631,6 +629,11 @@ static NSString* _StringFromAddressData(NSData* data) {
LOG_DEBUG(@"Did close connection on socket %i", _socket);
}
if (_opened) {
[self close];
}
[_server didEndConnection:self];
ARC_RELEASE(_server);
ARC_RELEASE(_localAddress);
ARC_RELEASE(_remoteAddress);
@@ -783,6 +786,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
unlink([_responsePath fileSystemRepresentation]);
}
#endif
if (_request) {
LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
} else {
+2
View File
@@ -128,6 +128,8 @@ extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
@interface GCDWebServer ()
@property(nonatomic, readonly) NSArray* handlers;
- (void)willStartConnection:(GCDWebServerConnection*)connection;
- (void)didEndConnection:(GCDWebServerConnection*)connection;
@end
@interface GCDWebServerHandler : NSObject
+74
View File
@@ -52,6 +52,77 @@ typedef enum {
kMode_StreamingResponse
} Mode;
@interface Delegate : NSObject <GCDWebServerDelegate, GCDWebDAVServerDelegate, GCDWebUploaderDelegate>
@end
@implementation Delegate
- (void)_logDelegateCall:(SEL)selector {
fprintf(stdout, "<DELEGATE METHOD \"%s\" CALLED>\n", [NSStringFromSelector(selector) UTF8String]);
}
- (void)webServerDidStart:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)webServerDidConnect:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)webServerDidDisconnect:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)webServerDidStop:(GCDWebServer*)server {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didDownloadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didUploadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didDeleteItemAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)davServer:(GCDWebDAVServer*)server didCreateDirectoryAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didDownloadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didDeleteItemAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
- (void)webUploader:(GCDWebUploader*)uploader didCreateDirectoryAtPath:(NSString*)path {
[self _logDelegateCall:_cmd];
}
@end
int main(int argc, const char* argv[]) {
int result = -1;
@autoreleasepool {
@@ -199,6 +270,8 @@ int main(int argc, const char* argv[]) {
#endif
if (webServer) {
Delegate* delegate = [[Delegate alloc] init];
webServer.delegate = delegate;
if (testDirectory) {
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
result = (int)[webServer runTestsInDirectory:testDirectory withPort:8080];
@@ -214,6 +287,7 @@ int main(int argc, const char* argv[]) {
}
#if !__has_feature(objc_arc)
[webServer release];
[delegate release];
#endif
}
}