Skip to content

Commit 2d8996b

Browse files
committed
Added connected state to GCDWebServer
1 parent c5ca0f7 commit 2d8996b

5 files changed

Lines changed: 173 additions & 6 deletions

File tree

GCDWebServer/Core/GCDWebServer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
4848
@protocol GCDWebServerDelegate <NSObject>
4949
@optional
5050
- (void)webServerDidStart:(GCDWebServer*)server;
51+
- (void)webServerDidConnect:(GCDWebServer*)server;
52+
- (void)webServerDidDisconnect:(GCDWebServer*)server;
5153
- (void)webServerDidStop:(GCDWebServer*)server;
5254
@end
5355

@@ -56,6 +58,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
5658
@property(nonatomic, readonly, getter=isRunning) BOOL running;
5759
@property(nonatomic, readonly) NSUInteger port;
5860
@property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active
61+
@property(nonatomic, readonly, getter=isConnected) BOOL connected;
5962
- (instancetype)init;
6063
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
6164
- (void)removeAllHandlers;
@@ -70,6 +73,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
7073
+ (Class)connectionClass; // Default is GCDWebServerConnection
7174
+ (NSString*)serverName; // Default is class name
7275
+ (BOOL)shouldAutomaticallyMapHEADToGET; // Default is YES which means HEAD requests are mapped to GET requests with the response body being discarded
76+
+ (NSTimeInterval)connectedStateCoalescingInterval; // Allows coalescing of fast sequences of -webServerDidConnect: / -webServerDidDisconnect: - Default is 1.0 seconds (set to 0.0 to disable)
7377
@end
7478

7579
@interface GCDWebServer (Extensions)

GCDWebServer/Core/GCDWebServer.m

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@
4444
@interface GCDWebServer () {
4545
@private
4646
id<GCDWebServerDelegate> __unsafe_unretained _delegate;
47+
dispatch_queue_t _syncQueue;
4748
NSMutableArray* _handlers;
49+
NSInteger _activeConnections; // Accessed only with _syncQueue
50+
BOOL _connected;
51+
CFRunLoopTimerRef _connectedTimer;
4852

4953
NSUInteger _port;
5054
dispatch_source_t _source;
@@ -120,7 +124,7 @@ - (void)dealloc {
120124

121125
@implementation GCDWebServer
122126

123-
@synthesize delegate=_delegate, handlers=_handlers, port=_port;
127+
@synthesize delegate=_delegate, handlers=_handlers, port=_port, connected=_connected;
124128

125129
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
126130

@@ -137,24 +141,99 @@ + (void)initialize {
137141
GCDWebServerInitializeFunctions();
138142
}
139143

144+
static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
145+
@autoreleasepool {
146+
[(ARC_BRIDGE GCDWebServer*)info _didDisconnect];
147+
}
148+
}
149+
140150
- (instancetype)init {
141151
if ((self = [super init])) {
152+
_syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
142153
_handlers = [[NSMutableArray alloc] init];
154+
CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
155+
if ([[self class] connectedStateCoalescingInterval] > 0.0) {
156+
_connectedTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _ConnectedTimerCallBack, &context);
157+
CFRunLoopAddTimer(CFRunLoopGetMain(), _connectedTimer, kCFRunLoopCommonModes);
158+
}
143159
}
144160
return self;
145161
}
146162

147163
- (void)dealloc {
164+
DCHECK(_connected == NO);
165+
DCHECK(_activeConnections == 0);
166+
148167
_delegate = nil;
149168
if (_source) {
150169
[self stop];
151170
}
152171

172+
if (_connectedTimer) {
173+
CFRunLoopTimerInvalidate(_connectedTimer);
174+
CFRelease(_connectedTimer);
175+
}
153176
ARC_RELEASE(_handlers);
177+
ARC_DISPATCH_RELEASE(_syncQueue);
154178

155179
ARC_DEALLOC(super);
156180
}
157181

182+
- (void)_didConnect {
183+
DCHECK(_connected == NO);
184+
_connected = YES;
185+
LOG_DEBUG(@"Did connect");
186+
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
187+
[_delegate webServerDidConnect:self];
188+
}
189+
}
190+
191+
// Called from any thread
192+
- (void)willStartConnection:(GCDWebServerConnection*)connection {
193+
dispatch_sync(_syncQueue, ^{
194+
195+
DCHECK(_activeConnections >= 0);
196+
if (_activeConnections == 0) {
197+
dispatch_async(dispatch_get_main_queue(), ^{
198+
if (_connectedTimer) {
199+
CFRunLoopTimerSetNextFireDate(_connectedTimer, HUGE_VAL);
200+
}
201+
if (_connected == NO) {
202+
[self _didConnect];
203+
}
204+
});
205+
}
206+
_activeConnections += 1;
207+
208+
});
209+
}
210+
211+
- (void)_didDisconnect {
212+
DCHECK(_connected == YES);
213+
_connected = NO;
214+
LOG_DEBUG(@"Did disconnect");
215+
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
216+
[_delegate webServerDidDisconnect:self];
217+
}
218+
}
219+
220+
// Called from any thread
221+
- (void)didEndConnection:(GCDWebServerConnection*)connection {
222+
dispatch_sync(_syncQueue, ^{
223+
DCHECK(_activeConnections > 0);
224+
_activeConnections -= 1;
225+
if (_activeConnections == 0) {
226+
dispatch_async(dispatch_get_main_queue(), ^{
227+
if (_connectedTimer) {
228+
CFRunLoopTimerSetNextFireDate(_connectedTimer, CFAbsoluteTimeGetCurrent() + [[self class] connectedStateCoalescingInterval]);
229+
} else {
230+
[self _didDisconnect];
231+
}
232+
});
233+
}
234+
});
235+
}
236+
158237
- (NSString*)bonjourName {
159238
CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL;
160239
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
@@ -346,6 +425,10 @@ + (BOOL)shouldAutomaticallyMapHEADToGET {
346425
return YES;
347426
}
348427

428+
+ (NSTimeInterval)connectedStateCoalescingInterval {
429+
return 1.0;
430+
}
431+
349432
@end
350433

351434
@implementation GCDWebServer (Extensions)

GCDWebServer/Core/GCDWebServerConnection.m

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,9 @@ - (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress re
584584
_localAddress = ARC_RETAIN(localAddress);
585585
_remoteAddress = ARC_RETAIN(remoteAddress);
586586
_socket = socket;
587+
LOG_DEBUG(@"Did open connection on socket %i", _socket);
588+
589+
[_server willStartConnection:self];
587590

588591
if (![self open]) {
589592
close(_socket);
@@ -592,7 +595,6 @@ - (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress re
592595
}
593596
_opened = YES;
594597

595-
LOG_DEBUG(@"Did open connection on socket %i", _socket);
596598
[self _readRequestHeaders];
597599
}
598600
return self;
@@ -620,17 +622,18 @@ - (NSString*)remoteAddressString {
620622
}
621623

622624
- (void)dealloc {
623-
if (_opened) {
624-
[self close];
625-
}
626-
627625
int result = close(_socket);
628626
if (result != 0) {
629627
LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno);
630628
} else {
631629
LOG_DEBUG(@"Did close connection on socket %i", _socket);
632630
}
633631

632+
if (_opened) {
633+
[self close];
634+
}
635+
636+
[_server didEndConnection:self];
634637
ARC_RELEASE(_server);
635638
ARC_RELEASE(_localAddress);
636639
ARC_RELEASE(_remoteAddress);
@@ -783,6 +786,7 @@ - (void)close {
783786
unlink([_responsePath fileSystemRepresentation]);
784787
}
785788
#endif
789+
786790
if (_request) {
787791
LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
788792
} else {

GCDWebServer/Core/GCDWebServerPrivate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
128128

129129
@interface GCDWebServer ()
130130
@property(nonatomic, readonly) NSArray* handlers;
131+
- (void)willStartConnection:(GCDWebServerConnection*)connection;
132+
- (void)didEndConnection:(GCDWebServerConnection*)connection;
131133
@end
132134

133135
@interface GCDWebServerHandler : NSObject

Mac/main.m

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,77 @@
5252
kMode_StreamingResponse
5353
} Mode;
5454

55+
@interface Delegate : NSObject <GCDWebServerDelegate, GCDWebDAVServerDelegate, GCDWebUploaderDelegate>
56+
@end
57+
58+
@implementation Delegate
59+
60+
- (void)_logDelegateCall:(SEL)selector {
61+
fprintf(stdout, "<DELEGATE METHOD \"%s\" CALLED>\n", [NSStringFromSelector(selector) UTF8String]);
62+
}
63+
64+
- (void)webServerDidStart:(GCDWebServer*)server {
65+
[self _logDelegateCall:_cmd];
66+
}
67+
68+
- (void)webServerDidConnect:(GCDWebServer*)server {
69+
[self _logDelegateCall:_cmd];
70+
}
71+
72+
- (void)webServerDidDisconnect:(GCDWebServer*)server {
73+
[self _logDelegateCall:_cmd];
74+
}
75+
76+
- (void)webServerDidStop:(GCDWebServer*)server {
77+
[self _logDelegateCall:_cmd];
78+
}
79+
80+
- (void)davServer:(GCDWebDAVServer*)server didDownloadFileAtPath:(NSString*)path {
81+
[self _logDelegateCall:_cmd];
82+
}
83+
84+
- (void)davServer:(GCDWebDAVServer*)server didUploadFileAtPath:(NSString*)path {
85+
[self _logDelegateCall:_cmd];
86+
}
87+
88+
- (void)davServer:(GCDWebDAVServer*)server didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
89+
[self _logDelegateCall:_cmd];
90+
}
91+
92+
- (void)davServer:(GCDWebDAVServer*)server didCopyItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
93+
[self _logDelegateCall:_cmd];
94+
}
95+
96+
- (void)davServer:(GCDWebDAVServer*)server didDeleteItemAtPath:(NSString*)path {
97+
[self _logDelegateCall:_cmd];
98+
}
99+
100+
- (void)davServer:(GCDWebDAVServer*)server didCreateDirectoryAtPath:(NSString*)path {
101+
[self _logDelegateCall:_cmd];
102+
}
103+
104+
- (void)webUploader:(GCDWebUploader*)uploader didDownloadFileAtPath:(NSString*)path {
105+
[self _logDelegateCall:_cmd];
106+
}
107+
108+
- (void)webUploader:(GCDWebUploader*)uploader didUploadFileAtPath:(NSString*)path {
109+
[self _logDelegateCall:_cmd];
110+
}
111+
112+
- (void)webUploader:(GCDWebUploader*)uploader didMoveItemFromPath:(NSString*)fromPath toPath:(NSString*)toPath {
113+
[self _logDelegateCall:_cmd];
114+
}
115+
116+
- (void)webUploader:(GCDWebUploader*)uploader didDeleteItemAtPath:(NSString*)path {
117+
[self _logDelegateCall:_cmd];
118+
}
119+
120+
- (void)webUploader:(GCDWebUploader*)uploader didCreateDirectoryAtPath:(NSString*)path {
121+
[self _logDelegateCall:_cmd];
122+
}
123+
124+
@end
125+
55126
int main(int argc, const char* argv[]) {
56127
int result = -1;
57128
@autoreleasepool {
@@ -199,6 +270,8 @@ int main(int argc, const char* argv[]) {
199270
#endif
200271

201272
if (webServer) {
273+
Delegate* delegate = [[Delegate alloc] init];
274+
webServer.delegate = delegate;
202275
if (testDirectory) {
203276
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
204277
result = (int)[webServer runTestsInDirectory:testDirectory withPort:8080];
@@ -214,6 +287,7 @@ int main(int argc, const char* argv[]) {
214287
}
215288
#if !__has_feature(objc_arc)
216289
[webServer release];
290+
[delegate release];
217291
#endif
218292
}
219293
}

0 commit comments

Comments
 (0)