Upgrade pods: Alamofire GCDWebServer MASShortcut
This commit is contained in:
25
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.h
generated
25
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.h
generated
@ -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
|
||||
|
||||
|
192
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.m
generated
192
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.m
generated
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:@"/"];
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user