Added connected state to GCDWebServer
This commit is contained in:
@ -48,6 +48,8 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
|
|||||||
@protocol GCDWebServerDelegate <NSObject>
|
@protocol GCDWebServerDelegate <NSObject>
|
||||||
@optional
|
@optional
|
||||||
- (void)webServerDidStart:(GCDWebServer*)server;
|
- (void)webServerDidStart:(GCDWebServer*)server;
|
||||||
|
- (void)webServerDidConnect:(GCDWebServer*)server;
|
||||||
|
- (void)webServerDidDisconnect:(GCDWebServer*)server;
|
||||||
- (void)webServerDidStop:(GCDWebServer*)server;
|
- (void)webServerDidStop:(GCDWebServer*)server;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -56,6 +58,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
|
|||||||
@property(nonatomic, readonly, getter=isRunning) BOOL running;
|
@property(nonatomic, readonly, getter=isRunning) BOOL running;
|
||||||
@property(nonatomic, readonly) NSUInteger port;
|
@property(nonatomic, readonly) NSUInteger port;
|
||||||
@property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active
|
@property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active
|
||||||
|
@property(nonatomic, readonly, getter=isConnected) BOOL connected;
|
||||||
- (instancetype)init;
|
- (instancetype)init;
|
||||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
||||||
- (void)removeAllHandlers;
|
- (void)removeAllHandlers;
|
||||||
@ -70,6 +73,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
|
|||||||
+ (Class)connectionClass; // Default is GCDWebServerConnection
|
+ (Class)connectionClass; // Default is GCDWebServerConnection
|
||||||
+ (NSString*)serverName; // Default is class name
|
+ (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
|
+ (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
|
@end
|
||||||
|
|
||||||
@interface GCDWebServer (Extensions)
|
@interface GCDWebServer (Extensions)
|
||||||
|
|||||||
@ -44,7 +44,11 @@
|
|||||||
@interface GCDWebServer () {
|
@interface GCDWebServer () {
|
||||||
@private
|
@private
|
||||||
id<GCDWebServerDelegate> __unsafe_unretained _delegate;
|
id<GCDWebServerDelegate> __unsafe_unretained _delegate;
|
||||||
|
dispatch_queue_t _syncQueue;
|
||||||
NSMutableArray* _handlers;
|
NSMutableArray* _handlers;
|
||||||
|
NSInteger _activeConnections; // Accessed only with _syncQueue
|
||||||
|
BOOL _connected;
|
||||||
|
CFRunLoopTimerRef _connectedTimer;
|
||||||
|
|
||||||
NSUInteger _port;
|
NSUInteger _port;
|
||||||
dispatch_source_t _source;
|
dispatch_source_t _source;
|
||||||
@ -120,7 +124,7 @@ static void _SignalHandler(int signal) {
|
|||||||
|
|
||||||
@implementation GCDWebServer
|
@implementation GCDWebServer
|
||||||
|
|
||||||
@synthesize delegate=_delegate, handlers=_handlers, port=_port;
|
@synthesize delegate=_delegate, handlers=_handlers, port=_port, connected=_connected;
|
||||||
|
|
||||||
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
|
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
|
||||||
|
|
||||||
@ -137,24 +141,99 @@ static void _SignalHandler(int signal) {
|
|||||||
GCDWebServerInitializeFunctions();
|
GCDWebServerInitializeFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
||||||
|
@autoreleasepool {
|
||||||
|
[(ARC_BRIDGE GCDWebServer*)info _didDisconnect];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
_syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
|
||||||
_handlers = [[NSMutableArray alloc] init];
|
_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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
|
DCHECK(_connected == NO);
|
||||||
|
DCHECK(_activeConnections == 0);
|
||||||
|
|
||||||
_delegate = nil;
|
_delegate = nil;
|
||||||
if (_source) {
|
if (_source) {
|
||||||
[self stop];
|
[self stop];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_connectedTimer) {
|
||||||
|
CFRunLoopTimerInvalidate(_connectedTimer);
|
||||||
|
CFRelease(_connectedTimer);
|
||||||
|
}
|
||||||
ARC_RELEASE(_handlers);
|
ARC_RELEASE(_handlers);
|
||||||
|
ARC_DISPATCH_RELEASE(_syncQueue);
|
||||||
|
|
||||||
ARC_DEALLOC(super);
|
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 {
|
- (NSString*)bonjourName {
|
||||||
CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL;
|
CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL;
|
||||||
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
|
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
|
||||||
@ -346,6 +425,10 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (NSTimeInterval)connectedStateCoalescingInterval {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GCDWebServer (Extensions)
|
@implementation GCDWebServer (Extensions)
|
||||||
|
|||||||
@ -584,6 +584,9 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
_localAddress = ARC_RETAIN(localAddress);
|
_localAddress = ARC_RETAIN(localAddress);
|
||||||
_remoteAddress = ARC_RETAIN(remoteAddress);
|
_remoteAddress = ARC_RETAIN(remoteAddress);
|
||||||
_socket = socket;
|
_socket = socket;
|
||||||
|
LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
||||||
|
|
||||||
|
[_server willStartConnection:self];
|
||||||
|
|
||||||
if (![self open]) {
|
if (![self open]) {
|
||||||
close(_socket);
|
close(_socket);
|
||||||
@ -592,7 +595,6 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
}
|
}
|
||||||
_opened = YES;
|
_opened = YES;
|
||||||
|
|
||||||
LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
|
||||||
[self _readRequestHeaders];
|
[self _readRequestHeaders];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@ -620,10 +622,6 @@ static NSString* _StringFromAddressData(NSData* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
if (_opened) {
|
|
||||||
[self close];
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = close(_socket);
|
int result = close(_socket);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno);
|
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);
|
LOG_DEBUG(@"Did close connection on socket %i", _socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_opened) {
|
||||||
|
[self close];
|
||||||
|
}
|
||||||
|
|
||||||
|
[_server didEndConnection:self];
|
||||||
ARC_RELEASE(_server);
|
ARC_RELEASE(_server);
|
||||||
ARC_RELEASE(_localAddress);
|
ARC_RELEASE(_localAddress);
|
||||||
ARC_RELEASE(_remoteAddress);
|
ARC_RELEASE(_remoteAddress);
|
||||||
@ -783,6 +786,7 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
|
|||||||
unlink([_responsePath fileSystemRepresentation]);
|
unlink([_responsePath fileSystemRepresentation]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_request) {
|
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);
|
LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -128,6 +128,8 @@ extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
|
|||||||
|
|
||||||
@interface GCDWebServer ()
|
@interface GCDWebServer ()
|
||||||
@property(nonatomic, readonly) NSArray* handlers;
|
@property(nonatomic, readonly) NSArray* handlers;
|
||||||
|
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
||||||
|
- (void)didEndConnection:(GCDWebServerConnection*)connection;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerHandler : NSObject
|
@interface GCDWebServerHandler : NSObject
|
||||||
|
|||||||
74
Mac/main.m
74
Mac/main.m
@ -52,6 +52,77 @@ typedef enum {
|
|||||||
kMode_StreamingResponse
|
kMode_StreamingResponse
|
||||||
} Mode;
|
} 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 main(int argc, const char* argv[]) {
|
||||||
int result = -1;
|
int result = -1;
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
@ -199,6 +270,8 @@ int main(int argc, const char* argv[]) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (webServer) {
|
if (webServer) {
|
||||||
|
Delegate* delegate = [[Delegate alloc] init];
|
||||||
|
webServer.delegate = delegate;
|
||||||
if (testDirectory) {
|
if (testDirectory) {
|
||||||
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
|
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
|
||||||
result = (int)[webServer runTestsInDirectory:testDirectory withPort:8080];
|
result = (int)[webServer runTestsInDirectory:testDirectory withPort:8080];
|
||||||
@ -214,6 +287,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
}
|
}
|
||||||
#if !__has_feature(objc_arc)
|
#if !__has_feature(objc_arc)
|
||||||
[webServer release];
|
[webServer release];
|
||||||
|
[delegate release];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user