Upgrade pods: Alamofire GCDWebServer MASShortcut

This commit is contained in:
Qiu Yuzhou
2019-09-10 14:23:26 +08:00
parent c593564059
commit 075949cdcb
97 changed files with 3392 additions and 2975 deletions

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
* GCDWebServerRequest instance created with the same basic info.
* Otherwise, it simply returns nil.
*/
typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery);
/**
* The GCDWebServerProcessBlock is called after the HTTP request has been fully
@ -69,6 +69,13 @@ typedef GCDWebServerResponse* _Nullable (^GCDWebServerProcessBlock)(__kindof GCD
typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* _Nullable response);
typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock);
/**
* The GCDWebServerBuiltInLoggerBlock is used to override the built-in logger at runtime.
* The block will be passed the log level and the log message, see setLogLevel for
* documentation of the log levels for the built-in logger.
*/
typedef void (^GCDWebServerBuiltInLoggerBlock)(int level, NSString* _Nonnull message);
/**
* The port used by the GCDWebServer (NSNumber / NSUInteger).
*
@ -365,7 +372,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
*
* Returns NO if the server failed to start and sets "error" argument if not NULL.
*/
- (BOOL)startWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error;
- (BOOL)startWithOptions:(nullable NSDictionary<NSString*, id>*)options error:(NSError** _Nullable)error;
/**
* Stops the server and prevents it to accepts new HTTP requests.
@ -444,7 +451,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
*
* @warning This method must be used from the main thread only.
*/
- (BOOL)runWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error;
- (BOOL)runWithOptions:(nullable NSDictionary<NSString*, id>*)options error:(NSError** _Nullable)error;
#endif
@ -573,6 +580,14 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
*/
+ (void)setLogLevel:(int)level;
/**
* Set a logger to be used instead of the built-in logger which logs to stderr.
*
* IMPORTANT: In order for this override to work, you should not be specifying
* a custom logger at compile time with "__GCDWEBSERVER_LOGGING_HEADER__".
*/
+ (void)setBuiltInLogger:(GCDWebServerBuiltInLoggerBlock)block;
/**
* Logs a message to the logging facility at the VERBOSE level.
*/
@ -613,7 +628,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
*
* Returns the number of failed tests or -1 if server failed to start.
*/
- (NSInteger)runTestsWithOptions:(nullable NSDictionary*)options inDirectory:(NSString*)path;
- (NSInteger)runTestsWithOptions:(nullable NSDictionary<NSString*, id>*)options inDirectory:(NSString*)path;
@end

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -85,18 +85,24 @@ static BOOL _run;
#ifdef __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__
static GCDWebServerBuiltInLoggerBlock _builtInLoggerBlock;
void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) {
static const char* levelNames[] = {"DEBUG", "VERBOSE", "INFO", "WARNING", "ERROR"};
static int enableLogging = -1;
if (enableLogging < 0) {
enableLogging = (isatty(STDERR_FILENO) ? 1 : 0);
}
if (enableLogging) {
if (_builtInLoggerBlock || enableLogging) {
va_list arguments;
va_start(arguments, format);
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
va_end(arguments);
fprintf(stderr, "[%s] %s\n", levelNames[level], [message UTF8String]);
if (_builtInLoggerBlock) {
_builtInLoggerBlock(level, message);
} else {
fprintf(stderr, "[%s] %s\n", levelNames[level], [message UTF8String]);
}
}
}
@ -141,14 +147,14 @@ static void _ExecuteMainThreadRunLoopSources() {
@implementation GCDWebServer {
dispatch_queue_t _syncQueue;
dispatch_group_t _sourceGroup;
NSMutableArray* _handlers;
NSMutableArray<GCDWebServerHandler*>* _handlers;
NSInteger _activeConnections; // Accessed through _syncQueue only
BOOL _connected; // Accessed on main thread only
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
NSDictionary* _options;
NSMutableDictionary* _authenticationBasicAccounts;
NSMutableDictionary* _authenticationDigestAccounts;
NSDictionary<NSString*, id>* _options;
NSMutableDictionary<NSString*, NSString*>* _authenticationBasicAccounts;
NSMutableDictionary<NSString*, NSString*>* _authenticationDigestAccounts;
Class _connectionClass;
CFTimeInterval _disconnectDelay;
dispatch_source_t _source4;
@ -206,10 +212,8 @@ static void _ExecuteMainThreadRunLoopSources() {
if (_backgroundTask == UIBackgroundTaskInvalid) {
GWS_LOG_DEBUG(@"Did start background task");
_backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
GWS_LOG_WARNING(@"Application is being suspended while %@ is still connected", [self class]);
[self _endBackgroundTask];
}];
} else {
GWS_DNOT_REACHED();
@ -238,22 +242,20 @@ static void _ExecuteMainThreadRunLoopSources() {
- (void)willStartConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{
GWS_DCHECK(_activeConnections >= 0);
if (_activeConnections == 0) {
GWS_DCHECK(self->_activeConnections >= 0);
if (self->_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectTimer) {
CFRunLoopTimerInvalidate(_disconnectTimer);
CFRelease(_disconnectTimer);
_disconnectTimer = NULL;
if (self->_disconnectTimer) {
CFRunLoopTimerInvalidate(self->_disconnectTimer);
CFRelease(self->_disconnectTimer);
self->_disconnectTimer = NULL;
}
if (_connected == NO) {
if (self->_connected == NO) {
[self _didConnect];
}
});
}
_activeConnections += 1;
self->_activeConnections += 1;
});
}
@ -292,22 +294,22 @@ static void _ExecuteMainThreadRunLoopSources() {
- (void)didEndConnection:(GCDWebServerConnection*)connection {
dispatch_sync(_syncQueue, ^{
GWS_DCHECK(_activeConnections > 0);
_activeConnections -= 1;
if (_activeConnections == 0) {
GWS_DCHECK(self->_activeConnections > 0);
self->_activeConnections -= 1;
if (self->_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if ((_disconnectDelay > 0.0) && (_source4 != NULL)) {
if (_disconnectTimer) {
CFRunLoopTimerInvalidate(_disconnectTimer);
CFRelease(_disconnectTimer);
if ((self->_disconnectDelay > 0.0) && (self->_source4 != NULL)) {
if (self->_disconnectTimer) {
CFRunLoopTimerInvalidate(self->_disconnectTimer);
CFRelease(self->_disconnectTimer);
}
_disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + _disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) {
self->_disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + self->_disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) {
GWS_DCHECK([NSThread isMainThread]);
[self _didDisconnect];
CFRelease(_disconnectTimer);
_disconnectTimer = NULL;
CFRelease(self->_disconnectTimer);
self->_disconnectTimer = NULL;
});
CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes);
CFRunLoopAddTimer(CFRunLoopGetMain(), self->_disconnectTimer, kCFRunLoopCommonModes);
} else {
[self _didDisconnect];
}
@ -412,19 +414,21 @@ static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef
}
}
static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) {
static inline id _GetOption(NSDictionary<NSString*, id>* options, NSString* key, id defaultValue) {
id value = [options objectForKey:key];
return value ? value : defaultValue;
}
static inline NSString* _EncodeBase64(NSString* string) {
NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding];
#if (TARGET_OS_IPHONE && !(__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0)) || (!TARGET_OS_IPHONE && !(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9))
if (![data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) {
return [data base64Encoding];
}
#endif
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_9)
return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding];
#else
if (@available(macOS 10.9, *)) {
return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding];
}
return [data base64Encoding];
#endif
}
- (int)_createListeningSocket:(BOOL)useIPv6
@ -469,7 +473,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
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_set_cancel_handler(source, ^{
@autoreleasepool {
int result = close(listeningSocket);
if (result != 0) {
@ -478,11 +481,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
GWS_LOG_DEBUG(@"Did close %s listening socket %i", isIPv6 ? "IPv6" : "IPv4", listeningSocket);
}
}
dispatch_group_leave(_sourceGroup);
dispatch_group_leave(self->_sourceGroup);
});
dispatch_source_set_event_handler(source, ^{
@autoreleasepool {
struct sockaddr_storage remoteSockAddr;
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
@ -503,13 +504,12 @@ static inline NSString* _EncodeBase64(NSString* string) {
int noSigPipe = 1;
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 = [(GCDWebServerConnection*)[self->_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
} else {
GWS_LOG_ERROR(@"Failed accepting %s socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
}
}
});
return source;
}
@ -517,9 +517,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (BOOL)_start:(NSError**)error {
GWS_DCHECK(_source4 == NULL);
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
BOOL bindToLocalhost = [_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
NSUInteger port = [(NSNumber*)_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
BOOL bindToLocalhost = [(NSNumber*)_GetOption(_options, GCDWebServerOption_BindToLocalhost, @NO) boolValue];
NSUInteger maxPendingConnections = [(NSNumber*)_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
struct sockaddr_in addr4;
bzero(&addr4, sizeof(addr4));
@ -553,27 +553,27 @@ static inline NSString* _EncodeBase64(NSString* string) {
return NO;
}
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
_serverName = [(NSString*)_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationRealm = [(NSString*)_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationBasicAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username];
[self->_authenticationBasicAccounts setObject:_EncodeBase64([NSString stringWithFormat:@"%@:%@", username, password]) forKey:username];
}];
} else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) {
_authenticationRealm = [_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationRealm = [(NSString*)_GetOption(_options, GCDWebServerOption_AuthenticationRealm, _serverName) copy];
_authenticationDigestAccounts = [[NSMutableDictionary alloc] init];
NSDictionary* accounts = _GetOption(_options, GCDWebServerOption_AuthenticationAccounts, @{});
[accounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* password, BOOL* stop) {
[_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, _authenticationRealm, password) forKey:username];
[self->_authenticationDigestAccounts setObject:GCDWebServerComputeMD5Digest(@"%@:%@:%@", username, self->_authenticationRealm, password) forKey:username];
}];
}
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
_shouldAutomaticallyMapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
_disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
_dispatchQueuePriority = [_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
_shouldAutomaticallyMapHEADToGET = [(NSNumber*)_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
_disconnectDelay = [(NSNumber*)_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue];
_dispatchQueuePriority = [(NSNumber*)_GetOption(_options, GCDWebServerOption_DispatchQueuePriority, @(DISPATCH_QUEUE_PRIORITY_DEFAULT)) longValue];
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
@ -604,7 +604,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
}
}
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
if ([(NSNumber*)_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
if (status == kDNSServiceErr_NoError) {
CFSocketContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
@ -632,7 +632,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
if ([_delegate respondsToSelector:@selector(webServerDidStart:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStart:self];
[self->_delegate webServerDidStart:self];
});
}
@ -693,10 +693,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
_authenticationDigestAccounts = nil;
dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectTimer) {
CFRunLoopTimerInvalidate(_disconnectTimer);
CFRelease(_disconnectTimer);
_disconnectTimer = NULL;
if (self->_disconnectTimer) {
CFRunLoopTimerInvalidate(self->_disconnectTimer);
CFRelease(self->_disconnectTimer);
self->_disconnectTimer = NULL;
[self _didDisconnect];
}
});
@ -704,7 +704,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
GWS_LOG_INFO(@"%@ stopped", [self class]);
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate webServerDidStop:self];
[self->_delegate webServerDidStop:self];
});
}
}
@ -729,11 +729,11 @@ static inline NSString* _EncodeBase64(NSString* string) {
#endif
- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error {
- (BOOL)startWithOptions:(NSDictionary<NSString*, id>*)options error:(NSError**)error {
if (_options == nil) {
_options = options ? [options copy] : @{};
#if TARGET_OS_IPHONE
_suspendInBackground = [_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue];
_suspendInBackground = [(NSNumber*)_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue];
if (((_suspendInBackground == NO) || ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)) && ![self _start:error])
#else
if (![self _start:error])
@ -840,7 +840,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
return [self runWithOptions:options error:NULL];
}
- (BOOL)runWithOptions:(NSDictionary*)options error:(NSError**)error {
- (BOOL)runWithOptions:(NSDictionary<NSString*, id>*)options error:(NSError**)error {
GWS_DCHECK([NSThread isMainThread]);
BOOL success = NO;
_run = YES;
@ -876,13 +876,11 @@ static inline NSString* _EncodeBase64(NSString* string) {
}
- (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<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:method]) {
return nil;
}
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
return [(GCDWebServerRequest*)[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
}
asyncProcessBlock:block];
}
@ -898,16 +896,14 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
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<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:method]) {
return nil;
}
if ([urlPath caseInsensitiveCompare:path] != NSOrderedSame) {
return nil;
}
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
return [(GCDWebServerRequest*)[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
}
asyncProcessBlock:block];
} else {
@ -927,8 +923,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
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<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:method]) {
return nil;
}
@ -951,10 +946,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
}
}
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
GCDWebServerRequest* request = [(GCDWebServerRequest*)[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
return request;
}
asyncProcessBlock:block];
} else {
@ -971,11 +965,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
path:path
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
response.cacheControlMaxAge = cacheAge;
return response;
}];
}
@ -984,7 +976,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
path:path
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
GCDWebServerResponse* response = nil;
if (allowRangeRequests) {
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
@ -994,34 +985,33 @@ static inline NSString* _EncodeBase64(NSString* string) {
}
response.cacheControlMaxAge = cacheAge;
return response;
}];
}
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:path];
if (enumerator == nil) {
NSArray* contents = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
if (contents == nil) {
return nil;
}
NSMutableString* html = [NSMutableString string];
[html appendString:@"<!DOCTYPE html>\n"];
[html appendString:@"<html><head><meta charset=\"utf-8\"></head><body>\n"];
[html appendString:@"<ul>\n"];
for (NSString* file in enumerator) {
if (![file hasPrefix:@"."]) {
NSString* type = [[enumerator fileAttributes] objectForKey:NSFileType];
for (NSString* entry in contents) {
if (![entry hasPrefix:@"."]) {
NSString* type = [[[NSFileManager defaultManager] attributesOfItemAtPath:[path stringByAppendingPathComponent:entry] error:NULL] objectForKey:NSFileType];
GWS_DCHECK(type);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSString* escapedFile = [file stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString* escapedFile = [entry stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
#pragma clang diagnostic pop
GWS_DCHECK(escapedFile);
if ([type isEqualToString:NSFileTypeRegular]) {
[html appendFormat:@"<li><a href=\"%@\">%@</a></li>\n", escapedFile, file];
[html appendFormat:@"<li><a href=\"%@\">%@</a></li>\n", escapedFile, entry];
} else if ([type isEqualToString:NSFileTypeDirectory]) {
[html appendFormat:@"<li><a href=\"%@/\">%@/</a></li>\n", escapedFile, file];
[html appendFormat:@"<li><a href=\"%@/\">%@/</a></li>\n", escapedFile, entry];
}
}
[enumerator skipDescendents];
}
[html appendString:@"</ul>\n"];
[html appendString:@"</body></html>\n"];
@ -1031,8 +1021,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
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<NSString*, NSString*>* requestHeaders, NSString* urlPath, NSDictionary<NSString*, NSString*>* urlQuery) {
if (![requestMethod isEqualToString:@"GET"]) {
return nil;
}
@ -1040,12 +1029,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
return nil;
}
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* filePath = [directoryPath stringByAppendingPathComponent:GCDWebServerNormalizePath([request.path substringFromIndex:basePath.length])];
NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
if (fileType) {
if ([fileType isEqualToString:NSFileTypeDirectory]) {
@ -1072,7 +1059,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
}
return response;
}];
} else {
GWS_DNOT_REACHED();
@ -1091,6 +1077,14 @@ static inline NSString* _EncodeBase64(NSString* string) {
#endif
}
+ (void)setBuiltInLogger:(GCDWebServerBuiltInLoggerBlock)block {
#if defined(__GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__)
_builtInLoggerBlock = block;
#else
GWS_DNOT_REACHED(); // Built-in logger must be enabled in order to override
#endif
}
- (void)logVerbose:(NSString*)format, ... {
va_list arguments;
va_start(arguments, format);
@ -1148,9 +1142,9 @@ static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData,
if (httpSocket > 0) {
struct sockaddr_in addr4;
bzero(&addr4, sizeof(addr4));
addr4.sin_len = sizeof(port);
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8080);
addr4.sin_port = htons(port);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
if (connect(httpSocket, (void*)&addr4, sizeof(addr4)) == 0) {
if (write(httpSocket, inData.bytes, inData.length) == (ssize_t)inData.length) {
@ -1190,7 +1184,7 @@ static void _LogResult(NSString* format, ...) {
fprintf(stdout, "%s\n", [message UTF8String]);
}
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path {
- (NSInteger)runTestsWithOptions:(NSDictionary<NSString*, id>*)options inDirectory:(NSString*)path {
GWS_DCHECK([NSThread isMainThread]);
NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs
NSInteger result = -1;
@ -1198,7 +1192,7 @@ static void _LogResult(NSString* format, ...) {
_ExecuteMainThreadRunLoopSources();
result = 0;
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
NSArray* files = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL] sortedArrayUsingSelector:@selector(localizedStandardCompare:)];
for (NSString* requestFile in files) {
if (![requestFile hasSuffix:@".request"]) {
continue;
@ -1258,7 +1252,7 @@ static void _LogResult(NSString* format, ...) {
if ([actualContentLength isEqualToString:expectedContentLength] && (actualBody.length > expectedBody.length)) { // Handle web browser closing connection before retrieving entire body (e.g. when playing a video file)
actualBody = [actualBody subdataWithRange:NSMakeRange(0, expectedBody.length)];
}
if (![actualBody isEqualToData:expectedBody]) {
if ((actualBody && expectedBody && ![actualBody isEqualToData:expectedBody]) || (actualBody && !expectedBody) || (!actualBody && expectedBody)) {
_LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
success = NO;
#if !TARGET_OS_IPHONE

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -130,7 +130,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* The default implementation returns the original URL.
*/
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers;
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers;
/**
* Assuming a valid HTTP request was received, this method is called before

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -193,22 +193,18 @@ NS_ASSUME_NONNULL_END
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked"));
}
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
CFHTTPMessageSetHeaderFieldValue(self->_responseMessage, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
}];
[self writeHeadersWithCompletionBlock:^(BOOL success) {
if (success) {
if (hasBody) {
[self writeBodyWithCompletionBlock:^(BOOL successInner) {
[_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
[self->_response performClose]; // TODO: There's nothing we can do on failure as headers have already been sent
}];
}
} else if (hasBody) {
[_response performClose];
[self->_response performClose];
}
}];
} else {
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
@ -238,15 +234,13 @@ NS_ASSUME_NONNULL_END
if (length) {
[self readBodyWithRemainingLength:length
completionBlock:^(BOOL success) {
NSError* localError = nil;
if ([_request performClose:&localError]) {
if ([self->_request performClose:&localError]) {
[self _startProcessingRequest];
} else {
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
}
}];
} else {
if ([_request performClose:&error]) {
@ -269,15 +263,13 @@ NS_ASSUME_NONNULL_END
NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData];
[self readNextBodyChunk:chunkData
completionBlock:^(BOOL success) {
NSError* localError = nil;
if ([_request performClose:&localError]) {
if ([self->_request performClose:&localError]) {
[self _startProcessingRequest];
} else {
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", self->_socket, error);
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
}
}];
}
@ -286,15 +278,14 @@ NS_ASSUME_NONNULL_END
NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
[self readHeaders:headersData
withCompletionBlock:^(NSData* extraData) {
if (extraData) {
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(_requestMessage)); // Method verbs are case-sensitive and uppercase
if (_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(self->_requestMessage)); // Method verbs are case-sensitive and uppercase
if (self->_server.shouldAutomaticallyMapHEADToGET && [requestMethod isEqualToString:@"HEAD"]) {
requestMethod = @"GET";
_virtualHEAD = YES;
self->_virtualHEAD = YES;
}
NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage));
NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(self->_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(self->_requestMessage));
if (requestURL) {
requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders];
GWS_DCHECK(requestURL);
@ -307,55 +298,53 @@ NS_ASSUME_NONNULL_END
NSString* queryString = requestURL ? CFBridgingRelease(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
for (_handler in _server.handlers) {
_request = _handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
if (_request) {
for (self->_handler in self->_server.handlers) {
self->_request = self->_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery);
if (self->_request) {
break;
}
}
if (_request) {
_request.localAddressData = self.localAddressData;
_request.remoteAddressData = self.remoteAddressData;
if ([_request hasBody]) {
[_request prepareForWriting];
if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
if (self->_request) {
self->_request.localAddressData = self.localAddressData;
self->_request.remoteAddressData = self.remoteAddressData;
if ([self->_request hasBody]) {
[self->_request prepareForWriting];
if (self->_request.usesChunkedTransferEncoding || (extraData.length <= self->_request.contentLength)) {
NSString* expectHeader = [requestHeaders objectForKey:@"Expect"];
if (expectHeader) {
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { // TODO: Actually validate request before continuing
[self writeData:_continueData
withCompletionBlock:^(BOOL success) {
if (success) {
if (_request.usesChunkedTransferEncoding) {
if (self->_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
[self _readBodyWithLength:self->_request.contentLength initialData:extraData];
}
}
}];
} else {
GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", self->_socket);
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
}
} else {
if (_request.usesChunkedTransferEncoding) {
if (self->_request.usesChunkedTransferEncoding) {
[self _readChunkedBodyWithInitialData:extraData];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
[self _readBodyWithLength:self->_request.contentLength initialData:extraData];
}
}
} else {
GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", self->_socket);
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
}
} else {
[self _startProcessingRequest];
}
} else {
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
GWS_DCHECK(_request);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented];
self->_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
GWS_DCHECK(self->_request);
[self abortRequest:self->_request withStatusCode:kGCDWebServerHTTPStatusCode_NotImplemented];
}
} else {
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
@ -364,7 +353,6 @@ NS_ASSUME_NONNULL_END
} else {
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
}
}];
}
@ -426,7 +414,6 @@ NS_ASSUME_NONNULL_END
- (void)readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
dispatch_read(_socket, length, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t buffer, int error) {
@autoreleasepool {
if (error == 0) {
size_t size = dispatch_data_get_size(buffer);
@ -439,19 +426,18 @@ NS_ASSUME_NONNULL_END
[self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
block(YES);
} else {
if (_totalBytesRead > 0) {
GWS_LOG_ERROR(@"No more data available on socket %i", _socket);
if (self->_totalBytesRead > 0) {
GWS_LOG_ERROR(@"No more data available on socket %i", self->_socket);
} else {
GWS_LOG_WARNING(@"No data received from socket %i", _socket);
GWS_LOG_WARNING(@"No data received from socket %i", self->_socket);
}
block(NO);
}
} else {
GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", self->_socket, strerror(error), error);
block(NO);
}
}
});
}
@ -460,29 +446,27 @@ NS_ASSUME_NONNULL_END
[self readData:headersData
withLength:NSUIntegerMax
completionBlock:^(BOOL success) {
if (success) {
NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
if (range.location == NSNotFound) {
[self readHeaders:headersData withCompletionBlock:block];
} else {
NSUInteger length = range.location + range.length;
if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
if (CFHTTPMessageAppendBytes(self->_requestMessage, headersData.bytes, length)) {
if (CFHTTPMessageIsHeaderComplete(self->_requestMessage)) {
block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
} else {
GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", self->_socket);
block(nil);
}
} else {
GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", self->_socket);
block(nil);
}
}
} else {
block(nil);
}
}];
}
@ -492,11 +476,10 @@ NS_ASSUME_NONNULL_END
[self readData:bodyData
withLength:length
completionBlock:^(BOOL success) {
if (success) {
if (bodyData.length <= length) {
NSError* error = nil;
if ([_request performWriteData:bodyData error:&error]) {
if ([self->_request performWriteData:bodyData error:&error]) {
NSUInteger remainingLength = length - bodyData.length;
if (remainingLength) {
[self readBodyWithRemainingLength:remainingLength completionBlock:block];
@ -504,18 +487,17 @@ NS_ASSUME_NONNULL_END
block(YES);
}
} else {
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", self->_socket, error);
block(NO);
}
} else {
GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket);
GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", self->_socket);
block(NO);
GWS_DNOT_REACHED();
}
} else {
block(NO);
}
}];
}
@ -575,13 +557,11 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
[self readData:chunkData
withLength:NSUIntegerMax
completionBlock:^(BOOL success) {
if (success) {
[self readNextBodyChunk:chunkData completionBlock:block];
} else {
block(NO);
}
}];
}
@ -594,18 +574,16 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
[data self]; // Keeps ARC from releasing data too early
});
dispatch_write(_socket, buffer, dispatch_get_global_queue(_server.dispatchQueuePriority, 0), ^(dispatch_data_t remainingData, int error) {
@autoreleasepool {
if (error == 0) {
GWS_DCHECK(remainingData == NULL);
[self didWriteBytes:data.bytes length:data.length];
block(YES);
} else {
GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
GWS_LOG_ERROR(@"Error while writing to socket %i: %s (%i)", self->_socket, strerror(error), error);
block(NO);
}
}
});
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
dispatch_release(buffer);
@ -622,15 +600,14 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
- (void)writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
GWS_DCHECK([_response hasBody]);
[_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
if (data) {
if (data.length) {
if (_response.usesChunkedTransferEncoding) {
if (self->_response.usesChunkedTransferEncoding) {
const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
size_t hexLength = strlen(hexString);
NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
if (chunk == nil) {
GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error);
GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", self->_socket, error);
block(NO);
return;
}
@ -647,31 +624,26 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
}
[self writeData:data
withCompletionBlock:^(BOOL success) {
if (success) {
[self writeBodyWithCompletionBlock:block];
} else {
block(NO);
}
}];
} else {
if (_response.usesChunkedTransferEncoding) {
if (self->_response.usesChunkedTransferEncoding) {
[self writeData:_lastChunkData
withCompletionBlock:^(BOOL success) {
block(success);
}];
} else {
block(YES);
}
}
} else {
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", self->_socket, error);
block(NO);
}
}];
}
@ -726,7 +698,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
#endif
}
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers {
- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary<NSString*, NSString*>*)headers {
return url;
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -41,7 +41,7 @@ extern "C" {
* types. Keys of the dictionary must be lowercased file extensions without
* the period, and the values must be the corresponding MIME types.
*/
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* _Nullable overrides);
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary<NSString*, NSString*>* _Nullable overrides);
/**
* Add percent-escapes to a string so it can be used in a URL.
@ -60,7 +60,7 @@ NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string);
* "application/x-www-form-urlencoded" form.
* http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
*/
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
NSDictionary<NSString*, NSString*>* GCDWebServerParseURLEncodedForm(NSString* form);
/**
* On OS X, returns the IPv4 or IPv6 address as a string of the primary
@ -102,6 +102,11 @@ NSString* GCDWebServerFormatISO8601(NSDate* date);
*/
NSDate* _Nullable GCDWebServerParseISO8601(NSString* string);
/**
* Removes "//", "/./" and "/../" components from path as well as any trailing slash.
*/
NSString* GCDWebServerNormalizePath(NSString* path);
#ifdef __cplusplus
}
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -166,8 +166,8 @@ NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length];
}
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* overrides) {
NSDictionary* builtInOverrides = @{ @"css" : @"text/css" };
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary<NSString*, NSString*>* overrides) {
NSDictionary* builtInOverrides = @{@"css" : @"text/css"};
NSString* mimeType = nil;
extension = [extension lowercaseString];
if (extension.length) {
@ -200,7 +200,7 @@ NSString* GCDWebServerUnescapeURLString(NSString* string) {
#pragma clang diagnostic pop
}
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
NSDictionary<NSString*, NSString*>* GCDWebServerParseURLEncodedForm(NSString* form) {
NSMutableDictionary* parameters = [NSMutableDictionary dictionary];
NSScanner* scanner = [[NSScanner alloc] initWithString:form];
[scanner setCharactersToBeSkipped:nil];
@ -314,3 +314,18 @@ NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
buffer[2 * CC_MD5_DIGEST_LENGTH] = 0;
return (NSString*)[NSString stringWithUTF8String:buffer];
}
NSString* GCDWebServerNormalizePath(NSString* path) {
NSMutableArray* components = [[NSMutableArray alloc] init];
for (NSString* component in [path componentsSeparatedByString:@"/"]) {
if ([component isEqualToString:@".."]) {
[components removeLastObject];
} else if (component.length && ![component isEqualToString:@"."]) {
[components addObject:component];
}
}
if (path.length && ([path characterAtIndex:0] == '/')) {
return [@"/" stringByAppendingString:[components componentsJoinedByString:@"/"]]; // Preserve initial slash
}
return [components componentsJoinedByString:@"/"];
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -170,7 +170,7 @@ static inline NSError* GCDWebServerMakePosixError(int code) {
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}];
}
extern void GCDWebServerInitializeFunctions();
extern void GCDWebServerInitializeFunctions(void);
extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value);
extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value);
extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute);
@ -185,11 +185,11 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
@end
@interface GCDWebServer ()
@property(nonatomic, readonly) NSMutableArray* handlers;
@property(nonatomic, readonly) NSMutableArray<GCDWebServerHandler*>* handlers;
@property(nonatomic, readonly, nullable) NSString* serverName;
@property(nonatomic, readonly, nullable) NSString* authenticationRealm;
@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationBasicAccounts;
@property(nonatomic, readonly, nullable) NSMutableDictionary* authenticationDigestAccounts;
@property(nonatomic, readonly, nullable) NSMutableDictionary<NSString*, NSString*>* authenticationBasicAccounts;
@property(nonatomic, readonly, nullable) NSMutableDictionary<NSString*, NSString*>* authenticationDigestAccounts;
@property(nonatomic, readonly) BOOL shouldAutomaticallyMapHEADToGET;
@property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority;
- (void)willStartConnection:(GCDWebServerConnection*)connection;
@ -213,7 +213,7 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
@end
@interface GCDWebServerResponse ()
@property(nonatomic, readonly) NSDictionary* additionalHeaders;
@property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* additionalHeaders;
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
- (void)prepareForReading;
- (BOOL)performOpen:(NSError**)error;

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -102,7 +102,7 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
/**
* Returns the HTTP headers for the request.
*/
@property(nonatomic, readonly) NSDictionary* headers;
@property(nonatomic, readonly) NSDictionary<NSString*, NSString*>* headers;
/**
* Returns the path component of the URL for the request.
@ -114,7 +114,7 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
*
* @warning This property will be nil if there is no query in the URL.
*/
@property(nonatomic, readonly, nullable) NSDictionary* query;
@property(nonatomic, readonly, nullable) NSDictionary<NSString*, NSString*>* query;
/**
* Returns the content type for the body of the request parsed from the
@ -186,7 +186,7 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
/**
* This method is the designated initializer for the class.
*/
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(nullable NSDictionary*)query;
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary<NSString*, NSString*>*)headers path:(NSString*)path query:(nullable NSDictionary<NSString*, NSString*>*)query;
/**
* Convenience method that checks if the contentType property is defined.

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -136,12 +136,12 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
@implementation GCDWebServerRequest {
BOOL _opened;
NSMutableArray* _decoders;
NSMutableArray<GCDWebServerBodyDecoder*>* _decoders;
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
NSMutableDictionary* _attributes;
NSMutableDictionary<NSString*, id>* _attributes;
}
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary<NSString*, NSString*>*)headers path:(NSString*)path query:(NSDictionary<NSString*, NSString*>*)query {
if ((self = [super init])) {
_method = [method copy];
_URL = url;
@ -188,7 +188,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
if ([rangeHeader hasPrefix:@"bytes="]) {
NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","];
if (components.count == 1) {
components = [[components firstObject] componentsSeparatedByString:@"-"];
components = [(NSString*)[components firstObject] componentsSeparatedByString:@"-"];
if (components.count == 2) {
NSString* startString = [components objectAtIndex:0];
NSInteger startValue = [startString integerValue];

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -33,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
* The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the
* GCDWebServerBodyReader object when reading data from it asynchronously.
*/
typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* _Nullable error);
typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* _Nullable data, NSError* _Nullable error);
/**
* This protocol is used by the GCDWebServerConnection to communicate with

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2015, Pierre-Olivier Latour
Copyright (c) 2012-2019, Pierre-Olivier Latour
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -150,12 +150,12 @@
@implementation GCDWebServerResponse {
BOOL _opened;
NSMutableArray* _encoders;
NSMutableArray<GCDWebServerBodyEncoder*>* _encoders;
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
}
+ (instancetype)response {
return [[[self class] alloc] init];
return [(GCDWebServerResponse*)[[self class] alloc] init];
}
- (instancetype)init {
@ -259,11 +259,11 @@
@implementation GCDWebServerResponse (Extensions)
+ (instancetype)responseWithStatusCode:(NSInteger)statusCode {
return [[self alloc] initWithStatusCode:statusCode];
return [(GCDWebServerResponse*)[self alloc] initWithStatusCode:statusCode];
}
+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
return [[self alloc] initWithRedirect:location permanent:permanent];
return [(GCDWebServerResponse*)[self alloc] initWithRedirect:location permanent:permanent];
}
- (instancetype)initWithStatusCode:(NSInteger)statusCode {