Make project build pass with swift 4.0
- pod update - Force compile rxcocoa and rxswift with swift 3.2
This commit is contained in:
51
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.h
generated
51
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.h
generated
@ -30,6 +30,8 @@
|
||||
#import "GCDWebServerRequest.h"
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The GCDWebServerMatchBlock is called for every handler added to the
|
||||
* GCDWebServer whenever a new HTTP request has started (i.e. HTTP headers have
|
||||
@ -40,7 +42,7 @@
|
||||
* GCDWebServerRequest instance created with the same basic info.
|
||||
* Otherwise, it simply returns nil.
|
||||
*/
|
||||
typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
|
||||
typedef GCDWebServerRequest* _Nullable (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
|
||||
|
||||
/**
|
||||
* The GCDWebServerProcessBlock is called after the HTTP request has been fully
|
||||
@ -52,7 +54,7 @@ typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod,
|
||||
* recommended to return a GCDWebServerErrorResponse on error so more useful
|
||||
* information can be returned to the client.
|
||||
*/
|
||||
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(__kindof GCDWebServerRequest* request);
|
||||
typedef GCDWebServerResponse* _Nullable (^GCDWebServerProcessBlock)(__kindof GCDWebServerRequest* request);
|
||||
|
||||
/**
|
||||
* The GCDWebServerAsynchronousProcessBlock works like the GCDWebServerProcessBlock
|
||||
@ -64,7 +66,7 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(__kindof GCDWebServerR
|
||||
* It's however recommended to return a GCDWebServerErrorResponse on error so more
|
||||
* useful information can be returned to the client.
|
||||
*/
|
||||
typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* response);
|
||||
typedef void (^GCDWebServerCompletionBlock)(GCDWebServerResponse* _Nullable response);
|
||||
typedef void (^GCDWebServerAsyncProcessBlock)(__kindof GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock);
|
||||
|
||||
/**
|
||||
@ -295,7 +297,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
/**
|
||||
* Sets the delegate for the server.
|
||||
*/
|
||||
@property(nonatomic, assign) id<GCDWebServerDelegate> delegate;
|
||||
@property(nonatomic, weak, nullable) id<GCDWebServerDelegate> delegate;
|
||||
|
||||
/**
|
||||
* Returns YES if the server is currently running.
|
||||
@ -315,7 +317,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* bonjourName;
|
||||
@property(nonatomic, readonly, nullable) NSString* bonjourName;
|
||||
|
||||
/**
|
||||
* Returns the Bonjour service type used by the server.
|
||||
@ -323,7 +325,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
* @warning This property is only valid if the server is running and Bonjour
|
||||
* registration has successfully completed, which can take up to a few seconds.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* bonjourType;
|
||||
@property(nonatomic, readonly, nullable) NSString* bonjourType;
|
||||
|
||||
/**
|
||||
* This method is the designated initializer for the class.
|
||||
@ -363,7 +365,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* Returns NO if the server failed to start and sets "error" argument if not NULL.
|
||||
*/
|
||||
- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error;
|
||||
- (BOOL)startWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error;
|
||||
|
||||
/**
|
||||
* Stops the server and prevents it to accepts new HTTP requests.
|
||||
@ -383,7 +385,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* @warning This property is only valid if the server is running.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL* serverURL;
|
||||
@property(nonatomic, readonly, nullable) NSURL* serverURL;
|
||||
|
||||
/**
|
||||
* Returns the server's Bonjour URL.
|
||||
@ -393,7 +395,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
* Also be aware this property will not automatically update if the Bonjour hostname
|
||||
* has been dynamically changed after the server started running (this should be rare).
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL* bonjourServerURL;
|
||||
@property(nonatomic, readonly, nullable) NSURL* bonjourServerURL;
|
||||
|
||||
/**
|
||||
* Returns the server's public URL.
|
||||
@ -401,7 +403,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
* @warning This property is only valid if the server is running and NAT port
|
||||
* mapping is active.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSURL* publicServerURL;
|
||||
@property(nonatomic, readonly, nullable) NSURL* publicServerURL;
|
||||
|
||||
/**
|
||||
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
||||
@ -418,7 +420,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* Returns NO if the server failed to start.
|
||||
*/
|
||||
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name;
|
||||
- (BOOL)startWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name;
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
|
||||
@ -431,7 +433,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* @warning This method must be used from the main thread only.
|
||||
*/
|
||||
- (BOOL)runWithPort:(NSUInteger)port bonjourName:(NSString*)name;
|
||||
- (BOOL)runWithPort:(NSUInteger)port bonjourName:(nullable NSString*)name;
|
||||
|
||||
/**
|
||||
* Runs the server synchronously using -startWithOptions: until a SIGTERM or
|
||||
@ -442,7 +444,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* @warning This method must be used from the main thread only.
|
||||
*/
|
||||
- (BOOL)runWithOptions:(NSDictionary*)options error:(NSError**)error;
|
||||
- (BOOL)runWithOptions:(nullable NSDictionary*)options error:(NSError** _Nullable)error;
|
||||
|
||||
#endif
|
||||
|
||||
@ -498,7 +500,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
* with a specific case-insensitive path with in-memory data.
|
||||
*/
|
||||
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge;
|
||||
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(nullable NSString*)contentType cacheAge:(NSUInteger)cacheAge;
|
||||
|
||||
/**
|
||||
* Adds a handler to the server to respond to incoming "GET" HTTP requests
|
||||
@ -515,7 +517,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
* The "indexFilename" argument allows to specify an "index" file name to use
|
||||
* when the request path corresponds to a directory.
|
||||
*/
|
||||
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
|
||||
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(nullable NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests;
|
||||
|
||||
@end
|
||||
|
||||
@ -530,11 +532,10 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* Currently supported third-party logging facilities are:
|
||||
* - XLFacility (by the same author as GCDWebServer): https://github.com/swisspol/XLFacility
|
||||
* - CocoaLumberjack: https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* For both the built-in logging facility and CocoaLumberjack, the default
|
||||
* logging level is INFO (or DEBUG if the preprocessor constant "DEBUG"
|
||||
* evaluates to non-zero at compile time).
|
||||
* For the built-in logging facility, the default logging level is INFO
|
||||
* (or DEBUG if the preprocessor constant "DEBUG" evaluates to non-zero at
|
||||
* compile time).
|
||||
*
|
||||
* It's possible to have GCDWebServer use a custom logging facility by defining
|
||||
* the "__GCDWEBSERVER_LOGGING_HEADER__" preprocessor constant in Xcode build
|
||||
@ -575,22 +576,22 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
/**
|
||||
* Logs a message to the logging facility at the VERBOSE level.
|
||||
*/
|
||||
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
||||
- (void)logVerbose:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the INFO level.
|
||||
*/
|
||||
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
||||
- (void)logInfo:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the WARNING level.
|
||||
*/
|
||||
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
||||
- (void)logWarning:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
/**
|
||||
* Logs a message to the logging facility at the ERROR level.
|
||||
*/
|
||||
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
|
||||
- (void)logError:(NSString*)format, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
@end
|
||||
|
||||
@ -612,8 +613,10 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
||||
*
|
||||
* Returns the number of failed tests or -1 if server failed to start.
|
||||
*/
|
||||
- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path;
|
||||
- (NSInteger)runTestsWithOptions:(nullable NSDictionary*)options inDirectory:(NSString*)path;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
304
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.m
generated
304
Pods/GCDWebServer/GCDWebServer/Core/GCDWebServer.m
generated
@ -77,12 +77,6 @@ GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Debug;
|
||||
#else
|
||||
GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Info;
|
||||
#endif
|
||||
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__)
|
||||
#if DEBUG
|
||||
DDLogLevel GCDWebServerLogLevel = DDLogLevelDebug;
|
||||
#else
|
||||
DDLogLevel GCDWebServerLogLevel = DDLogLevelInfo;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
@ -132,18 +126,9 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
|
||||
#endif
|
||||
|
||||
@interface GCDWebServerHandler () {
|
||||
@private
|
||||
GCDWebServerMatchBlock _matchBlock;
|
||||
GCDWebServerAsyncProcessBlock _asyncProcessBlock;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerHandler
|
||||
|
||||
@synthesize matchBlock=_matchBlock, asyncProcessBlock=_asyncProcessBlock;
|
||||
|
||||
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
||||
- (instancetype)initWithMatchBlock:(GCDWebServerMatchBlock _Nonnull)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock _Nonnull)processBlock {
|
||||
if ((self = [super init])) {
|
||||
_matchBlock = [matchBlock copy];
|
||||
_asyncProcessBlock = [processBlock copy];
|
||||
@ -153,26 +138,19 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServer () {
|
||||
@private
|
||||
id<GCDWebServerDelegate> __unsafe_unretained _delegate;
|
||||
@implementation GCDWebServer {
|
||||
dispatch_queue_t _syncQueue;
|
||||
dispatch_group_t _sourceGroup;
|
||||
NSMutableArray* _handlers;
|
||||
NSInteger _activeConnections; // Accessed through _syncQueue only
|
||||
BOOL _connected; // Accessed on main thread only
|
||||
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
|
||||
|
||||
|
||||
NSDictionary* _options;
|
||||
NSString* _serverName;
|
||||
NSString* _authenticationRealm;
|
||||
NSMutableDictionary* _authenticationBasicAccounts;
|
||||
NSMutableDictionary* _authenticationDigestAccounts;
|
||||
Class _connectionClass;
|
||||
BOOL _mapHEADToGET;
|
||||
CFTimeInterval _disconnectDelay;
|
||||
dispatch_queue_priority_t _dispatchQueuePriority;
|
||||
NSUInteger _port;
|
||||
dispatch_source_t _source4;
|
||||
dispatch_source_t _source6;
|
||||
CFNetServiceRef _registrationService;
|
||||
@ -191,13 +169,6 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
BOOL _recording;
|
||||
#endif
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServer
|
||||
|
||||
@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm,
|
||||
authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts,
|
||||
shouldAutomaticallyMapHEADToGET=_mapHEADToGET, dispatchQueuePriority=_dispatchQueuePriority;
|
||||
|
||||
+ (void)initialize {
|
||||
GCDWebServerInitializeFunctions();
|
||||
@ -220,7 +191,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
GWS_DCHECK(_activeConnections == 0);
|
||||
GWS_DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
|
||||
GWS_DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
|
||||
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||
dispatch_release(_sourceGroup);
|
||||
dispatch_release(_syncQueue);
|
||||
@ -235,10 +206,10 @@ 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();
|
||||
@ -253,13 +224,13 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
GWS_DCHECK(_connected == NO);
|
||||
_connected = YES;
|
||||
GWS_LOG_DEBUG(@"Did connect");
|
||||
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||
[self _startBackgroundTask];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) {
|
||||
[_delegate webServerDidConnect:self];
|
||||
}
|
||||
@ -267,7 +238,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
|
||||
- (void)willStartConnection:(GCDWebServerConnection*)connection {
|
||||
dispatch_sync(_syncQueue, ^{
|
||||
|
||||
|
||||
GWS_DCHECK(_activeConnections >= 0);
|
||||
if (_activeConnections == 0) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@ -282,7 +253,7 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
});
|
||||
}
|
||||
_activeConnections += 1;
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -309,11 +280,11 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
GWS_DCHECK(_connected == YES);
|
||||
_connected = NO;
|
||||
GWS_LOG_DEBUG(@"Did disconnect");
|
||||
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
[self _endBackgroundTask];
|
||||
#endif
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(webServerDidDisconnect:)]) {
|
||||
[_delegate webServerDidDisconnect:self];
|
||||
}
|
||||
@ -356,9 +327,10 @@ static void _ExecuteMainThreadRunLoopSources() {
|
||||
}
|
||||
|
||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
||||
[self addHandlerWithMatchBlock:matchBlock asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(processBlock(request));
|
||||
}];
|
||||
[self addHandlerWithMatchBlock:matchBlock
|
||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(processBlock(request));
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
||||
@ -464,7 +436,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
if (listeningSocket > 0) {
|
||||
int yes = 1;
|
||||
setsockopt(listeningSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
|
||||
|
||||
if (bind(listeningSocket, address, length) == 0) {
|
||||
if (listen(listeningSocket, (int)maxPendingConnections) == 0) {
|
||||
GWS_LOG_DEBUG(@"Did open %s listening socket %i", useIPv6 ? "IPv6" : "IPv4", listeningSocket);
|
||||
@ -483,7 +455,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno);
|
||||
close(listeningSocket);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
if (error) {
|
||||
*error = GCDWebServerMakePosixError(errno);
|
||||
@ -497,7 +469,7 @@ 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) {
|
||||
@ -507,17 +479,17 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
}
|
||||
}
|
||||
dispatch_group_leave(_sourceGroup);
|
||||
|
||||
|
||||
});
|
||||
dispatch_source_set_event_handler(source, ^{
|
||||
|
||||
|
||||
@autoreleasepool {
|
||||
struct sockaddr_storage remoteSockAddr;
|
||||
socklen_t remoteAddrLen = sizeof(remoteSockAddr);
|
||||
int socket = accept(listeningSocket, (struct sockaddr*)&remoteSockAddr, &remoteAddrLen);
|
||||
if (socket > 0) {
|
||||
NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen];
|
||||
|
||||
|
||||
struct sockaddr_storage localSockAddr;
|
||||
socklen_t localAddrLen = sizeof(localSockAddr);
|
||||
NSData* localAddress = nil;
|
||||
@ -527,28 +499,28 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
[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;
|
||||
}
|
||||
|
||||
- (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];
|
||||
|
||||
|
||||
struct sockaddr_in addr4;
|
||||
bzero(&addr4, sizeof(addr4));
|
||||
addr4.sin_len = sizeof(addr4);
|
||||
@ -568,7 +540,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
GWS_LOG_ERROR(@"Failed retrieving socket address: %s (%i)", strerror(errno), errno);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct sockaddr_in6 addr6;
|
||||
bzero(&addr6, sizeof(addr6));
|
||||
addr6.sin6_len = sizeof(addr6);
|
||||
@ -580,7 +552,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
close(listeningSocket4);
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
_serverName = [_GetOption(_options, GCDWebServerOption_ServerName, NSStringFromClass([self class])) copy];
|
||||
NSString* authenticationMethod = _GetOption(_options, GCDWebServerOption_AuthenticationMethod, nil);
|
||||
if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_Basic]) {
|
||||
@ -599,27 +571,27 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
}];
|
||||
}
|
||||
_connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]);
|
||||
_mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue];
|
||||
_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];
|
||||
|
||||
|
||||
_source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO];
|
||||
_source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES];
|
||||
_port = port;
|
||||
_bindToLocalhost = bindToLocalhost;
|
||||
|
||||
|
||||
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, nil);
|
||||
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
||||
if (bonjourName) {
|
||||
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (__bridge CFStringRef)bonjourType, (__bridge CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
|
||||
if (_registrationService) {
|
||||
CFNetServiceClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
||||
|
||||
|
||||
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
|
||||
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||
CFStreamError streamError = {0};
|
||||
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
|
||||
|
||||
|
||||
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
|
||||
if (_resolutionService) {
|
||||
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
||||
@ -631,7 +603,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
GWS_LOG_ERROR(@"Failed creating CFNetService for registration");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ([_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) {
|
||||
@ -654,7 +626,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dispatch_resume(_source4);
|
||||
dispatch_resume(_source6);
|
||||
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
|
||||
@ -663,13 +635,13 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
[_delegate webServerDidStart:self];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_stop {
|
||||
GWS_DCHECK(_source4 != NULL);
|
||||
|
||||
|
||||
if (_dnsService) {
|
||||
_dnsAddress = nil;
|
||||
_dnsPort = 0;
|
||||
@ -685,7 +657,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
DNSServiceRefDeallocate(_dnsService);
|
||||
_dnsService = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (_registrationService) {
|
||||
if (_resolutionService) {
|
||||
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||
@ -700,7 +672,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
CFRelease(_registrationService);
|
||||
_registrationService = NULL;
|
||||
}
|
||||
|
||||
|
||||
dispatch_source_cancel(_source6);
|
||||
dispatch_source_cancel(_source4);
|
||||
dispatch_group_wait(_sourceGroup, DISPATCH_TIME_FOREVER); // Wait until the cancellation handlers have been called which guarantees the listening sockets are closed
|
||||
@ -714,12 +686,12 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
_source4 = NULL;
|
||||
_port = 0;
|
||||
_bindToLocalhost = NO;
|
||||
|
||||
|
||||
_serverName = nil;
|
||||
_authenticationRealm = nil;
|
||||
_authenticationBasicAccounts = nil;
|
||||
_authenticationDigestAccounts = nil;
|
||||
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (_disconnectTimer) {
|
||||
CFRunLoopTimerInvalidate(_disconnectTimer);
|
||||
@ -728,7 +700,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
[self _didDisconnect];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
GWS_LOG_INFO(@"%@ stopped", [self class]);
|
||||
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@ -896,32 +868,38 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
@implementation GCDWebServer (Handlers)
|
||||
|
||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||
[self addDefaultHandlerForMethod:method requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(block(request));
|
||||
}];
|
||||
[self addDefaultHandlerForMethod:method
|
||||
requestClass:aClass
|
||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(block(request));
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||
|
||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||
|
||||
if (![requestMethod isEqualToString:method]) {
|
||||
return nil;
|
||||
}
|
||||
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||
|
||||
} asyncProcessBlock:block];
|
||||
|
||||
}
|
||||
asyncProcessBlock:block];
|
||||
}
|
||||
|
||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||
[self addHandlerForMethod:method path:path requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(block(request));
|
||||
}];
|
||||
[self addHandlerForMethod:method
|
||||
path:path
|
||||
requestClass:aClass
|
||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(block(request));
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
||||
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||
|
||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest*(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||
|
||||
if (![requestMethod isEqualToString:method]) {
|
||||
return nil;
|
||||
}
|
||||
@ -929,24 +907,28 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
return nil;
|
||||
}
|
||||
return [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||
|
||||
} asyncProcessBlock:block];
|
||||
|
||||
}
|
||||
asyncProcessBlock:block];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||
[self addHandlerForMethod:method pathRegex:regex requestClass:aClass asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(block(request));
|
||||
}];
|
||||
[self addHandlerForMethod:method
|
||||
pathRegex:regex
|
||||
requestClass:aClass
|
||||
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||
completionBlock(block(request));
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block {
|
||||
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* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||
|
||||
if (![requestMethod isEqualToString:method]) {
|
||||
return nil;
|
||||
}
|
||||
@ -972,8 +954,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery];
|
||||
[request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures];
|
||||
return request;
|
||||
|
||||
} asyncProcessBlock:block];
|
||||
|
||||
}
|
||||
asyncProcessBlock:block];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
@ -984,29 +967,35 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
@implementation GCDWebServer (GETHandlers)
|
||||
|
||||
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge {
|
||||
[self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||
|
||||
GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
|
||||
response.cacheControlMaxAge = cacheAge;
|
||||
return response;
|
||||
|
||||
}];
|
||||
[self addHandlerForMethod:@"GET"
|
||||
path:path
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
|
||||
GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
|
||||
response.cacheControlMaxAge = cacheAge;
|
||||
return response;
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
||||
[self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||
|
||||
GCDWebServerResponse* response = nil;
|
||||
if (allowRangeRequests) {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
|
||||
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||
} else {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment];
|
||||
}
|
||||
response.cacheControlMaxAge = cacheAge;
|
||||
return response;
|
||||
|
||||
}];
|
||||
[self addHandlerForMethod:@"GET"
|
||||
path:path
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
|
||||
GCDWebServerResponse* response = nil;
|
||||
if (allowRangeRequests) {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
|
||||
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||
} else {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment];
|
||||
}
|
||||
response.cacheControlMaxAge = cacheAge;
|
||||
return response;
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
|
||||
@ -1042,8 +1031,8 @@ 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* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||
|
||||
if (![requestMethod isEqualToString:@"GET"]) {
|
||||
return nil;
|
||||
}
|
||||
@ -1051,39 +1040,40 @@ 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* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
|
||||
if (fileType) {
|
||||
if ([fileType isEqualToString:NSFileTypeDirectory]) {
|
||||
if (indexFilename) {
|
||||
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
||||
NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
|
||||
if ([indexType isEqualToString:NSFileTypeRegular]) {
|
||||
return [GCDWebServerFileResponse responseWithFile:indexPath];
|
||||
|
||||
}
|
||||
processBlock:^GCDWebServerResponse*(GCDWebServerRequest* request) {
|
||||
|
||||
GCDWebServerResponse* response = nil;
|
||||
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
||||
NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
|
||||
if (fileType) {
|
||||
if ([fileType isEqualToString:NSFileTypeDirectory]) {
|
||||
if (indexFilename) {
|
||||
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
||||
NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
|
||||
if ([indexType isEqualToString:NSFileTypeRegular]) {
|
||||
return [GCDWebServerFileResponse responseWithFile:indexPath];
|
||||
}
|
||||
}
|
||||
response = [server _responseWithContentsOfDirectory:filePath];
|
||||
} else if ([fileType isEqualToString:NSFileTypeRegular]) {
|
||||
if (allowRangeRequests) {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
||||
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||
} else {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath];
|
||||
}
|
||||
}
|
||||
}
|
||||
response = [server _responseWithContentsOfDirectory:filePath];
|
||||
} else if ([fileType isEqualToString:NSFileTypeRegular]) {
|
||||
if (allowRangeRequests) {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
||||
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||
if (response) {
|
||||
response.cacheControlMaxAge = cacheAge;
|
||||
} else {
|
||||
response = [GCDWebServerFileResponse responseWithFile:filePath];
|
||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (response) {
|
||||
response.cacheControlMaxAge = cacheAge;
|
||||
} else {
|
||||
response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound];
|
||||
}
|
||||
return response;
|
||||
|
||||
}];
|
||||
return response;
|
||||
|
||||
}];
|
||||
} else {
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
@ -1096,8 +1086,6 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
||||
+ (void)setLogLevel:(int)level {
|
||||
#if defined(__GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__)
|
||||
[XLSharedFacility setMinLogLevel:level];
|
||||
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__)
|
||||
GCDWebServerLogLevel = level;
|
||||
#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__)
|
||||
GCDWebServerLogLevel = level;
|
||||
#endif
|
||||
@ -1204,11 +1192,11 @@ static void _LogResult(NSString* format, ...) {
|
||||
|
||||
- (NSInteger)runTestsWithOptions:(NSDictionary*)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
|
||||
NSArray* ignoredHeaders = @[ @"Date", @"Etag" ]; // Dates are always different by definition and ETags depend on file system node IDs
|
||||
NSInteger result = -1;
|
||||
if ([self startWithOptions:options error:NULL]) {
|
||||
_ExecuteMainThreadRunLoopSources();
|
||||
|
||||
|
||||
result = 0;
|
||||
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
|
||||
for (NSString* requestFile in files) {
|
||||
@ -1230,19 +1218,19 @@ static void _LogResult(NSString* format, ...) {
|
||||
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
||||
NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]];
|
||||
if (responseData) {
|
||||
CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
|
||||
CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromData(responseData, NO);
|
||||
if (expectedResponse) {
|
||||
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromPerformingRequest(requestData, self.port);
|
||||
if (actualResponse) {
|
||||
success = YES;
|
||||
|
||||
|
||||
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
||||
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
||||
if (actualStatusCode != expectedStatusCode) {
|
||||
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
||||
success = NO;
|
||||
}
|
||||
|
||||
|
||||
NSDictionary* expectedHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
||||
NSDictionary* actualHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
||||
for (NSString* expectedHeader in expectedHeaders) {
|
||||
@ -1262,7 +1250,7 @@ static void _LogResult(NSString* format, ...) {
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length")));
|
||||
NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse));
|
||||
NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length")));
|
||||
@ -1275,20 +1263,20 @@ static void _LogResult(NSString* format, ...) {
|
||||
success = NO;
|
||||
#if !TARGET_OS_IPHONE
|
||||
#if DEBUG
|
||||
if (GCDWebServerIsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
|
||||
NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||
NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||
if (GCDWebServerIsTextContentType((NSString*)[expectedHeaders objectForKey:@"Content-Type"])) {
|
||||
NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:(NSString*)[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||
NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:(NSString*)[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
[task setLaunchPath:@"/usr/bin/opendiff"];
|
||||
[task setArguments:@[expectedPath, actualPath]];
|
||||
[task setArguments:@[ expectedPath, actualPath ]];
|
||||
[task launch];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CFRelease(actualResponse);
|
||||
}
|
||||
CFRelease(expectedResponse);
|
||||
@ -1311,9 +1299,9 @@ static void _LogResult(NSString* format, ...) {
|
||||
}
|
||||
_ExecuteMainThreadRunLoopSources();
|
||||
}
|
||||
|
||||
|
||||
[self stop];
|
||||
|
||||
|
||||
_ExecuteMainThreadRunLoopSources();
|
||||
}
|
||||
return result;
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#import "GCDWebServer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GCDWebServerHandler;
|
||||
|
||||
/**
|
||||
@ -139,7 +141,7 @@
|
||||
* The default implementation checks for HTTP authentication if applicable
|
||||
* and returns a barebone 401 status code response if authentication failed.
|
||||
*/
|
||||
- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request;
|
||||
- (nullable GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request;
|
||||
|
||||
/**
|
||||
* Assuming a valid HTTP request was received and -preflightRequest: returned nil,
|
||||
@ -169,7 +171,7 @@
|
||||
* @warning If the request was invalid (e.g. the HTTP headers were malformed),
|
||||
* the "request" argument will be nil.
|
||||
*/
|
||||
- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode;
|
||||
- (void)abortRequest:(nullable GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode;
|
||||
|
||||
/**
|
||||
* Called when the connection is closed.
|
||||
@ -177,3 +179,5 @@
|
||||
- (void)close;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -34,20 +36,24 @@ extern "C" {
|
||||
/**
|
||||
* Converts a file extension to the corresponding MIME type.
|
||||
* If there is no match, "application/octet-stream" is returned.
|
||||
*
|
||||
* Overrides allow to customize the built-in mapping from extensions to MIME
|
||||
* 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);
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* _Nullable overrides);
|
||||
|
||||
/**
|
||||
* Add percent-escapes to a string so it can be used in a URL.
|
||||
* The legal characters ":@/?&=+" are also escaped to ensure compatibility
|
||||
* with URL encoded forms and URL queries.
|
||||
*/
|
||||
NSString* GCDWebServerEscapeURLString(NSString* string);
|
||||
NSString* _Nullable GCDWebServerEscapeURLString(NSString* string);
|
||||
|
||||
/**
|
||||
* Unescapes a URL percent-encoded string.
|
||||
*/
|
||||
NSString* GCDWebServerUnescapeURLString(NSString* string);
|
||||
NSString* _Nullable GCDWebServerUnescapeURLString(NSString* string);
|
||||
|
||||
/**
|
||||
* Extracts the unescaped names and values from an
|
||||
@ -63,7 +69,7 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
|
||||
* On iOS, returns the IPv4 or IPv6 address as a string of the WiFi
|
||||
* interface if connected or nil otherwise.
|
||||
*/
|
||||
NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6);
|
||||
NSString* _Nullable GCDWebServerGetPrimaryIPAddress(BOOL useIPv6);
|
||||
|
||||
/**
|
||||
* Converts a date into a string using RFC822 formatting.
|
||||
@ -79,7 +85,7 @@ NSString* GCDWebServerFormatRFC822(NSDate* date);
|
||||
*
|
||||
* @warning Timezones other than GMT are not supported by this function.
|
||||
*/
|
||||
NSDate* GCDWebServerParseRFC822(NSString* string);
|
||||
NSDate* _Nullable GCDWebServerParseRFC822(NSString* string);
|
||||
|
||||
/**
|
||||
* Converts a date into a string using IOS 8601 formatting.
|
||||
@ -94,8 +100,10 @@ NSString* GCDWebServerFormatISO8601(NSDate* date);
|
||||
* @warning Only "calendar" variant is supported at this time and timezones
|
||||
* other than GMT are not supported either.
|
||||
*/
|
||||
NSDate* GCDWebServerParseISO8601(NSString* string);
|
||||
NSDate* _Nullable GCDWebServerParseISO8601(NSString* string);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -83,21 +83,28 @@ NSString* GCDWebServerNormalizeHeaderValue(NSString* value) {
|
||||
}
|
||||
|
||||
NSString* GCDWebServerTruncateHeaderValue(NSString* value) {
|
||||
NSRange range = [value rangeOfString:@";"];
|
||||
return range.location != NSNotFound ? [value substringToIndex:range.location] : value;
|
||||
if (value) {
|
||||
NSRange range = [value rangeOfString:@";"];
|
||||
if (range.location != NSNotFound) {
|
||||
return [value substringToIndex:range.location];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerExtractHeaderValueParameter(NSString* value, NSString* name) {
|
||||
NSString* parameter = nil;
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:value];
|
||||
[scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive
|
||||
NSString* string = [NSString stringWithFormat:@"%@=", name];
|
||||
if ([scanner scanUpToString:string intoString:NULL]) {
|
||||
[scanner scanString:string intoString:NULL];
|
||||
if ([scanner scanString:@"\"" intoString:NULL]) {
|
||||
[scanner scanUpToString:@"\"" intoString:¶meter];
|
||||
} else {
|
||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter];
|
||||
if (value) {
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:value];
|
||||
[scanner setCaseSensitive:NO]; // Assume parameter names are case-insensitive
|
||||
NSString* string = [NSString stringWithFormat:@"%@=", name];
|
||||
if ([scanner scanUpToString:string intoString:NULL]) {
|
||||
[scanner scanString:string intoString:NULL];
|
||||
if ([scanner scanString:@"\"" intoString:NULL]) {
|
||||
[scanner scanUpToString:@"\"" intoString:¶meter];
|
||||
} else {
|
||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:¶meter];
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameter;
|
||||
@ -159,17 +166,15 @@ NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
|
||||
return [NSString stringWithFormat:@"<%lu bytes>", (unsigned long)data.length];
|
||||
}
|
||||
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
||||
static NSDictionary* _overrides = nil;
|
||||
if (_overrides == nil) {
|
||||
_overrides = [[NSDictionary alloc] initWithObjectsAndKeys:
|
||||
@"text/css", @"css",
|
||||
nil];
|
||||
}
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension, NSDictionary* overrides) {
|
||||
NSDictionary* builtInOverrides = @{ @"css" : @"text/css" };
|
||||
NSString* mimeType = nil;
|
||||
extension = [extension lowercaseString];
|
||||
if (extension.length) {
|
||||
mimeType = [_overrides objectForKey:extension];
|
||||
mimeType = [overrides objectForKey:extension];
|
||||
if (mimeType == nil) {
|
||||
mimeType = [builtInOverrides objectForKey:extension];
|
||||
}
|
||||
if (mimeType == nil) {
|
||||
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
|
||||
if (uti) {
|
||||
@ -205,13 +210,13 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||
break;
|
||||
}
|
||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||
|
||||
|
||||
NSString* value = nil;
|
||||
[scanner scanUpToString:@"&" intoString:&value];
|
||||
if (value == nil) {
|
||||
value = @"";
|
||||
}
|
||||
|
||||
|
||||
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil;
|
||||
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
@ -222,7 +227,7 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||
GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value);
|
||||
GWS_DNOT_REACHED();
|
||||
}
|
||||
|
||||
|
||||
if ([scanner isAtEnd]) {
|
||||
break;
|
||||
}
|
||||
@ -232,15 +237,16 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||
}
|
||||
|
||||
NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService) {
|
||||
NSString* string = nil;
|
||||
char hostBuffer[NI_MAXHOST];
|
||||
char serviceBuffer[NI_MAXSERV];
|
||||
if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) >= 0) {
|
||||
string = includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : [NSString stringWithUTF8String:hostBuffer];
|
||||
} else {
|
||||
if (getnameinfo(addr, addr->sa_len, hostBuffer, sizeof(hostBuffer), serviceBuffer, sizeof(serviceBuffer), NI_NUMERICHOST | NI_NUMERICSERV | NI_NOFQDN) != 0) {
|
||||
#if DEBUG
|
||||
GWS_DNOT_REACHED();
|
||||
#else
|
||||
return @"";
|
||||
#endif
|
||||
}
|
||||
return string;
|
||||
return includeService ? [NSString stringWithFormat:@"%s:%s", hostBuffer, serviceBuffer] : (NSString*)[NSString stringWithUTF8String:hostBuffer];
|
||||
}
|
||||
|
||||
NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
@ -255,7 +261,10 @@ NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
if (store) {
|
||||
CFPropertyListRef info = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4")); // There is no equivalent for IPv6 but the primary interface should be the same
|
||||
if (info) {
|
||||
primaryInterface = [[NSString stringWithString:[(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"]] UTF8String];
|
||||
NSString* interface = [(__bridge NSDictionary*)info objectForKey:@"PrimaryInterface"];
|
||||
if (interface) {
|
||||
primaryInterface = [[NSString stringWithString:interface] UTF8String]; // Copy string to auto-release pool
|
||||
}
|
||||
CFRelease(info);
|
||||
}
|
||||
CFRelease(store);
|
||||
@ -267,9 +276,9 @@ NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) {
|
||||
struct ifaddrs* list;
|
||||
if (getifaddrs(&list) >= 0) {
|
||||
for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) {
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV
|
||||
// Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator
|
||||
// Assumption holds for Apple TV running tvOS
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_TV
|
||||
// Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator
|
||||
// Assumption holds for Apple TV running tvOS
|
||||
if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1"))
|
||||
#else
|
||||
if (strcmp(ifap->ifa_name, primaryInterface))
|
||||
@ -303,5 +312,5 @@ NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) {
|
||||
buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo;
|
||||
}
|
||||
buffer[2 * CC_MD5_DIGEST_LENGTH] = 0;
|
||||
return [NSString stringWithUTF8String:buffer];
|
||||
return (NSString*)[NSString stringWithUTF8String:buffer];
|
||||
}
|
||||
|
@ -81,27 +81,6 @@
|
||||
#define GWS_DCHECK(__CONDITION__) XLOG_DEBUG_CHECK(__CONDITION__)
|
||||
#define GWS_DNOT_REACHED() XLOG_DEBUG_UNREACHABLE()
|
||||
|
||||
/**
|
||||
* Automatically detect if CocoaLumberJack is available and if so use
|
||||
* it as a logging facility.
|
||||
*/
|
||||
|
||||
#elif defined(__has_include) && __has_include("CocoaLumberjack/CocoaLumberjack.h")
|
||||
|
||||
#import <CocoaLumberjack/CocoaLumberjack.h>
|
||||
|
||||
#define __GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__
|
||||
|
||||
#undef LOG_LEVEL_DEF
|
||||
#define LOG_LEVEL_DEF GCDWebServerLogLevel
|
||||
extern DDLogLevel GCDWebServerLogLevel;
|
||||
|
||||
#define GWS_LOG_DEBUG(...) DDLogDebug(__VA_ARGS__)
|
||||
#define GWS_LOG_VERBOSE(...) DDLogVerbose(__VA_ARGS__)
|
||||
#define GWS_LOG_INFO(...) DDLogInfo(__VA_ARGS__)
|
||||
#define GWS_LOG_WARNING(...) DDLogWarn(__VA_ARGS__)
|
||||
#define GWS_LOG_ERROR(...) DDLogError(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* If all of the above fail, then use GCDWebServer built-in
|
||||
* logging facility.
|
||||
@ -120,17 +99,32 @@ typedef NS_ENUM(int, GCDWebServerLoggingLevel) {
|
||||
};
|
||||
|
||||
extern GCDWebServerLoggingLevel GCDWebServerLogLevel;
|
||||
extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3);
|
||||
extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* _Nonnull format, ...) NS_FORMAT_FUNCTION(2, 3);
|
||||
|
||||
#if DEBUG
|
||||
#define GWS_LOG_DEBUG(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_DEBUG(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Debug) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Debug, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define GWS_LOG_DEBUG(...)
|
||||
#endif
|
||||
#define GWS_LOG_VERBOSE(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_INFO(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_WARNING(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_ERROR(...) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); } while (0)
|
||||
#define GWS_LOG_VERBOSE(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Verbose) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Verbose, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_INFO(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Info) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Info, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_WARNING(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Warning) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Warning, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define GWS_LOG_ERROR(...) \
|
||||
do { \
|
||||
if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Error) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Error, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
@ -143,10 +137,10 @@ extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* for
|
||||
#if DEBUG
|
||||
|
||||
#define GWS_DCHECK(__CONDITION__) \
|
||||
do { \
|
||||
if (!(__CONDITION__)) { \
|
||||
abort(); \
|
||||
} \
|
||||
do { \
|
||||
if (!(__CONDITION__)) { \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define GWS_DNOT_REACHED() abort()
|
||||
|
||||
@ -159,6 +153,8 @@ extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* for
|
||||
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* GCDWebServer internal constants and APIs.
|
||||
*/
|
||||
@ -171,29 +167,29 @@ static inline BOOL GCDWebServerIsValidByteRange(NSRange range) {
|
||||
}
|
||||
|
||||
static inline NSError* GCDWebServerMakePosixError(int code) {
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithUTF8String:strerror(code)]}];
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : (NSString*)[NSString stringWithUTF8String:strerror(code)]}];
|
||||
}
|
||||
|
||||
extern void GCDWebServerInitializeFunctions();
|
||||
extern NSString* GCDWebServerNormalizeHeaderValue(NSString* value);
|
||||
extern NSString* GCDWebServerTruncateHeaderValue(NSString* value);
|
||||
extern NSString* GCDWebServerExtractHeaderValueParameter(NSString* header, NSString* attribute);
|
||||
extern NSString* _Nullable GCDWebServerNormalizeHeaderValue(NSString* _Nullable value);
|
||||
extern NSString* _Nullable GCDWebServerTruncateHeaderValue(NSString* _Nullable value);
|
||||
extern NSString* _Nullable GCDWebServerExtractHeaderValueParameter(NSString* _Nullable value, NSString* attribute);
|
||||
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
|
||||
extern BOOL GCDWebServerIsTextContentType(NSString* type);
|
||||
extern NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType);
|
||||
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||
extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService);
|
||||
|
||||
@interface GCDWebServerConnection ()
|
||||
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
|
||||
- (instancetype)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
|
||||
@end
|
||||
|
||||
@interface GCDWebServer ()
|
||||
@property(nonatomic, readonly) NSArray* handlers;
|
||||
@property(nonatomic, readonly) NSString* serverName;
|
||||
@property(nonatomic, readonly) NSString* authenticationRealm;
|
||||
@property(nonatomic, readonly) NSDictionary* authenticationBasicAccounts;
|
||||
@property(nonatomic, readonly) NSDictionary* authenticationDigestAccounts;
|
||||
@property(nonatomic, readonly) NSMutableArray* 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) BOOL shouldAutomaticallyMapHEADToGET;
|
||||
@property(nonatomic, readonly) dispatch_queue_priority_t dispatchQueuePriority;
|
||||
- (void)willStartConnection:(GCDWebServerConnection*)connection;
|
||||
@ -207,13 +203,13 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
|
||||
|
||||
@interface GCDWebServerRequest ()
|
||||
@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
|
||||
@property(nonatomic, readwrite) NSData* localAddressData;
|
||||
@property(nonatomic, readwrite) NSData* remoteAddressData;
|
||||
@property(nonatomic) NSData* localAddressData;
|
||||
@property(nonatomic) NSData* remoteAddressData;
|
||||
- (void)prepareForWriting;
|
||||
- (BOOL)performOpen:(NSError**)error;
|
||||
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
|
||||
- (BOOL)performClose:(NSError**)error;
|
||||
- (void)setAttribute:(id)attribute forKey:(NSString*)key;
|
||||
- (void)setAttribute:(nullable id)attribute forKey:(NSString*)key;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerResponse ()
|
||||
@ -224,3 +220,5 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
|
||||
- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
|
||||
- (void)performClose;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest
|
||||
* with the contents of any regular expression captures done on the request path.
|
||||
@ -112,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) NSDictionary* query;
|
||||
@property(nonatomic, readonly, nullable) NSDictionary* query;
|
||||
|
||||
/**
|
||||
* Returns the content type for the body of the request parsed from the
|
||||
@ -122,7 +124,7 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
|
||||
* "application/octet-stream" if a body is present but there was no
|
||||
* "Content-Type" header.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* contentType;
|
||||
@property(nonatomic, readonly, nullable) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Returns the content length for the body of the request parsed from the
|
||||
@ -137,12 +139,12 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
|
||||
/**
|
||||
* Returns the parsed "If-Modified-Since" header or nil if absent or malformed.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSDate* ifModifiedSince;
|
||||
@property(nonatomic, readonly, nullable) NSDate* ifModifiedSince;
|
||||
|
||||
/**
|
||||
* Returns the parsed "If-None-Match" header or nil if absent or malformed.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString* ifNoneMatch;
|
||||
@property(nonatomic, readonly, nullable) NSString* ifNoneMatch;
|
||||
|
||||
/**
|
||||
* Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed.
|
||||
@ -184,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:(NSDictionary*)query;
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(nullable NSDictionary*)query;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the contentType property is defined.
|
||||
@ -201,6 +203,8 @@ extern NSString* const GCDWebServerRequestAttribute_RegexCaptures;
|
||||
*
|
||||
* @return The attribute value for the key.
|
||||
*/
|
||||
- (id)attributeForKey:(NSString*)key;
|
||||
- (nullable id)attributeForKey:(NSString*)key;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -39,22 +39,17 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
#define kGZipInitialBufferSize (256 * 1024)
|
||||
|
||||
@interface GCDWebServerBodyDecoder : NSObject <GCDWebServerBodyWriter>
|
||||
- (id)initWithRequest:(GCDWebServerRequest*)request writer:(id<GCDWebServerBodyWriter>)writer;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder
|
||||
@end
|
||||
|
||||
@interface GCDWebServerBodyDecoder () {
|
||||
@private
|
||||
@implementation GCDWebServerBodyDecoder {
|
||||
GCDWebServerRequest* __unsafe_unretained _request;
|
||||
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerBodyDecoder
|
||||
|
||||
- (id)initWithRequest:(GCDWebServerRequest*)request writer:(id<GCDWebServerBodyWriter>)writer {
|
||||
- (instancetype)initWithRequest:(GCDWebServerRequest* _Nonnull)request writer:(id<GCDWebServerBodyWriter> _Nonnull)writer {
|
||||
if ((self = [super init])) {
|
||||
_request = request;
|
||||
_writer = writer;
|
||||
@ -76,14 +71,10 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipDecoder () {
|
||||
@private
|
||||
@implementation GCDWebServerGZipDecoder {
|
||||
z_stream _stream;
|
||||
BOOL _finished;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerGZipDecoder
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
int result = inflateInit2(&_stream, 15 + 16);
|
||||
@ -143,77 +134,55 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerRequest () {
|
||||
@private
|
||||
NSString* _method;
|
||||
NSURL* _url;
|
||||
NSDictionary* _headers;
|
||||
NSString* _path;
|
||||
NSDictionary* _query;
|
||||
NSString* _type;
|
||||
BOOL _chunked;
|
||||
NSUInteger _length;
|
||||
NSDate* _modifiedSince;
|
||||
NSString* _noneMatch;
|
||||
NSRange _range;
|
||||
BOOL _gzipAccepted;
|
||||
NSData* _localAddress;
|
||||
NSData* _remoteAddress;
|
||||
|
||||
@implementation GCDWebServerRequest {
|
||||
BOOL _opened;
|
||||
NSMutableArray* _decoders;
|
||||
NSMutableDictionary* _attributes;
|
||||
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
||||
NSMutableDictionary* _attributes;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerRequest : NSObject
|
||||
|
||||
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, ifModifiedSince=_modifiedSince, ifNoneMatch=_noneMatch,
|
||||
byteRange=_range, acceptsGzipContentEncoding=_gzipAccepted, usesChunkedTransferEncoding=_chunked, localAddressData=_localAddress, remoteAddressData=_remoteAddress;
|
||||
|
||||
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||
if ((self = [super init])) {
|
||||
_method = [method copy];
|
||||
_url = url;
|
||||
_URL = url;
|
||||
_headers = headers;
|
||||
_path = [path copy];
|
||||
_query = query;
|
||||
|
||||
_type = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]);
|
||||
_chunked = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"];
|
||||
|
||||
_contentType = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]);
|
||||
_usesChunkedTransferEncoding = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"];
|
||||
NSString* lengthHeader = [_headers objectForKey:@"Content-Length"];
|
||||
if (lengthHeader) {
|
||||
NSInteger length = [lengthHeader integerValue];
|
||||
if (_chunked || (length < 0)) {
|
||||
GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _url);
|
||||
if (_usesChunkedTransferEncoding || (length < 0)) {
|
||||
GWS_LOG_WARNING(@"Invalid 'Content-Length' header '%@' for '%@' request on \"%@\"", lengthHeader, _method, _URL);
|
||||
GWS_DNOT_REACHED();
|
||||
return nil;
|
||||
}
|
||||
_length = length;
|
||||
if (_type == nil) {
|
||||
_type = kGCDWebServerDefaultMimeType;
|
||||
_contentLength = length;
|
||||
if (_contentType == nil) {
|
||||
_contentType = kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
} else if (_chunked) {
|
||||
if (_type == nil) {
|
||||
_type = kGCDWebServerDefaultMimeType;
|
||||
} else if (_usesChunkedTransferEncoding) {
|
||||
if (_contentType == nil) {
|
||||
_contentType = kGCDWebServerDefaultMimeType;
|
||||
}
|
||||
_length = NSUIntegerMax;
|
||||
_contentLength = NSUIntegerMax;
|
||||
} else {
|
||||
if (_type) {
|
||||
GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _url);
|
||||
_type = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense
|
||||
if (_contentType) {
|
||||
GWS_LOG_WARNING(@"Ignoring 'Content-Type' header for '%@' request on \"%@\"", _method, _URL);
|
||||
_contentType = nil; // Content-Type without Content-Length or chunked-encoding doesn't make sense
|
||||
}
|
||||
_length = NSUIntegerMax;
|
||||
_contentLength = NSUIntegerMax;
|
||||
}
|
||||
|
||||
|
||||
NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"];
|
||||
if (modifiedHeader) {
|
||||
_modifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy];
|
||||
_ifModifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy];
|
||||
}
|
||||
_noneMatch = [_headers objectForKey:@"If-None-Match"];
|
||||
|
||||
_range = NSMakeRange(NSUIntegerMax, 0);
|
||||
_ifNoneMatch = [_headers objectForKey:@"If-None-Match"];
|
||||
|
||||
_byteRange = NSMakeRange(NSUIntegerMax, 0);
|
||||
NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]);
|
||||
if (rangeHeader) {
|
||||
if ([rangeHeader hasPrefix:@"bytes="]) {
|
||||
@ -226,27 +195,27 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
NSString* endString = [components objectAtIndex:1];
|
||||
NSInteger endValue = [endString integerValue];
|
||||
if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999"
|
||||
_range.location = startValue;
|
||||
_range.length = endValue - startValue + 1;
|
||||
_byteRange.location = startValue;
|
||||
_byteRange.length = endValue - startValue + 1;
|
||||
} else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-"
|
||||
_range.location = startValue;
|
||||
_range.length = NSUIntegerMax;
|
||||
_byteRange.location = startValue;
|
||||
_byteRange.length = NSUIntegerMax;
|
||||
} else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500"
|
||||
_range.location = NSUIntegerMax;
|
||||
_range.length = endValue;
|
||||
_byteRange.location = NSUIntegerMax;
|
||||
_byteRange.length = endValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((_range.location == NSUIntegerMax) && (_range.length == 0)) { // Ignore "Range" header if syntactically invalid
|
||||
if ((_byteRange.location == NSUIntegerMax) && (_byteRange.length == 0)) { // Ignore "Range" header if syntactically invalid
|
||||
GWS_LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ([[_headers objectForKey:@"Accept-Encoding"] rangeOfString:@"gzip"].location != NSNotFound) {
|
||||
_gzipAccepted = YES;
|
||||
_acceptsGzipContentEncoding = YES;
|
||||
}
|
||||
|
||||
|
||||
_decoders = [[NSMutableArray alloc] init];
|
||||
_attributes = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
@ -254,11 +223,11 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
}
|
||||
|
||||
- (BOOL)hasBody {
|
||||
return _type ? YES : NO;
|
||||
return _contentType ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasByteRange {
|
||||
return GCDWebServerIsValidByteRange(_range);
|
||||
return GCDWebServerIsValidByteRange(_byteRange);
|
||||
}
|
||||
|
||||
- (id)attributeForKey:(NSString*)key {
|
||||
@ -287,7 +256,7 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
}
|
||||
|
||||
- (BOOL)performOpen:(NSError**)error {
|
||||
GWS_DCHECK(_type);
|
||||
GWS_DCHECK(_contentType);
|
||||
GWS_DCHECK(_writer);
|
||||
if (_opened) {
|
||||
GWS_DNOT_REACHED();
|
||||
@ -312,11 +281,11 @@ NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerReque
|
||||
}
|
||||
|
||||
- (NSString*)localAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_localAddress.bytes, YES);
|
||||
return GCDWebServerStringFromSockAddr(_localAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)remoteAddressString {
|
||||
return GCDWebServerStringFromSockAddr(_remoteAddress.bytes, YES);
|
||||
return GCDWebServerStringFromSockAddr(_remoteAddressData.bytes, YES);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
|
@ -27,11 +27,13 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
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* error);
|
||||
typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* _Nullable error);
|
||||
|
||||
/**
|
||||
* This protocol is used by the GCDWebServerConnection to communicate with
|
||||
@ -62,7 +64,7 @@ typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* err
|
||||
* or an empty NSData there is no more body data, or nil on error and set
|
||||
* the "error" argument which is guaranteed to be non-NULL.
|
||||
*/
|
||||
- (NSData*)readData:(NSError**)error;
|
||||
- (nullable NSData*)readData:(NSError**)error;
|
||||
|
||||
/**
|
||||
* This method is called after all body data has been sent.
|
||||
@ -102,7 +104,7 @@ typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* err
|
||||
*
|
||||
* @warning This property must be set if a body is present.
|
||||
*/
|
||||
@property(nonatomic, copy) NSString* contentType;
|
||||
@property(nonatomic, copy, nullable) NSString* contentType;
|
||||
|
||||
/**
|
||||
* Sets the content length for the body of the response. If a body is present
|
||||
@ -136,14 +138,14 @@ typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* err
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
@property(nonatomic, retain) NSDate* lastModifiedDate;
|
||||
@property(nonatomic, nullable) NSDate* lastModifiedDate;
|
||||
|
||||
/**
|
||||
* Sets the ETag for the response using the "ETag" header.
|
||||
*
|
||||
* The default value is nil.
|
||||
*/
|
||||
@property(nonatomic, copy) NSString* eTag;
|
||||
@property(nonatomic, copy, nullable) NSString* eTag;
|
||||
|
||||
/**
|
||||
* Enables gzip encoding for the response body.
|
||||
@ -174,7 +176,7 @@ typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* err
|
||||
* @warning Do not attempt to override the primary headers used
|
||||
* by GCDWebServerResponse like "Content-Type", "ETag", etc...
|
||||
*/
|
||||
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header;
|
||||
- (void)setValue:(nullable NSString*)value forAdditionalHeader:(NSString*)header;
|
||||
|
||||
/**
|
||||
* Convenience method that checks if the contentType property is defined.
|
||||
@ -206,3 +208,5 @@ typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* err
|
||||
- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -37,22 +37,17 @@
|
||||
#define kGZipInitialBufferSize (256 * 1024)
|
||||
|
||||
@interface GCDWebServerBodyEncoder : NSObject <GCDWebServerBodyReader>
|
||||
- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder
|
||||
@end
|
||||
|
||||
@interface GCDWebServerBodyEncoder () {
|
||||
@private
|
||||
@implementation GCDWebServerBodyEncoder {
|
||||
GCDWebServerResponse* __unsafe_unretained _response;
|
||||
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerBodyEncoder
|
||||
|
||||
- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader {
|
||||
- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id<GCDWebServerBodyReader> _Nonnull)reader {
|
||||
if ((self = [super init])) {
|
||||
_response = response;
|
||||
_reader = reader;
|
||||
@ -74,16 +69,12 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerGZipEncoder () {
|
||||
@private
|
||||
@implementation GCDWebServerGZipEncoder {
|
||||
z_stream _stream;
|
||||
BOOL _finished;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerGZipEncoder
|
||||
|
||||
- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader {
|
||||
- (instancetype)initWithResponse:(GCDWebServerResponse* _Nonnull)response reader:(id<GCDWebServerBodyReader> _Nonnull)reader {
|
||||
if ((self = [super initWithResponse:response reader:reader])) {
|
||||
response.contentLength = NSUIntegerMax; // Make sure "Content-Length" header is not set since we don't know it
|
||||
[response setValue:@"gzip" forAdditionalHeader:@"Content-Encoding"];
|
||||
@ -157,28 +148,11 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerResponse () {
|
||||
@private
|
||||
NSString* _type;
|
||||
NSUInteger _length;
|
||||
NSInteger _status;
|
||||
NSUInteger _maxAge;
|
||||
NSDate* _lastModified;
|
||||
NSString* _eTag;
|
||||
NSMutableDictionary* _headers;
|
||||
BOOL _chunked;
|
||||
BOOL _gzipped;
|
||||
|
||||
@implementation GCDWebServerResponse {
|
||||
BOOL _opened;
|
||||
NSMutableArray* _encoders;
|
||||
id<GCDWebServerBodyReader> __unsafe_unretained _reader;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerResponse
|
||||
|
||||
@synthesize contentType=_type, contentLength=_length, statusCode=_status, cacheControlMaxAge=_maxAge, lastModifiedDate=_lastModified, eTag=_eTag,
|
||||
gzipContentEncodingEnabled=_gzipped, additionalHeaders=_headers;
|
||||
|
||||
+ (instancetype)response {
|
||||
return [[[self class] alloc] init];
|
||||
@ -186,26 +160,26 @@
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_type = nil;
|
||||
_length = NSUIntegerMax;
|
||||
_status = kGCDWebServerHTTPStatusCode_OK;
|
||||
_maxAge = 0;
|
||||
_headers = [[NSMutableDictionary alloc] init];
|
||||
_contentType = nil;
|
||||
_contentLength = NSUIntegerMax;
|
||||
_statusCode = kGCDWebServerHTTPStatusCode_OK;
|
||||
_cacheControlMaxAge = 0;
|
||||
_additionalHeaders = [[NSMutableDictionary alloc] init];
|
||||
_encoders = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header {
|
||||
[_headers setValue:value forKey:header];
|
||||
[_additionalHeaders setValue:value forKey:header];
|
||||
}
|
||||
|
||||
- (BOOL)hasBody {
|
||||
return _type ? YES : NO;
|
||||
return _contentType ? YES : NO;
|
||||
}
|
||||
|
||||
- (BOOL)usesChunkedTransferEncoding {
|
||||
return (_type != nil) && (_length == NSUIntegerMax);
|
||||
return (_contentType != nil) && (_contentLength == NSUIntegerMax);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
@ -222,7 +196,7 @@
|
||||
|
||||
- (void)prepareForReading {
|
||||
_reader = self;
|
||||
if (_gzipped) {
|
||||
if (_gzipContentEncodingEnabled) {
|
||||
GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader];
|
||||
[_encoders addObject:encoder];
|
||||
_reader = encoder;
|
||||
@ -230,7 +204,7 @@
|
||||
}
|
||||
|
||||
- (BOOL)performOpen:(NSError**)error {
|
||||
GWS_DCHECK(_type);
|
||||
GWS_DCHECK(_contentType);
|
||||
GWS_DCHECK(_reader);
|
||||
if (_opened) {
|
||||
GWS_DNOT_REACHED();
|
||||
@ -257,24 +231,24 @@
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_status];
|
||||
if (_type) {
|
||||
[description appendFormat:@"\nContent Type = %@", _type];
|
||||
NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_statusCode];
|
||||
if (_contentType) {
|
||||
[description appendFormat:@"\nContent Type = %@", _contentType];
|
||||
}
|
||||
if (_length != NSUIntegerMax) {
|
||||
[description appendFormat:@"\nContent Length = %lu", (unsigned long)_length];
|
||||
if (_contentLength != NSUIntegerMax) {
|
||||
[description appendFormat:@"\nContent Length = %lu", (unsigned long)_contentLength];
|
||||
}
|
||||
[description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_maxAge];
|
||||
if (_lastModified) {
|
||||
[description appendFormat:@"\nLast Modified Date = %@", _lastModified];
|
||||
[description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_cacheControlMaxAge];
|
||||
if (_lastModifiedDate) {
|
||||
[description appendFormat:@"\nLast Modified Date = %@", _lastModifiedDate];
|
||||
}
|
||||
if (_eTag) {
|
||||
[description appendFormat:@"\nETag = %@", _eTag];
|
||||
}
|
||||
if (_headers.count) {
|
||||
if (_additionalHeaders.count) {
|
||||
[description appendString:@"\n"];
|
||||
for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]];
|
||||
for (NSString* header in [[_additionalHeaders allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
[description appendFormat:@"\n%@: %@", header, [_additionalHeaders objectForKey:header]];
|
||||
}
|
||||
}
|
||||
return description;
|
||||
|
Reference in New Issue
Block a user