diff --git a/Frameworks/CGDWebServer/Core/GCDWebServer.h b/Frameworks/CGDWebServer/Core/GCDWebServer.h deleted file mode 100755 index b446035..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServer.h +++ /dev/null @@ -1,586 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - -#import "GCDWebServerRequest.h" -#import "GCDWebServerResponse.h" - -/** - * The GCDWebServerMatchBlock is called for every handler added to the - * GCDWebServer whenever a new HTTP request has started (i.e. HTTP headers have - * been received). The block is passed the basic info for the request (HTTP method, - * URL, headers...) and must decide if it wants to handle it or not. - * - * If the handler can handle the request, the block must return a new - * 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); - -/** - * The GCDWebServerProcessBlock is called after the HTTP request has been fully - * received (i.e. the entire HTTP body has been read). The block is passed the - * GCDWebServerRequest created at the previous step by the GCDWebServerMatchBlock. - * - * The block must return a GCDWebServerResponse or nil on error, which will - * result in a 500 HTTP status code returned to the client. It's however - * recommended to return a GCDWebServerErrorResponse on error so more useful - * information can be returned to the client. - */ -typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request); - -/** - * The GCDWebServerAsynchronousProcessBlock works like the GCDWebServerProcessBlock - * except the GCDWebServerResponse can be returned to the server at a later time - * allowing for asynchronous generation of the response. - * - * The block must eventually call "completionBlock" passing a GCDWebServerResponse - * or nil on error, which will result in a 500 HTTP status code returned to the client. - * 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 (^GCDWebServerAsyncProcessBlock)(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock); - -/** - * The port used by the GCDWebServer (NSNumber / NSUInteger). - * - * The default value is 0 i.e. let the OS pick a random port. - */ -extern NSString* const GCDWebServerOption_Port; - -/** - * The Bonjour name used by the GCDWebServer (NSString). If set to an empty string, - * the name will automatically take the value of the GCDWebServerOption_ServerName - * option. If this option is set to nil, Bonjour will be disabled. - * - * The default value is nil. - */ -extern NSString* const GCDWebServerOption_BonjourName; - -/** - * The Bonjour service type used by the GCDWebServer (NSString). - * - * The default value is "_http._tcp", the service type for HTTP web servers. - */ -extern NSString* const GCDWebServerOption_BonjourType; - -/** - * Only accept HTTP requests coming from localhost i.e. not from the outside - * network (NSNumber / BOOL). - * - * The default value is NO. - * - * @warning Bonjour should be disabled if using this option since the server - * will not be reachable from the outside network anyway. - */ -extern NSString* const GCDWebServerOption_BindToLocalhost; - -/** - * The maximum number of incoming HTTP requests that can be queued waiting to - * be handled before new ones are dropped (NSNumber / NSUInteger). - * - * The default value is 16. - */ -extern NSString* const GCDWebServerOption_MaxPendingConnections; - -/** - * The value for "Server" HTTP header used by the GCDWebServer (NSString). - * - * The default value is the GCDWebServer class name. - */ -extern NSString* const GCDWebServerOption_ServerName; - -/** - * The authentication method used by the GCDWebServer - * (one of "GCDWebServerAuthenticationMethod_..."). - * - * The default value is nil i.e. authentication is disabled. - */ -extern NSString* const GCDWebServerOption_AuthenticationMethod; - -/** - * The authentication realm used by the GCDWebServer (NSString). - * - * The default value is the same as the GCDWebServerOption_ServerName option. - */ -extern NSString* const GCDWebServerOption_AuthenticationRealm; - -/** - * The authentication accounts used by the GCDWebServer - * (NSDictionary of username / password pairs). - * - * The default value is nil i.e. no accounts. - */ -extern NSString* const GCDWebServerOption_AuthenticationAccounts; - -/** - * The class used by the GCDWebServer when instantiating GCDWebServerConnection - * (subclass of GCDWebServerConnection). - * - * The default value is the GCDWebServerConnection class. - */ -extern NSString* const GCDWebServerOption_ConnectionClass; - -/** - * Allow the GCDWebServer to pretend "HEAD" requests are actually "GET" ones - * and automatically discard the HTTP body of the response (NSNumber / BOOL). - * - * The default value is YES. - */ -extern NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET; - -/** - * The interval expressed in seconds used by the GCDWebServer to decide how to - * coalesce calls to -webServerDidConnect: and -webServerDidDisconnect: - * (NSNumber / double). Coalescing will be disabled if the interval is <= 0.0. - * - * The default value is 1.0 second. - */ -extern NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval; - -#if TARGET_OS_IPHONE - -/** - * Enables the GCDWebServer to automatically suspend itself (as if -stop was - * called) when the iOS app goes into the background and the last - * GCDWebServerConnection is closed, then resume itself (as if -start was called) - * when the iOS app comes back to the foreground (NSNumber / BOOL). - * - * See the README.md file for more information about this option. - * - * The default value is YES. - * - * @warning The running property will be NO while the GCDWebServer is suspended. - */ -extern NSString* const GCDWebServerOption_AutomaticallySuspendInBackground; - -#endif - -/** - * HTTP Basic Authentication scheme (see https://tools.ietf.org/html/rfc2617). - * - * @warning Use of this authentication scheme is not recommended as the - * passwords are sent in clear. - */ -extern NSString* const GCDWebServerAuthenticationMethod_Basic; - -/** - * HTTP Digest Access Authentication scheme (see https://tools.ietf.org/html/rfc2617). - */ -extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess; - -@class GCDWebServer; - -/** - * Delegate methods for GCDWebServer. - * - * @warning These methods are always called on the main thread in a serialized way. - */ -@protocol GCDWebServerDelegate -@optional - -/** - * This method is called after the server has successfully started. - */ -- (void)webServerDidStart:(GCDWebServer*)server; - -/** - * This method is called after the Bonjour registration for the server has - * successfully completed. - */ -- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server; - -/** - * This method is called when the first GCDWebServerConnection is opened by the - * server to serve a series of HTTP requests. - * - * A series of HTTP requests is considered ongoing as long as new HTTP requests - * keep coming (and new GCDWebServerConnection instances keep being opened), - * until before the last HTTP request has been responded to (and the - * corresponding last GCDWebServerConnection closed). - */ -- (void)webServerDidConnect:(GCDWebServer*)server; - -/** - * This method is called when the last GCDWebServerConnection is closed after - * the server has served a series of HTTP requests. - * - * The GCDWebServerOption_ConnectedStateCoalescingInterval option can be used - * to have the server wait some extra delay before considering that the series - * of HTTP requests has ended (in case there some latency between consecutive - * requests). This effectively coalesces the calls to -webServerDidConnect: - * and -webServerDidDisconnect:. - */ -- (void)webServerDidDisconnect:(GCDWebServer*)server; - -/** - * This method is called after the server has stopped. - */ -- (void)webServerDidStop:(GCDWebServer*)server; - -@end - -/** - * The GCDWebServer class listens for incoming HTTP requests on a given port, - * then passes each one to a "handler" capable of generating an HTTP response - * for it, which is then sent back to the client. - * - * GCDWebServer instances can be created and used from any thread but it's - * recommended to have the main thread's runloop be running so internal callbacks - * can be handled e.g. for Bonjour registration. - * - * See the README.md file for more information about the architecture of GCDWebServer. - */ -@interface GCDWebServer : NSObject - -/** - * Sets the delegate for the server. - */ -@property(nonatomic, assign) id delegate; - -/** - * Returns YES if the server is currently running. - */ -@property(nonatomic, readonly, getter=isRunning) BOOL running; - -/** - * Returns the port used by the server. - * - * @warning This property is only valid if the server is running. - */ -@property(nonatomic, readonly) NSUInteger port; - -/** - * Returns the Bonjour name used by the server. - * - * @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; - -/** - * Returns the Bonjour service type used by the server. - * - * @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; - -/** - * This method is the designated initializer for the class. - */ -- (instancetype)init; - -/** - * Adds to the server a handler that generates responses synchronously when handling incoming HTTP requests. - * - * Handlers are called in a LIFO queue, so if multiple handlers can potentially - * respond to a given request, the latest added one wins. - * - * @warning Addling handlers while the server is running is not allowed. - */ -- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock; - -/** - * Adds to the server a handler that generates responses asynchronously when handling incoming HTTP requests. - * - * Handlers are called in a LIFO queue, so if multiple handlers can potentially - * respond to a given request, the latest added one wins. - * - * @warning Addling handlers while the server is running is not allowed. - */ -- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock; - -/** - * Removes all handlers previously added to the server. - * - * @warning Removing handlers while the server is running is not allowed. - */ -- (void)removeAllHandlers; - -/** - * Starts the server with explicit options. This method is the designated way - * to start the server. - * - * Returns NO if the server failed to start and sets "error" argument if not NULL. - */ -- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error; - -/** - * Stops the server and prevents it to accepts new HTTP requests. - * - * @warning Stopping the server does not abort GCDWebServerConnection instances - * currently handling already received HTTP requests. These connections will - * continue to execute normally until completion. - */ -- (void)stop; - -@end - -@interface GCDWebServer (Extensions) - -/** - * Returns the server's URL. - * - * @warning This property is only valid if the server is running. - */ -@property(nonatomic, readonly) NSURL* serverURL; - -/** - * Returns the server's Bonjour URL. - * - * @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. - * 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; - -/** - * Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS) - * using the default Bonjour name. - * - * Returns NO if the server failed to start. - */ -- (BOOL)start; - -/** - * Starts the server on a given port and with a specific Bonjour name. - * Pass a nil Bonjour name to disable Bonjour entirely or an empty string to - * use the default name. - * - * Returns NO if the server failed to start. - */ -- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name; - -#if !TARGET_OS_IPHONE - -/** - * Runs the server synchronously using -startWithPort:bonjourName: until a - * SIGINT signal is received i.e. Ctrl-C. This method is intended to be used - * by command line tools. - * - * Returns NO if the server failed to start. - * - * @warning This method must be used from the main thread only. - */ -- (BOOL)runWithPort:(NSUInteger)port bonjourName:(NSString*)name; - -/** - * Runs the server synchronously using -startWithOptions: until a SIGTERM or - * SIGINT signal is received i.e. Ctrl-C in Terminal. This method is intended to - * be used by command line tools. - * - * Returns NO if the server failed to start and sets "error" argument if not NULL. - * - * @warning This method must be used from the main thread only. - */ -- (BOOL)runWithOptions:(NSDictionary*)options error:(NSError**)error; - -#endif - -@end - -@interface GCDWebServer (Handlers) - -/** - * Adds a default handler to the server to handle all incoming HTTP requests - * with a given HTTP method and generate responses synchronously. - */ -- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; - -/** - * Adds a default handler to the server to handle all incoming HTTP requests - * with a given HTTP method and generate responses asynchronously. - */ -- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block; - -/** - * Adds a handler to the server to handle incoming HTTP requests with a given - * HTTP method and a specific case-insensitive path and generate responses - * synchronously. - */ -- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; - -/** - * Adds a handler to the server to handle incoming HTTP requests with a given - * HTTP method and a specific case-insensitive path and generate responses - * asynchronously. - */ -- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block; - -/** - * Adds a handler to the server to handle incoming HTTP requests with a given - * HTTP method and a path matching a case-insensitive regular expression and - * generate responses synchronously. - */ -- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; - -/** - * Adds a handler to the server to handle incoming HTTP requests with a given - * HTTP method and a path matching a case-insensitive regular expression and - * generate responses asynchronously. - */ -- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block; - -@end - -@interface GCDWebServer (GETHandlers) - -/** - * 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; - -/** - * Adds a handler to the server to respond to incoming "GET" HTTP requests - * with a specific case-insensitive path with a file. - */ -- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests; - -/** - * Adds a handler to the server to respond to incoming "GET" HTTP requests - * with a case-insensitive path inside a base path with the corresponding file - * inside a local directory. If no local file matches the request path, a 401 - * HTTP status code is returned to the client. - * - * 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; - -@end - -/** - * GCDWebServer provides its own built-in logging facility which is used by - * default. It simply sends log messages to stderr assuming it is connected - * to a terminal type device. - * - * GCDWebServer is also compatible with a limited set of third-party logging - * facilities. If one of them is available at compile time, GCDWebServer will - * automatically use it in place of the built-in one. - * - * 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). - * - * It's possible to have GCDWebServer use a custom logging facility by defining - * the "__GCDWEBSERVER_LOGGING_HEADER__" preprocessor constant in Xcode build - * settings to the name of a custom header file (escaped like \"MyLogging.h\"). - * This header file must define the following set of macros: - * - * GWS_LOG_DEBUG(...) - * GWS_LOG_VERBOSE(...) - * GWS_LOG_INFO(...) - * GWS_LOG_WARNING(...) - * GWS_LOG_ERROR(...) - * GWS_LOG_EXCEPTION(__EXCEPTION__) - * - * IMPORTANT: Except for GWS_LOG_EXCEPTION() which gets passed an NSException, - * these macros must behave like NSLog(). Furthermore the GWS_LOG_DEBUG() macro - * should not do anything unless the preprocessor constant "DEBUG" evaluates to - * non-zero. - * - * The logging methods below send log messages to the same logging facility - * used by GCDWebServer. They can be used for consistency wherever you interact - * with GCDWebServer in your code (e.g. in the implementation of handlers). - */ -@interface GCDWebServer (Logging) - -/** - * Sets the log level of the logging facility below which log messages are discarded. - * - * @warning The interpretation of the "level" argument depends on the logging - * facility used at compile time. - * - * If using the built-in logging facility, the log levels are as follow: - * DEBUG = 0 - * VERBOSE = 1 - * INFO = 2 - * WARNING = 3 - * ERROR = 4 - * EXCEPTION = 5 - */ -+ (void)setLogLevel:(int)level; - -/** - * Logs a message to the logging facility at the VERBOSE level. - */ -- (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); - -/** - * Logs a message to the logging facility at the WARNING level. - */ -- (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); - -/** - * Logs an exception to the logging facility at the EXCEPTION level. - */ -- (void)logException:(NSException*)exception; - -@end - -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - -@interface GCDWebServer (Testing) - -/** - * Activates recording of HTTP requests and responses which create files in the - * current directory containing the raw data for all requests and responses. - * - * @warning The current directory must not contain any prior recording files. - */ -@property(nonatomic, getter=isRecordingEnabled) BOOL recordingEnabled; - -/** - * Runs tests by playing back pre-recorded HTTP requests in the given directory - * and comparing the generated responses with the pre-recorded ones. - * - * Returns the number of failed tests or -1 if server failed to start. - */ -- (NSInteger)runTestsWithOptions:(NSDictionary*)options inDirectory:(NSString*)path; - -@end - -#endif diff --git a/Frameworks/CGDWebServer/Core/GCDWebServer.m b/Frameworks/CGDWebServer/Core/GCDWebServer.m deleted file mode 100755 index 2d8d801..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServer.m +++ /dev/null @@ -1,1214 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import -#if TARGET_OS_IPHONE -#import -#else -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ -#import -#endif -#endif -#import - -#import "GCDWebServerPrivate.h" - -#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR -#define kDefaultPort 80 -#else -#define kDefaultPort 8080 -#endif - -NSString* const GCDWebServerOption_Port = @"Port"; -NSString* const GCDWebServerOption_BonjourName = @"BonjourName"; -NSString* const GCDWebServerOption_BonjourType = @"BonjourType"; -NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost"; -NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections"; -NSString* const GCDWebServerOption_ServerName = @"ServerName"; -NSString* const GCDWebServerOption_AuthenticationMethod = @"AuthenticationMethod"; -NSString* const GCDWebServerOption_AuthenticationRealm = @"AuthenticationRealm"; -NSString* const GCDWebServerOption_AuthenticationAccounts = @"AuthenticationAccounts"; -NSString* const GCDWebServerOption_ConnectionClass = @"ConnectionClass"; -NSString* const GCDWebServerOption_AutomaticallyMapHEADToGET = @"AutomaticallyMapHEADToGET"; -NSString* const GCDWebServerOption_ConnectedStateCoalescingInterval = @"ConnectedStateCoalescingInterval"; -#if TARGET_OS_IPHONE -NSString* const GCDWebServerOption_AutomaticallySuspendInBackground = @"AutomaticallySuspendInBackground"; -#endif - -NSString* const GCDWebServerAuthenticationMethod_Basic = @"Basic"; -NSString* const GCDWebServerAuthenticationMethod_DigestAccess = @"DigestAccess"; - -#if defined(__GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__) -#if DEBUG -GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Debug; -#else -GCDWebServerLoggingLevel GCDWebServerLogLevel = kGCDWebServerLoggingLevel_Info; -#endif -#elif defined(__GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__) -#if DEBUG -int GCDWebServerLogLevel = LOG_LEVEL_DEBUG; -#else -int GCDWebServerLogLevel = LOG_LEVEL_INFO; -#endif -#endif - -#if !TARGET_OS_IPHONE -static BOOL _run; -#endif - -#ifdef __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ - -void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) { - static const char* levelNames[] = {"DEBUG", "VERBOSE", "INFO", "WARNING", "ERROR", "EXCEPTION"}; - static int enableLogging = -1; - if (enableLogging < 0) { - enableLogging = (isatty(STDERR_FILENO) ? 1 : 0); - } - if (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]); - } -} - -#endif - -#if !TARGET_OS_IPHONE - -static void _SignalHandler(int signal) { - _run = NO; - printf("\n"); -} - -#endif - -#if !TARGET_OS_IPHONE || defined(__GCDWEBSERVER_ENABLE_TESTING__) - -// This utility function is used to ensure scheduled callbacks on the main thread are called when running the server synchronously -// https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html -// The main queue works with the application’s run loop to interleave the execution of queued tasks with the execution of other event sources attached to the run loop -// TODO: Ensure all scheduled blocks on the main queue are also executed -static void _ExecuteMainThreadRunLoopSources() { - SInt32 result; - do { - result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true); - } while (result == kCFRunLoopRunHandledSource); -} - -#endif - -@interface GCDWebServerHandler () { -@private - GCDWebServerMatchBlock _matchBlock; - GCDWebServerAsyncProcessBlock _asyncProcessBlock; -} -@end - -@implementation GCDWebServerHandler - -@synthesize matchBlock=_matchBlock, asyncProcessBlock=_asyncProcessBlock; - -- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock { - if ((self = [super init])) { - _matchBlock = [matchBlock copy]; - _asyncProcessBlock = [processBlock copy]; - } - return self; -} - -@end - -@interface GCDWebServer () { -@private - id __unsafe_unretained _delegate; - 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; - NSUInteger _port; - dispatch_source_t _source4; - dispatch_source_t _source6; - CFNetServiceRef _registrationService; - CFNetServiceRef _resolutionService; -#if TARGET_OS_IPHONE - BOOL _suspendInBackground; - UIBackgroundTaskIdentifier _backgroundTask; -#endif -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - BOOL _recording; -#endif -} -@end - -@implementation GCDWebServer - -@synthesize delegate=_delegate, handlers=_handlers, port=_port, serverName=_serverName, authenticationRealm=_authenticationRealm, - authenticationBasicAccounts=_authenticationBasicAccounts, authenticationDigestAccounts=_authenticationDigestAccounts, - shouldAutomaticallyMapHEADToGET=_mapHEADToGET; - -+ (void)initialize { - GCDWebServerInitializeFunctions(); -} - -- (instancetype)init { - if ((self = [super init])) { - _syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL); - _sourceGroup = dispatch_group_create(); - _handlers = [[NSMutableArray alloc] init]; -#if TARGET_OS_IPHONE - _backgroundTask = UIBackgroundTaskInvalid; -#endif - } - return self; -} - -- (void)dealloc { - GWS_DCHECK(_connected == NO); - 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); -#endif -} - -#if TARGET_OS_IPHONE - -// Always called on main thread -- (void)_startBackgroundTask { - GWS_DCHECK([NSThread isMainThread]); - 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(); - } -} - -#endif - -// Always called on main thread -- (void)_didConnect { - GWS_DCHECK([NSThread isMainThread]); - GWS_DCHECK(_connected == NO); - _connected = YES; - GWS_LOG_DEBUG(@"Did connect"); - -#if TARGET_OS_IPHONE - [self _startBackgroundTask]; -#endif - - if ([_delegate respondsToSelector:@selector(webServerDidConnect:)]) { - [_delegate webServerDidConnect:self]; - } -} - -- (void)willStartConnection:(GCDWebServerConnection*)connection { - dispatch_sync(_syncQueue, ^{ - - GWS_DCHECK(_activeConnections >= 0); - if (_activeConnections == 0) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (_disconnectTimer) { - CFRunLoopTimerInvalidate(_disconnectTimer); - CFRelease(_disconnectTimer); - _disconnectTimer = NULL; - } - if (_connected == NO) { - [self _didConnect]; - } - }); - } - _activeConnections += 1; - - }); -} - -#if TARGET_OS_IPHONE - -// Always called on main thread -- (void)_endBackgroundTask { - GWS_DCHECK([NSThread isMainThread]); - if (_backgroundTask != UIBackgroundTaskInvalid) { - if (_suspendInBackground && ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) && _source4) { - [self _stop]; - } - [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask]; - _backgroundTask = UIBackgroundTaskInvalid; - GWS_LOG_DEBUG(@"Did end background task"); - } -} - -#endif - -// Always called on main thread -- (void)_didDisconnect { - GWS_DCHECK([NSThread isMainThread]); - 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]; - } -} - -- (void)didEndConnection:(GCDWebServerConnection*)connection { - dispatch_sync(_syncQueue, ^{ - GWS_DCHECK(_activeConnections > 0); - _activeConnections -= 1; - if (_activeConnections == 0) { - dispatch_async(dispatch_get_main_queue(), ^{ - if ((_disconnectDelay > 0.0) && (_source4 != NULL)) { - if (_disconnectTimer) { - CFRunLoopTimerInvalidate(_disconnectTimer); - CFRelease(_disconnectTimer); - } - _disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + _disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) { - GWS_DCHECK([NSThread isMainThread]); - [self _didDisconnect]; - CFRelease(_disconnectTimer); - _disconnectTimer = NULL; - }); - CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes); - } else { - [self _didDisconnect]; - } - }); - } - }); -} - -- (NSString*)bonjourName { - CFStringRef name = _resolutionService ? CFNetServiceGetName(_resolutionService) : NULL; - return name && CFStringGetLength(name) ? CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil; -} - -- (NSString*)bonjourType { - CFStringRef type = _resolutionService ? CFNetServiceGetType(_resolutionService) : NULL; - return type && CFStringGetLength(type) ? CFBridgingRelease(CFStringCreateCopy(kCFAllocatorDefault, type)) : nil; -} - -- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock { - [self addHandlerWithMatchBlock:matchBlock asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) { - completionBlock(processBlock(request)); - }]; -} - -- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock { - GWS_DCHECK(_options == nil); - GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock asyncProcessBlock:processBlock]; - [_handlers insertObject:handler atIndex:0]; -} - -- (void)removeAllHandlers { - GWS_DCHECK(_options == nil); - [_handlers removeAllObjects]; -} - -static void _NetServiceRegisterCallBack(CFNetServiceRef service, CFStreamError* error, void* info) { - GWS_DCHECK([NSThread isMainThread]); - @autoreleasepool { - if (error->error) { - GWS_LOG_ERROR(@"Bonjour registration error %i (domain %i)", (int)error->error, (int)error->domain); - } else { - GCDWebServer* server = (__bridge GCDWebServer*)info; - GWS_LOG_VERBOSE(@"Bonjour registration complete for %@", [server class]); - CFNetServiceResolveWithTimeout(server->_resolutionService, 1.0, NULL); - } - } -} - -static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* error, void* info) { - GWS_DCHECK([NSThread isMainThread]); - @autoreleasepool { - if (error->error) { - if ((error->domain != kCFStreamErrorDomainNetServices) && (error->error != kCFNetServicesErrorTimeout)) { - GWS_LOG_ERROR(@"Bonjour resolution error %i (domain %i)", (int)error->error, (int)error->domain); - } - } else { - GCDWebServer* server = (__bridge GCDWebServer*)info; - GWS_LOG_INFO(@"%@ now reachable at %@", [server class], server.bonjourServerURL); - if ([server.delegate respondsToSelector:@selector(webServerDidCompleteBonjourRegistration:)]) { - [server.delegate webServerDidCompleteBonjourRegistration:server]; - } - } - } -} - -static inline id _GetOption(NSDictionary* 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 - return [[NSString alloc] initWithData:[data base64EncodedDataWithOptions:0] encoding:NSASCIIStringEncoding]; -} - -- (int)_createListeningSocket:(BOOL)useIPv6 - localAddress:(const void*)address - length:(socklen_t)length - maxPendingConnections:(NSUInteger)maxPendingConnections - error:(NSError**)error { - int listeningSocket = socket(useIPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, IPPROTO_TCP); - 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); - return listeningSocket; - } else { - if (error) { - *error = GCDWebServerMakePosixError(errno); - } - GWS_LOG_ERROR(@"Failed starting %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); - close(listeningSocket); - } - } else { - if (error) { - *error = GCDWebServerMakePosixError(errno); - } - GWS_LOG_ERROR(@"Failed binding %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); - close(listeningSocket); - } - - } else { - if (error) { - *error = GCDWebServerMakePosixError(errno); - } - GWS_LOG_ERROR(@"Failed creating %s listening socket: %s (%i)", useIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); - } - return -1; -} - -- (dispatch_source_t)_createDispatchSourceWithListeningSocket:(int)listeningSocket isIPv6:(BOOL)isIPv6 { - dispatch_group_enter(_sourceGroup); - dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, listeningSocket, 0, kGCDWebServerGCDQueue); - dispatch_source_set_cancel_handler(source, ^{ - - @autoreleasepool { - int result = close(listeningSocket); - if (result != 0) { - GWS_LOG_ERROR(@"Failed closing %s listening socket: %s (%i)", isIPv6 ? "IPv6" : "IPv4", strerror(errno), errno); - } else { - GWS_LOG_DEBUG(@"Did close %s listening socket %i", isIPv6 ? "IPv6" : "IPv4", listeningSocket); - } - } - dispatch_group_leave(_sourceGroup); - - }); - dispatch_source_set_event_handler(source, ^{ - - @autoreleasepool { - struct sockaddr remoteSockAddr; - socklen_t remoteAddrLen = sizeof(remoteSockAddr); - int socket = accept(listeningSocket, &remoteSockAddr, &remoteAddrLen); - if (socket > 0) { - NSData* remoteAddress = [NSData dataWithBytes:&remoteSockAddr length:remoteAddrLen]; - - struct sockaddr localSockAddr; - socklen_t localAddrLen = sizeof(localSockAddr); - NSData* localAddress = nil; - if (getsockname(socket, &localSockAddr, &localAddrLen) == 0) { - localAddress = [NSData dataWithBytes:&localSockAddr length:localAddrLen]; - GWS_DCHECK((!isIPv6 && localSockAddr.sa_family == AF_INET) || (isIPv6 && localSockAddr.sa_family == AF_INET6)); - } 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); - addr4.sin_family = AF_INET; - addr4.sin_port = htons(port); - addr4.sin_addr.s_addr = bindToLocalhost ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY); - int listeningSocket4 = [self _createListeningSocket:NO localAddress:&addr4 length:sizeof(addr4) maxPendingConnections:maxPendingConnections error:error]; - if (listeningSocket4 <= 0) { - return NO; - } - if (port == 0) { - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - if (getsockname(listeningSocket4, &addr, &addrlen) == 0) { - struct sockaddr_in* sockaddr = (struct sockaddr_in*)&addr; - port = ntohs(sockaddr->sin_port); - } else { - 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); - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(port); - addr6.sin6_addr = bindToLocalhost ? in6addr_loopback : in6addr_any; - int listeningSocket6 = [self _createListeningSocket:YES localAddress:&addr6 length:sizeof(addr6) maxPendingConnections:maxPendingConnections error:error]; - if (listeningSocket6 <= 0) { - 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]) { - _authenticationRealm = [_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]; - }]; - } else if ([authenticationMethod isEqualToString:GCDWebServerAuthenticationMethod_DigestAccess]) { - _authenticationRealm = [_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]; - }]; - } - _connectionClass = _GetOption(_options, GCDWebServerOption_ConnectionClass, [GCDWebServerConnection class]); - _mapHEADToGET = [_GetOption(_options, GCDWebServerOption_AutomaticallyMapHEADToGET, @YES) boolValue]; - _disconnectDelay = [_GetOption(_options, GCDWebServerOption_ConnectedStateCoalescingInterval, @1.0) doubleValue]; - - _source4 = [self _createDispatchSourceWithListeningSocket:listeningSocket4 isIPv6:NO]; - _source6 = [self _createDispatchSourceWithListeningSocket:listeningSocket6 isIPv6:YES]; - _port = port; - - 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); - CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes); - } - } else { - GWS_LOG_ERROR(@"Failed creating CFNetService"); - } - } - - dispatch_resume(_source4); - dispatch_resume(_source6); - 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]; - }); - } - - return YES; -} - -- (void)_stop { - GWS_DCHECK(_source4 != NULL); - - if (_registrationService) { - if (_resolutionService) { - CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes); - CFNetServiceSetClient(_resolutionService, NULL, NULL); - CFNetServiceCancel(_resolutionService); - CFRelease(_resolutionService); - _resolutionService = NULL; - } - CFNetServiceUnscheduleFromRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes); - CFNetServiceSetClient(_registrationService, NULL, NULL); - CFNetServiceCancel(_registrationService); - 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 -#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE - dispatch_release(_source6); -#endif - _source6 = NULL; -#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE - dispatch_release(_source4); -#endif - _source4 = NULL; - _port = 0; - - _serverName = nil; - _authenticationRealm = nil; - _authenticationBasicAccounts = nil; - _authenticationDigestAccounts = nil; - - dispatch_async(dispatch_get_main_queue(), ^{ - if (_disconnectTimer) { - CFRunLoopTimerInvalidate(_disconnectTimer); - CFRelease(_disconnectTimer); - _disconnectTimer = NULL; - [self _didDisconnect]; - } - }); - - GWS_LOG_INFO(@"%@ stopped", [self class]); - if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [_delegate webServerDidStop:self]; - }); - } -} - -#if TARGET_OS_IPHONE - -- (void)_didEnterBackground:(NSNotification*)notification { - GWS_DCHECK([NSThread isMainThread]); - GWS_LOG_DEBUG(@"Did enter background"); - if ((_backgroundTask == UIBackgroundTaskInvalid) && _source4) { - [self _stop]; - } -} - -- (void)_willEnterForeground:(NSNotification*)notification { - GWS_DCHECK([NSThread isMainThread]); - GWS_LOG_DEBUG(@"Will enter foreground"); - if (!_source4) { - [self _start:NULL]; // TODO: There's probably nothing we can do on failure - } -} - -#endif - -- (BOOL)startWithOptions:(NSDictionary*)options error:(NSError**)error { - if (_options == nil) { - _options = [options copy]; -#if TARGET_OS_IPHONE - _suspendInBackground = [_GetOption(_options, GCDWebServerOption_AutomaticallySuspendInBackground, @YES) boolValue]; - if (((_suspendInBackground == NO) || ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground)) && ![self _start:error]) -#else - if (![self _start:error]) -#endif - { - _options = nil; - return NO; - } -#if TARGET_OS_IPHONE - if (_suspendInBackground) { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; - } -#endif - return YES; - } else { - GWS_DNOT_REACHED(); - } - return NO; -} - -- (BOOL)isRunning { - return (_options ? YES : NO); -} - -- (void)stop { - if (_options) { -#if TARGET_OS_IPHONE - if (_suspendInBackground) { - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; - } -#endif - if (_source4) { - [self _stop]; - } - _options = nil; - } else { - GWS_DNOT_REACHED(); - } -} - -@end - -@implementation GCDWebServer (Extensions) - -- (NSURL*)serverURL { - if (_source4) { - NSString* ipAddress = GCDWebServerGetPrimaryIPAddress(NO); // We can't really use IPv6 anyway as it doesn't work great with HTTP URLs in practice - if (ipAddress) { - if (_port != 80) { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", ipAddress, (int)_port]]; - } else { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", ipAddress]]; - } - } - } - return nil; -} - -- (NSURL*)bonjourServerURL { - if (_source4 && _resolutionService) { - NSString* name = (__bridge NSString*)CFNetServiceGetTargetHost(_resolutionService); - if (name.length) { - name = [name substringToIndex:(name.length - 1)]; // Strip trailing period at end of domain - if (_port != 80) { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", name, (int)_port]]; - } else { - return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", name]]; - } - } - } - return nil; -} - -- (BOOL)start { - return [self startWithPort:kDefaultPort bonjourName:@""]; -} - -- (BOOL)startWithPort:(NSUInteger)port bonjourName:(NSString*)name { - NSMutableDictionary* options = [NSMutableDictionary dictionary]; - [options setObject:[NSNumber numberWithInteger:port] forKey:GCDWebServerOption_Port]; - [options setValue:name forKey:GCDWebServerOption_BonjourName]; - return [self startWithOptions:options error:NULL]; -} - -#if !TARGET_OS_IPHONE - -- (BOOL)runWithPort:(NSUInteger)port bonjourName:(NSString*)name { - NSMutableDictionary* options = [NSMutableDictionary dictionary]; - [options setObject:[NSNumber numberWithInteger:port] forKey:GCDWebServerOption_Port]; - [options setValue:name forKey:GCDWebServerOption_BonjourName]; - return [self runWithOptions:options error:NULL]; -} - -- (BOOL)runWithOptions:(NSDictionary*)options error:(NSError**)error { - GWS_DCHECK([NSThread isMainThread]); - BOOL success = NO; - _run = YES; - void (*termHandler)(int) = signal(SIGTERM, _SignalHandler); - void (*intHandler)(int) = signal(SIGINT, _SignalHandler); - if ((termHandler != SIG_ERR) && (intHandler != SIG_ERR)) { - if ([self startWithOptions:options error:error]) { - while (_run) { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0, true); - } - [self stop]; - success = YES; - } - _ExecuteMainThreadRunLoopSources(); - signal(SIGINT, intHandler); - signal(SIGTERM, termHandler); - } - return success; -} - -#endif - -@end - -@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)); - }]; -} - -- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass asyncProcessBlock:(GCDWebServerAsyncProcessBlock)block { - [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]; -} - -- (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)); - }]; -} - -- (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) { - - 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]; - - } 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)); - }]; -} - -- (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) { - - if (![requestMethod isEqualToString:method]) { - return nil; - } - - NSArray* matches = [expression matchesInString:urlPath options:0 range:NSMakeRange(0, urlPath.length)]; - if (matches.count == 0) { - return nil; - } - - NSMutableArray* captures = [NSMutableArray array]; - for (NSTextCheckingResult* result in matches) { - // Start at 1; index 0 is the whole string - for (NSUInteger i = 1; i < result.numberOfRanges; i++) { - [captures addObject:[urlPath substringWithRange:[result rangeAtIndex:i]]]; - } - } - - GCDWebServerRequest* request = [[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]; - [request setAttribute:captures forKey:GCDWebServerRequestAttribute_RegexCaptures]; - return request; - - } asyncProcessBlock:block]; - } else { - GWS_DNOT_REACHED(); - } -} - -@end - -@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; - - }]; -} - -- (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; - - }]; -} - -- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path { - NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:path]; - if (enumerator == nil) { - return nil; - } - NSMutableString* html = [NSMutableString string]; - [html appendString:@"\n"]; - [html appendString:@"\n"]; - [html appendString:@"
    \n"]; - for (NSString* file in enumerator) { - if (![file hasPrefix:@"."]) { - NSString* type = [[enumerator fileAttributes] objectForKey:NSFileType]; - NSString* escapedFile = [file stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - GWS_DCHECK(escapedFile); - if ([type isEqualToString:NSFileTypeRegular]) { - [html appendFormat:@"
  • %@
  • \n", escapedFile, file]; - } else if ([type isEqualToString:NSFileTypeDirectory]) { - [html appendFormat:@"
  • %@/
  • \n", escapedFile, file]; - } - } - [enumerator skipDescendents]; - } - [html appendString:@"
\n"]; - [html appendString:@"\n"]; - return [GCDWebServerDataResponse responseWithHTML:html]; -} - -- (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) { - - if (![requestMethod isEqualToString:@"GET"]) { - return nil; - } - if (![urlPath hasPrefix:basePath]) { - 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]; - } - } - 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]; - } - } - } - if (response) { - response.cacheControlMaxAge = cacheAge; - } else { - response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NotFound]; - } - return response; - - }]; - } else { - GWS_DNOT_REACHED(); - } -} - -@end - -@implementation GCDWebServer (Logging) - -+ (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 -} - -- (void)logVerbose:(NSString*)format, ... { - va_list arguments; - va_start(arguments, format); - GWS_LOG_VERBOSE(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); - va_end(arguments); -} - -- (void)logInfo:(NSString*)format, ... { - va_list arguments; - va_start(arguments, format); - GWS_LOG_INFO(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); - va_end(arguments); -} - -- (void)logWarning:(NSString*)format, ... { - va_list arguments; - va_start(arguments, format); - GWS_LOG_WARNING(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); - va_end(arguments); -} - -- (void)logError:(NSString*)format, ... { - va_list arguments; - va_start(arguments, format); - GWS_LOG_ERROR(@"%@", [[NSString alloc] initWithFormat:format arguments:arguments]); - va_end(arguments); -} - -- (void)logException:(NSException*)exception { - GWS_LOG_EXCEPTION(exception); -} - -@end - -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - -@implementation GCDWebServer (Testing) - -- (void)setRecordingEnabled:(BOOL)flag { - _recording = flag; -} - -- (BOOL)isRecordingEnabled { - return _recording; -} - -static CFHTTPMessageRef _CreateHTTPMessageFromData(NSData* data, BOOL isRequest) { - CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, isRequest); - if (CFHTTPMessageAppendBytes(message, data.bytes, data.length)) { - return message; - } - CFRelease(message); - return NULL; -} - -static CFHTTPMessageRef _CreateHTTPMessageFromPerformingRequest(NSData* inData, NSUInteger port) { - CFHTTPMessageRef response = NULL; - int httpSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (httpSocket > 0) { - struct sockaddr_in addr4; - bzero(&addr4, sizeof(addr4)); - addr4.sin_len = sizeof(port); - addr4.sin_family = AF_INET; - addr4.sin_port = htons(8080); - 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) { - NSMutableData* outData = [[NSMutableData alloc] initWithLength:(256 * 1024)]; - NSUInteger length = 0; - while (1) { - ssize_t result = read(httpSocket, (char*)outData.mutableBytes + length, outData.length - length); - if (result < 0) { - length = NSUIntegerMax; - break; - } else if (result == 0) { - break; - } - length += result; - if (length >= outData.length) { - outData.length = 2 * outData.length; - } - } - if (length != NSUIntegerMax) { - outData.length = length; - response = _CreateHTTPMessageFromData(outData, NO); - } else { - GWS_DNOT_REACHED(); - } - } - } - close(httpSocket); - } - return response; -} - -static void _LogResult(NSString* format, ...) { - va_list arguments; - va_start(arguments, format); - NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; - va_end(arguments); - fprintf(stdout, "%s\n", [message UTF8String]); -} - -- (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 - 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) { - if (![requestFile hasSuffix:@".request"]) { - continue; - } - @autoreleasepool { - NSString* index = [[requestFile componentsSeparatedByString:@"-"] firstObject]; - BOOL success = NO; - NSData* requestData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:requestFile]]; - if (requestData) { - CFHTTPMessageRef request = _CreateHTTPMessageFromData(requestData, YES); - if (request) { - NSString* requestMethod = CFBridgingRelease(CFHTTPMessageCopyRequestMethod(request)); - NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(request)); - _LogResult(@"[%i] %@ %@", (int)[index integerValue], requestMethod, requestURL.path); - NSString* prefix = [index stringByAppendingString:@"-"]; - for (NSString* responseFile in files) { - if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) { - NSData* responseData = [NSData dataWithContentsOfFile:[path stringByAppendingPathComponent:responseFile]]; - if (responseData) { - 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) { - if ([ignoredHeaders containsObject:expectedHeader]) { - continue; - } - NSString* expectedValue = [expectedHeaders objectForKey:expectedHeader]; - NSString* actualValue = [actualHeaders objectForKey:expectedHeader]; - if (![actualValue isEqualToString:expectedValue]) { - _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", expectedHeader, expectedValue, actualValue); - success = NO; - } - } - for (NSString* actualHeader in actualHeaders) { - if (![expectedHeaders objectForKey:actualHeader]) { - _LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", actualHeader, nil, [actualHeaders objectForKey:actualHeader]); - success = NO; - } - } - - NSString* expectedContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(expectedResponse, CFSTR("Content-Length"))); - NSData* expectedBody = CFBridgingRelease(CFHTTPMessageCopyBody(expectedResponse)); - NSString* actualContentLength = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(actualResponse, CFSTR("Content-Length"))); - NSData* actualBody = CFBridgingRelease(CFHTTPMessageCopyBody(actualResponse)); - 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]) { - _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 -#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 ([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 launch]; - } - } -#endif -#endif - } - - CFRelease(actualResponse); - } - CFRelease(expectedResponse); - } - } else { - GWS_DNOT_REACHED(); - } - break; - } - } - CFRelease(request); - } - } else { - GWS_DNOT_REACHED(); - } - _LogResult(@""); - if (!success) { - ++result; - } - } - _ExecuteMainThreadRunLoopSources(); - } - - [self stop]; - - _ExecuteMainThreadRunLoopSources(); - } - return result; -} - -@end - -#endif diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerConnection.h b/Frameworks/CGDWebServer/Core/GCDWebServerConnection.h deleted file mode 100755 index 8e4aaf5..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerConnection.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServer.h" - -@class GCDWebServerHandler; - -/** - * The GCDWebServerConnection class is instantiated by GCDWebServer to handle - * each new HTTP connection. Each instance stays alive until the connection is - * closed. - * - * You cannot use this class directly, but it is made public so you can - * subclass it to override some hooks. Use the GCDWebServerOption_ConnectionClass - * option for GCDWebServer to install your custom subclass. - * - * @warning The GCDWebServerConnection retains the GCDWebServer until the - * connection is closed. - */ -@interface GCDWebServerConnection : NSObject - -/** - * Returns the GCDWebServer that owns the connection. - */ -@property(nonatomic, readonly) GCDWebServer* server; - -/** - * Returns YES if the connection is using IPv6. - */ -@property(nonatomic, readonly, getter=isUsingIPv6) BOOL usingIPv6; - -/** - * Returns the address of the local peer (i.e. server) of the connection - * as a raw "struct sockaddr". - */ -@property(nonatomic, readonly) NSData* localAddressData; - -/** - * Returns the address of the local peer (i.e. server) of the connection - * as a string. - */ -@property(nonatomic, readonly) NSString* localAddressString; - -/** - * Returns the address of the remote peer (i.e. client) of the connection - * as a raw "struct sockaddr". - */ -@property(nonatomic, readonly) NSData* remoteAddressData; - -/** - * Returns the address of the remote peer (i.e. client) of the connection - * as a string. - */ -@property(nonatomic, readonly) NSString* remoteAddressString; - -/** - * Returns the total number of bytes received from the remote peer (i.e. client) - * so far. - */ -@property(nonatomic, readonly) NSUInteger totalBytesRead; - -/** - * Returns the total number of bytes sent to the remote peer (i.e. client) so far. - */ -@property(nonatomic, readonly) NSUInteger totalBytesWritten; - -@end - -/** - * Hooks to customize the behavior of GCDWebServer HTTP connections. - * - * @warning These methods can be called on any GCD thread. - * Be sure to also call "super" when overriding them. - */ -@interface GCDWebServerConnection (Subclassing) - -/** - * This method is called when the connection is opened. - * - * Return NO to reject the connection e.g. after validating the local - * or remote address. - */ -- (BOOL)open; - -/** - * This method is called whenever data has been received - * from the remote peer (i.e. client). - * - * @warning Do not attempt to modify this data. - */ -- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length; - -/** - * This method is called whenever data has been sent - * to the remote peer (i.e. client). - * - * @warning Do not attempt to modify this data. - */ -- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length; - -/** - * This method is called after the HTTP headers have been received to - * allow replacing the request URL by another one. - * - * The default implementation returns the original URL. - */ -- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers; - -/** - * Assuming a valid HTTP request was received, this method is called before - * the request is processed. - * - * Return a non-nil GCDWebServerResponse to bypass the request processing entirely. - * - * The default implementation checks for HTTP authentication if applicable - * and returns a barebone 401 status code response if authentication failed. - */ -- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request; - -/** - * Assuming a valid HTTP request was received and -preflightRequest: returned nil, - * this method is called to process the request by executing the handler's - * process block. - */ -- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion; - -/** - * Assuming a valid HTTP request was received and either -preflightRequest: - * or -processRequest:completion: returned a non-nil GCDWebServerResponse, - * this method is called to override the response. - * - * You can either modify the current response and return it, or return a - * completely new one. - * - * The default implementation replaces any response matching the "ETag" or - * "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304) - * one. - */ -- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request; - -/** - * This method is called if any error happens while validing or processing - * the request or if no GCDWebServerResponse was generated during processing. - * - * @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; - -/** - * Called when the connection is closed. - */ -- (void)close; - -@end diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerConnection.m b/Frameworks/CGDWebServer/Core/GCDWebServerConnection.m deleted file mode 100755 index d4e3f39..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerConnection.m +++ /dev/null @@ -1,845 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import -#import -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ -#import -#endif - -#import "GCDWebServerPrivate.h" - -#define kHeadersReadCapacity (1 * 1024) -#define kBodyReadCapacity (256 * 1024) - -typedef void (^ReadDataCompletionBlock)(BOOL success); -typedef void (^ReadHeadersCompletionBlock)(NSData* extraData); -typedef void (^ReadBodyCompletionBlock)(BOOL success); - -typedef void (^WriteDataCompletionBlock)(BOOL success); -typedef void (^WriteHeadersCompletionBlock)(BOOL success); -typedef void (^WriteBodyCompletionBlock)(BOOL success); - -static NSData* _CRLFData = nil; -static NSData* _CRLFCRLFData = nil; -static NSData* _continueData = nil; -static NSData* _lastChunkData = nil; -static NSString* _digestAuthenticationNonce = nil; -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ -static int32_t _connectionCounter = 0; -#endif - -@interface GCDWebServerConnection () { -@private - GCDWebServer* _server; - NSData* _localAddress; - NSData* _remoteAddress; - CFSocketNativeHandle _socket; - NSUInteger _bytesRead; - NSUInteger _bytesWritten; - BOOL _virtualHEAD; - - CFHTTPMessageRef _requestMessage; - GCDWebServerRequest* _request; - GCDWebServerHandler* _handler; - CFHTTPMessageRef _responseMessage; - GCDWebServerResponse* _response; - NSInteger _statusCode; - - BOOL _opened; -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - NSUInteger _connectionIndex; - NSString* _requestPath; - int _requestFD; - NSString* _responsePath; - int _responseFD; -#endif -} -@end - -@implementation GCDWebServerConnection (Read) - -- (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block { - dispatch_read(_socket, length, kGCDWebServerGCDQueue, ^(dispatch_data_t buffer, int error) { - - @autoreleasepool { - if (error == 0) { - size_t size = dispatch_data_get_size(buffer); - if (size > 0) { - NSUInteger originalLength = data.length; - dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) { - [data appendBytes:chunkBytes length:chunkSize]; - return true; - }); - [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)]; - block(YES); - } else { - if (_bytesRead > 0) { - GWS_LOG_ERROR(@"No more data available on socket %i", _socket); - } else { - GWS_LOG_WARNING(@"No data received from socket %i", _socket); - } - block(NO); - } - } else { - GWS_LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error); - block(NO); - } - } - - }); -} - -- (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block { - GWS_DCHECK(_requestMessage); - [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)) { - block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]); - } else { - GWS_LOG_ERROR(@"Failed parsing request headers from socket %i", _socket); - block(nil); - } - } else { - GWS_LOG_ERROR(@"Failed appending request headers data from socket %i", _socket); - block(nil); - } - } - } else { - block(nil); - } - - }]; -} - -- (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block { - GWS_DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]); - NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity]; - [self _readData:bodyData withLength:length completionBlock:^(BOOL success) { - - if (success) { - if (bodyData.length <= length) { - NSError* error = nil; - if ([_request performWriteData:bodyData error:&error]) { - NSUInteger remainingLength = length - bodyData.length; - if (remainingLength) { - [self _readBodyWithRemainingLength:remainingLength completionBlock:block]; - } else { - block(YES); - } - } else { - GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); - block(NO); - } - } else { - GWS_LOG_ERROR(@"Unexpected extra content reading request body on socket %i", _socket); - block(NO); - GWS_DNOT_REACHED(); - } - } else { - block(NO); - } - - }]; -} - -static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) { - char buffer[size + 1]; - bcopy(bytes, buffer, size); - buffer[size] = 0; - char* end = NULL; - long result = strtol(buffer, &end, 16); - return ((end != NULL) && (*end == 0) && (result >= 0) ? result : NSNotFound); -} - -- (void)_readNextBodyChunk:(NSMutableData*)chunkData completionBlock:(ReadBodyCompletionBlock)block { - GWS_DCHECK([_request hasBody] && [_request usesChunkedTransferEncoding]); - - while (1) { - NSRange range = [chunkData rangeOfData:_CRLFData options:0 range:NSMakeRange(0, chunkData.length)]; - if (range.location == NSNotFound) { - break; - } - NSRange extensionRange = [chunkData rangeOfData:[NSData dataWithBytes:";" length:1] options:0 range:NSMakeRange(0, range.location)]; // Ignore chunk extensions - NSUInteger length = _ScanHexNumber((char*)chunkData.bytes, extensionRange.location != NSNotFound ? extensionRange.location : range.location); - if (length != NSNotFound) { - if (length) { - if (chunkData.length < range.location + range.length + length + 2) { - break; - } - const char* ptr = (char*)chunkData.bytes + range.location + range.length + length; - if ((*ptr == '\r') && (*(ptr + 1) == '\n')) { - NSError* error = nil; - if ([_request performWriteData:[chunkData subdataWithRange:NSMakeRange(range.location + range.length, length)] error:&error]) { - [chunkData replaceBytesInRange:NSMakeRange(0, range.location + range.length + length + 2) withBytes:NULL length:0]; - } else { - GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); - block(NO); - return; - } - } else { - GWS_LOG_ERROR(@"Missing terminating CRLF sequence for chunk reading request body on socket %i", _socket); - block(NO); - return; - } - } else { - NSRange trailerRange = [chunkData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(range.location, chunkData.length - range.location)]; // Ignore trailers - if (trailerRange.location != NSNotFound) { - block(YES); - return; - } - } - } else { - GWS_LOG_ERROR(@"Invalid chunk length reading request body on socket %i", _socket); - block(NO); - return; - } - } - - [self _readData:chunkData withLength:NSUIntegerMax completionBlock:^(BOOL success) { - - if (success) { - [self _readNextBodyChunk:chunkData completionBlock:block]; - } else { - block(NO); - } - - }]; -} - -@end - -@implementation GCDWebServerConnection (Write) - -- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block { - dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, kGCDWebServerGCDQueue, ^{ - [data self]; // Keeps ARC from releasing data too early - }); - dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(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); - block(NO); - } - } - - }); -#if !OS_OBJECT_USE_OBJC_RETAIN_RELEASE - dispatch_release(buffer); -#endif -} - -- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block { - GWS_DCHECK(_responseMessage); - CFDataRef data = CFHTTPMessageCopySerializedMessage(_responseMessage); - [self _writeData:(__bridge NSData*)data withCompletionBlock:block]; - CFRelease(data); -} - -- (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block { - GWS_DCHECK([_response hasBody]); - [_response performReadDataWithCompletion:^(NSData* data, NSError* error) { - - if (data) { - if (data.length) { - if (_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); - block(NO); - return; - } - char* ptr = (char*)[(NSMutableData*)chunk mutableBytes]; - bcopy(hexString, ptr, hexLength); - ptr += hexLength; - *ptr++ = '\r'; - *ptr++ = '\n'; - bcopy(data.bytes, ptr, data.length); - ptr += data.length; - *ptr++ = '\r'; - *ptr = '\n'; - data = chunk; - } - [self _writeData:data withCompletionBlock:^(BOOL success) { - - if (success) { - [self _writeBodyWithCompletionBlock:block]; - } else { - block(NO); - } - - }]; - } else { - if (_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); - block(NO); - } - - }]; -} - -@end - -@implementation GCDWebServerConnection - -@synthesize server=_server, localAddressData=_localAddress, remoteAddressData=_remoteAddress, totalBytesRead=_bytesRead, totalBytesWritten=_bytesWritten; - -+ (void)initialize { - if (_CRLFData == nil) { - _CRLFData = [[NSData alloc] initWithBytes:"\r\n" length:2]; - GWS_DCHECK(_CRLFData); - } - if (_CRLFCRLFData == nil) { - _CRLFCRLFData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; - GWS_DCHECK(_CRLFCRLFData); - } - if (_continueData == nil) { - CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1); - _continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message)); - CFRelease(message); - GWS_DCHECK(_continueData); - } - if (_lastChunkData == nil) { - _lastChunkData = [[NSData alloc] initWithBytes:"0\r\n\r\n" length:5]; - } - if (_digestAuthenticationNonce == nil) { - CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); - _digestAuthenticationNonce = GCDWebServerComputeMD5Digest(@"%@", CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid))); - CFRelease(uuid); - } -} - -- (BOOL)isUsingIPv6 { - const struct sockaddr* localSockAddr = _localAddress.bytes; - return (localSockAddr->sa_family == AF_INET6); -} - -- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode { - _statusCode = statusCode; - _responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1); - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close")); - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (__bridge CFStringRef)_server.serverName); - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (__bridge CFStringRef)GCDWebServerFormatRFC822([NSDate date])); -} - -- (void)_startProcessingRequest { - GWS_DCHECK(_responseMessage == NULL); - - GCDWebServerResponse* preflightResponse = [self preflightRequest:_request]; - if (preflightResponse) { - [self _finishProcessingRequest:preflightResponse]; - } else { - [self processRequest:_request completion:^(GCDWebServerResponse* processResponse) { - [self _finishProcessingRequest:processResponse]; - }]; - } -} - -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html -- (void)_finishProcessingRequest:(GCDWebServerResponse*)response { - GWS_DCHECK(_responseMessage == NULL); - BOOL hasBody = NO; - - if (response) { - response = [self overrideResponse:response forRequest:_request]; - } - if (response) { - if ([response hasBody]) { - [response prepareForReading]; - hasBody = !_virtualHEAD; - } - NSError* error = nil; - if (hasBody && ![response performOpen:&error]) { - GWS_LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error); - } else { - _response = response; - } - } - - if (_response) { - [self _initializeResponseHeadersWithStatusCode:_response.statusCode]; - if (_response.lastModifiedDate) { - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Last-Modified"), (__bridge CFStringRef)GCDWebServerFormatRFC822(_response.lastModifiedDate)); - } - if (_response.eTag) { - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("ETag"), (__bridge CFStringRef)_response.eTag); - } - if ((_response.statusCode >= 200) && (_response.statusCode < 300)) { - if (_response.cacheControlMaxAge > 0) { - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (__bridge CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)_response.cacheControlMaxAge]); - } else { - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache")); - } - } - if (_response.contentType != nil) { - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (__bridge CFStringRef)GCDWebServerNormalizeHeaderValue(_response.contentType)); - } - if (_response.contentLength != NSUIntegerMax) { - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (__bridge CFStringRef)[NSString stringWithFormat:@"%lu", (unsigned long)_response.contentLength]); - } - if (_response.usesChunkedTransferEncoding) { - CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Transfer-Encoding"), CFSTR("chunked")); - } - [_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) { - CFHTTPMessageSetHeaderFieldValue(_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 - - }]; - } - } else if (hasBody) { - [_response performClose]; - } - - }]; - } else { - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - -} - -- (void)_readBodyWithLength:(NSUInteger)length initialData:(NSData*)initialData { - NSError* error = nil; - if (![_request performOpen:&error]) { - GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - return; - } - - if (initialData.length) { - if (![_request performWriteData:initialData error:&error]) { - GWS_LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error); - if (![_request performClose:&error]) { - GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); - } - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - return; - } - length -= initialData.length; - } - - if (length) { - [self _readBodyWithRemainingLength:length completionBlock:^(BOOL success) { - - NSError* localError = nil; - if ([_request performClose:&localError]) { - [self _startProcessingRequest]; - } else { - GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - - }]; - } else { - if ([_request performClose:&error]) { - [self _startProcessingRequest]; - } else { - GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - } -} - -- (void)_readChunkedBodyWithInitialData:(NSData*)initialData { - NSError* error = nil; - if (![_request performOpen:&error]) { - GWS_LOG_ERROR(@"Failed opening request body for socket %i: %@", _socket, error); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - return; - } - - NSMutableData* chunkData = [[NSMutableData alloc] initWithData:initialData]; - [self _readNextBodyChunk:chunkData completionBlock:^(BOOL success) { - - NSError* localError = nil; - if ([_request performClose:&localError]) { - [self _startProcessingRequest]; - } else { - GWS_LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - - }]; -} - -- (void)_readRequestHeaders { - _requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true); - 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"]) { - requestMethod = @"GET"; - _virtualHEAD = YES; - } - NSDictionary* requestHeaders = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones - NSURL* requestURL = CFBridgingRelease(CFHTTPMessageCopyRequestURL(_requestMessage)); - if (requestURL) { - requestURL = [self rewriteRequestURL:requestURL withMethod:requestMethod headers:requestHeaders]; - GWS_DCHECK(requestURL); - } - NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(CFBridgingRelease(CFURLCopyPath((CFURLRef)requestURL))) : nil; // Don't use -[NSURL path] which strips the ending slash - 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) { - break; - } - } - if (_request) { - if ([_request hasBody]) { - [_request prepareForWriting]; - if (_request.usesChunkedTransferEncoding || (extraData.length <= _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) { - [self _readChunkedBodyWithInitialData:extraData]; - } else { - [self _readBodyWithLength:_request.contentLength initialData:extraData]; - } - } - - }]; - } else { - GWS_LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket); - [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed]; - } - } else { - if (_request.usesChunkedTransferEncoding) { - [self _readChunkedBodyWithInitialData:extraData]; - } else { - [self _readBodyWithLength:_request.contentLength initialData:extraData]; - } - } - } else { - GWS_LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket); - [self abortRequest:_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_MethodNotAllowed]; - } - } else { - [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - GWS_DNOT_REACHED(); - } - } else { - [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; - } - - }]; -} - -- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket { - if ((self = [super init])) { - _server = server; - _localAddress = localAddress; - _remoteAddress = remoteAddress; - _socket = socket; - GWS_LOG_DEBUG(@"Did open connection on socket %i", _socket); - - [_server willStartConnection:self]; - - if (![self open]) { - close(_socket); - return nil; - } - _opened = YES; - - [self _readRequestHeaders]; - } - return self; -} - -- (NSString*)localAddressString { - return GCDWebServerStringFromSockAddr(_localAddress.bytes, YES); -} - -- (NSString*)remoteAddressString { - return GCDWebServerStringFromSockAddr(_remoteAddress.bytes, YES); -} - -- (void)dealloc { - int result = close(_socket); - if (result != 0) { - GWS_LOG_ERROR(@"Failed closing socket %i for connection: %s (%i)", _socket, strerror(errno), errno); - } else { - GWS_LOG_DEBUG(@"Did close connection on socket %i", _socket); - } - - if (_opened) { - [self close]; - } - - [_server didEndConnection:self]; - - if (_requestMessage) { - CFRelease(_requestMessage); - } - - if (_responseMessage) { - CFRelease(_responseMessage); - } -} - -@end - -@implementation GCDWebServerConnection (Subclassing) - -- (BOOL)open { -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - if (_server.recordingEnabled) { - _connectionIndex = OSAtomicIncrement32(&_connectionCounter); - - _requestPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; - _requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - GWS_DCHECK(_requestFD > 0); - - _responsePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; - _responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - GWS_DCHECK(_responseFD > 0); - } -#endif - - return YES; -} - -- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length { - GWS_LOG_DEBUG(@"Connection received %lu bytes on socket %i", (unsigned long)length, _socket); - _bytesRead += length; - -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) { - GWS_LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno); - close(_requestFD); - _requestFD = 0; - } -#endif -} - -- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length { - GWS_LOG_DEBUG(@"Connection sent %lu bytes on socket %i", (unsigned long)length, _socket); - _bytesWritten += length; - -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) { - GWS_LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno); - close(_responseFD); - _responseFD = 0; - } -#endif -} - -- (NSURL*)rewriteRequestURL:(NSURL*)url withMethod:(NSString*)method headers:(NSDictionary*)headers { - return url; -} - -// https://tools.ietf.org/html/rfc2617 -- (GCDWebServerResponse*)preflightRequest:(GCDWebServerRequest*)request { - GWS_LOG_DEBUG(@"Connection on socket %i preflighting request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead); - GCDWebServerResponse* response = nil; - if (_server.authenticationBasicAccounts) { - __block BOOL authenticated = NO; - NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"]; - if ([authorizationHeader hasPrefix:@"Basic "]) { - NSString* basicAccount = [authorizationHeader substringFromIndex:6]; - [_server.authenticationBasicAccounts enumerateKeysAndObjectsUsingBlock:^(NSString* username, NSString* digest, BOOL* stop) { - if ([basicAccount isEqualToString:digest]) { - authenticated = YES; - *stop = YES; - } - }]; - } - if (!authenticated) { - response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized]; - [response setValue:[NSString stringWithFormat:@"Basic realm=\"%@\"", _server.authenticationRealm] forAdditionalHeader:@"WWW-Authenticate"]; - } - } else if (_server.authenticationDigestAccounts) { - BOOL authenticated = NO; - BOOL isStaled = NO; - NSString* authorizationHeader = [request.headers objectForKey:@"Authorization"]; - if ([authorizationHeader hasPrefix:@"Digest "]) { - NSString* realm = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"realm"); - if ([realm isEqualToString:_server.authenticationRealm]) { - NSString* nonce = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"nonce"); - if ([nonce isEqualToString:_digestAuthenticationNonce]) { - NSString* username = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"username"); - NSString* uri = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"uri"); - NSString* actualResponse = GCDWebServerExtractHeaderValueParameter(authorizationHeader, @"response"); - NSString* ha1 = [_server.authenticationDigestAccounts objectForKey:username]; - NSString* ha2 = GCDWebServerComputeMD5Digest(@"%@:%@", request.method, uri); // We cannot use "request.path" as the query string is required - NSString* expectedResponse = GCDWebServerComputeMD5Digest(@"%@:%@:%@", ha1, _digestAuthenticationNonce, ha2); - if ([actualResponse isEqualToString:expectedResponse]) { - authenticated = YES; - } - } else if (nonce.length) { - isStaled = YES; - } - } - } - if (!authenticated) { - response = [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_Unauthorized]; - [response setValue:[NSString stringWithFormat:@"Digest realm=\"%@\", nonce=\"%@\"%@", _server.authenticationRealm, _digestAuthenticationNonce, isStaled ? @", stale=TRUE" : @""] forAdditionalHeader:@"WWW-Authenticate"]; // TODO: Support Quality of Protection ("qop") - } - } - return response; -} - -- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion { - GWS_LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead); - @try { - _handler.asyncProcessBlock(request, completion); - } - @catch (NSException* exception) { - GWS_LOG_EXCEPTION(exception); - } -} - -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26 -static inline BOOL _CompareResources(NSString* responseETag, NSString* requestETag, NSDate* responseLastModified, NSDate* requestLastModified) { - if ([requestETag isEqualToString:@"*"] && (!responseLastModified || !requestLastModified || ([responseLastModified compare:requestLastModified] != NSOrderedDescending))) { - return YES; - } else { - if ([responseETag isEqualToString:requestETag]) { - return YES; - } - if (responseLastModified && requestLastModified && ([responseLastModified compare:requestLastModified] != NSOrderedDescending)) { - return YES; - } - } - return NO; -} - -- (GCDWebServerResponse*)overrideResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request { - if ((response.statusCode >= 200) && (response.statusCode < 300) && _CompareResources(response.eTag, request.ifNoneMatch, response.lastModifiedDate, request.ifModifiedSince)) { - NSInteger code = [request.method isEqualToString:@"HEAD"] || [request.method isEqualToString:@"GET"] ? kGCDWebServerHTTPStatusCode_NotModified : kGCDWebServerHTTPStatusCode_PreconditionFailed; - GCDWebServerResponse* newResponse = [GCDWebServerResponse responseWithStatusCode:code]; - newResponse.cacheControlMaxAge = response.cacheControlMaxAge; - newResponse.lastModifiedDate = response.lastModifiedDate; - newResponse.eTag = response.eTag; - GWS_DCHECK(newResponse); - return newResponse; - } - return response; -} - -- (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode { - GWS_DCHECK(_responseMessage == NULL); - GWS_DCHECK((statusCode >= 400) && (statusCode < 600)); - [self _initializeResponseHeadersWithStatusCode:statusCode]; - [self _writeHeadersWithCompletionBlock:^(BOOL success) { - ; // Nothing more to do - }]; - GWS_LOG_DEBUG(@"Connection aborted with status code %i on socket %i", (int)statusCode, _socket); -} - -- (void)close { -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - if (_requestPath) { - BOOL success = NO; - NSError* error = nil; - if (_requestFD > 0) { - close(_requestFD); - NSString* name = [NSString stringWithFormat:@"%03lu-%@.request", (unsigned long)_connectionIndex, _virtualHEAD ? @"HEAD" : _request.method]; - success = [[NSFileManager defaultManager] moveItemAtPath:_requestPath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error]; - } - if (!success) { - GWS_LOG_ERROR(@"Failed saving recorded request: %@", error); - GWS_DNOT_REACHED(); - } - unlink([_requestPath fileSystemRepresentation]); - } - - if (_responsePath) { - BOOL success = NO; - NSError* error = nil; - if (_responseFD > 0) { - close(_responseFD); - NSString* name = [NSString stringWithFormat:@"%03lu-%i.response", (unsigned long)_connectionIndex, (int)_statusCode]; - success = [[NSFileManager defaultManager] moveItemAtPath:_responsePath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error]; - } - if (!success) { - GWS_LOG_ERROR(@"Failed saving recorded response: %@", error); - GWS_DNOT_REACHED(); - } - unlink([_responsePath fileSystemRepresentation]); - } -#endif - - if (_request) { - GWS_LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten); - } else { - GWS_LOG_VERBOSE(@"[%@] %@ %i \"(invalid request)\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, (unsigned long)_bytesRead, (unsigned long)_bytesWritten); - } -} - -@end diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerFunctions.h b/Frameworks/CGDWebServer/Core/GCDWebServerFunctions.h deleted file mode 100755 index a8b2857..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerFunctions.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Converts a file extension to the corresponding MIME type. - * If there is no match, "application/octet-stream" is returned. - */ -NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension); - -/** - * 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); - -/** - * Unescapes a URL percent-encoded string. - */ -NSString* GCDWebServerUnescapeURLString(NSString* string); - -/** - * Extracts the unescaped names and values from an - * "application/x-www-form-urlencoded" form. - * http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 - */ -NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form); - -/** - * On OS X, returns the IPv4 or IPv6 address as a string of the primary - * connected service or nil if not available. - * - * On iOS, returns the IPv4 or IPv6 address as a string of the WiFi - * interface if connected or nil otherwise. - */ -NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6); - -/** - * Converts a date into a string using RFC822 formatting. - * https://tools.ietf.org/html/rfc822#section-5 - * https://tools.ietf.org/html/rfc1123#section-5.2.14 - */ -NSString* GCDWebServerFormatRFC822(NSDate* date); - -/** - * Converts a RFC822 formatted string into a date. - * https://tools.ietf.org/html/rfc822#section-5 - * https://tools.ietf.org/html/rfc1123#section-5.2.14 - * - * @warning Timezones other than GMT are not supported by this function. - */ -NSDate* GCDWebServerParseRFC822(NSString* string); - -/** - * Converts a date into a string using IOS 8601 formatting. - * http://tools.ietf.org/html/rfc3339#section-5.6 - */ -NSString* GCDWebServerFormatISO8601(NSDate* date); - -/** - * Converts a ISO 8601 formatted string into a date. - * http://tools.ietf.org/html/rfc3339#section-5.6 - * - * @warning Only "calendar" variant is supported at this time and timezones - * other than GMT are not supported either. - */ -NSDate* GCDWebServerParseISO8601(NSString* string); - -#ifdef __cplusplus -} -#endif diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerFunctions.m b/Frameworks/CGDWebServer/Core/GCDWebServerFunctions.m deleted file mode 100755 index d81af0b..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerFunctions.m +++ /dev/null @@ -1,299 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import -#if TARGET_OS_IPHONE -#import -#else -#import -#endif -#import - -#import -#import -#import - -#import "GCDWebServerPrivate.h" - -static NSDateFormatter* _dateFormatterRFC822 = nil; -static NSDateFormatter* _dateFormatterISO8601 = nil; -static dispatch_queue_t _dateFormatterQueue = NULL; - -// TODO: Handle RFC 850 and ANSI C's asctime() format -void GCDWebServerInitializeFunctions() { - GWS_DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread - if (_dateFormatterRFC822 == nil) { - _dateFormatterRFC822 = [[NSDateFormatter alloc] init]; - _dateFormatterRFC822.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; - _dateFormatterRFC822.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"; - _dateFormatterRFC822.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; - GWS_DCHECK(_dateFormatterRFC822); - } - if (_dateFormatterISO8601 == nil) { - _dateFormatterISO8601 = [[NSDateFormatter alloc] init]; - _dateFormatterISO8601.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; - _dateFormatterISO8601.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'"; - _dateFormatterISO8601.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; - GWS_DCHECK(_dateFormatterISO8601); - } - if (_dateFormatterQueue == NULL) { - _dateFormatterQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); - GWS_DCHECK(_dateFormatterQueue); - } -} - -NSString* GCDWebServerNormalizeHeaderValue(NSString* value) { - if (value) { - NSRange range = [value rangeOfString:@";"]; // Assume part before ";" separator is case-insensitive - if (range.location != NSNotFound) { - value = [[[value substringToIndex:range.location] lowercaseString] stringByAppendingString:[value substringFromIndex:range.location]]; - } else { - value = [value lowercaseString]; - } - } - return value; -} - -NSString* GCDWebServerTruncateHeaderValue(NSString* value) { - NSRange range = [value rangeOfString:@";"]; - return range.location != NSNotFound ? [value substringToIndex:range.location] : 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]; - } - } - return parameter; -} - -// http://www.w3schools.com/tags/ref_charactersets.asp -NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) { - NSStringEncoding encoding = kCFStringEncodingInvalidId; - if (charset) { - encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset)); - } - return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding); -} - -NSString* GCDWebServerFormatRFC822(NSDate* date) { - __block NSString* string; - dispatch_sync(_dateFormatterQueue, ^{ - string = [_dateFormatterRFC822 stringFromDate:date]; - }); - return string; -} - -NSDate* GCDWebServerParseRFC822(NSString* string) { - __block NSDate* date; - dispatch_sync(_dateFormatterQueue, ^{ - date = [_dateFormatterRFC822 dateFromString:string]; - }); - return date; -} - -NSString* GCDWebServerFormatISO8601(NSDate* date) { - __block NSString* string; - dispatch_sync(_dateFormatterQueue, ^{ - string = [_dateFormatterISO8601 stringFromDate:date]; - }); - return string; -} - -NSDate* GCDWebServerParseISO8601(NSString* string) { - __block NSDate* date; - dispatch_sync(_dateFormatterQueue, ^{ - date = [_dateFormatterISO8601 dateFromString:string]; - }); - return date; -} - -BOOL GCDWebServerIsTextContentType(NSString* type) { - return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]); -} - -NSString* GCDWebServerDescribeData(NSData* data, NSString* type) { - if (GCDWebServerIsTextContentType(type)) { - NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset"); - NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)]; - if (string) { - return string; - } - } - 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* mimeType = nil; - extension = [extension lowercaseString]; - if (extension.length) { - mimeType = [_overrides objectForKey:extension]; - if (mimeType == nil) { - CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL); - if (uti) { - mimeType = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)); - CFRelease(uti); - } - } - } - return mimeType ? mimeType : kGCDWebServerDefaultMimeType; -} - -NSString* GCDWebServerEscapeURLString(NSString* string) { - return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":@/?&=+"), kCFStringEncodingUTF8)); -} - -NSString* GCDWebServerUnescapeURLString(NSString* string) { - return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8)); -} - -NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) { - NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; - NSScanner* scanner = [[NSScanner alloc] initWithString:form]; - [scanner setCharactersToBeSkipped:nil]; - while (1) { - NSString* key = nil; - if (![scanner scanUpToString:@"=" intoString:&key] || [scanner isAtEnd]) { - 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:@" "]; - NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil; - if (unescapedKey && unescapedValue) { - [parameters setObject:unescapedValue forKey:unescapedKey]; - } else { - GWS_LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value); - GWS_DNOT_REACHED(); - } - - if ([scanner isAtEnd]) { - break; - } - [scanner setScanLocation:([scanner scanLocation] + 1)]; - } - return parameters; -} - -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 { - GWS_DNOT_REACHED(); - } - return string; -} - -NSString* GCDWebServerGetPrimaryIPAddress(BOOL useIPv6) { - NSString* address = nil; -#if TARGET_OS_IPHONE -#if !TARGET_IPHONE_SIMULATOR - const char* primaryInterface = "en0"; // WiFi interface on iOS -#endif -#else - const char* primaryInterface = NULL; - SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("GCDWebServer"), NULL, NULL); - 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]; - CFRelease(info); - } - CFRelease(store); - } - if (primaryInterface == NULL) { - primaryInterface = "lo0"; - } -#endif - struct ifaddrs* list; - if (getifaddrs(&list) >= 0) { - for (struct ifaddrs* ifap = list; ifap; ifap = ifap->ifa_next) { -#if TARGET_IPHONE_SIMULATOR - if (strcmp(ifap->ifa_name, "en0") && strcmp(ifap->ifa_name, "en1")) // Assume en0 is Ethernet and en1 is WiFi since there is no way to use SystemConfiguration framework in iOS Simulator -#else - if (strcmp(ifap->ifa_name, primaryInterface)) -#endif - { - continue; - } - if ((ifap->ifa_flags & IFF_UP) && ((!useIPv6 && (ifap->ifa_addr->sa_family == AF_INET)) || (useIPv6 && (ifap->ifa_addr->sa_family == AF_INET6)))) { - address = GCDWebServerStringFromSockAddr(ifap->ifa_addr, NO); - break; - } - } - freeifaddrs(list); - } - return address; -} - -NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) { - va_list arguments; - va_start(arguments, format); - const char* string = [[[NSString alloc] initWithFormat:format arguments:arguments] UTF8String]; - va_end(arguments); - unsigned char md5[CC_MD5_DIGEST_LENGTH]; - CC_MD5(string, (CC_LONG)strlen(string), md5); - char buffer[2 * CC_MD5_DIGEST_LENGTH + 1]; - for (int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i) { - unsigned char byte = md5[i]; - unsigned char byteHi = (byte & 0xF0) >> 4; - buffer[2 * i + 0] = byteHi >= 10 ? 'a' + byteHi - 10 : '0' + byteHi; - unsigned char byteLo = byte & 0x0F; - buffer[2 * i + 1] = byteLo >= 10 ? 'a' + byteLo - 10 : '0' + byteLo; - } - buffer[2 * CC_MD5_DIGEST_LENGTH] = 0; - return [NSString stringWithUTF8String:buffer]; -} diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerHTTPStatusCodes.h b/Frameworks/CGDWebServer/Core/GCDWebServerHTTPStatusCodes.h deleted file mode 100755 index 7af51a2..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerHTTPStatusCodes.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html -// http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml - -#import - -/** - * Convenience constants for "informational" HTTP status codes. - */ -typedef NS_ENUM(NSInteger, GCDWebServerInformationalHTTPStatusCode) { - kGCDWebServerHTTPStatusCode_Continue = 100, - kGCDWebServerHTTPStatusCode_SwitchingProtocols = 101, - kGCDWebServerHTTPStatusCode_Processing = 102 -}; - -/** - * Convenience constants for "successful" HTTP status codes. - */ -typedef NS_ENUM(NSInteger, GCDWebServerSuccessfulHTTPStatusCode) { - kGCDWebServerHTTPStatusCode_OK = 200, - kGCDWebServerHTTPStatusCode_Created = 201, - kGCDWebServerHTTPStatusCode_Accepted = 202, - kGCDWebServerHTTPStatusCode_NonAuthoritativeInformation = 203, - kGCDWebServerHTTPStatusCode_NoContent = 204, - kGCDWebServerHTTPStatusCode_ResetContent = 205, - kGCDWebServerHTTPStatusCode_PartialContent = 206, - kGCDWebServerHTTPStatusCode_MultiStatus = 207, - kGCDWebServerHTTPStatusCode_AlreadyReported = 208 -}; - -/** - * Convenience constants for "redirection" HTTP status codes. - */ -typedef NS_ENUM(NSInteger, GCDWebServerRedirectionHTTPStatusCode) { - kGCDWebServerHTTPStatusCode_MultipleChoices = 300, - kGCDWebServerHTTPStatusCode_MovedPermanently = 301, - kGCDWebServerHTTPStatusCode_Found = 302, - kGCDWebServerHTTPStatusCode_SeeOther = 303, - kGCDWebServerHTTPStatusCode_NotModified = 304, - kGCDWebServerHTTPStatusCode_UseProxy = 305, - kGCDWebServerHTTPStatusCode_TemporaryRedirect = 307, - kGCDWebServerHTTPStatusCode_PermanentRedirect = 308 -}; - -/** - * Convenience constants for "client error" HTTP status codes. - */ -typedef NS_ENUM(NSInteger, GCDWebServerClientErrorHTTPStatusCode) { - kGCDWebServerHTTPStatusCode_BadRequest = 400, - kGCDWebServerHTTPStatusCode_Unauthorized = 401, - kGCDWebServerHTTPStatusCode_PaymentRequired = 402, - kGCDWebServerHTTPStatusCode_Forbidden = 403, - kGCDWebServerHTTPStatusCode_NotFound = 404, - kGCDWebServerHTTPStatusCode_MethodNotAllowed = 405, - kGCDWebServerHTTPStatusCode_NotAcceptable = 406, - kGCDWebServerHTTPStatusCode_ProxyAuthenticationRequired = 407, - kGCDWebServerHTTPStatusCode_RequestTimeout = 408, - kGCDWebServerHTTPStatusCode_Conflict = 409, - kGCDWebServerHTTPStatusCode_Gone = 410, - kGCDWebServerHTTPStatusCode_LengthRequired = 411, - kGCDWebServerHTTPStatusCode_PreconditionFailed = 412, - kGCDWebServerHTTPStatusCode_RequestEntityTooLarge = 413, - kGCDWebServerHTTPStatusCode_RequestURITooLong = 414, - kGCDWebServerHTTPStatusCode_UnsupportedMediaType = 415, - kGCDWebServerHTTPStatusCode_RequestedRangeNotSatisfiable = 416, - kGCDWebServerHTTPStatusCode_ExpectationFailed = 417, - kGCDWebServerHTTPStatusCode_UnprocessableEntity = 422, - kGCDWebServerHTTPStatusCode_Locked = 423, - kGCDWebServerHTTPStatusCode_FailedDependency = 424, - kGCDWebServerHTTPStatusCode_UpgradeRequired = 426, - kGCDWebServerHTTPStatusCode_PreconditionRequired = 428, - kGCDWebServerHTTPStatusCode_TooManyRequests = 429, - kGCDWebServerHTTPStatusCode_RequestHeaderFieldsTooLarge = 431 -}; - -/** - * Convenience constants for "server error" HTTP status codes. - */ -typedef NS_ENUM(NSInteger, GCDWebServerServerErrorHTTPStatusCode) { - kGCDWebServerHTTPStatusCode_InternalServerError = 500, - kGCDWebServerHTTPStatusCode_NotImplemented = 501, - kGCDWebServerHTTPStatusCode_BadGateway = 502, - kGCDWebServerHTTPStatusCode_ServiceUnavailable = 503, - kGCDWebServerHTTPStatusCode_GatewayTimeout = 504, - kGCDWebServerHTTPStatusCode_HTTPVersionNotSupported = 505, - kGCDWebServerHTTPStatusCode_InsufficientStorage = 507, - kGCDWebServerHTTPStatusCode_LoopDetected = 508, - kGCDWebServerHTTPStatusCode_NotExtended = 510, - kGCDWebServerHTTPStatusCode_NetworkAuthenticationRequired = 511 -}; diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerPrivate.h b/Frameworks/CGDWebServer/Core/GCDWebServerPrivate.h deleted file mode 100755 index 3ab85c6..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerPrivate.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import -#import - -/** - * All GCDWebServer headers. - */ - -#import "GCDWebServerHTTPStatusCodes.h" -#import "GCDWebServerFunctions.h" - -#import "GCDWebServer.h" -#import "GCDWebServerConnection.h" - -#import "GCDWebServerDataRequest.h" -#import "GCDWebServerFileRequest.h" -#import "GCDWebServerMultiPartFormRequest.h" -#import "GCDWebServerURLEncodedFormRequest.h" - -#import "GCDWebServerDataResponse.h" -#import "GCDWebServerErrorResponse.h" -#import "GCDWebServerFileResponse.h" -#import "GCDWebServerStreamedResponse.h" - -/** - * Check if a custom logging facility should be used instead. - */ - -#if defined(__GCDWEBSERVER_LOGGING_HEADER__) - -#define __GCDWEBSERVER_LOGGING_FACILITY_CUSTOM__ - -#import __GCDWEBSERVER_LOGGING_HEADER__ - -/** - * Automatically detect if XLFacility is available and if so use it as a - * logging facility. - */ - -#elif defined(__has_include) && __has_include("XLFacilityMacros.h") - -#define __GCDWEBSERVER_LOGGING_FACILITY_XLFACILITY__ - -#undef XLOG_TAG -#define XLOG_TAG @"gcdwebserver.internal" - -#import "XLFacilityMacros.h" - -#define GWS_LOG_DEBUG(...) XLOG_DEBUG(__VA_ARGS__) -#define GWS_LOG_VERBOSE(...) XLOG_VERBOSE(__VA_ARGS__) -#define GWS_LOG_INFO(...) XLOG_INFO(__VA_ARGS__) -#define GWS_LOG_WARNING(...) XLOG_WARNING(__VA_ARGS__) -#define GWS_LOG_ERROR(...) XLOG_ERROR(__VA_ARGS__) -#define GWS_LOG_EXCEPTION(__EXCEPTION__) XLOG_EXCEPTION(__EXCEPTION__) - -#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("DDLogMacros.h") - -#import "DDLogMacros.h" - -#define __GCDWEBSERVER_LOGGING_FACILITY_COCOALUMBERJACK__ - -#undef LOG_LEVEL_DEF -#define LOG_LEVEL_DEF GCDWebServerLogLevel -extern int 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__) -#define GWS_LOG_EXCEPTION(__EXCEPTION__) DDLogError(@"%@", __EXCEPTION__) - -/** - * If all of the above fail, then use GCDWebServer built-in - * logging facility. - */ - -#else - -#define __GCDWEBSERVER_LOGGING_FACILITY_BUILTIN__ - -typedef NS_ENUM(int, GCDWebServerLoggingLevel) { - kGCDWebServerLoggingLevel_Debug = 0, - kGCDWebServerLoggingLevel_Verbose, - kGCDWebServerLoggingLevel_Info, - kGCDWebServerLoggingLevel_Warning, - kGCDWebServerLoggingLevel_Error, - kGCDWebServerLoggingLevel_Exception -}; - -extern GCDWebServerLoggingLevel GCDWebServerLogLevel; -extern void GCDWebServerLogMessage(GCDWebServerLoggingLevel level, NSString* format, ...) NS_FORMAT_FUNCTION(2, 3); - -#if DEBUG -#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_EXCEPTION(__EXCEPTION__) do { if (GCDWebServerLogLevel <= kGCDWebServerLoggingLevel_Exception) GCDWebServerLogMessage(kGCDWebServerLoggingLevel_Exception, @"%@", __EXCEPTION__); } while (0) - -#endif - -/** - * Consistency check macros used when building Debug only. - */ - -#if !defined(GWS_DCHECK) || !defined(GWS_DNOT_REACHED) - -#if DEBUG - -#define GWS_DCHECK(__CONDITION__) \ - do { \ - if (!(__CONDITION__)) { \ - abort(); \ - } \ - } while (0) -#define GWS_DNOT_REACHED() abort() - -#else - -#define GWS_DCHECK(__CONDITION__) -#define GWS_DNOT_REACHED() - -#endif - -#endif - -/** - * GCDWebServer internal constants and APIs. - */ - -#define kGCDWebServerDefaultMimeType @"application/octet-stream" -#define kGCDWebServerGCDQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) -#define kGCDWebServerErrorDomain @"GCDWebServerErrorDomain" - -static inline BOOL GCDWebServerIsValidByteRange(NSRange range) { - return ((range.location != NSUIntegerMax) || (range.length > 0)); -} - -static inline NSError* GCDWebServerMakePosixError(int code) { - return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: [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 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* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOOL includeService); - -@interface GCDWebServerConnection () -- (id)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) BOOL shouldAutomaticallyMapHEADToGET; -- (void)willStartConnection:(GCDWebServerConnection*)connection; -- (void)didEndConnection:(GCDWebServerConnection*)connection; -@end - -@interface GCDWebServerHandler : NSObject -@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock; -@property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock; -@end - -@interface GCDWebServerRequest () -@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; -- (void)prepareForWriting; -- (BOOL)performOpen:(NSError**)error; -- (BOOL)performWriteData:(NSData*)data error:(NSError**)error; -- (BOOL)performClose:(NSError**)error; -- (void)setAttribute:(id)attribute forKey:(NSString*)key; -@end - -@interface GCDWebServerResponse () -@property(nonatomic, readonly) NSDictionary* additionalHeaders; -@property(nonatomic, readonly) BOOL usesChunkedTransferEncoding; -- (void)prepareForReading; -- (BOOL)performOpen:(NSError**)error; -- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; -- (void)performClose; -@end diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerRequest.h b/Frameworks/CGDWebServer/Core/GCDWebServerRequest.h deleted file mode 100755 index 3b517b6..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerRequest.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - -/** - * Attribute key to retrieve an NSArray containing NSStrings from a GCDWebServerRequest - * with the contents of any regular expression captures done on the request path. - * - * @warning This attribute will only be set on the request if adding a handler using - * -addHandlerForMethod:pathRegex:requestClass:processBlock:. - */ -extern NSString* const GCDWebServerRequestAttribute_RegexCaptures; - -/** - * This protocol is used by the GCDWebServerConnection to communicate with - * the GCDWebServerRequest and write the received HTTP body data. - * - * Note that multiple GCDWebServerBodyWriter objects can be chained together - * internally e.g. to automatically decode gzip encoded content before - * passing it on to the GCDWebServerRequest. - * - * @warning These methods can be called on any GCD thread. - */ -@protocol GCDWebServerBodyWriter - -/** - * This method is called before any body data is received. - * - * It should return YES on success or NO on failure and set the "error" argument - * which is guaranteed to be non-NULL. - */ -- (BOOL)open:(NSError**)error; - -/** - * This method is called whenever body data has been received. - * - * It should return YES on success or NO on failure and set the "error" argument - * which is guaranteed to be non-NULL. - */ -- (BOOL)writeData:(NSData*)data error:(NSError**)error; - -/** - * This method is called after all body data has been received. - * - * It should return YES on success or NO on failure and set the "error" argument - * which is guaranteed to be non-NULL. - */ -- (BOOL)close:(NSError**)error; - -@end - -/** - * The GCDWebServerRequest class is instantiated by the GCDWebServerConnection - * after the HTTP headers have been received. Each instance wraps a single HTTP - * request. If a body is present, the methods from the GCDWebServerBodyWriter - * protocol will be called by the GCDWebServerConnection to receive it. - * - * The default implementation of the GCDWebServerBodyWriter protocol on the class - * simply ignores the body data. - * - * @warning GCDWebServerRequest instances can be created and used on any GCD thread. - */ -@interface GCDWebServerRequest : NSObject - -/** - * Returns the HTTP method for the request. - */ -@property(nonatomic, readonly) NSString* method; - -/** - * Returns the URL for the request. - */ -@property(nonatomic, readonly) NSURL* URL; - -/** - * Returns the HTTP headers for the request. - */ -@property(nonatomic, readonly) NSDictionary* headers; - -/** - * Returns the path component of the URL for the request. - */ -@property(nonatomic, readonly) NSString* path; - -/** - * Returns the parsed and unescaped query component of the URL for the request. - * - * @warning This property will be nil if there is no query in the URL. - */ -@property(nonatomic, readonly) NSDictionary* query; - -/** - * Returns the content type for the body of the request parsed from the - * "Content-Type" header. - * - * This property will be nil if the request has no body or set to - * "application/octet-stream" if a body is present but there was no - * "Content-Type" header. - */ -@property(nonatomic, readonly) NSString* contentType; - -/** - * Returns the content length for the body of the request parsed from the - * "Content-Length" header. - * - * This property will be set to "NSUIntegerMax" if the request has no body or - * if there is a body but no "Content-Length" header, typically because - * chunked transfer encoding is used. - */ -@property(nonatomic, readonly) NSUInteger contentLength; - -/** - * Returns the parsed "If-Modified-Since" header or nil if absent or malformed. - */ -@property(nonatomic, readonly) NSDate* ifModifiedSince; - -/** - * Returns the parsed "If-None-Match" header or nil if absent or malformed. - */ -@property(nonatomic, readonly) NSString* ifNoneMatch; - -/** - * Returns the parsed "Range" header or (NSUIntegerMax, 0) if absent or malformed. - * The range will be set to (offset, length) if expressed from the beginning - * of the entity body, or (NSUIntegerMax, length) if expressed from its end. - */ -@property(nonatomic, readonly) NSRange byteRange; - -/** - * Returns YES if the client supports gzip content encoding according to the - * "Accept-Encoding" header. - */ -@property(nonatomic, readonly) BOOL acceptsGzipContentEncoding; - -/** - * 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; - -/** - * Convenience method that checks if the contentType property is defined. - */ -- (BOOL)hasBody; - -/** - * Convenience method that checks if the byteRange property is defined. - */ -- (BOOL)hasByteRange; - -/** - * Retrieves an attribute associated with this request using the given key. - * - * @return The attribute value for the key. - */ -- (id)attributeForKey:(NSString*)key; - -@end diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerRequest.m b/Frameworks/CGDWebServer/Core/GCDWebServerRequest.m deleted file mode 100755 index cc14993..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerRequest.m +++ /dev/null @@ -1,319 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import - -#import "GCDWebServerPrivate.h" - -NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures"; - -#define kZlibErrorDomain @"ZlibErrorDomain" -#define kGZipInitialBufferSize (256 * 1024) - -@interface GCDWebServerBodyDecoder : NSObject -- (id)initWithRequest:(GCDWebServerRequest*)request writer:(id)writer; -@end - -@interface GCDWebServerGZipDecoder : GCDWebServerBodyDecoder -@end - -@interface GCDWebServerBodyDecoder () { -@private - GCDWebServerRequest* __unsafe_unretained _request; - id __unsafe_unretained _writer; -} -@end - -@implementation GCDWebServerBodyDecoder - -- (id)initWithRequest:(GCDWebServerRequest*)request writer:(id)writer { - if ((self = [super init])) { - _request = request; - _writer = writer; - } - return self; -} - -- (BOOL)open:(NSError**)error { - return [_writer open:error]; -} - -- (BOOL)writeData:(NSData*)data error:(NSError**)error { - return [_writer writeData:data error:error]; -} - -- (BOOL)close:(NSError**)error { - return [_writer close:error]; -} - -@end - -@interface GCDWebServerGZipDecoder () { -@private - z_stream _stream; - BOOL _finished; -} -@end - -@implementation GCDWebServerGZipDecoder - -- (BOOL)open:(NSError**)error { - int result = inflateInit2(&_stream, 15 + 16); - if (result != Z_OK) { - *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; - return NO; - } - if (![super open:error]) { - deflateEnd(&_stream); - return NO; - } - return YES; -} - -- (BOOL)writeData:(NSData*)data error:(NSError**)error { - GWS_DCHECK(!_finished); - _stream.next_in = (Bytef*)data.bytes; - _stream.avail_in = (uInt)data.length; - NSMutableData* decodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; - if (decodedData == nil) { - GWS_DNOT_REACHED(); - return NO; - } - NSUInteger length = 0; - while (1) { - NSUInteger maxLength = decodedData.length - length; - _stream.next_out = (Bytef*)((char*)decodedData.mutableBytes + length); - _stream.avail_out = (uInt)maxLength; - int result = inflate(&_stream, Z_NO_FLUSH); - if ((result != Z_OK) && (result != Z_STREAM_END)) { - *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; - return NO; - } - length += maxLength - _stream.avail_out; - if (_stream.avail_out > 0) { - if (result == Z_STREAM_END) { - _finished = YES; - } - break; - } - decodedData.length = 2 * decodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available - } - decodedData.length = length; - BOOL success = length ? [super writeData:decodedData error:error] : YES; // No need to call writer if we have no data yet - return success; -} - -- (BOOL)close:(NSError**)error { - GWS_DCHECK(_finished); - inflateEnd(&_stream); - return [super close:error]; -} - -@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; - - BOOL _opened; - NSMutableArray* _decoders; - NSMutableDictionary* _attributes; - id __unsafe_unretained _writer; -} -@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; - -- (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; - _headers = headers; - _path = [path copy]; - _query = query; - - _type = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Content-Type"]); - _chunked = [GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Transfer-Encoding"]) isEqualToString:@"chunked"]; - NSString* lengthHeader = [_headers objectForKey:@"Content-Length"]; - if (lengthHeader) { - NSInteger length = [lengthHeader integerValue]; - if (_chunked || (length < 0)) { - GWS_DNOT_REACHED(); - return nil; - } - _length = length; - if (_type == nil) { - _type = kGCDWebServerDefaultMimeType; - } - } else if (_chunked) { - if (_type == nil) { - _type = kGCDWebServerDefaultMimeType; - } - _length = NSUIntegerMax; - } else { - if (_type) { - GWS_DNOT_REACHED(); - return nil; - } - _length = NSUIntegerMax; - } - - NSString* modifiedHeader = [_headers objectForKey:@"If-Modified-Since"]; - if (modifiedHeader) { - _modifiedSince = [GCDWebServerParseRFC822(modifiedHeader) copy]; - } - _noneMatch = [_headers objectForKey:@"If-None-Match"]; - - _range = NSMakeRange(NSUIntegerMax, 0); - NSString* rangeHeader = GCDWebServerNormalizeHeaderValue([_headers objectForKey:@"Range"]); - if (rangeHeader) { - if ([rangeHeader hasPrefix:@"bytes="]) { - NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","]; - if (components.count == 1) { - components = [[components firstObject] componentsSeparatedByString:@"-"]; - if (components.count == 2) { - NSString* startString = [components objectAtIndex:0]; - NSInteger startValue = [startString integerValue]; - 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; - } else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-" - _range.location = startValue; - _range.length = NSUIntegerMax; - } else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500" - _range.location = NSUIntegerMax; - _range.length = endValue; - } - } - } - } - if ((_range.location == NSUIntegerMax) && (_range.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; - } - - _decoders = [[NSMutableArray alloc] init]; - _attributes = [[NSMutableDictionary alloc] init]; - } - return self; -} - -- (BOOL)hasBody { - return _type ? YES : NO; -} - -- (BOOL)hasByteRange { - return GCDWebServerIsValidByteRange(_range); -} - -- (id)attributeForKey:(NSString*)key { - return [_attributes objectForKey:key]; -} - -- (BOOL)open:(NSError**)error { - return YES; -} - -- (BOOL)writeData:(NSData*)data error:(NSError**)error { - return YES; -} - -- (BOOL)close:(NSError**)error { - return YES; -} - -- (void)prepareForWriting { - _writer = self; - if ([GCDWebServerNormalizeHeaderValue([self.headers objectForKey:@"Content-Encoding"]) isEqualToString:@"gzip"]) { - GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer]; - [_decoders addObject:decoder]; - _writer = decoder; - } -} - -- (BOOL)performOpen:(NSError**)error { - GWS_DCHECK(_type); - GWS_DCHECK(_writer); - if (_opened) { - GWS_DNOT_REACHED(); - return NO; - } - _opened = YES; - return [_writer open:error]; -} - -- (BOOL)performWriteData:(NSData*)data error:(NSError**)error { - GWS_DCHECK(_opened); - return [_writer writeData:data error:error]; -} - -- (BOOL)performClose:(NSError**)error { - GWS_DCHECK(_opened); - return [_writer close:error]; -} - -- (void)setAttribute:(id)attribute forKey:(NSString*)key { - [_attributes setValue:attribute forKey:key]; -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path]; - for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) { - [description appendFormat:@"\n %@ = %@", argument, [_query objectForKey:argument]]; - } - [description appendString:@"\n"]; - for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) { - [description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]]; - } - return description; -} - -@end diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerResponse.h b/Frameworks/CGDWebServer/Core/GCDWebServerResponse.h deleted file mode 100755 index 01ef2ab..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerResponse.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import - -/** - * The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the - * GCDWebServerBodyReader object when reading data from it asynchronously. - */ -typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* error); - -/** - * This protocol is used by the GCDWebServerConnection to communicate with - * the GCDWebServerResponse and read the HTTP body data to send. - * - * Note that multiple GCDWebServerBodyReader objects can be chained together - * internally e.g. to automatically apply gzip encoding to the content before - * passing it on to the GCDWebServerResponse. - * - * @warning These methods can be called on any GCD thread. - */ -@protocol GCDWebServerBodyReader - -@required - -/** - * This method is called before any body data is sent. - * - * It should return YES on success or NO on failure and set the "error" argument - * which is guaranteed to be non-NULL. - */ -- (BOOL)open:(NSError**)error; - -/** - * This method is called whenever body data is sent. - * - * It should return a non-empty NSData if there is body data available, - * 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; - -/** - * This method is called after all body data has been sent. - */ -- (void)close; - -@optional - -/** - * If this method is implemented, it will be preferred over -readData:. - * - * It must call the passed block when data is available, passing a non-empty - * NSData if there is body data available, or an empty NSData there is no more - * body data, or nil on error and pass an NSError along. - */ -- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block; - -@end - -/** - * The GCDWebServerResponse class is used to wrap a single HTTP response. - * It is instantiated by the handler of the GCDWebServer that handled the request. - * If a body is present, the methods from the GCDWebServerBodyReader protocol - * will be called by the GCDWebServerConnection to send it. - * - * The default implementation of the GCDWebServerBodyReader protocol - * on the class simply returns an empty body. - * - * @warning GCDWebServerResponse instances can be created and used on any GCD thread. - */ -@interface GCDWebServerResponse : NSObject - -/** - * Sets the content type for the body of the response. - * - * The default value is nil i.e. the response has no body. - * - * @warning This property must be set if a body is present. - */ -@property(nonatomic, copy) NSString* contentType; - -/** - * Sets the content length for the body of the response. If a body is present - * but this property is set to "NSUIntegerMax", this means the length of the body - * cannot be known ahead of time. Chunked transfer encoding will be - * automatically enabled by the GCDWebServerConnection to comply with HTTP/1.1 - * specifications. - * - * The default value is "NSUIntegerMax" i.e. the response has no body or its length - * is undefined. - */ -@property(nonatomic) NSUInteger contentLength; - -/** - * Sets the HTTP status code for the response. - * - * The default value is 200 i.e. "OK". - */ -@property(nonatomic) NSInteger statusCode; - -/** - * Sets the caching hint for the response using the "Cache-Control" header. - * This value is expressed in seconds. - * - * The default value is 0 i.e. "no-cache". - */ -@property(nonatomic) NSUInteger cacheControlMaxAge; - -/** - * Sets the last modified date for the response using the "Last-Modified" header. - * - * The default value is nil. - */ -@property(nonatomic, retain) NSDate* lastModifiedDate; - -/** - * Sets the ETag for the response using the "ETag" header. - * - * The default value is nil. - */ -@property(nonatomic, copy) NSString* eTag; - -/** - * Enables gzip encoding for the response body. - * - * The default value is NO. - * - * @warning Enabling gzip encoding will remove any "Content-Length" header - * since the length of the body is not known anymore. The client will still - * be able to determine the body length when connection is closed per - * HTTP/1.1 specifications. - */ -@property(nonatomic, getter=isGZipContentEncodingEnabled) BOOL gzipContentEncodingEnabled; - -/** - * Creates an empty response. - */ -+ (instancetype)response; - -/** - * This method is the designated initializer for the class. - */ -- (instancetype)init; - -/** - * Sets an additional HTTP header on the response. - * Pass a nil value to remove an additional header. - * - * @warning Do not attempt to override the primary headers used - * by GCDWebServerResponse like "Content-Type", "ETag", etc... - */ -- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header; - -/** - * Convenience method that checks if the contentType property is defined. - */ -- (BOOL)hasBody; - -@end - -@interface GCDWebServerResponse (Extensions) - -/** - * Creates a empty response with a specific HTTP status code. - */ -+ (instancetype)responseWithStatusCode:(NSInteger)statusCode; - -/** - * Creates an HTTP redirect response to a new URL. - */ -+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent; - -/** - * Initializes an empty response with a specific HTTP status code. - */ -- (instancetype)initWithStatusCode:(NSInteger)statusCode; - -/** - * Initializes an HTTP redirect response to a new URL. - */ -- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent; - -@end diff --git a/Frameworks/CGDWebServer/Core/GCDWebServerResponse.m b/Frameworks/CGDWebServer/Core/GCDWebServerResponse.m deleted file mode 100755 index b0a220b..0000000 --- a/Frameworks/CGDWebServer/Core/GCDWebServerResponse.m +++ /dev/null @@ -1,305 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import - -#import "GCDWebServerPrivate.h" - -#define kZlibErrorDomain @"ZlibErrorDomain" -#define kGZipInitialBufferSize (256 * 1024) - -@interface GCDWebServerBodyEncoder : NSObject -- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id)reader; -@end - -@interface GCDWebServerGZipEncoder : GCDWebServerBodyEncoder -@end - -@interface GCDWebServerBodyEncoder () { -@private - GCDWebServerResponse* __unsafe_unretained _response; - id __unsafe_unretained _reader; -} -@end - -@implementation GCDWebServerBodyEncoder - -- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id)reader { - if ((self = [super init])) { - _response = response; - _reader = reader; - } - return self; -} - -- (BOOL)open:(NSError**)error { - return [_reader open:error]; -} - -- (NSData*)readData:(NSError**)error { - return [_reader readData:error]; -} - -- (void)close { - [_reader close]; -} - -@end - -@interface GCDWebServerGZipEncoder () { -@private - z_stream _stream; - BOOL _finished; -} -@end - -@implementation GCDWebServerGZipEncoder - -- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id)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"]; - } - return self; -} - -- (BOOL)open:(NSError**)error { - int result = deflateInit2(&_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); - if (result != Z_OK) { - *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; - return NO; - } - if (![super open:error]) { - deflateEnd(&_stream); - return NO; - } - return YES; -} - -- (NSData*)readData:(NSError**)error { - NSMutableData* encodedData; - if (_finished) { - encodedData = [[NSMutableData alloc] init]; - } else { - encodedData = [[NSMutableData alloc] initWithLength:kGZipInitialBufferSize]; - if (encodedData == nil) { - GWS_DNOT_REACHED(); - return nil; - } - NSUInteger length = 0; - do { - NSData* data = [super readData:error]; - if (data == nil) { - return nil; - } - _stream.next_in = (Bytef*)data.bytes; - _stream.avail_in = (uInt)data.length; - while (1) { - NSUInteger maxLength = encodedData.length - length; - _stream.next_out = (Bytef*)((char*)encodedData.mutableBytes + length); - _stream.avail_out = (uInt)maxLength; - int result = deflate(&_stream, data.length ? Z_NO_FLUSH : Z_FINISH); - if (result == Z_STREAM_END) { - _finished = YES; - } else if (result != Z_OK) { - *error = [NSError errorWithDomain:kZlibErrorDomain code:result userInfo:nil]; - return nil; - } - length += maxLength - _stream.avail_out; - if (_stream.avail_out > 0) { - break; - } - encodedData.length = 2 * encodedData.length; // zlib has used all the output buffer so resize it and try again in case more data is available - } - GWS_DCHECK(_stream.avail_in == 0); - } while (length == 0); // Make sure we don't return an empty NSData if not in finished state - encodedData.length = length; - } - return encodedData; -} - -- (void)close { - deflateEnd(&_stream); - [super close]; -} - -@end - -@interface GCDWebServerResponse () { -@private - NSString* _type; - NSUInteger _length; - NSInteger _status; - NSUInteger _maxAge; - NSDate* _lastModified; - NSString* _eTag; - NSMutableDictionary* _headers; - BOOL _chunked; - BOOL _gzipped; - - BOOL _opened; - NSMutableArray* _encoders; - id __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]; -} - -- (instancetype)init { - if ((self = [super init])) { - _type = nil; - _length = NSUIntegerMax; - _status = kGCDWebServerHTTPStatusCode_OK; - _maxAge = 0; - _headers = [[NSMutableDictionary alloc] init]; - _encoders = [[NSMutableArray alloc] init]; - } - return self; -} - -- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header { - [_headers setValue:value forKey:header]; -} - -- (BOOL)hasBody { - return _type ? YES : NO; -} - -- (BOOL)usesChunkedTransferEncoding { - return (_type != nil) && (_length == NSUIntegerMax); -} - -- (BOOL)open:(NSError**)error { - return YES; -} - -- (NSData*)readData:(NSError**)error { - return [NSData data]; -} - -- (void)close { - ; -} - -- (void)prepareForReading { - _reader = self; - if (_gzipped) { - GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader]; - [_encoders addObject:encoder]; - _reader = encoder; - } -} - -- (BOOL)performOpen:(NSError**)error { - GWS_DCHECK(_type); - GWS_DCHECK(_reader); - if (_opened) { - GWS_DNOT_REACHED(); - return NO; - } - _opened = YES; - return [_reader open:error]; -} - -- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { - if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) { - [_reader asyncReadDataWithCompletion:block]; - } else { - NSError* error = nil; - NSData* data = [_reader readData:&error]; - block(data, error); - } -} - -- (void)performClose { - GWS_DCHECK(_opened); - [_reader close]; -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithFormat:@"Status Code = %i", (int)_status]; - if (_type) { - [description appendFormat:@"\nContent Type = %@", _type]; - } - if (_length != NSUIntegerMax) { - [description appendFormat:@"\nContent Length = %lu", (unsigned long)_length]; - } - [description appendFormat:@"\nCache Control Max Age = %lu", (unsigned long)_maxAge]; - if (_lastModified) { - [description appendFormat:@"\nLast Modified Date = %@", _lastModified]; - } - if (_eTag) { - [description appendFormat:@"\nETag = %@", _eTag]; - } - if (_headers.count) { - [description appendString:@"\n"]; - for (NSString* header in [[_headers allKeys] sortedArrayUsingSelector:@selector(compare:)]) { - [description appendFormat:@"\n%@: %@", header, [_headers objectForKey:header]]; - } - } - return description; -} - -@end - -@implementation GCDWebServerResponse (Extensions) - -+ (instancetype)responseWithStatusCode:(NSInteger)statusCode { - return [[self alloc] initWithStatusCode:statusCode]; -} - -+ (instancetype)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent { - return [[self alloc] initWithRedirect:location permanent:permanent]; -} - -- (instancetype)initWithStatusCode:(NSInteger)statusCode { - if ((self = [self init])) { - self.statusCode = statusCode; - } - return self; -} - -- (instancetype)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent { - if ((self = [self init])) { - self.statusCode = permanent ? kGCDWebServerHTTPStatusCode_MovedPermanently : kGCDWebServerHTTPStatusCode_TemporaryRedirect; - [self setValue:[location absoluteString] forAdditionalHeader:@"Location"]; - } - return self; -} - -@end diff --git a/Frameworks/CGDWebServer/LICENSE b/Frameworks/CGDWebServer/LICENSE deleted file mode 100755 index 12335de..0000000 --- a/Frameworks/CGDWebServer/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2012-2014, Pierre-Olivier Latour -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerDataRequest.h b/Frameworks/CGDWebServer/Requests/GCDWebServerDataRequest.h deleted file mode 100755 index ef94b97..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerDataRequest.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerRequest.h" - -/** - * The GCDWebServerDataRequest subclass of GCDWebServerRequest stores the body - * of the HTTP request in memory. - */ -@interface GCDWebServerDataRequest : GCDWebServerRequest - -/** - * Returns the data for the request body. - */ -@property(nonatomic, readonly) NSData* data; - -@end - -@interface GCDWebServerDataRequest (Extensions) - -/** - * Returns the data for the request body interpreted as text. If the content - * type of the body is not a text one, or if an error occurs, nil is returned. - * - * The text encoding used to interpret the data is extracted from the - * "Content-Type" header or defaults to UTF-8. - */ -@property(nonatomic, readonly) NSString* text; - -/** - * Returns the data for the request body interpreted as a JSON object. If the - * content type of the body is not JSON, or if an error occurs, nil is returned. - */ -@property(nonatomic, readonly) id jsonObject; - -@end diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerDataRequest.m b/Frameworks/CGDWebServer/Requests/GCDWebServerDataRequest.m deleted file mode 100755 index 4f0ed75..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerDataRequest.m +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import "GCDWebServerPrivate.h" - -@interface GCDWebServerDataRequest () { -@private - NSMutableData* _data; - - NSString* _text; - id _jsonObject; -} -@end - -@implementation GCDWebServerDataRequest - -@synthesize data=_data; - -- (BOOL)open:(NSError**)error { - if (self.contentLength != NSUIntegerMax) { - _data = [[NSMutableData alloc] initWithCapacity:self.contentLength]; - } else { - _data = [[NSMutableData alloc] init]; - } - if (_data == nil) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed allocating memory"}]; - return NO; - } - return YES; -} - -- (BOOL)writeData:(NSData*)data error:(NSError**)error { - [_data appendData:data]; - return YES; -} - -- (BOOL)close:(NSError**)error { - return YES; -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithString:[super description]]; - if (_data) { - [description appendString:@"\n\n"]; - [description appendString:GCDWebServerDescribeData(_data, self.contentType)]; - } - return description; -} - -@end - -@implementation GCDWebServerDataRequest (Extensions) - -- (NSString*)text { - if (_text == nil) { - if ([self.contentType hasPrefix:@"text/"]) { - NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); - _text = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; - } else { - GWS_DNOT_REACHED(); - } - } - return _text; -} - -- (id)jsonObject { - if (_jsonObject == nil) { - NSString* mimeType = GCDWebServerTruncateHeaderValue(self.contentType); - if ([mimeType isEqualToString:@"application/json"] || [mimeType isEqualToString:@"text/json"] || [mimeType isEqualToString:@"text/javascript"]) { - _jsonObject = [NSJSONSerialization JSONObjectWithData:_data options:0 error:NULL]; - } else { - GWS_DNOT_REACHED(); - } - } - return _jsonObject; -} - -@end diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerFileRequest.h b/Frameworks/CGDWebServer/Requests/GCDWebServerFileRequest.h deleted file mode 100755 index 427a800..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerFileRequest.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerRequest.h" - -/** - * The GCDWebServerFileRequest subclass of GCDWebServerRequest stores the body - * of the HTTP request to a file on disk. - */ -@interface GCDWebServerFileRequest : GCDWebServerRequest - -/** - * Returns the path to the temporary file containing the request body. - * - * @warning This temporary file will be automatically deleted when the - * GCDWebServerFileRequest is deallocated. If you want to preserve this file, - * you must move it to a different location beforehand. - */ -@property(nonatomic, readonly) NSString* temporaryPath; - -@end diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerFileRequest.m b/Frameworks/CGDWebServer/Requests/GCDWebServerFileRequest.m deleted file mode 100755 index d0c2118..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerFileRequest.m +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import "GCDWebServerPrivate.h" - -@interface GCDWebServerFileRequest () { -@private - NSString* _temporaryPath; - int _file; -} -@end - -@implementation GCDWebServerFileRequest - -@synthesize temporaryPath=_temporaryPath; - -- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { - if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { - _temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; - } - return self; -} - -- (void)dealloc { - unlink([_temporaryPath fileSystemRepresentation]); -} - -- (BOOL)open:(NSError**)error { - _file = open([_temporaryPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (_file <= 0) { - *error = GCDWebServerMakePosixError(errno); - return NO; - } - return YES; -} - -- (BOOL)writeData:(NSData*)data error:(NSError**)error { - if (write(_file, data.bytes, data.length) != (ssize_t)data.length) { - *error = GCDWebServerMakePosixError(errno); - return NO; - } - return YES; -} - -- (BOOL)close:(NSError**)error { - if (close(_file) < 0) { - *error = GCDWebServerMakePosixError(errno); - return NO; - } -#ifdef __GCDWEBSERVER_ENABLE_TESTING__ - NSString* creationDateHeader = [self.headers objectForKey:@"X-GCDWebServer-CreationDate"]; - if (creationDateHeader) { - NSDate* date = GCDWebServerParseISO8601(creationDateHeader); - if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileCreationDate: date} ofItemAtPath:_temporaryPath error:error]) { - return NO; - } - } - NSString* modifiedDateHeader = [self.headers objectForKey:@"X-GCDWebServer-ModifiedDate"]; - if (modifiedDateHeader) { - NSDate* date = GCDWebServerParseRFC822(modifiedDateHeader); - if (!date || ![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: date} ofItemAtPath:_temporaryPath error:error]) { - return NO; - } - } -#endif - return YES; -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithString:[super description]]; - [description appendFormat:@"\n\n{%@}", _temporaryPath]; - return description; -} - -@end diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerMultiPartFormRequest.h b/Frameworks/CGDWebServer/Requests/GCDWebServerMultiPartFormRequest.h deleted file mode 100755 index 2463ca2..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerMultiPartFormRequest.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerRequest.h" - -/** - * The GCDWebServerMultiPart class is an abstract class that wraps the content - * of a part. - */ -@interface GCDWebServerMultiPart : NSObject - -/** - * Returns the control name retrieved from the part headers. - */ -@property(nonatomic, readonly) NSString* controlName; - -/** - * Returns the content type retrieved from the part headers or "text/plain" - * if not available (per HTTP specifications). - */ -@property(nonatomic, readonly) NSString* contentType; - -/** - * Returns the MIME type component of the content type for the part. - */ -@property(nonatomic, readonly) NSString* mimeType; - -@end - -/** - * The GCDWebServerMultiPartArgument subclass of GCDWebServerMultiPart wraps - * the content of a part as data in memory. - */ -@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart - -/** - * Returns the data for the part. - */ -@property(nonatomic, readonly) NSData* data; - -/** - * Returns the data for the part interpreted as text. If the content - * type of the part is not a text one, or if an error occurs, nil is returned. - * - * The text encoding used to interpret the data is extracted from the - * "Content-Type" header or defaults to UTF-8. - */ -@property(nonatomic, readonly) NSString* string; - -@end - -/** - * The GCDWebServerMultiPartFile subclass of GCDWebServerMultiPart wraps - * the content of a part as a file on disk. - */ -@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart - -/** - * Returns the file name retrieved from the part headers. - */ -@property(nonatomic, readonly) NSString* fileName; - -/** - * Returns the path to the temporary file containing the part data. - * - * @warning This temporary file will be automatically deleted when the - * GCDWebServerMultiPartFile is deallocated. If you want to preserve this file, - * you must move it to a different location beforehand. - */ -@property(nonatomic, readonly) NSString* temporaryPath; - -@end - -/** - * The GCDWebServerMultiPartFormRequest subclass of GCDWebServerRequest - * parses the body of the HTTP request as a multipart encoded form. - */ -@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest - -/** - * Returns the argument parts from the multipart encoded form as - * name / GCDWebServerMultiPartArgument pairs. - */ -@property(nonatomic, readonly) NSArray* arguments; - -/** - * Returns the files parts from the multipart encoded form as - * name / GCDWebServerMultiPartFile pairs. - */ -@property(nonatomic, readonly) NSArray* files; - -/** - * Returns the MIME type for multipart encoded forms - * i.e. "multipart/form-data". - */ -+ (NSString*)mimeType; - -/** - * Returns the first argument for a given control name or nil if not found. - */ -- (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name; - -/** - * Returns the first file for a given control name or nil if not found. - */ -- (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name; - -@end diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerMultiPartFormRequest.m b/Frameworks/CGDWebServer/Requests/GCDWebServerMultiPartFormRequest.m deleted file mode 100755 index e1c0015..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerMultiPartFormRequest.m +++ /dev/null @@ -1,439 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import "GCDWebServerPrivate.h" - -#define kMultiPartBufferSize (256 * 1024) - -typedef enum { - kParserState_Undefined = 0, - kParserState_Start, - kParserState_Headers, - kParserState_Content, - kParserState_End -} ParserState; - -@interface GCDWebServerMIMEStreamParser : NSObject -- (id)initWithBoundary:(NSString*)boundary defaultControlName:(NSString*)name arguments:(NSMutableArray*)arguments files:(NSMutableArray*)files; -- (BOOL)appendBytes:(const void*)bytes length:(NSUInteger)length; -- (BOOL)isAtEnd; -@end - -static NSData* _newlineData = nil; -static NSData* _newlinesData = nil; -static NSData* _dashNewlineData = nil; - -@interface GCDWebServerMultiPart () { -@private - NSString* _controlName; - NSString* _contentType; - NSString* _mimeType; -} -@end - -@implementation GCDWebServerMultiPart - -@synthesize controlName=_controlName, contentType=_contentType, mimeType=_mimeType; - -- (id)initWithControlName:(NSString*)name contentType:(NSString*)type { - if ((self = [super init])) { - _controlName = [name copy]; - _contentType = [type copy]; - _mimeType = GCDWebServerTruncateHeaderValue(_contentType); - } - return self; -} - -@end - -@interface GCDWebServerMultiPartArgument () { -@private - NSData* _data; - NSString* _string; -} -@end - -@implementation GCDWebServerMultiPartArgument - -@synthesize data=_data, string=_string; - -- (id)initWithControlName:(NSString*)name contentType:(NSString*)type data:(NSData*)data { - if ((self = [super initWithControlName:name contentType:type])) { - _data = data; - - if ([self.contentType hasPrefix:@"text/"]) { - NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); - _string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)]; - } - } - return self; -} - -- (NSString*)description { - return [NSString stringWithFormat:@"<%@ | '%@' | %lu bytes>", [self class], self.mimeType, (unsigned long)_data.length]; -} - -@end - -@interface GCDWebServerMultiPartFile () { -@private - NSString* _fileName; - NSString* _temporaryPath; -} -@end - -@implementation GCDWebServerMultiPartFile - -@synthesize fileName=_fileName, temporaryPath=_temporaryPath; - -- (id)initWithControlName:(NSString*)name contentType:(NSString*)type fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath { - if ((self = [super initWithControlName:name contentType:type])) { - _fileName = [fileName copy]; - _temporaryPath = [temporaryPath copy]; - } - return self; -} - -- (void)dealloc { - unlink([_temporaryPath fileSystemRepresentation]); -} - -- (NSString*)description { - return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName]; -} - -@end - -@interface GCDWebServerMIMEStreamParser () { -@private - NSData* _boundary; - NSString* _defaultcontrolName; - ParserState _state; - NSMutableData* _data; - NSMutableArray* _arguments; - NSMutableArray* _files; - - NSString* _controlName; - NSString* _fileName; - NSString* _contentType; - NSString* _tmpPath; - int _tmpFile; - GCDWebServerMIMEStreamParser* _subParser; -} -@end - -@implementation GCDWebServerMIMEStreamParser - -+ (void)initialize { - if (_newlineData == nil) { - _newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2]; - GWS_DCHECK(_newlineData); - } - if (_newlinesData == nil) { - _newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; - GWS_DCHECK(_newlinesData); - } - if (_dashNewlineData == nil) { - _dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4]; - GWS_DCHECK(_dashNewlineData); - } -} - -- (id)initWithBoundary:(NSString*)boundary defaultControlName:(NSString*)name arguments:(NSMutableArray*)arguments files:(NSMutableArray*)files { - NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil; - if (data == nil) { - GWS_DNOT_REACHED(); - return nil; - } - if ((self = [super init])) { - _boundary = data; - _defaultcontrolName = name; - _arguments = arguments; - _files = files; - _data = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize]; - _state = kParserState_Start; - } - return self; -} - -- (void)dealloc { - if (_tmpFile > 0) { - close(_tmpFile); - unlink([_tmpPath fileSystemRepresentation]); - } -} - -// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 -- (BOOL)_parseData { - BOOL success = YES; - - if (_state == kParserState_Headers) { - NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)]; - if (range.location != NSNotFound) { - - _controlName = nil; - _fileName = nil; - _contentType = nil; - _tmpPath = nil; - _subParser = nil; - NSString* headers = [[NSString alloc] initWithData:[_data subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding]; - if (headers) { - for (NSString* header in [headers componentsSeparatedByString:@"\r\n"]) { - NSRange subRange = [header rangeOfString:@":"]; - if (subRange.location != NSNotFound) { - NSString* name = [header substringToIndex:subRange.location]; - NSString* value = [[header substringFromIndex:(subRange.location + subRange.length)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if ([name caseInsensitiveCompare:@"Content-Type"] == NSOrderedSame) { - _contentType = GCDWebServerNormalizeHeaderValue(value); - } else if ([name caseInsensitiveCompare:@"Content-Disposition"] == NSOrderedSame) { - NSString* contentDisposition = GCDWebServerNormalizeHeaderValue(value); - if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"form-data"]) { - _controlName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"name"); - _fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"); - } else if ([GCDWebServerTruncateHeaderValue(contentDisposition) isEqualToString:@"file"]) { - _controlName = _defaultcontrolName; - _fileName = GCDWebServerExtractHeaderValueParameter(contentDisposition, @"filename"); - } - } - } else { - GWS_DNOT_REACHED(); - } - } - if (_contentType == nil) { - _contentType = @"text/plain"; - } - } else { - GWS_LOG_ERROR(@"Failed decoding headers in part of 'multipart/form-data'"); - GWS_DNOT_REACHED(); - } - if (_controlName) { - if ([GCDWebServerTruncateHeaderValue(_contentType) isEqualToString:@"multipart/mixed"]) { - NSString* boundary = GCDWebServerExtractHeaderValueParameter(_contentType, @"boundary"); - _subParser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:_controlName arguments:_arguments files:_files]; - if (_subParser == nil) { - GWS_DNOT_REACHED(); - success = NO; - } - } else if (_fileName) { - NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; - _tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (_tmpFile > 0) { - _tmpPath = [path copy]; - } else { - GWS_DNOT_REACHED(); - success = NO; - } - } - } else { - GWS_DNOT_REACHED(); - success = NO; - } - - [_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0]; - _state = kParserState_Content; - } - } - - if ((_state == kParserState_Start) || (_state == kParserState_Content)) { - NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.length)]; - if (range.location != NSNotFound) { - NSRange subRange = NSMakeRange(range.location + range.length, _data.length - range.location - range.length); - NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange]; - NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange]; - if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) { - - if (_state == kParserState_Content) { - const void* dataBytes = _data.bytes; - NSUInteger dataLength = range.location - 2; - if (_subParser) { - if (![_subParser appendBytes:dataBytes length:(dataLength + 2)] || ![_subParser isAtEnd]) { - GWS_DNOT_REACHED(); - success = NO; - } - _subParser = nil; - } else if (_tmpPath) { - ssize_t result = write(_tmpFile, dataBytes, dataLength); - if (result == (ssize_t)dataLength) { - if (close(_tmpFile) == 0) { - _tmpFile = 0; - GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithControlName:_controlName contentType:_contentType fileName:_fileName temporaryPath:_tmpPath]; - [_files addObject:file]; - } else { - GWS_DNOT_REACHED(); - success = NO; - } - } else { - GWS_DNOT_REACHED(); - success = NO; - } - _tmpPath = nil; - } else { - NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength]; - GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithControlName:_controlName contentType:_contentType data:data]; - [_arguments addObject:argument]; - } - } - - if (subRange1.location != NSNotFound) { - [_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0]; - _state = kParserState_Headers; - success = [self _parseData]; - } else { - _state = kParserState_End; - } - } - } else { - NSUInteger margin = 2 * _boundary.length; - if (_data.length > margin) { - NSUInteger length = _data.length - margin; - if (_subParser) { - if ([_subParser appendBytes:_data.bytes length:length]) { - [_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0]; - } else { - GWS_DNOT_REACHED(); - success = NO; - } - } else if (_tmpPath) { - ssize_t result = write(_tmpFile, _data.bytes, length); - if (result == (ssize_t)length) { - [_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0]; - } else { - GWS_DNOT_REACHED(); - success = NO; - } - } - } - } - } - - return success; -} - -- (BOOL)appendBytes:(const void*)bytes length:(NSUInteger)length { - [_data appendBytes:bytes length:length]; - return [self _parseData]; -} - -- (BOOL)isAtEnd { - return (_state == kParserState_End); -} - -@end - -@interface GCDWebServerMultiPartFormRequest () { -@private - GCDWebServerMIMEStreamParser* _parser; - NSMutableArray* _arguments; - NSMutableArray* _files; -} -@end - -@implementation GCDWebServerMultiPartFormRequest - -@synthesize arguments=_arguments, files=_files; - -+ (NSString*)mimeType { - return @"multipart/form-data"; -} - -- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { - if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { - _arguments = [[NSMutableArray alloc] init]; - _files = [[NSMutableArray alloc] init]; - } - return self; -} - -- (BOOL)open:(NSError**)error { - NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary"); - _parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary defaultControlName:nil arguments:_arguments files:_files]; - if (_parser == nil) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed starting to parse multipart form data"}]; - return NO; - } - return YES; -} - -- (BOOL)writeData:(NSData*)data error:(NSError**)error { - if (![_parser appendBytes:data.bytes length:data.length]) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed continuing to parse multipart form data"}]; - return NO; - } - return YES; -} - -- (BOOL)close:(NSError**)error { - BOOL atEnd = [_parser isAtEnd]; - _parser = nil; - if (!atEnd) { - *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed finishing to parse multipart form data"}]; - return NO; - } - return YES; -} - -- (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name { - for (GCDWebServerMultiPartArgument* argument in _arguments) { - if ([argument.controlName isEqualToString:name]) { - return argument; - } - } - return nil; -} - -- (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name { - for (GCDWebServerMultiPartFile* file in _files) { - if ([file.controlName isEqualToString:name]) { - return file; - } - } - return nil; -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithString:[super description]]; - if (_arguments.count) { - [description appendString:@"\n"]; - for (GCDWebServerMultiPartArgument* argument in _arguments) { - [description appendFormat:@"\n%@ (%@)\n", argument.controlName, argument.contentType]; - [description appendString:GCDWebServerDescribeData(argument.data, argument.contentType)]; - } - } - if (_files.count) { - [description appendString:@"\n"]; - for (GCDWebServerMultiPartFile* file in _files) { - [description appendFormat:@"\n%@ (%@): %@\n{%@}", file.controlName, file.contentType, file.fileName, file.temporaryPath]; - } - } - return description; -} - -@end diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h b/Frameworks/CGDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h deleted file mode 100755 index e36eac3..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerURLEncodedFormRequest.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerDataRequest.h" - -/** - * The GCDWebServerURLEncodedFormRequest subclass of GCDWebServerRequest - * parses the body of the HTTP request as a URL encoded form using - * GCDWebServerParseURLEncodedForm(). - */ -@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest - -/** - * Returns the unescaped control names and values for the URL encoded form. - * - * The text encoding used to interpret the data is extracted from the - * "Content-Type" header or defaults to UTF-8. - */ -@property(nonatomic, readonly) NSDictionary* arguments; - -/** - * Returns the MIME type for URL encoded forms - * i.e. "application/x-www-form-urlencoded". - */ -+ (NSString*)mimeType; - -@end diff --git a/Frameworks/CGDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m b/Frameworks/CGDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m deleted file mode 100755 index f210fa0..0000000 --- a/Frameworks/CGDWebServer/Requests/GCDWebServerURLEncodedFormRequest.m +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import "GCDWebServerPrivate.h" - -@interface GCDWebServerURLEncodedFormRequest () { -@private - NSDictionary* _arguments; -} -@end - -@implementation GCDWebServerURLEncodedFormRequest - -@synthesize arguments=_arguments; - -+ (NSString*)mimeType { - return @"application/x-www-form-urlencoded"; -} - -- (BOOL)close:(NSError**)error { - if (![super close:error]) { - return NO; - } - - NSString* charset = GCDWebServerExtractHeaderValueParameter(self.contentType, @"charset"); - NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)]; - _arguments = GCDWebServerParseURLEncodedForm(string); - GWS_DCHECK(_arguments); - - return YES; -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithString:[super description]]; - [description appendString:@"\n"]; - for (NSString* argument in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) { - [description appendFormat:@"\n%@ = %@", argument, [_arguments objectForKey:argument]]; - } - return description; -} - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerDataResponse.h b/Frameworks/CGDWebServer/Responses/GCDWebServerDataResponse.h deleted file mode 100755 index b0c6493..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerDataResponse.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerResponse.h" - -/** - * The GCDWebServerDataResponse subclass of GCDWebServerResponse reads the body - * of the HTTP response from memory. - */ -@interface GCDWebServerDataResponse : GCDWebServerResponse - -/** - * Creates a response with data in memory and a given content type. - */ -+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type; - -/** - * This method is the designated initializer for the class. - */ -- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type; - -@end - -@interface GCDWebServerDataResponse (Extensions) - -/** - * Creates a data response from text encoded using UTF-8. - */ -+ (instancetype)responseWithText:(NSString*)text; - -/** - * Creates a data response from HTML encoded using UTF-8. - */ -+ (instancetype)responseWithHTML:(NSString*)html; - -/** - * Creates a data response from an HTML template encoded using UTF-8. - * See -initWithHTMLTemplate:variables: for details. - */ -+ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; - -/** - * Creates a data response from a serialized JSON object and the default - * "application/json" content type. - */ -+ (instancetype)responseWithJSONObject:(id)object; - -/** - * Creates a data response from a serialized JSON object and a custom - * content type. - */ -+ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type; - -/** - * Initializes a data response from text encoded using UTF-8. - */ -- (instancetype)initWithText:(NSString*)text; - -/** - * Initializes a data response from HTML encoded using UTF-8. - */ -- (instancetype)initWithHTML:(NSString*)html; - -/** - * Initializes a data response from an HTML template encoded using UTF-8. - * - * All occurences of "%variable%" within the HTML template are replaced with - * their corresponding values. - */ -- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; - -/** - * Initializes a data response from a serialized JSON object and the default - * "application/json" content type. - */ -- (instancetype)initWithJSONObject:(id)object; - -/** - * Initializes a data response from a serialized JSON object and a custom - * content type. - */ -- (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type; - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerDataResponse.m b/Frameworks/CGDWebServer/Responses/GCDWebServerDataResponse.m deleted file mode 100755 index ea02799..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerDataResponse.m +++ /dev/null @@ -1,143 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import "GCDWebServerPrivate.h" - -@interface GCDWebServerDataResponse () { -@private - NSData* _data; - BOOL _done; -} -@end - -@implementation GCDWebServerDataResponse - -+ (instancetype)responseWithData:(NSData*)data contentType:(NSString*)type { - return [[[self class] alloc] initWithData:data contentType:type]; -} - -- (instancetype)initWithData:(NSData*)data contentType:(NSString*)type { - if (data == nil) { - GWS_DNOT_REACHED(); - return nil; - } - - if ((self = [super init])) { - _data = data; - - self.contentType = type; - self.contentLength = data.length; - } - return self; -} - -- (NSData*)readData:(NSError**)error { - NSData* data; - if (_done) { - data = [NSData data]; - } else { - data = _data; - _done = YES; - } - return data; -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithString:[super description]]; - [description appendString:@"\n\n"]; - [description appendString:GCDWebServerDescribeData(_data, self.contentType)]; - return description; -} - -@end - -@implementation GCDWebServerDataResponse (Extensions) - -+ (instancetype)responseWithText:(NSString*)text { - return [[self alloc] initWithText:text]; -} - -+ (instancetype)responseWithHTML:(NSString*)html { - return [[self alloc] initWithHTML:html]; -} - -+ (instancetype)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { - return [[self alloc] initWithHTMLTemplate:path variables:variables]; -} - -+ (instancetype)responseWithJSONObject:(id)object { - return [[self alloc] initWithJSONObject:object]; -} - -+ (instancetype)responseWithJSONObject:(id)object contentType:(NSString*)type { - return [[self alloc] initWithJSONObject:object contentType:type]; -} - -- (instancetype)initWithText:(NSString*)text { - NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding]; - if (data == nil) { - GWS_DNOT_REACHED(); - return nil; - } - return [self initWithData:data contentType:@"text/plain; charset=utf-8"]; -} - -- (instancetype)initWithHTML:(NSString*)html { - NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding]; - if (data == nil) { - GWS_DNOT_REACHED(); - return nil; - } - return [self initWithData:data contentType:@"text/html; charset=utf-8"]; -} - -- (instancetype)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables { - NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; - [variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) { - [html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)]; - }]; - id response = [self initWithHTML:html]; - return response; -} - -- (instancetype)initWithJSONObject:(id)object { - return [self initWithJSONObject:object contentType:@"application/json"]; -} - -- (instancetype)initWithJSONObject:(id)object contentType:(NSString*)type { - NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL]; - if (data == nil) { - return nil; - } - return [self initWithData:data contentType:type]; -} - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerErrorResponse.h b/Frameworks/CGDWebServer/Responses/GCDWebServerErrorResponse.h deleted file mode 100755 index 381b122..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerErrorResponse.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerDataResponse.h" -#import "GCDWebServerHTTPStatusCodes.h" - -/** - * The GCDWebServerDataResponse subclass of GCDWebServerDataResponse generates - * an HTML body from an HTTP status code and an error message. - */ -@interface GCDWebServerErrorResponse : GCDWebServerDataResponse - -/** - * Creates a client error response with the corresponding HTTP status code. - */ -+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); - -/** - * Creates a server error response with the corresponding HTTP status code. - */ -+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); - -/** - * Creates a client error response with the corresponding HTTP status code - * and an underlying NSError. - */ -+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); - -/** - * Creates a server error response with the corresponding HTTP status code - * and an underlying NSError. - */ -+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); - -/** - * Initializes a client error response with the corresponding HTTP status code. - */ -- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); - -/** - * Initializes a server error response with the corresponding HTTP status code. - */ -- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... NS_FORMAT_FUNCTION(2,3); - -/** - * Initializes a client error response with the corresponding HTTP status code - * and an underlying NSError. - */ -- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); - -/** - * Initializes a server error response with the corresponding HTTP status code - * and an underlying NSError. - */ -- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... NS_FORMAT_FUNCTION(3,4); - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerErrorResponse.m b/Frameworks/CGDWebServer/Responses/GCDWebServerErrorResponse.m deleted file mode 100755 index c2e4422..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerErrorResponse.m +++ /dev/null @@ -1,128 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import "GCDWebServerPrivate.h" - -@interface GCDWebServerErrorResponse () -- (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments; -@end - -@implementation GCDWebServerErrorResponse - -+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); - va_list arguments; - va_start(arguments, format); - GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; - va_end(arguments); - return response; -} - -+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); - va_list arguments; - va_start(arguments, format); - GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; - va_end(arguments); - return response; -} - -+ (instancetype)responseWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); - va_list arguments; - va_start(arguments, format); - GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; - va_end(arguments); - return response; -} - -+ (instancetype)responseWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); - va_list arguments; - va_start(arguments, format); - GCDWebServerErrorResponse* response = [[self alloc] initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; - va_end(arguments); - return response; -} - -static inline NSString* _EscapeHTMLString(NSString* string) { - return [string stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; -} - -- (instancetype)initWithStatusCode:(NSInteger)statusCode underlyingError:(NSError*)underlyingError messageFormat:(NSString*)format arguments:(va_list)arguments { - NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments]; - NSString* title = [NSString stringWithFormat:@"HTTP Error %i", (int)statusCode]; - NSString* error = underlyingError ? [NSString stringWithFormat:@"[%@] %@ (%li)", underlyingError.domain, _EscapeHTMLString(underlyingError.localizedDescription), (long)underlyingError.code] : @""; - NSString* html = [NSString stringWithFormat:@"%@

%@: %@

%@

", - title, title, _EscapeHTMLString(message), error]; - if ((self = [self initWithHTML:html])) { - self.statusCode = statusCode; - } - return self; -} - -- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); - va_list arguments; - va_start(arguments, format); - self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; - va_end(arguments); - return self; -} - -- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); - va_list arguments; - va_start(arguments, format); - self = [self initWithStatusCode:errorCode underlyingError:nil messageFormat:format arguments:arguments]; - va_end(arguments); - return self; -} - -- (instancetype)initWithClientError:(GCDWebServerClientErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 400) && ((NSInteger)errorCode < 500)); - va_list arguments; - va_start(arguments, format); - self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; - va_end(arguments); - return self; -} - -- (instancetype)initWithServerError:(GCDWebServerServerErrorHTTPStatusCode)errorCode underlyingError:(NSError*)underlyingError message:(NSString*)format, ... { - GWS_DCHECK(((NSInteger)errorCode >= 500) && ((NSInteger)errorCode < 600)); - va_list arguments; - va_start(arguments, format); - self = [self initWithStatusCode:errorCode underlyingError:underlyingError messageFormat:format arguments:arguments]; - va_end(arguments); - return self; -} - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerFileResponse.h b/Frameworks/CGDWebServer/Responses/GCDWebServerFileResponse.h deleted file mode 100755 index 19d284d..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerFileResponse.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerResponse.h" - -/** - * The GCDWebServerFileResponse subclass of GCDWebServerResponse reads the body - * of the HTTP response from a file on disk. - * - * It will automatically set the contentType, lastModifiedDate and eTag - * properties of the GCDWebServerResponse according to the file extension and - * metadata. - */ -@interface GCDWebServerFileResponse : GCDWebServerResponse - -/** - * Creates a response with the contents of a file. - */ -+ (instancetype)responseWithFile:(NSString*)path; - -/** - * Creates a response like +responseWithFile: and sets the "Content-Disposition" - * HTTP header for a download if the "attachment" argument is YES. - */ -+ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment; - -/** - * Creates a response like +responseWithFile: but restricts the file contents - * to a specific byte range. - * - * See -initWithFile:byteRange: for details. - */ -+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range; - -/** - * Creates a response like +responseWithFile:byteRange: and sets the - * "Content-Disposition" HTTP header for a download if the "attachment" - * argument is YES. - */ -+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; - -/** - * Initializes a response with the contents of a file. - */ -- (instancetype)initWithFile:(NSString*)path; - -/** - * Initializes a response like +responseWithFile: and sets the - * "Content-Disposition" HTTP header for a download if the "attachment" - * argument is YES. - */ -- (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment; - -/** - * Initializes a response like -initWithFile: but restricts the file contents - * to a specific byte range. This range should be set to (NSUIntegerMax, 0) for - * the full file, (offset, length) if expressed from the beginning of the file, - * or (NSUIntegerMax, length) if expressed from the end of the file. The "offset" - * and "length" values will be automatically adjusted to be compatible with the - * actual size of the file. - * - * This argument would typically be set to the value of the byteRange property - * of the current GCDWebServerRequest. - */ -- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range; - -/** - * This method is the designated initializer for the class. - */ -- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment; - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerFileResponse.m b/Frameworks/CGDWebServer/Responses/GCDWebServerFileResponse.m deleted file mode 100755 index 2004327..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerFileResponse.m +++ /dev/null @@ -1,181 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import - -#import "GCDWebServerPrivate.h" - -#define kFileReadBufferSize (32 * 1024) - -@interface GCDWebServerFileResponse () { -@private - NSString* _path; - NSUInteger _offset; - NSUInteger _size; - int _file; -} -@end - -@implementation GCDWebServerFileResponse - -+ (instancetype)responseWithFile:(NSString*)path { - return [[[self class] alloc] initWithFile:path]; -} - -+ (instancetype)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment { - return [[[self class] alloc] initWithFile:path isAttachment:attachment]; -} - -+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range { - return [[[self class] alloc] initWithFile:path byteRange:range]; -} - -+ (instancetype)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { - return [[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment]; -} - -- (instancetype)initWithFile:(NSString*)path { - return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:NO]; -} - -- (instancetype)initWithFile:(NSString*)path isAttachment:(BOOL)attachment { - return [self initWithFile:path byteRange:NSMakeRange(NSUIntegerMax, 0) isAttachment:attachment]; -} - -- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range { - return [self initWithFile:path byteRange:range isAttachment:NO]; -} - -static inline NSDate* _NSDateFromTimeSpec(const struct timespec* t) { - return [NSDate dateWithTimeIntervalSince1970:((NSTimeInterval)t->tv_sec + (NSTimeInterval)t->tv_nsec / 1000000000.0)]; -} - -- (instancetype)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment { - struct stat info; - if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) { - GWS_DNOT_REACHED(); - return nil; - } -#ifndef __LP64__ - if (info.st_size >= (off_t)4294967295) { // In 32 bit mode, we can't handle files greater than 4 GiBs (don't use "NSUIntegerMax" here to avoid potential unsigned to signed conversion issues) - GWS_DNOT_REACHED(); - return nil; - } -#endif - NSUInteger fileSize = (NSUInteger)info.st_size; - - BOOL hasByteRange = GCDWebServerIsValidByteRange(range); - if (hasByteRange) { - if (range.location != NSUIntegerMax) { - range.location = MIN(range.location, fileSize); - range.length = MIN(range.length, fileSize - range.location); - } else { - range.length = MIN(range.length, fileSize); - range.location = fileSize - range.length; - } - if (range.length == 0) { - return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header - } - } else { - range.location = 0; - range.length = fileSize; - } - - if ((self = [super init])) { - _path = [path copy]; - _offset = range.location; - _size = range.length; - if (hasByteRange) { - [self setStatusCode:kGCDWebServerHTTPStatusCode_PartialContent]; - [self setValue:[NSString stringWithFormat:@"bytes %lu-%lu/%lu", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), (unsigned long)fileSize] forAdditionalHeader:@"Content-Range"]; - GWS_LOG_DEBUG(@"Using content bytes range [%lu-%lu] for file \"%@\"", (unsigned long)_offset, (unsigned long)(_offset + _size - 1), path); - } - - if (attachment) { - NSString* fileName = [path lastPathComponent]; - NSData* data = [[fileName stringByReplacingOccurrencesOfString:@"\"" withString:@""] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES]; - NSString* lossyFileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil; - if (lossyFileName) { - NSString* value = [NSString stringWithFormat:@"attachment; filename=\"%@\"; filename*=UTF-8''%@", lossyFileName, GCDWebServerEscapeURLString(fileName)]; - [self setValue:value forAdditionalHeader:@"Content-Disposition"]; - } else { - GWS_DNOT_REACHED(); - } - } - - self.contentType = GCDWebServerGetMimeTypeForExtension([_path pathExtension]); - self.contentLength = _size; - self.lastModifiedDate = _NSDateFromTimeSpec(&info.st_mtimespec); - self.eTag = [NSString stringWithFormat:@"%llu/%li/%li", info.st_ino, info.st_mtimespec.tv_sec, info.st_mtimespec.tv_nsec]; - } - return self; -} - -- (BOOL)open:(NSError**)error { - _file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY); - if (_file <= 0) { - *error = GCDWebServerMakePosixError(errno); - return NO; - } - if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) { - *error = GCDWebServerMakePosixError(errno); - close(_file); - return NO; - } - return YES; -} - -- (NSData*)readData:(NSError**)error { - size_t length = MIN((NSUInteger)kFileReadBufferSize, _size); - NSMutableData* data = [[NSMutableData alloc] initWithLength:length]; - ssize_t result = read(_file, data.mutableBytes, length); - if (result < 0) { - *error = GCDWebServerMakePosixError(errno); - return nil; - } - if (result > 0) { - [data setLength:result]; - _size -= result; - } - return data; -} - -- (void)close { - close(_file); -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithString:[super description]]; - [description appendFormat:@"\n\n{%@}", _path]; - return description; -} - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerStreamedResponse.h b/Frameworks/CGDWebServer/Responses/GCDWebServerStreamedResponse.h deleted file mode 100755 index 4f39625..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerStreamedResponse.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import "GCDWebServerResponse.h" - -/** - * The GCDWebServerStreamBlock is called to stream the data for the HTTP body. - * The block must return empty NSData when done or nil on error and set the - * "error" argument which is guaranteed to be non-NULL. - */ -typedef NSData* (^GCDWebServerStreamBlock)(NSError** error); - -/** - * The GCDWebServerAsyncStreamBlock works like the GCDWebServerStreamBlock - * except the streamed data can be returned at a later time allowing for - * truly asynchronous generation of the data. - * - * The block must return empty NSData when done or nil on error and set the - * "error" argument which is guaranteed to be non-NULL. - * - * The block must regularly call "completionBlock" passing new streamed data. - * Eventually it must call "completionBlock" passing an empty NSData indicating - * the end of the stream has been reached, or pass nil and an NSError in case of - * error. - */ -typedef void (^GCDWebServerAsyncStreamBlock)(GCDWebServerBodyReaderCompletionBlock completionBlock); - -/** - * The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams - * the body of the HTTP response using a GCD block. - */ -@interface GCDWebServerStreamedResponse : GCDWebServerResponse - -/** - * Creates a response with streamed data and a given content type. - */ -+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; - -/** - * Creates a response with async streamed data and a given content type. - */ -+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; - -/** - * Initializes a response with streamed data and a given content type. - */ -- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; - -/** - * This method is the designated initializer for the class. - */ -- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block; - -@end diff --git a/Frameworks/CGDWebServer/Responses/GCDWebServerStreamedResponse.m b/Frameworks/CGDWebServer/Responses/GCDWebServerStreamedResponse.m deleted file mode 100755 index 7f96943..0000000 --- a/Frameworks/CGDWebServer/Responses/GCDWebServerStreamedResponse.m +++ /dev/null @@ -1,79 +0,0 @@ -/* - Copyright (c) 2012-2014, Pierre-Olivier Latour - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * The name of Pierre-Olivier Latour may not be used to endorse - or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if !__has_feature(objc_arc) -#error GCDWebServer requires ARC -#endif - -#import "GCDWebServerPrivate.h" - -@interface GCDWebServerStreamedResponse () { -@private - GCDWebServerAsyncStreamBlock _block; -} -@end - -@implementation GCDWebServerStreamedResponse - -+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { - return [[[self class] alloc] initWithContentType:type streamBlock:block]; -} - -+ (instancetype)responseWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { - return [[[self class] alloc] initWithContentType:type asyncStreamBlock:block]; -} - -- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block { - return [self initWithContentType:type asyncStreamBlock:^(GCDWebServerBodyReaderCompletionBlock completionBlock) { - - NSError* error = nil; - NSData* data = block(&error); - completionBlock(data, error); - - }]; -} - -- (instancetype)initWithContentType:(NSString*)type asyncStreamBlock:(GCDWebServerAsyncStreamBlock)block { - if ((self = [super init])) { - _block = [block copy]; - - self.contentType = type; - } - return self; -} - -- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block { - _block(block); -} - -- (NSString*)description { - NSMutableString* description = [NSMutableString stringWithString:[super description]]; - [description appendString:@"\n\n"]; - return description; -} - -@end diff --git a/Frameworks/CGDWebServer/VERSION b/Frameworks/CGDWebServer/VERSION deleted file mode 100644 index 131ea86..0000000 --- a/Frameworks/CGDWebServer/VERSION +++ /dev/null @@ -1,4 +0,0 @@ -Version: 2.5.2 -Tag: 2.5.2 -Commit SHA hash: a5d83abdd001a610cc162c316d2be65a94e64063 -Link: https://github.com/swisspol/GCDWebServer/tree/2.5.2 \ No newline at end of file