123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- /*
- Copyright (c) 2012-2015, 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 <TargetConditionals.h>
- #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;
- /**
- * Request a port mapping in the NAT gateway (NSNumber / BOOL).
- *
- * This uses the DNSService API under the hood which supports IPv4 mappings only.
- *
- * The default value is NO.
- *
- * @warning The external port set up by the NAT gateway may be different than
- * the one used by the GCDWebServer.
- */
- extern NSString* const GCDWebServerOption_RequestNATPortMapping;
- /**
- * Only accept HTTP requests coming from localhost i.e. not from the outside
- * network (NSNumber / BOOL).
- *
- * The default value is NO.
- *
- * @warning Bonjour and NAT port mapping 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 <NSObject>
- @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.
- *
- * Use the "bonjourServerURL" property to retrieve the Bonjour address of the
- * server.
- */
- - (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
- /**
- * This method is called after the NAT port mapping for the server has been
- * updated.
- *
- * Use the "publicServerURL" property to retrieve the public address of the
- * server.
- */
- - (void)webServerDidUpdateNATPortMapping:(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<GCDWebServerDelegate> 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;
- /**
- * Returns the server's public URL.
- *
- * @warning This property is only valid if the server is running and NAT port
- * mapping is active.
- */
- @property(nonatomic, readonly) NSURL* publicServerURL;
- /**
- * 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
|