|
@@ -141,7 +141,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
|
|
|
|
@implementation GCDWebServerHandler
|
|
@implementation GCDWebServerHandler
|
|
|
|
|
|
-@synthesize matchBlock=_matchBlock, asyncProcessBlock=_asyncProcessBlock;
|
|
|
|
|
|
+@synthesize matchBlock = _matchBlock, asyncProcessBlock = _asyncProcessBlock;
|
|
|
|
|
|
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
|
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
|
if ((self = [super init])) {
|
|
if ((self = [super init])) {
|
|
@@ -162,7 +162,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
NSInteger _activeConnections; // Accessed through _syncQueue only
|
|
NSInteger _activeConnections; // Accessed through _syncQueue only
|
|
BOOL _connected; // Accessed on main thread only
|
|
BOOL _connected; // Accessed on main thread only
|
|
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
|
|
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
|
|
-
|
|
|
|
|
|
+
|
|
NSDictionary* _options;
|
|
NSDictionary* _options;
|
|
NSString* _serverName;
|
|
NSString* _serverName;
|
|
NSString* _authenticationRealm;
|
|
NSString* _authenticationRealm;
|
|
@@ -195,9 +195,9 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
|
|
|
|
@implementation GCDWebServer
|
|
@implementation GCDWebServer
|
|
|
|
|
|
-@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm,
|
|
|
|
- authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts,
|
|
|
|
- shouldAutomaticallyMapHEADToGET=_mapHEADToGET, dispatchQueuePriority=_dispatchQueuePriority;
|
|
|
|
|
|
+@synthesize delegate = _delegate, handlers = _handlers, port = _port, serverName = _serverName, authenticationRealm = _authenticationRealm,
|
|
|
|
+ authenticationBasicAccounts = _authenticationBasicAccounts, authenticationDigestAccounts = _authenticationDigestAccounts,
|
|
|
|
+ shouldAutomaticallyMapHEADToGET = _mapHEADToGET, dispatchQueuePriority = _dispatchQueuePriority;
|
|
|
|
|
|
+ (void)initialize {
|
|
+ (void)initialize {
|
|
GCDWebServerInitializeFunctions();
|
|
GCDWebServerInitializeFunctions();
|
|
@@ -220,7 +220,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
GWS_DCHECK(_activeConnections == 0);
|
|
GWS_DCHECK(_activeConnections == 0);
|
|
GWS_DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
|
|
GWS_DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
|
|
GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
|
|
GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
|
|
-
|
|
|
|
|
|
+
|
|
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
|
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
|
dispatch_release(_sourceGroup);
|
|
dispatch_release(_sourceGroup);
|
|
dispatch_release(_syncQueue);
|
|
dispatch_release(_syncQueue);
|
|
@@ -235,10 +235,10 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
if (_backgroundTask == UIBackgroundTaskInvalid) {
|
|
if (_backgroundTask == UIBackgroundTaskInvalid) {
|
|
GWS_LOG_DEBUG(@"Did start background task");
|
|
GWS_LOG_DEBUG(@"Did start background task");
|
|
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
|
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
|
-
|
|
|
|
|
|
+
|
|
GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]);
|
|
GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]);
|
|
[self _endBackgroundTask];
|
|
[self _endBackgroundTask];
|
|
-
|
|
|
|
|
|
+
|
|
}];
|
|
}];
|
|
} else {
|
|
} else {
|
|
GWS_DNOT_REACHED();
|
|
GWS_DNOT_REACHED();
|
|
@@ -253,13 +253,13 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
GWS_DCHECK(_connected == NO);
|
|
GWS_DCHECK(_connected == NO);
|
|
_connected = YES;
|
|
_connected = YES;
|
|
GWS_LOG_DEBUG(@"Did connect");
|
|
GWS_LOG_DEBUG(@"Did connect");
|
|
-
|
|
|
|
|
|
+
|
|
#if TARGET_OS_IPHONE
|
|
#if TARGET_OS_IPHONE
|
|
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
|
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
|
[self _startBackgroundTask];
|
|
[self _startBackgroundTask];
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
-
|
|
|
|
|
|
+
|
|
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
|
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
|
[_delegate webServerDidConnect:self];
|
|
[_delegate webServerDidConnect:self];
|
|
}
|
|
}
|
|
@@ -267,7 +267,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
|
|
|
|
- (void)willStartConnection:(GCDWebServerConnection*)connection {
|
|
- (void)willStartConnection:(GCDWebServerConnection*)connection {
|
|
dispatch_sync(_syncQueue, ^{
|
|
dispatch_sync(_syncQueue, ^{
|
|
-
|
|
|
|
|
|
+
|
|
GWS_DCHECK(_activeConnections >= 0);
|
|
GWS_DCHECK(_activeConnections >= 0);
|
|
if (_activeConnections == 0) {
|
|
if (_activeConnections == 0) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -282,7 +282,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
_activeConnections += 1;
|
|
_activeConnections += 1;
|
|
-
|
|
|
|
|
|
+
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
@@ -309,11 +309,11 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
GWS_DCHECK(_connected == YES);
|
|
GWS_DCHECK(_connected == YES);
|
|
_connected = NO;
|
|
_connected = NO;
|
|
GWS_LOG_DEBUG(@"Did disconnect");
|
|
GWS_LOG_DEBUG(@"Did disconnect");
|
|
-
|
|
|
|
|
|
+
|
|
#if TARGET_OS_IPHONE
|
|
#if TARGET_OS_IPHONE
|
|
[self _endBackgroundTask];
|
|
[self _endBackgroundTask];
|
|
#endif
|
|
#endif
|
|
-
|
|
|
|
|
|
+
|
|
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
|
|
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
|
|
[_delegate webServerDidDisconnect:self];
|
|
[_delegate webServerDidDisconnect:self];
|
|
}
|
|
}
|
|
@@ -356,9 +356,10 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
|
- [self addHandlerWithMatchBlock:matchBlock asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
- completionBlock(processBlock(request));
|
|
|
|
- }];
|
|
|
|
|
|
+ [self addHandlerWithMatchBlock:matchBlock
|
|
|
|
+ asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
+ completionBlock(processBlock(request));
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
|
@@ -464,7 +465,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
if (listeningSocket > 0) {
|
|
if (listeningSocket > 0) {
|
|
int yes = 1;
|
|
int yes = 1;
|
|
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
|
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
|
-
|
|
|
|
|
|
+
|
|
if (bind(listeningSocket, address, length) == 0) {
|
|
if (bind(listeningSocket, address, length) == 0) {
|
|
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
|
|
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
|
|
GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket);
|
|
GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket);
|
|
@@ -483,7 +484,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
|
GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
|
close(listeningSocket);
|
|
close(listeningSocket);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
} else {
|
|
} else {
|
|
if (error) {
|
|
if (error) {
|
|
*error = GCDWebServerMakePosixError(errno);
|
|
*error = GCDWebServerMakePosixError(errno);
|
|
@@ -497,7 +498,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
dispatch_group_enter(_sourceGroup);
|
|
dispatch_group_enter(_sourceGroup);
|
|
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0));
|
|
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, dispatch_get_global_queue(_dispatchQueuePriority, 0));
|
|
dispatch_source_set_cancel_handler(source, ^{
|
|
dispatch_source_set_cancel_handler(source, ^{
|
|
-
|
|
|
|
|
|
+
|
|
@autoreleasepool {
|
|
@autoreleasepool {
|
|
int result = close(listeningSocket);
|
|
int result = close(listeningSocket);
|
|
if (result != 0) {
|
|
if (result != 0) {
|
|
@@ -507,17 +508,17 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
dispatch_group_leave(_sourceGroup);
|
|
dispatch_group_leave(_sourceGroup);
|
|
-
|
|
|
|
|
|
+
|
|
});
|
|
});
|
|
dispatch_source_set_event_handler(source, ^{
|
|
dispatch_source_set_event_handler(source, ^{
|
|
-
|
|
|
|
|
|
+
|
|
@autoreleasepool {
|
|
@autoreleasepool {
|
|
struct sockaddr_storage remoteSockAddr;
|
|
struct sockaddr_storage remoteSockAddr;
|
|
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
|
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
|
int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen);
|
|
int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen);
|
|
if (socket > 0) {
|
|
if (socket > 0) {
|
|
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
|
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
|
-
|
|
|
|
|
|
+
|
|
struct sockaddr_storage localSockAddr;
|
|
struct sockaddr_storage localSockAddr;
|
|
socklen_t localAddrLen = sizeof(localSockAddr);
|
|
socklen_t localAddrLen = sizeof(localSockAddr);
|
|
NSData* localAddress = nil;
|
|
NSData* localAddress = nil;
|
|
@@ -527,28 +528,28 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
} else {
|
|
} else {
|
|
GWS_DNOT_REACHED();
|
|
GWS_DNOT_REACHED();
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
int noSigPipe = 1;
|
|
int noSigPipe = 1;
|
|
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
|
|
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)); // Make sure this socket cannot generate SIG_PIPE
|
|
-
|
|
|
|
|
|
+
|
|
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
|
|
GCDWebServerConnection* connection = [[_connectionClass alloc] initWithServer:self localAddress:localAddress remoteAddress:remoteAddress socket:socket]; // Connection will automatically retain itself while opened
|
|
[connection self]; // Prevent compiler from complaining about unused variable / useless statement
|
|
[connection self]; // Prevent compiler from complaining about unused variable / useless statement
|
|
} else {
|
|
} else {
|
|
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
|
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
});
|
|
});
|
|
return source;
|
|
return source;
|
|
}
|
|
}
|
|
|
|
|
|
- (BOOL)_start:(NSError**)error {
|
|
- (BOOL)_start:(NSError**)error {
|
|
GWS_DCHECK(_source4 == NULL);
|
|
GWS_DCHECK(_source4 == NULL);
|
|
-
|
|
|
|
|
|
+
|
|
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
|
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
|
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
|
|
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
|
|
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
|
|
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
|
|
-
|
|
|
|
|
|
+
|
|
struct sockaddr_in addr4;
|
|
struct sockaddr_in addr4;
|
|
bzero(&addr4, sizeof(addr4));
|
|
bzero(&addr4, sizeof(addr4));
|
|
addr4.sin_len = sizeof(addr4);
|
|
addr4.sin_len = sizeof(addr4);
|
|
@@ -568,7 +569,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
|
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
struct sockaddr_in6 addr6;
|
|
struct sockaddr_in6 addr6;
|
|
bzero(&addr6, sizeof(addr6));
|
|
bzero(&addr6, sizeof(addr6));
|
|
addr6.sin6_len = sizeof(addr6);
|
|
addr6.sin6_len = sizeof(addr6);
|
|
@@ -580,7 +581,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
close(listeningSocket4);
|
|
close(listeningSocket4);
|
|
return NO;
|
|
return NO;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
|
|
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
|
|
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
|
|
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
|
|
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
|
|
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
|
|
@@ -602,24 +603,24 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
|
|
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
|
|
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
|
|
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
|
|
_dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
|
|
_dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
|
|
-
|
|
|
|
|
|
+
|
|
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
|
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
|
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
|
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
|
_port = port;
|
|
_port = port;
|
|
_bindToLocalhost = bindToLocalhost;
|
|
_bindToLocalhost = bindToLocalhost;
|
|
-
|
|
|
|
|
|
+
|
|
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
|
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
|
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
|
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
|
if (bonjourName) {
|
|
if (bonjourName) {
|
|
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (__bridge CFStringRef)bonjourType, (__bridge CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
|
|
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (__bridge CFStringRef)bonjourType, (__bridge CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
|
|
if (_registrationService) {
|
|
if (_registrationService) {
|
|
CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
|
CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
|
-
|
|
|
|
|
|
+
|
|
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
|
|
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
|
|
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
|
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
|
CFStreamError streamError = {0};
|
|
CFStreamError streamError = {0};
|
|
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
|
|
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
|
|
-
|
|
|
|
|
|
+
|
|
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
|
|
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
|
|
if (_resolutionService) {
|
|
if (_resolutionService) {
|
|
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
|
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
|
@@ -631,7 +632,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
GWS_LOG_ERROR(@"Failed creating CFNetService for registration");
|
|
GWS_LOG_ERROR(@"Failed creating CFNetService for registration");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
|
|
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
|
|
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
|
|
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
|
|
if (status == kDNSServiceErr_NoError) {
|
|
if (status == kDNSServiceErr_NoError) {
|
|
@@ -654,7 +655,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status);
|
|
GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
dispatch_resume(_source4);
|
|
dispatch_resume(_source4);
|
|
dispatch_resume(_source6);
|
|
dispatch_resume(_source6);
|
|
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
|
|
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
|
|
@@ -663,13 +664,13 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
[_delegate webServerDidStart:self];
|
|
[_delegate webServerDidStart:self];
|
|
});
|
|
});
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return YES;
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
|
|
- (void)_stop {
|
|
- (void)_stop {
|
|
GWS_DCHECK(_source4 != NULL);
|
|
GWS_DCHECK(_source4 != NULL);
|
|
-
|
|
|
|
|
|
+
|
|
if (_dnsService) {
|
|
if (_dnsService) {
|
|
_dnsAddress = nil;
|
|
_dnsAddress = nil;
|
|
_dnsPort = 0;
|
|
_dnsPort = 0;
|
|
@@ -685,7 +686,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
DNSServiceRefDeallocate(_dnsService);
|
|
DNSServiceRefDeallocate(_dnsService);
|
|
_dnsService = NULL;
|
|
_dnsService = NULL;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
if (_registrationService) {
|
|
if (_registrationService) {
|
|
if (_resolutionService) {
|
|
if (_resolutionService) {
|
|
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
|
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
|
@@ -700,7 +701,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
CFRelease(_registrationService);
|
|
CFRelease(_registrationService);
|
|
_registrationService = NULL;
|
|
_registrationService = NULL;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
dispatch_source_cancel(_source6);
|
|
dispatch_source_cancel(_source6);
|
|
dispatch_source_cancel(_source4);
|
|
dispatch_source_cancel(_source4);
|
|
dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed
|
|
dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed
|
|
@@ -714,12 +715,12 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
_source4 = NULL;
|
|
_source4 = NULL;
|
|
_port = 0;
|
|
_port = 0;
|
|
_bindToLocalhost = NO;
|
|
_bindToLocalhost = NO;
|
|
-
|
|
|
|
|
|
+
|
|
_serverName = nil;
|
|
_serverName = nil;
|
|
_authenticationRealm = nil;
|
|
_authenticationRealm = nil;
|
|
_authenticationBasicAccounts = nil;
|
|
_authenticationBasicAccounts = nil;
|
|
_authenticationDigestAccounts = nil;
|
|
_authenticationDigestAccounts = nil;
|
|
-
|
|
|
|
|
|
+
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (_disconnectTimer) {
|
|
if (_disconnectTimer) {
|
|
CFRunLoopTimerInvalidate(_disconnectTimer);
|
|
CFRunLoopTimerInvalidate(_disconnectTimer);
|
|
@@ -728,7 +729,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
[self _didDisconnect];
|
|
[self _didDisconnect];
|
|
}
|
|
}
|
|
});
|
|
});
|
|
-
|
|
|
|
|
|
+
|
|
GWS_LOG_INFO(@"%@ stopped", [self class]);
|
|
GWS_LOG_INFO(@"%@ stopped", [self class]);
|
|
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
|
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
@@ -896,32 +897,38 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
@implementation GCDWebServer (Handlers)
|
|
@implementation GCDWebServer (Handlers)
|
|
|
|
|
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
|
- [self addDefaultHandlerForMethod:method requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
- completionBlock(block(request));
|
|
|
|
- }];
|
|
|
|
|
|
+ [self addDefaultHandlerForMethod:method
|
|
|
|
+ requestClass:aClass
|
|
|
|
+ asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
+ completionBlock(block(request));
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
|
- [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
-
|
|
|
|
|
|
+ [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
+
|
|
if (![requestMethod isEqualToString:method]) {
|
|
if (![requestMethod isEqualToString:method]) {
|
|
return nil;
|
|
return nil;
|
|
}
|
|
}
|
|
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
-
|
|
|
|
- } asyncProcessBlock:block];
|
|
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ asyncProcessBlock:block];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
|
- [self addHandlerForMethod:method path:path requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
- completionBlock(block(request));
|
|
|
|
- }];
|
|
|
|
|
|
+ [self addHandlerForMethod:method
|
|
|
|
+ path:path
|
|
|
|
+ requestClass:aClass
|
|
|
|
+ asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
+ completionBlock(block(request));
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
|
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
|
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
|
- [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
-
|
|
|
|
|
|
+ [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
+
|
|
if (![requestMethod isEqualToString:method]) {
|
|
if (![requestMethod isEqualToString:method]) {
|
|
return nil;
|
|
return nil;
|
|
}
|
|
}
|
|
@@ -929,24 +936,28 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
return nil;
|
|
return nil;
|
|
}
|
|
}
|
|
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
-
|
|
|
|
- } asyncProcessBlock:block];
|
|
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ asyncProcessBlock:block];
|
|
} else {
|
|
} else {
|
|
GWS_DNOT_REACHED();
|
|
GWS_DNOT_REACHED();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
|
- [self addHandlerForMethod:method pathRegex:regex requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
- completionBlock(block(request));
|
|
|
|
- }];
|
|
|
|
|
|
+ [self addHandlerForMethod:method
|
|
|
|
+ pathRegex:regex
|
|
|
|
+ requestClass:aClass
|
|
|
|
+ asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
|
|
|
+ completionBlock(block(request));
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
|
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
|
|
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
|
|
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
|
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
|
- [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
-
|
|
|
|
|
|
+ [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
+
|
|
if (![requestMethod isEqualToString:method]) {
|
|
if (![requestMethod isEqualToString:method]) {
|
|
return nil;
|
|
return nil;
|
|
}
|
|
}
|
|
@@ -972,8 +983,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
|
|
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
|
|
return request;
|
|
return request;
|
|
-
|
|
|
|
- } asyncProcessBlock:block];
|
|
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ asyncProcessBlock:block];
|
|
} else {
|
|
} else {
|
|
GWS_DNOT_REACHED();
|
|
GWS_DNOT_REACHED();
|
|
}
|
|
}
|
|
@@ -984,29 +996,35 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
@implementation GCDWebServer (GETHandlers)
|
|
@implementation GCDWebServer (GETHandlers)
|
|
|
|
|
|
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge {
|
|
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge {
|
|
- [self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
|
|
|
-
|
|
|
|
- GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
|
|
|
|
- response.cacheControlMaxAge = cacheAge;
|
|
|
|
- return response;
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
|
|
+ [self addHandlerForMethod:@"GET"
|
|
|
|
+ path:path
|
|
|
|
+ requestClass:[GCDWebServerRequest class]
|
|
|
|
+ processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
|
|
|
+
|
|
|
|
+ GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
|
|
|
|
+ response.cacheControlMaxAge = cacheAge;
|
|
|
|
+ return response;
|
|
|
|
+
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
|
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
|
- [self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
|
|
|
-
|
|
|
|
- GCDWebServerResponse* response = nil;
|
|
|
|
- if (allowRangeRequests) {
|
|
|
|
- response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
|
|
|
|
- [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
|
|
|
- } else {
|
|
|
|
- response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment];
|
|
|
|
- }
|
|
|
|
- response.cacheControlMaxAge = cacheAge;
|
|
|
|
- return response;
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
|
|
+ [self addHandlerForMethod:@"GET"
|
|
|
|
+ path:path
|
|
|
|
+ requestClass:[GCDWebServerRequest class]
|
|
|
|
+ processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
|
|
|
+
|
|
|
|
+ GCDWebServerResponse* response = nil;
|
|
|
|
+ if (allowRangeRequests) {
|
|
|
|
+ response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
|
|
|
|
+ [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
|
|
|
+ } else {
|
|
|
|
+ response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment];
|
|
|
|
+ }
|
|
|
|
+ response.cacheControlMaxAge = cacheAge;
|
|
|
|
+ return response;
|
|
|
|
+
|
|
|
|
+ }];
|
|
}
|
|
}
|
|
|
|
|
|
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
|
|
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
|
|
@@ -1042,8 +1060,8 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
|
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
|
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
|
|
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
|
|
GCDWebServer* __unsafe_unretained server = self;
|
|
GCDWebServer* __unsafe_unretained server = self;
|
|
- [self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
-
|
|
|
|
|
|
+ [self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
|
|
|
+
|
|
if (![requestMethod isEqualToString:@"GET"]) {
|
|
if (![requestMethod isEqualToString:@"GET"]) {
|
|
return nil;
|
|
return nil;
|
|
}
|
|
}
|
|
@@ -1051,39 +1069,40 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|
return nil;
|
|
return nil;
|
|
}
|
|
}
|
|
return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
return [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
|
-
|
|
|
|
- } processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
|
|
|
-
|
|
|
|
- GCDWebServerResponse* response = nil;
|
|
|
|
- NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
|
|
|
- NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
|
|
|
|
- if (fileType) {
|
|
|
|
- if ([fileType isEqualToString:NSFileTypeDirectory]) {
|
|
|
|
- if (indexFilename) {
|
|
|
|
- NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
|
|
|
- NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
|
|
|
|
- if ([indexType isEqualToString:NSFileTypeRegular]) {
|
|
|
|
- return [GCDWebServerFileResponse responseWithFile:indexPath];
|
|
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
|
|
|
+
|
|
|
|
+ GCDWebServerResponse* response = nil;
|
|
|
|
+ NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
|
|
|
+ NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
|
|
|
|
+ if (fileType) {
|
|
|
|
+ if ([fileType isEqualToString:NSFileTypeDirectory]) {
|
|
|
|
+ if (indexFilename) {
|
|
|
|
+ NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
|
|
|
+ NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
|
|
|
|
+ if ([indexType isEqualToString:NSFileTypeRegular]) {
|
|
|
|
+ return [GCDWebServerFileResponse responseWithFile:indexPath];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ response = [server _responseWithContentsOfDirectory:filePath];
|
|
|
|
+ } else if ([fileType isEqualToString:NSFileTypeRegular]) {
|
|
|
|
+ if (allowRangeRequests) {
|
|
|
|
+ response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
|
|
|
+ [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
|
|
|
+ } else {
|
|
|
|
+ response = [GCDWebServerFileResponse responseWithFile:filePath];
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- response = [server _responseWithContentsOfDirectory:filePath];
|
|
|
|
- } else if ([fileType isEqualToString:NSFileTypeRegular]) {
|
|
|
|
- if (allowRangeRequests) {
|
|
|
|
- response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
|
|
|
- [response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
|
|
|
|
|
+ if (response) {
|
|
|
|
+ response.cacheControlMaxAge = cacheAge;
|
|
} else {
|
|
} else {
|
|
- response = [GCDWebServerFileResponse responseWithFile:filePath];
|
|
|
|
|
|
+ response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
|
|
}
|
|
}
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (response) {
|
|
|
|
- response.cacheControlMaxAge = cacheAge;
|
|
|
|
- } else {
|
|
|
|
- response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
|
|
|
|
- }
|
|
|
|
- return response;
|
|
|
|
-
|
|
|
|
- }];
|
|
|
|
|
|
+ return response;
|
|
|
|
+
|
|
|
|
+ }];
|
|
} else {
|
|
} else {
|
|
GWS_DNOT_REACHED();
|
|
GWS_DNOT_REACHED();
|
|
}
|
|
}
|
|
@@ -1204,11 +1223,11 @@ static void _LogResult(NSString* format, ...) {
|
|
|
|
|
|
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path {
|
|
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path {
|
|
GWS_DCHECK([NSThread isMainThread]);
|
|
GWS_DCHECK([NSThread isMainThread]);
|
|
- NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs
|
|
|
|
|
|
+ NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs
|
|
NSInteger result = -1;
|
|
NSInteger result = -1;
|
|
if ([self startWithOptions:options error:NULL]) {
|
|
if ([self startWithOptions:options error:NULL]) {
|
|
_ExecuteMainThreadRunLoopSources();
|
|
_ExecuteMainThreadRunLoopSources();
|
|
-
|
|
|
|
|
|
+
|
|
result = 0;
|
|
result = 0;
|
|
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
|
|
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
|
|
for (NSString* requestFile in files) {
|
|
for (NSString* requestFile in files) {
|
|
@@ -1230,19 +1249,19 @@ static void _LogResult(NSString* format, ...) {
|
|
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
|
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
|
NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
|
|
NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
|
|
if (responseData) {
|
|
if (responseData) {
|
|
- CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
|
|
|
|
|
|
+ CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
|
|
if (expectedResponse) {
|
|
if (expectedResponse) {
|
|
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port);
|
|
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port);
|
|
if (actualResponse) {
|
|
if (actualResponse) {
|
|
success = YES;
|
|
success = YES;
|
|
-
|
|
|
|
|
|
+
|
|
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
|
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
|
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
|
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
|
if (actualStatusCode != expectedStatusCode) {
|
|
if (actualStatusCode != expectedStatusCode) {
|
|
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
|
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
|
success = NO;
|
|
success = NO;
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
|
NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
|
NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
|
NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
|
for (NSString* expectedHeader in expectedHeaders) {
|
|
for (NSString* expectedHeader in expectedHeaders) {
|
|
@@ -1262,7 +1281,7 @@ static void _LogResult(NSString* format, ...) {
|
|
success = NO;
|
|
success = NO;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length")));
|
|
NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length")));
|
|
NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse));
|
|
NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse));
|
|
NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length")));
|
|
NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length")));
|
|
@@ -1281,14 +1300,14 @@ static void _LogResult(NSString* format, ...) {
|
|
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
|
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
|
NSTask* task = [[NSTask alloc] init];
|
|
NSTask* task = [[NSTask alloc] init];
|
|
[task setLaunchPath:@"/usr/bin/opendiff"];
|
|
[task setLaunchPath:@"/usr/bin/opendiff"];
|
|
- [task setArguments:@[expectedPath, actualPath]];
|
|
|
|
|
|
+ [task setArguments:@[ expectedPath, actualPath ]];
|
|
[task launch];
|
|
[task launch];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
CFRelease(actualResponse);
|
|
CFRelease(actualResponse);
|
|
}
|
|
}
|
|
CFRelease(expectedResponse);
|
|
CFRelease(expectedResponse);
|
|
@@ -1311,9 +1330,9 @@ static void _LogResult(NSString* format, ...) {
|
|
}
|
|
}
|
|
_ExecuteMainThreadRunLoopSources();
|
|
_ExecuteMainThreadRunLoopSources();
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
[self stop];
|
|
[self stop];
|
|
-
|
|
|
|
|
|
+
|
|
_ExecuteMainThreadRunLoopSources();
|
|
_ExecuteMainThreadRunLoopSources();
|
|
}
|
|
}
|
|
return result;
|
|
return result;
|