mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-02-11 00:00:07 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e59cf4b6df | ||
|
|
9e8f0e00f3 | ||
|
|
d7650a71e0 | ||
|
|
420ddc3eac | ||
|
|
143e38c968 | ||
|
|
8e9fe4c4c1 | ||
|
|
95bccff2f7 | ||
|
|
780a608d6c | ||
|
|
18d93bbf47 | ||
|
|
b35ebd7d58 | ||
|
|
a11b047233 | ||
|
|
4eac9d4f8e | ||
|
|
d1e2a1a12f | ||
|
|
54d5abd3a8 | ||
|
|
a9fee8d7e2 | ||
|
|
6b15bdaa4e | ||
|
|
3771cf4e92 | ||
|
|
a5d83abdd0 | ||
|
|
f1e9f1a37c | ||
|
|
00b5ec87ba | ||
|
|
cf94e70a42 | ||
|
|
d47409c776 | ||
|
|
a9db13475b | ||
|
|
17fad0f1b9 | ||
|
|
5493d9e803 | ||
|
|
12b1edb958 | ||
|
|
7544a6dc4e |
@@ -1,19 +1,19 @@
|
|||||||
# http://guides.cocoapods.org/syntax/podspec.html
|
# http://guides.cocoapods.org/syntax/podspec.html
|
||||||
# Verify Podspec with:
|
# http://guides.cocoapods.org/making/getting-setup-with-trunk.html
|
||||||
# sudo gem update cocoapods
|
# $ sudo gem update cocoapods
|
||||||
# pod spec lint GCDWebServer.podspec --verbose
|
# (optional) $ pod trunk register {email} {name} --description={computer}
|
||||||
# Add to source line:
|
# $ pod trunk push
|
||||||
# :tag => s.version.to_s
|
# DELETE THIS SECTION BEFORE PROCEEDING!
|
||||||
|
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'GCDWebServer'
|
s.name = 'GCDWebServer'
|
||||||
s.version = '2.5'
|
s.version = '3.0.1'
|
||||||
s.author = { 'Pierre-Olivier Latour' => 'info@pol-online.net' }
|
s.author = { 'Pierre-Olivier Latour' => 'info@pol-online.net' }
|
||||||
s.license = { :type => 'BSD', :file => 'LICENSE' }
|
s.license = { :type => 'BSD', :file => 'LICENSE' }
|
||||||
s.homepage = 'https://github.com/swisspol/GCDWebServer'
|
s.homepage = 'https://github.com/swisspol/GCDWebServer'
|
||||||
s.summary = 'Lightweight GCD based HTTP server for OS X & iOS (includes web based uploader & WebDAV server)'
|
s.summary = 'Lightweight GCD based HTTP server for OS X & iOS (includes web based uploader & WebDAV server)'
|
||||||
|
|
||||||
s.source = { :git => 'https://github.com/swisspol/GCDWebServer.git' }
|
s.source = { :git => 'https://github.com/swisspol/GCDWebServer.git', :tag => s.version.to_s }
|
||||||
s.ios.deployment_target = '5.0'
|
s.ios.deployment_target = '5.0'
|
||||||
s.osx.deployment_target = '10.7'
|
s.osx.deployment_target = '10.7'
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
@@ -28,7 +28,6 @@ Pod::Spec.new do |s|
|
|||||||
cs.ios.frameworks = 'MobileCoreServices', 'CFNetwork'
|
cs.ios.frameworks = 'MobileCoreServices', 'CFNetwork'
|
||||||
cs.osx.library = 'z'
|
cs.osx.library = 'z'
|
||||||
cs.osx.framework = 'SystemConfiguration'
|
cs.osx.framework = 'SystemConfiguration'
|
||||||
cs.compiler_flags = '-DNDEBUG' # TODO: Only set this for Release configuration
|
|
||||||
end
|
end
|
||||||
|
|
||||||
s.subspec 'WebDAV' do |cs|
|
s.subspec 'WebDAV' do |cs|
|
||||||
|
|||||||
@@ -112,6 +112,8 @@
|
|||||||
E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
|
E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
E221129A1690B7B10048D2B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
|
E221129A1690B7B10048D2B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; };
|
E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
E26DC18719E84B2200C68DDC /* GCDWebServer.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = GCDWebServer.podspec; sourceTree = "<group>"; };
|
||||||
|
E26DC18819E84BC000C68DDC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
E28BAE1618F99C810095C089 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
|
E28BAE1618F99C810095C089 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
|
||||||
E28BAE1718F99C810095C089 /* GCDWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; };
|
E28BAE1718F99C810095C089 /* GCDWebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServer.m; sourceTree = "<group>"; };
|
||||||
E28BAE1818F99C810095C089 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; };
|
E28BAE1818F99C810095C089 /* GCDWebServerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerConnection.h; sourceTree = "<group>"; };
|
||||||
@@ -183,6 +185,8 @@
|
|||||||
08FB7794FE84155DC02AAC07 /* LittleCMS */ = {
|
08FB7794FE84155DC02AAC07 /* LittleCMS */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E26DC18819E84BC000C68DDC /* README.md */,
|
||||||
|
E26DC18719E84B2200C68DDC /* GCDWebServer.podspec */,
|
||||||
E28BAE1418F99C810095C089 /* GCDWebServer */,
|
E28BAE1418F99C810095C089 /* GCDWebServer */,
|
||||||
E2A0E80718F3432600C580B1 /* GCDWebDAVServer */,
|
E2A0E80718F3432600C580B1 /* GCDWebDAVServer */,
|
||||||
E2BE850618E77ECA0061360B /* GCDWebUploader */,
|
E2BE850618E77ECA0061360B /* GCDWebUploader */,
|
||||||
@@ -370,7 +374,7 @@
|
|||||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 0510;
|
LastUpgradeCheck = 0610;
|
||||||
};
|
};
|
||||||
buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "GCDWebServer" */;
|
buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "GCDWebServer" */;
|
||||||
compatibilityVersion = "Xcode 3.2";
|
compatibilityVersion = "Xcode 3.2";
|
||||||
@@ -490,7 +494,6 @@
|
|||||||
1DEB928608733DD80010E9CD /* Debug */ = {
|
1DEB928608733DD80010E9CD /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD)";
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -500,7 +503,6 @@
|
|||||||
1DEB928708733DD80010E9CD /* Release */ = {
|
1DEB928708733DD80010E9CD /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD)";
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -512,6 +514,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
|
||||||
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = __GCDWEBSERVER_ENABLE_TESTING__;
|
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = __GCDWEBSERVER_ENABLE_TESTING__;
|
||||||
HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2";
|
HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2";
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
@@ -531,6 +534,7 @@
|
|||||||
"-Wno-padded",
|
"-Wno-padded",
|
||||||
"-Wno-documentation",
|
"-Wno-documentation",
|
||||||
"-Wno-documentation-unknown-command",
|
"-Wno-documentation-unknown-command",
|
||||||
|
"-Wno-objc-missing-property-synthesis",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -539,7 +543,6 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = __GCDWEBSERVER_ENABLE_TESTING__;
|
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = __GCDWEBSERVER_ENABLE_TESTING__;
|
||||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||||
HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2";
|
HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2";
|
||||||
@@ -550,10 +553,9 @@
|
|||||||
E22112761690B4DF0048D2B2 /* Debug */ = {
|
E22112761690B4DF0048D2B2 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD)";
|
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
INFOPLIST_FILE = iOS/Info.plist;
|
INFOPLIST_FILE = iOS/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 5.1.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
PROVISIONING_PROFILE = "";
|
PROVISIONING_PROFILE = "";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -564,10 +566,9 @@
|
|||||||
E22112771690B4DF0048D2B2 /* Release */ = {
|
E22112771690B4DF0048D2B2 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD)";
|
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
INFOPLIST_FILE = iOS/Info.plist;
|
INFOPLIST_FILE = iOS/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 5.1.1;
|
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
PROVISIONING_PROFILE = "";
|
PROVISIONING_PROFILE = "";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|||||||
@@ -33,8 +33,8 @@
|
|||||||
/**
|
/**
|
||||||
* Log levels used by GCDWebServer.
|
* Log levels used by GCDWebServer.
|
||||||
*
|
*
|
||||||
* @warning kGCDWebServerLogLevel_Debug is only available if "NDEBUG" is not
|
* @warning kGCDWebServerLogLevel_Debug is only functional if the preprocessor
|
||||||
* defined when building.
|
* constant "DEBUG" is is non-zero at build time.
|
||||||
*/
|
*/
|
||||||
typedef NS_ENUM(int, GCDWebServerLogLevel) {
|
typedef NS_ENUM(int, GCDWebServerLogLevel) {
|
||||||
kGCDWebServerLogLevel_Debug = 0,
|
kGCDWebServerLogLevel_Debug = 0,
|
||||||
@@ -69,6 +69,19 @@ typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod,
|
|||||||
*/
|
*/
|
||||||
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request);
|
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 port used by the GCDWebServer (NSNumber / NSUInteger).
|
||||||
*
|
*
|
||||||
@@ -77,16 +90,18 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
|
|||||||
extern NSString* const GCDWebServerOption_Port;
|
extern NSString* const GCDWebServerOption_Port;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Bonjour name used by the GCDWebServer (NSString).
|
* 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 an empty string i.e. use the computer / device name.
|
* The default value is an empty string.
|
||||||
*/
|
*/
|
||||||
extern NSString* const GCDWebServerOption_BonjourName;
|
extern NSString* const GCDWebServerOption_BonjourName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Bonjour service type used by the GCDWebServer (NSString).
|
* The Bonjour service type used by the GCDWebServer (NSString).
|
||||||
*
|
*
|
||||||
* The default value is "_http._tcp", standard HTTP web server.
|
* The default value is "_http._tcp", the service type for HTTP web servers.
|
||||||
*/
|
*/
|
||||||
extern NSString* const GCDWebServerOption_BonjourType;
|
extern NSString* const GCDWebServerOption_BonjourType;
|
||||||
|
|
||||||
@@ -287,7 +302,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
- (instancetype)init;
|
- (instancetype)init;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a handler to the server to handle incoming HTTP requests.
|
* 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
|
* Handlers are called in a LIFO queue, so if multiple handlers can potentially
|
||||||
* respond to a given request, the latest added one wins.
|
* respond to a given request, the latest added one wins.
|
||||||
@@ -296,6 +311,16 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
*/
|
*/
|
||||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
- (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.
|
* Removes all handlers previously added to the server.
|
||||||
*
|
*
|
||||||
@@ -336,12 +361,14 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
*
|
*
|
||||||
* @warning This property is only valid if the server is running and Bonjour
|
* @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.
|
* 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;
|
@property(nonatomic, readonly) NSURL* bonjourServerURL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
||||||
* using the computer / device name for as the Bonjour name.
|
* using the default Bonjour name.
|
||||||
*
|
*
|
||||||
* Returns NO if the server failed to start.
|
* Returns NO if the server failed to start.
|
||||||
*/
|
*/
|
||||||
@@ -350,7 +377,7 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
/**
|
/**
|
||||||
* Starts the server on a given port and with a specific Bonjour name.
|
* 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
|
* Pass a nil Bonjour name to disable Bonjour entirely or an empty string to
|
||||||
* use the computer / device name.
|
* use the default name.
|
||||||
*
|
*
|
||||||
* Returns NO if the server failed to start.
|
* Returns NO if the server failed to start.
|
||||||
*/
|
*/
|
||||||
@@ -388,22 +415,44 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a default handler to the server to handle all incoming HTTP requests
|
* Adds a default handler to the server to handle all incoming HTTP requests
|
||||||
* with a given HTTP method.
|
* with a given HTTP method and generate responses synchronously.
|
||||||
*/
|
*/
|
||||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
- (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
|
* Adds a handler to the server to handle incoming HTTP requests with a given
|
||||||
* HTTP method and a specific case-insensitive path.
|
* 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;
|
- (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
|
* 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.
|
* 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;
|
- (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
|
@end
|
||||||
|
|
||||||
@interface GCDWebServer (GETHandlers)
|
@interface GCDWebServer (GETHandlers)
|
||||||
@@ -440,8 +489,9 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
/**
|
/**
|
||||||
* Sets the current log level below which logged messages are discarded.
|
* Sets the current log level below which logged messages are discarded.
|
||||||
*
|
*
|
||||||
* The default level is either DEBUG or INFO if "NDEBUG" is defined at build-time.
|
* The default level is INFO (or DEBUG if the preprocessor constant "DEBUG"
|
||||||
* It can also be set at runtime with the "logLevel" environment variable.
|
* is non-zero at build time).
|
||||||
|
* It can also be set at run time with the "logLevel" environment variable.
|
||||||
*/
|
*/
|
||||||
+ (void)setLogLevel:(GCDWebServerLogLevel)level;
|
+ (void)setLogLevel:(GCDWebServerLogLevel)level;
|
||||||
|
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ NSString* const GCDWebServerAuthenticationMethod_Basic = @"Basic";
|
|||||||
NSString* const GCDWebServerAuthenticationMethod_DigestAccess = @"DigestAccess";
|
NSString* const GCDWebServerAuthenticationMethod_DigestAccess = @"DigestAccess";
|
||||||
|
|
||||||
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
|
#ifndef __GCDWEBSERVER_LOGGING_HEADER__
|
||||||
#ifdef NDEBUG
|
#if DEBUG
|
||||||
GCDWebServerLogLevel GCDLogLevel = kGCDWebServerLogLevel_Info;
|
|
||||||
#else
|
|
||||||
GCDWebServerLogLevel GCDLogLevel = kGCDWebServerLogLevel_Debug;
|
GCDWebServerLogLevel GCDLogLevel = kGCDWebServerLogLevel_Debug;
|
||||||
|
#else
|
||||||
|
GCDWebServerLogLevel GCDLogLevel = kGCDWebServerLogLevel_Info;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -114,25 +114,25 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
@interface GCDWebServerHandler () {
|
@interface GCDWebServerHandler () {
|
||||||
@private
|
@private
|
||||||
GCDWebServerMatchBlock _matchBlock;
|
GCDWebServerMatchBlock _matchBlock;
|
||||||
GCDWebServerProcessBlock _processBlock;
|
GCDWebServerAsyncProcessBlock _asyncProcessBlock;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GCDWebServerHandler
|
@implementation GCDWebServerHandler
|
||||||
|
|
||||||
@synthesize matchBlock=_matchBlock, processBlock=_processBlock;
|
@synthesize matchBlock=_matchBlock, asyncProcessBlock=_asyncProcessBlock;
|
||||||
|
|
||||||
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_matchBlock = [matchBlock copy];
|
_matchBlock = [matchBlock copy];
|
||||||
_processBlock = [processBlock copy];
|
_asyncProcessBlock = [processBlock copy];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
ARC_RELEASE(_matchBlock);
|
ARC_RELEASE(_matchBlock);
|
||||||
ARC_RELEASE(_processBlock);
|
ARC_RELEASE(_asyncProcessBlock);
|
||||||
|
|
||||||
ARC_DEALLOC(super);
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,6 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
NSMutableArray* _handlers;
|
NSMutableArray* _handlers;
|
||||||
NSInteger _activeConnections; // Accessed through _syncQueue only
|
NSInteger _activeConnections; // Accessed through _syncQueue only
|
||||||
BOOL _connected; // Accessed on main thread only
|
BOOL _connected; // Accessed on main thread only
|
||||||
BOOL _disconnecting; // Accessed on main thread only
|
|
||||||
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
|
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
|
||||||
|
|
||||||
NSDictionary* _options;
|
NSDictionary* _options;
|
||||||
@@ -160,7 +159,8 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
CFTimeInterval _disconnectDelay;
|
CFTimeInterval _disconnectDelay;
|
||||||
NSUInteger _port;
|
NSUInteger _port;
|
||||||
dispatch_source_t _source;
|
dispatch_source_t _source;
|
||||||
CFNetServiceRef _service;
|
CFNetServiceRef _registrationService;
|
||||||
|
CFNetServiceRef _resolutionService;
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
BOOL _suspendInBackground;
|
BOOL _suspendInBackground;
|
||||||
UIBackgroundTaskIdentifier _backgroundTask;
|
UIBackgroundTaskIdentifier _backgroundTask;
|
||||||
@@ -192,23 +192,11 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
GCDWebServerInitializeFunctions();
|
GCDWebServerInitializeFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
|
||||||
DCHECK([NSThread isMainThread]);
|
|
||||||
GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
|
|
||||||
@autoreleasepool {
|
|
||||||
[server _didDisconnect];
|
|
||||||
}
|
|
||||||
server->_disconnecting = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
|
_syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
|
||||||
_sourceSemaphore = dispatch_semaphore_create(0);
|
_sourceSemaphore = dispatch_semaphore_create(0);
|
||||||
_handlers = [[NSMutableArray alloc] init];
|
_handlers = [[NSMutableArray alloc] init];
|
||||||
CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
|
|
||||||
_disconnectTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _DisconnectTimerCallBack, &context);
|
|
||||||
CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes);
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
_backgroundTask = UIBackgroundTaskInvalid;
|
_backgroundTask = UIBackgroundTaskInvalid;
|
||||||
#endif
|
#endif
|
||||||
@@ -220,9 +208,8 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
|||||||
DCHECK(_connected == NO);
|
DCHECK(_connected == NO);
|
||||||
DCHECK(_activeConnections == 0);
|
DCHECK(_activeConnections == 0);
|
||||||
DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
|
DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
|
||||||
|
DCHECK(_disconnectTimer == NULL); // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
|
||||||
|
|
||||||
CFRunLoopTimerInvalidate(_disconnectTimer);
|
|
||||||
CFRelease(_disconnectTimer);
|
|
||||||
ARC_RELEASE(_handlers);
|
ARC_RELEASE(_handlers);
|
||||||
ARC_DISPATCH_RELEASE(_sourceSemaphore);
|
ARC_DISPATCH_RELEASE(_sourceSemaphore);
|
||||||
ARC_DISPATCH_RELEASE(_syncQueue);
|
ARC_DISPATCH_RELEASE(_syncQueue);
|
||||||
@@ -272,9 +259,10 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
|||||||
DCHECK(_activeConnections >= 0);
|
DCHECK(_activeConnections >= 0);
|
||||||
if (_activeConnections == 0) {
|
if (_activeConnections == 0) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
if (_disconnecting) {
|
if (_disconnectTimer) {
|
||||||
CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
|
CFRunLoopTimerInvalidate(_disconnectTimer);
|
||||||
_disconnecting = NO;
|
CFRelease(_disconnectTimer);
|
||||||
|
_disconnectTimer = NULL;
|
||||||
}
|
}
|
||||||
if (_connected == NO) {
|
if (_connected == NO) {
|
||||||
[self _didConnect];
|
[self _didConnect];
|
||||||
@@ -328,8 +316,17 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
|||||||
if (_activeConnections == 0) {
|
if (_activeConnections == 0) {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
if ((_disconnectDelay > 0.0) && (_source != NULL)) {
|
if ((_disconnectDelay > 0.0) && (_source != NULL)) {
|
||||||
CFRunLoopTimerSetNextFireDate(_disconnectTimer, CFAbsoluteTimeGetCurrent() + _disconnectDelay);
|
if (_disconnectTimer) {
|
||||||
_disconnecting = YES;
|
CFRunLoopTimerInvalidate(_disconnectTimer);
|
||||||
|
CFRelease(_disconnectTimer);
|
||||||
|
}
|
||||||
|
_disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + _disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) {
|
||||||
|
DCHECK([NSThread isMainThread]);
|
||||||
|
[self _didDisconnect];
|
||||||
|
CFRelease(_disconnectTimer);
|
||||||
|
_disconnectTimer = NULL;
|
||||||
|
});
|
||||||
|
CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes);
|
||||||
} else {
|
} else {
|
||||||
[self _didDisconnect];
|
[self _didDisconnect];
|
||||||
}
|
}
|
||||||
@@ -339,18 +336,24 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)bonjourName {
|
- (NSString*)bonjourName {
|
||||||
CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL;
|
CFStringRef name = _resolutionService ? CFNetServiceGetName(_resolutionService) : NULL;
|
||||||
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
|
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)bonjourType {
|
- (NSString*)bonjourType {
|
||||||
CFStringRef type = _service ? CFNetServiceGetType(_service) : NULL;
|
CFStringRef type = _resolutionService ? CFNetServiceGetType(_resolutionService) : NULL;
|
||||||
return type && CFStringGetLength(type) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, type)) : nil;
|
return type && CFStringGetLength(type) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, type)) : nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)handlerBlock {
|
- (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 {
|
||||||
DCHECK(_options == nil);
|
DCHECK(_options == nil);
|
||||||
GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock processBlock:handlerBlock];
|
GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock asyncProcessBlock:processBlock];
|
||||||
[_handlers insertObject:handler atIndex:0];
|
[_handlers insertObject:handler atIndex:0];
|
||||||
ARC_RELEASE(handler);
|
ARC_RELEASE(handler);
|
||||||
}
|
}
|
||||||
@@ -360,11 +363,26 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
|||||||
[_handlers removeAllObjects];
|
[_handlers removeAllObjects];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* error, void* info) {
|
static void _NetServiceRegisterCallBack(CFNetServiceRef service, CFStreamError* error, void* info) {
|
||||||
DCHECK([NSThread isMainThread]);
|
DCHECK([NSThread isMainThread]);
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
if (error->error) {
|
if (error->error) {
|
||||||
LOG_ERROR(@"Bonjour error %i (domain %i)", (int)error->error, (int)error->domain);
|
LOG_ERROR(@"Bonjour registration error %i (domain %i)", (int)error->error, (int)error->domain);
|
||||||
|
} else {
|
||||||
|
GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
|
||||||
|
LOG_VERBOSE(@"Bonjour registration complete for %@", [server class]);
|
||||||
|
CFNetServiceResolveWithTimeout(server->_resolutionService, 1.0, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* error, void* info) {
|
||||||
|
DCHECK([NSThread isMainThread]);
|
||||||
|
@autoreleasepool {
|
||||||
|
if (error->error) {
|
||||||
|
if ((error->domain != kCFStreamErrorDomainNetServices) && (error->error != kCFNetServicesErrorTimeout)) {
|
||||||
|
LOG_ERROR(@"Bonjour resolution error %i (domain %i)", (int)error->error, (int)error->domain);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
|
GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
|
||||||
LOG_INFO(@"%@ now reachable at %@", [server class], server.bonjourServerURL);
|
LOG_INFO(@"%@ now reachable at %@", [server class], server.bonjourServerURL);
|
||||||
@@ -392,8 +410,9 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
|
|
||||||
- (BOOL)_start:(NSError**)error {
|
- (BOOL)_start:(NSError**)error {
|
||||||
DCHECK(_source == NULL);
|
DCHECK(_source == NULL);
|
||||||
|
|
||||||
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
NSUInteger port = [_GetOption(_options, GCDWebServerOption_Port, @0) unsignedIntegerValue];
|
||||||
NSString* name = _GetOption(_options, GCDWebServerOption_BonjourName, @"");
|
NSString* bonjourName = _GetOption(_options, GCDWebServerOption_BonjourName, @"");
|
||||||
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
NSString* bonjourType = _GetOption(_options, GCDWebServerOption_BonjourType, @"_http._tcp");
|
||||||
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
|
NSUInteger maxPendingConnections = [_GetOption(_options, GCDWebServerOption_MaxPendingConnections, @16) unsignedIntegerValue];
|
||||||
int listeningSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
int listeningSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
@@ -491,14 +510,21 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
_port = port;
|
_port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name) {
|
if (bonjourName) {
|
||||||
_service = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (ARC_BRIDGE CFStringRef)bonjourType, (ARC_BRIDGE CFStringRef)name, (SInt32)_port);
|
_registrationService = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), (ARC_BRIDGE CFStringRef)bonjourType, (ARC_BRIDGE CFStringRef)(bonjourName.length ? bonjourName : _serverName), (SInt32)_port);
|
||||||
if (_service) {
|
if (_registrationService) {
|
||||||
CFNetServiceClientContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
|
CFNetServiceClientContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
|
||||||
CFNetServiceSetClient(_service, _NetServiceClientCallBack, &context);
|
|
||||||
CFNetServiceScheduleWithRunLoop(_service, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
CFNetServiceSetClient(_registrationService, _NetServiceRegisterCallBack, &context);
|
||||||
|
CFNetServiceScheduleWithRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
CFStreamError streamError = {0};
|
CFStreamError streamError = {0};
|
||||||
CFNetServiceRegisterWithOptions(_service, 0, &streamError);
|
CFNetServiceRegisterWithOptions(_registrationService, 0, &streamError);
|
||||||
|
|
||||||
|
_resolutionService = CFNetServiceCreateCopy(kCFAllocatorDefault, _registrationService);
|
||||||
|
if (_resolutionService) {
|
||||||
|
CFNetServiceSetClient(_resolutionService, _NetServiceResolveCallBack, &context);
|
||||||
|
CFNetServiceScheduleWithRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(@"Failed creating CFNetService");
|
LOG_ERROR(@"Failed creating CFNetService");
|
||||||
}
|
}
|
||||||
@@ -537,11 +563,19 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
- (void)_stop {
|
- (void)_stop {
|
||||||
DCHECK(_source != NULL);
|
DCHECK(_source != NULL);
|
||||||
|
|
||||||
if (_service) {
|
if (_registrationService) {
|
||||||
CFNetServiceUnscheduleFromRunLoop(_service, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
if (_resolutionService) {
|
||||||
CFNetServiceSetClient(_service, NULL, NULL);
|
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
CFRelease(_service);
|
CFNetServiceSetClient(_resolutionService, NULL, NULL);
|
||||||
_service = NULL;
|
CFNetServiceCancel(_resolutionService);
|
||||||
|
CFRelease(_resolutionService);
|
||||||
|
_resolutionService = NULL;
|
||||||
|
}
|
||||||
|
CFNetServiceUnscheduleFromRunLoop(_registrationService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
|
CFNetServiceSetClient(_registrationService, NULL, NULL);
|
||||||
|
CFNetServiceCancel(_registrationService);
|
||||||
|
CFRelease(_registrationService);
|
||||||
|
_registrationService = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_source_cancel(_source);
|
dispatch_source_cancel(_source);
|
||||||
@@ -560,9 +594,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
_authenticationDigestAccounts = nil;
|
_authenticationDigestAccounts = nil;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
if (_disconnecting) {
|
if (_disconnectTimer) {
|
||||||
CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
|
CFRunLoopTimerInvalidate(_disconnectTimer);
|
||||||
_disconnecting = NO;
|
CFRelease(_disconnectTimer);
|
||||||
|
_disconnectTimer = NULL;
|
||||||
[self _didDisconnect];
|
[self _didDisconnect];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -623,7 +658,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isRunning {
|
- (BOOL)isRunning {
|
||||||
return (_source ? YES : NO);
|
return (_options ? YES : NO);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stop {
|
- (void)stop {
|
||||||
@@ -663,13 +698,14 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL*)bonjourServerURL {
|
- (NSURL*)bonjourServerURL {
|
||||||
if (_source && _service) {
|
if (_source && _resolutionService) {
|
||||||
CFStringRef name = CFNetServiceGetName(_service);
|
NSString* name = (ARC_BRIDGE NSString*)CFNetServiceGetTargetHost(_resolutionService);
|
||||||
if (name && CFStringGetLength(name)) {
|
if (name.length) {
|
||||||
|
name = [name substringToIndex:(name.length - 1)]; // Strip trailing period at end of domain
|
||||||
if (_port != 80) {
|
if (_port != 80) {
|
||||||
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@.local:%i/", name, (int)_port]];
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", name, (int)_port]];
|
||||||
} else {
|
} else {
|
||||||
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@.local/", name]];
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", name]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -724,6 +760,12 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
@implementation GCDWebServer (Handlers)
|
@implementation GCDWebServer (Handlers)
|
||||||
|
|
||||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
- (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) {
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
if (![requestMethod isEqualToString:method]) {
|
if (![requestMethod isEqualToString:method]) {
|
||||||
@@ -731,10 +773,16 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
}
|
}
|
||||||
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||||
|
|
||||||
} processBlock:block];
|
} asyncProcessBlock:block];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)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]]) {
|
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
||||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
@@ -746,13 +794,19 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
}
|
}
|
||||||
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||||
|
|
||||||
} processBlock:block];
|
} asyncProcessBlock:block];
|
||||||
} else {
|
} else {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
- (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];
|
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
|
||||||
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
||||||
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
@@ -760,12 +814,25 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
if (![requestMethod isEqualToString:method]) {
|
if (![requestMethod isEqualToString:method]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
if ([expression firstMatchInString:urlPath options:0 range:NSMakeRange(0, urlPath.length)] == nil) {
|
|
||||||
|
NSArray* matches = [expression matchesInString:urlPath options:0 range:NSMakeRange(0, urlPath.length)];
|
||||||
|
if (matches.count == 0) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
|
||||||
|
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 ARC_AUTORELEASE(request);
|
||||||
|
|
||||||
} processBlock:block];
|
} asyncProcessBlock:block];
|
||||||
} else {
|
} else {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -849,17 +916,18 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
|
|
||||||
GCDWebServerResponse* response = nil;
|
GCDWebServerResponse* response = nil;
|
||||||
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
||||||
BOOL isDirectory;
|
NSString* fileType = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL] fileType];
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory]) {
|
if (fileType) {
|
||||||
if (isDirectory) {
|
if ([fileType isEqualToString:NSFileTypeDirectory]) {
|
||||||
if (indexFilename) {
|
if (indexFilename) {
|
||||||
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:indexPath isDirectory:&isDirectory] && !isDirectory) {
|
NSString* indexType = [[[NSFileManager defaultManager] attributesOfItemAtPath:indexPath error:NULL] fileType];
|
||||||
|
if ([indexType isEqualToString:NSFileTypeRegular]) {
|
||||||
return [GCDWebServerFileResponse responseWithFile:indexPath];
|
return [GCDWebServerFileResponse responseWithFile:indexPath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = [server _responseWithContentsOfDirectory:filePath];
|
response = [server _responseWithContentsOfDirectory:filePath];
|
||||||
} else {
|
} else if ([fileType isEqualToString:NSFileTypeRegular]) {
|
||||||
if (allowRangeRequests) {
|
if (allowRangeRequests) {
|
||||||
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
||||||
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||||
@@ -1070,7 +1138,7 @@ static void _LogResult(NSString* format, ...) {
|
|||||||
_LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
|
_LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
|
||||||
success = NO;
|
success = NO;
|
||||||
#if !TARGET_OS_IPHONE
|
#if !TARGET_OS_IPHONE
|
||||||
#ifndef NDEBUG
|
#if DEBUG
|
||||||
if (GCDWebServerIsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
|
if (GCDWebServerIsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
|
||||||
NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||||
NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||||
|
|||||||
@@ -138,13 +138,14 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Assuming a valid HTTP request was received and -preflightRequest: returned nil,
|
* Assuming a valid HTTP request was received and -preflightRequest: returned nil,
|
||||||
* this method is called to process the request.
|
* this method is called to process the request by executing the handler's
|
||||||
|
* process block.
|
||||||
*/
|
*/
|
||||||
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block;
|
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assuming a valid HTTP request was received and either -preflightRequest:
|
* Assuming a valid HTTP request was received and either -preflightRequest:
|
||||||
* or -processRequest:withBlock: returned a non-nil GCDWebServerResponse,
|
* or -processRequest:completion: returned a non-nil GCDWebServerResponse,
|
||||||
* this method is called to override the response.
|
* this method is called to override the response.
|
||||||
*
|
*
|
||||||
* You can either modify the current response and return it, or return a
|
* You can either modify the current response and return it, or return a
|
||||||
|
|||||||
@@ -373,15 +373,24 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (ARC_BRIDGE CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (ARC_BRIDGE CFStringRef)GCDWebServerFormatRFC822([NSDate date]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)_startProcessingRequest {
|
||||||
|
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
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
- (void)_processRequest {
|
- (void)_finishProcessingRequest:(GCDWebServerResponse*)response {
|
||||||
DCHECK(_responseMessage == NULL);
|
DCHECK(_responseMessage == NULL);
|
||||||
BOOL hasBody = NO;
|
BOOL hasBody = NO;
|
||||||
|
|
||||||
GCDWebServerResponse* response = [self preflightRequest:_request];
|
|
||||||
if (!response) {
|
|
||||||
response = [self processRequest:_request withBlock:_handler.processBlock];
|
|
||||||
}
|
|
||||||
if (response) {
|
if (response) {
|
||||||
response = [self overrideResponse:response forRequest:_request];
|
response = [self overrideResponse:response forRequest:_request];
|
||||||
}
|
}
|
||||||
@@ -471,7 +480,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
|
|
||||||
NSError* localError = nil;
|
NSError* localError = nil;
|
||||||
if ([_request performClose:&localError]) {
|
if ([_request performClose:&localError]) {
|
||||||
[self _processRequest];
|
[self _startProcessingRequest];
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
@@ -480,7 +489,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
if ([_request performClose:&error]) {
|
if ([_request performClose:&error]) {
|
||||||
[self _processRequest];
|
[self _startProcessingRequest];
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
@@ -501,7 +510,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
|
|
||||||
NSError* localError = nil;
|
NSError* localError = nil;
|
||||||
if ([_request performClose:&localError]) {
|
if ([_request performClose:&localError]) {
|
||||||
[self _processRequest];
|
[self _startProcessingRequest];
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
LOG_ERROR(@"Failed closing request body for socket %i: %@", _socket, error);
|
||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
|
||||||
@@ -572,7 +581,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
|||||||
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
|
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
[self _processRequest];
|
[self _startProcessingRequest];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
|
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
|
||||||
@@ -772,16 +781,14 @@ static NSString* _StringFromAddressData(NSData* data) {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
|
- (void)processRequest:(GCDWebServerRequest*)request completion:(GCDWebServerCompletionBlock)completion {
|
||||||
LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
LOG_DEBUG(@"Connection on socket %i processing request \"%@ %@\" with %lu bytes body", _socket, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead);
|
||||||
GCDWebServerResponse* response = nil;
|
|
||||||
@try {
|
@try {
|
||||||
response = block(request);
|
_handler.asyncProcessBlock(request, completion);
|
||||||
}
|
}
|
||||||
@catch (NSException* exception) {
|
@catch (NSException* exception) {
|
||||||
LOG_EXCEPTION(exception);
|
LOG_EXCEPTION(exception);
|
||||||
}
|
}
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
|
||||||
|
|||||||
@@ -204,10 +204,13 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||||
|
NSString* unescapedKey = key ? GCDWebServerUnescapeURLString(key) : nil;
|
||||||
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||||
if (key && value) {
|
NSString* unescapedValue = value ? GCDWebServerUnescapeURLString(value) : nil;
|
||||||
[parameters setObject:GCDWebServerUnescapeURLString(value) forKey:GCDWebServerUnescapeURLString(key)];
|
if (unescapedKey && unescapedValue) {
|
||||||
|
[parameters setObject:unescapedValue forKey:unescapedKey];
|
||||||
} else {
|
} else {
|
||||||
|
LOG_WARNING(@"Failed parsing URL encoded form for key \"%@\" and value \"%@\"", key, value);
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,7 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <TargetConditionals.h>
|
#import <os/object.h>
|
||||||
#import <AvailabilityMacros.h>
|
|
||||||
|
|
||||||
#if __has_feature(objc_arc)
|
#if __has_feature(objc_arc)
|
||||||
#define ARC_BRIDGE __bridge
|
#define ARC_BRIDGE __bridge
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
#define ARC_RELEASE(__OBJECT__)
|
#define ARC_RELEASE(__OBJECT__)
|
||||||
#define ARC_AUTORELEASE(__OBJECT__) __OBJECT__
|
#define ARC_AUTORELEASE(__OBJECT__) __OBJECT__
|
||||||
#define ARC_DEALLOC(__OBJECT__)
|
#define ARC_DEALLOC(__OBJECT__)
|
||||||
#if (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0)) || (!TARGET_OS_IPHONE && (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8))
|
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
|
||||||
#define ARC_DISPATCH_RETAIN(__OBJECT__)
|
#define ARC_DISPATCH_RETAIN(__OBJECT__)
|
||||||
#define ARC_DISPATCH_RELEASE(__OBJECT__)
|
#define ARC_DISPATCH_RELEASE(__OBJECT__)
|
||||||
#else
|
#else
|
||||||
@@ -85,13 +84,7 @@ extern void GCDLogMessage(GCDWebServerLogLevel level, NSString* format, ...) NS_
|
|||||||
#define LOG_ERROR(...) do { if (GCDLogLevel <= kGCDWebServerLogLevel_Error) GCDLogMessage(kGCDWebServerLogLevel_Error, __VA_ARGS__); } while (0)
|
#define LOG_ERROR(...) do { if (GCDLogLevel <= kGCDWebServerLogLevel_Error) GCDLogMessage(kGCDWebServerLogLevel_Error, __VA_ARGS__); } while (0)
|
||||||
#define LOG_EXCEPTION(__EXCEPTION__) do { if (GCDLogLevel <= kGCDWebServerLogLevel_Exception) GCDLogMessage(kGCDWebServerLogLevel_Exception, @"%@", __EXCEPTION__); } while (0)
|
#define LOG_EXCEPTION(__EXCEPTION__) do { if (GCDLogLevel <= kGCDWebServerLogLevel_Exception) GCDLogMessage(kGCDWebServerLogLevel_Exception, @"%@", __EXCEPTION__); } while (0)
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#if DEBUG
|
||||||
|
|
||||||
#define DCHECK(__CONDITION__)
|
|
||||||
#define DNOT_REACHED()
|
|
||||||
#define LOG_DEBUG(...)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define DCHECK(__CONDITION__) \
|
#define DCHECK(__CONDITION__) \
|
||||||
do { \
|
do { \
|
||||||
@@ -102,6 +95,12 @@ extern void GCDLogMessage(GCDWebServerLogLevel level, NSString* format, ...) NS_
|
|||||||
#define DNOT_REACHED() abort()
|
#define DNOT_REACHED() abort()
|
||||||
#define LOG_DEBUG(...) do { if (GCDLogLevel <= kGCDWebServerLogLevel_Debug) GCDLogMessage(kGCDWebServerLogLevel_Debug, __VA_ARGS__); } while (0)
|
#define LOG_DEBUG(...) do { if (GCDLogLevel <= kGCDWebServerLogLevel_Debug) GCDLogMessage(kGCDWebServerLogLevel_Debug, __VA_ARGS__); } while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define DCHECK(__CONDITION__)
|
||||||
|
#define DNOT_REACHED()
|
||||||
|
#define LOG_DEBUG(...)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -144,8 +143,7 @@ extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_F
|
|||||||
|
|
||||||
@interface GCDWebServerHandler : NSObject
|
@interface GCDWebServerHandler : NSObject
|
||||||
@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock;
|
@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock;
|
||||||
@property(nonatomic, readonly) GCDWebServerProcessBlock processBlock;
|
@property(nonatomic, readonly) GCDWebServerAsyncProcessBlock asyncProcessBlock;
|
||||||
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerRequest ()
|
@interface GCDWebServerRequest ()
|
||||||
@@ -154,6 +152,7 @@ extern NSString* GCDWebServerComputeMD5Digest(NSString* format, ...) NS_FORMAT_F
|
|||||||
- (BOOL)performOpen:(NSError**)error;
|
- (BOOL)performOpen:(NSError**)error;
|
||||||
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
|
- (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
|
||||||
- (BOOL)performClose:(NSError**)error;
|
- (BOOL)performClose:(NSError**)error;
|
||||||
|
- (void)setAttribute:(id)attribute forKey:(NSString*)key;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerResponse ()
|
@interface GCDWebServerResponse ()
|
||||||
|
|||||||
@@ -27,6 +27,15 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* This protocol is used by the GCDWebServerConnection to communicate with
|
||||||
* the GCDWebServerRequest and write the received HTTP body data.
|
* the GCDWebServerRequest and write the received HTTP body data.
|
||||||
@@ -163,4 +172,11 @@
|
|||||||
*/
|
*/
|
||||||
- (BOOL)hasByteRange;
|
- (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
|
@end
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#import "GCDWebServerPrivate.h"
|
#import "GCDWebServerPrivate.h"
|
||||||
|
|
||||||
|
NSString* const GCDWebServerRequestAttribute_RegexCaptures = @"GCDWebServerRequestAttribute_RegexCaptures";
|
||||||
|
|
||||||
#define kZlibErrorDomain @"ZlibErrorDomain"
|
#define kZlibErrorDomain @"ZlibErrorDomain"
|
||||||
#define kGZipInitialBufferSize (256 * 1024)
|
#define kGZipInitialBufferSize (256 * 1024)
|
||||||
|
|
||||||
@@ -152,6 +154,7 @@
|
|||||||
|
|
||||||
BOOL _opened;
|
BOOL _opened;
|
||||||
NSMutableArray* _decoders;
|
NSMutableArray* _decoders;
|
||||||
|
NSMutableDictionary* _attributes;
|
||||||
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
id<GCDWebServerBodyWriter> __unsafe_unretained _writer;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
@@ -238,6 +241,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
_decoders = [[NSMutableArray alloc] init];
|
_decoders = [[NSMutableArray alloc] init];
|
||||||
|
_attributes = [[NSMutableDictionary alloc] init];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -252,6 +256,7 @@
|
|||||||
ARC_RELEASE(_modifiedSince);
|
ARC_RELEASE(_modifiedSince);
|
||||||
ARC_RELEASE(_noneMatch);
|
ARC_RELEASE(_noneMatch);
|
||||||
ARC_RELEASE(_decoders);
|
ARC_RELEASE(_decoders);
|
||||||
|
ARC_RELEASE(_attributes);
|
||||||
|
|
||||||
ARC_DEALLOC(super);
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
@@ -264,6 +269,10 @@
|
|||||||
return GCDWebServerIsValidByteRange(_range);
|
return GCDWebServerIsValidByteRange(_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id)attributeForKey:(NSString*)key {
|
||||||
|
return [_attributes objectForKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)open:(NSError**)error {
|
- (BOOL)open:(NSError**)error {
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
@@ -307,6 +316,10 @@
|
|||||||
return [_writer close:error];
|
return [_writer close:error];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setAttribute:(id)attribute forKey:(NSString*)key {
|
||||||
|
[_attributes setValue:attribute forKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
- (NSString*)description {
|
- (NSString*)description {
|
||||||
NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path];
|
NSMutableString* description = [NSMutableString stringWithFormat:@"%@ %@", _method, _path];
|
||||||
for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
for (NSString* argument in [[_query allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
|
||||||
|
|||||||
@@ -28,11 +28,11 @@
|
|||||||
#import "GCDWebServerStreamedResponse.h"
|
#import "GCDWebServerStreamedResponse.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GCDWebServerStreamingBlock is called to stream the data for the HTTP body.
|
* 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
|
* The block must return empty NSData when done or nil on error and set the
|
||||||
* "error" argument which is guaranteed to be non-NULL.
|
* "error" argument which is guaranteed to be non-NULL.
|
||||||
*/
|
*/
|
||||||
typedef NSData* (^GCDWebServerStreamingBlock)(NSError** error);
|
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams
|
* The GCDWebServerStreamedResponse subclass of GCDWebServerResponse streams
|
||||||
@@ -43,11 +43,11 @@ typedef NSData* (^GCDWebServerStreamingBlock)(NSError** error);
|
|||||||
/**
|
/**
|
||||||
* Creates a response with streamed data and a given content type.
|
* Creates a response with streamed data and a given content type.
|
||||||
*/
|
*/
|
||||||
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block;
|
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is the designated initializer for the class.
|
* This method is the designated initializer for the class.
|
||||||
*/
|
*/
|
||||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block;
|
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -29,17 +29,17 @@
|
|||||||
|
|
||||||
@interface GCDWebServerStreamedResponse () {
|
@interface GCDWebServerStreamedResponse () {
|
||||||
@private
|
@private
|
||||||
GCDWebServerStreamingBlock _block;
|
GCDWebServerStreamBlock _block;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GCDWebServerStreamedResponse
|
@implementation GCDWebServerStreamedResponse
|
||||||
|
|
||||||
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block {
|
+ (instancetype)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||||
return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]);
|
return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamingBlock)block {
|
- (instancetype)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_block = [block copy];
|
_block = [block copy];
|
||||||
|
|
||||||
|
|||||||
@@ -322,6 +322,9 @@
|
|||||||
NSString* title = server.title;
|
NSString* title = server.title;
|
||||||
if (title == nil) {
|
if (title == nil) {
|
||||||
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
|
||||||
|
if (title == nil) {
|
||||||
|
title = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
||||||
|
}
|
||||||
#if !TARGET_OS_IPHONE
|
#if !TARGET_OS_IPHONE
|
||||||
if (title == nil) {
|
if (title == nil) {
|
||||||
title = [[NSProcessInfo processInfo] processName];
|
title = [[NSProcessInfo processInfo] processName];
|
||||||
|
|||||||
25
Mac/main.m
25
Mac/main.m
@@ -52,6 +52,7 @@ typedef enum {
|
|||||||
kMode_WebDAV,
|
kMode_WebDAV,
|
||||||
kMode_WebUploader,
|
kMode_WebUploader,
|
||||||
kMode_StreamingResponse,
|
kMode_StreamingResponse,
|
||||||
|
kMode_AsyncResponse
|
||||||
} Mode;
|
} Mode;
|
||||||
|
|
||||||
@interface Delegate : NSObject <GCDWebServerDelegate, GCDWebDAVServerDelegate, GCDWebUploaderDelegate>
|
@interface Delegate : NSObject <GCDWebServerDelegate, GCDWebDAVServerDelegate, GCDWebUploaderDelegate>
|
||||||
@@ -142,7 +143,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
NSString* authenticationPassword = nil;
|
NSString* authenticationPassword = nil;
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password]\n\n", basename((char*)argv[0]));
|
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password]\n\n", basename((char*)argv[0]));
|
||||||
} else {
|
} else {
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
if (argv[i][0] != '-') {
|
if (argv[i][0] != '-') {
|
||||||
@@ -164,6 +165,8 @@ int main(int argc, const char* argv[]) {
|
|||||||
mode = kMode_WebUploader;
|
mode = kMode_WebUploader;
|
||||||
} else if (!strcmp(argv[i], "streamingResponse")) {
|
} else if (!strcmp(argv[i], "streamingResponse")) {
|
||||||
mode = kMode_StreamingResponse;
|
mode = kMode_StreamingResponse;
|
||||||
|
} else if (!strcmp(argv[i], "asyncResponse")) {
|
||||||
|
mode = kMode_AsyncResponse;
|
||||||
}
|
}
|
||||||
} else if (!strcmp(argv[i], "-record")) {
|
} else if (!strcmp(argv[i], "-record")) {
|
||||||
recording = YES;
|
recording = YES;
|
||||||
@@ -328,6 +331,24 @@ int main(int argc, const char* argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test async responses
|
||||||
|
case kMode_AsyncResponse: {
|
||||||
|
fprintf(stdout, "Running in Async Response mode");
|
||||||
|
webServer = [[GCDWebServer alloc] init];
|
||||||
|
[webServer addHandlerForMethod:@"GET"
|
||||||
|
path:@"/"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[@"Hello World!" dataUsingEncoding:NSUTF8StringEncoding] contentType:@"text/plain"];
|
||||||
|
completionBlock(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
}];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#if __has_feature(objc_arc)
|
#if __has_feature(objc_arc)
|
||||||
fprintf(stdout, " (ARC is ON)\n");
|
fprintf(stdout, " (ARC is ON)\n");
|
||||||
@@ -338,7 +359,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
if (webServer) {
|
if (webServer) {
|
||||||
Delegate* delegate = [[Delegate alloc] init];
|
Delegate* delegate = [[Delegate alloc] init];
|
||||||
if (testDirectory) {
|
if (testDirectory) {
|
||||||
#ifndef NDEBUG
|
#if DEBUG
|
||||||
webServer.delegate = delegate;
|
webServer.delegate = delegate;
|
||||||
#endif
|
#endif
|
||||||
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
|
fprintf(stdout, "<RUNNING TESTS FROM \"%s\">\n\n", [testDirectory UTF8String]);
|
||||||
|
|||||||
51
README.md
51
README.md
@@ -13,6 +13,7 @@ GCDWebServer is a modern and lightweight GCD based HTTP 1.1 server designed to b
|
|||||||
* Available under a friendly [New BSD License](LICENSE)
|
* Available under a friendly [New BSD License](LICENSE)
|
||||||
|
|
||||||
Extra built-in features:
|
Extra built-in features:
|
||||||
|
* Allow implementation of fully asynchronous handlers of incoming HTTP requests
|
||||||
* Minimize memory usage with disk streaming of large HTTP request or response bodies
|
* Minimize memory usage with disk streaming of large HTTP request or response bodies
|
||||||
* Parser for [web forms](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4) submitted using "application/x-www-form-urlencoded" or "multipart/form-data" encodings (including file uploads)
|
* Parser for [web forms](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4) submitted using "application/x-www-form-urlencoded" or "multipart/form-data" encodings (including file uploads)
|
||||||
* [JSON](http://www.json.org/) parsing and serialization for request and response HTTP bodies
|
* [JSON](http://www.json.org/) parsing and serialization for request and response HTTP bodies
|
||||||
@@ -41,15 +42,15 @@ Download or check out the [latest release](https://github.com/swisspol/GCDWebSer
|
|||||||
|
|
||||||
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile:
|
Alternatively, you can install GCDWebServer using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile:
|
||||||
```
|
```
|
||||||
pod "GCDWebServer", "~> 2.0"
|
pod "GCDWebServer", "~> 3.0"
|
||||||
```
|
```
|
||||||
If you want to use GCDWebUploader, use this line instead:
|
If you want to use GCDWebUploader, use this line instead:
|
||||||
```
|
```
|
||||||
pod "GCDWebServer/WebUploader", "~> 2.0"
|
pod "GCDWebServer/WebUploader", "~> 3.0"
|
||||||
```
|
```
|
||||||
Or this line for GCDWebDAVServer:
|
Or this line for GCDWebDAVServer:
|
||||||
```
|
```
|
||||||
pod "GCDWebServer/WebDAV", "~> 2.0"
|
pod "GCDWebServer/WebDAV", "~> 3.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Hello World
|
Hello World
|
||||||
@@ -146,6 +147,38 @@ println("Visit \(webServer.serverURL) in your web browser")
|
|||||||
#import "GCDWebServerDataResponse.h"
|
#import "GCDWebServerDataResponse.h"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Asynchronous HTTP Responses
|
||||||
|
===========================
|
||||||
|
|
||||||
|
New in GCDWebServer 3.0 is the ability to process HTTP requests aysnchronously i.e. add handlers to the server that generate their ```GCDWebServerResponse``` asynchronously. This is achieved by adding handlers that use a ```GCDWebServerAsyncProcessBlock``` instead of a ```GCDWebServerProcessBlock```. Here's an example:
|
||||||
|
|
||||||
|
***Synchronous version***
|
||||||
|
```objectivec
|
||||||
|
[webServer addDefaultHandlerForMethod:@"GET"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
|
***Asynchronous version***
|
||||||
|
```objectivec
|
||||||
|
[webServer addDefaultHandlerForMethod:@"GET"
|
||||||
|
requestClass:[GCDWebServerRequest class]
|
||||||
|
asyncProcessBlock:^(GCDWebServerRequest* request, GCDWebServerCompletionBlock completionBlock) {
|
||||||
|
|
||||||
|
// Do some async operation like network access or file I/O (simulated here using dispatch_after())
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithHTML:@"<html><body><p>Hello World</p></body></html>"];
|
||||||
|
completionBlock(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
}];
|
||||||
|
```
|
||||||
|
|
||||||
Web Based Uploads in iOS Apps
|
Web Based Uploads in iOS Apps
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
@@ -249,9 +282,9 @@ GCDWebServer relies on "handlers" to process incoming web requests and generatin
|
|||||||
|
|
||||||
Handlers require 2 GCD blocks:
|
Handlers require 2 GCD blocks:
|
||||||
* The ```GCDWebServerMatchBlock``` is called on every handler added to the ```GCDWebServer``` instance whenever a web request has started (i.e. HTTP headers have been received). It is passed the basic info for the web request (HTTP method, URL, headers...) and must decide if it wants to handle it or not. If yes, it must return a new ```GCDWebServerRequest``` instance (see above) created with this info. Otherwise, it simply returns nil.
|
* The ```GCDWebServerMatchBlock``` is called on every handler added to the ```GCDWebServer``` instance whenever a web request has started (i.e. HTTP headers have been received). It is passed the basic info for the web request (HTTP method, URL, headers...) and must decide if it wants to handle it or not. If yes, it must return a new ```GCDWebServerRequest``` instance (see above) created with this info. Otherwise, it simply returns nil.
|
||||||
* The ```GCDWebServerProcessBlock``` is called after the web request has been fully received and is passed the ```GCDWebServerRequest``` instance created at the previous step. It must return a ```GCDWebServerResponse``` instance (see above) or nil on error, which will result in a 500 HTTP status code returned to the client. It's however recommended to return an instance of [GCDWebServerErrorResponse](GCDWebServer/Responses/GCDWebServerErrorResponse.h) on error so more useful information can be returned to the client.
|
* The ```GCDWebServerProcessBlock``` or ```GCDWebServerAsyncProcessBlock``` is called after the web request has been fully received and is passed the ```GCDWebServerRequest``` instance created at the previous step. It must return synchronously (if using ```GCDWebServerProcessBlock```) or asynchronously (if using ```GCDWebServerAsyncProcessBlock```) a ```GCDWebServerResponse``` instance (see above) or nil on error, which will result in a 500 HTTP status code returned to the client. It's however recommended to return an instance of [GCDWebServerErrorResponse](GCDWebServer/Responses/GCDWebServerErrorResponse.h) on error so more useful information can be returned to the client.
|
||||||
|
|
||||||
Note that most methods on ```GCDWebServer``` to add handlers only require the ```GCDWebServerProcessBlock``` as they already provide a built-in ```GCDWebServerMatchBlock``` e.g. to match a URL path with a Regex.
|
Note that most methods on ```GCDWebServer``` to add handlers only require the ```GCDWebServerProcessBlock``` or ```GCDWebServerAsyncProcessBlock``` as they already provide a built-in ```GCDWebServerMatchBlock``` e.g. to match a URL path with a Regex.
|
||||||
|
|
||||||
GCDWebServer & Background Mode for iOS Apps
|
GCDWebServer & Background Mode for iOS Apps
|
||||||
===========================================
|
===========================================
|
||||||
@@ -270,20 +303,20 @@ HTTP connections are often initiated in batches (or bursts), for instance when l
|
|||||||
Debug Builds & Custom Logging
|
Debug Builds & Custom Logging
|
||||||
=============================
|
=============================
|
||||||
|
|
||||||
When building GCDWebServer in "Debug" mode versus "Release" mode, GCDWebServer logs a lot more information and also performs a number of internal consistency checks. To disable this behavior, make sure to define the preprocessor constant ```NDEBUG``` when compiling GCDWebServer. In Xcode target settings, this can be done by adding ```NDEBUG``` to the build setting ```GCC_PREPROCESSOR_DEFINITIONS``` when building in Release configuration (this is done automatically for you if you use CocoaPods).
|
When building GCDWebServer in "Debug" mode versus "Release" mode, GCDWebServer logs a lot more information and also performs a number of internal consistency checks. To enable this behavior, define the preprocessor constant ```DEBUG=1``` when compiling GCDWebServer. In Xcode target settings, this can be done by adding ```DEBUG=1``` to the build setting ```GCC_PREPROCESSOR_DEFINITIONS``` when building in Debug configuration.
|
||||||
|
|
||||||
It's also possible to replace the logging system used by GCDWebServer by a custom one. Simply define the preprocessor constant ```__GCDWEBSERVER_LOGGING_HEADER__``` to the name of a header file (e.g. "MyLogging.h") that defines these macros:
|
It's also possible to replace the logging system used by GCDWebServer by a custom one. Simply define the preprocessor constant ```__GCDWEBSERVER_LOGGING_HEADER__``` to the name of a header file (e.g. "MyLogging.h") that defines these macros:
|
||||||
|
|
||||||
```
|
```
|
||||||
#define LOG_DEBUG(...) // Should not do anything if NDEBUG is defined
|
#define LOG_DEBUG(...) // Should not do anything unless the preprocessor constant DEBUG is non-zero
|
||||||
#define LOG_VERBOSE(...)
|
#define LOG_VERBOSE(...)
|
||||||
#define LOG_INFO(...)
|
#define LOG_INFO(...)
|
||||||
#define LOG_WARNING(...)
|
#define LOG_WARNING(...)
|
||||||
#define LOG_ERROR(...)
|
#define LOG_ERROR(...)
|
||||||
#define LOG_EXCEPTION(__EXCEPTION__)
|
#define LOG_EXCEPTION(__EXCEPTION__)
|
||||||
|
|
||||||
#define DCHECK(__CONDITION__) // Should not do anything if NDEBUG is defined or abort if __CONDITION__ is false
|
#define DCHECK(__CONDITION__) // Should not do anything unless the preprocessor constant DEBUG is non-zero
|
||||||
#define DNOT_REACHED() // Should not do anything if NDEBUG is defined
|
#define DNOT_REACHED() // Should not do anything unless the preprocessor constant DEBUG is non-zero
|
||||||
```
|
```
|
||||||
|
|
||||||
Advanced Example 1: Implementing HTTP Redirects
|
Advanced Example 1: Implementing HTTP Redirects
|
||||||
|
|||||||
24
Run-Tests.sh
24
Run-Tests.sh
@@ -32,19 +32,35 @@ function runTests {
|
|||||||
logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3"
|
logLevel=2 $1 -mode "$2" -root "$PAYLOAD_DIR/Payload" -tests "$3"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build for iOS in manual memory management mode (TODO: run tests on iOS)
|
# Build for iOS in manual memory management mode and for oldest deployment target (TODO: run tests on iOS)
|
||||||
|
rm -rf "$MRC_BUILD_DIR"
|
||||||
|
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" "IPHONEOS_DEPLOYMENT_TARGET=5.1.1" > /dev/null
|
||||||
|
|
||||||
|
# Build for iOS in manual memory management mode and for default deployment target (TODO: run tests on iOS)
|
||||||
rm -rf "$MRC_BUILD_DIR"
|
rm -rf "$MRC_BUILD_DIR"
|
||||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
|
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
|
||||||
|
|
||||||
# Build for iOS in ARC mode (TODO: run tests on iOS)
|
# Build for iOS in ARC mode and for oldest deployment target (TODO: run tests on iOS)
|
||||||
|
rm -rf "$ARC_BUILD_DIR"
|
||||||
|
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" "IPHONEOS_DEPLOYMENT_TARGET=5.1.1" > /dev/null
|
||||||
|
|
||||||
|
# Build for iOS in ARC mode and for default deployment target (TODO: run tests on iOS)
|
||||||
rm -rf "$ARC_BUILD_DIR"
|
rm -rf "$ARC_BUILD_DIR"
|
||||||
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
|
xcodebuild -sdk "$IOS_SDK" -target "$IOS_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
|
||||||
|
|
||||||
# Build for OS X in manual memory management mode
|
# Build for OS X in manual memory management mode and for oldest deployment target
|
||||||
|
rm -rf "$MRC_BUILD_DIR"
|
||||||
|
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null
|
||||||
|
|
||||||
|
# Build for OS X in manual memory management mode and for default deployment target
|
||||||
rm -rf "$MRC_BUILD_DIR"
|
rm -rf "$MRC_BUILD_DIR"
|
||||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
|
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$MRC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=NO" > /dev/null
|
||||||
|
|
||||||
# Build for OS X in ARC mode
|
# Build for OS X in ARC mode and for oldest deployment target
|
||||||
|
rm -rf "$ARC_BUILD_DIR"
|
||||||
|
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" "MACOSX_DEPLOYMENT_TARGET=10.7" > /dev/null
|
||||||
|
|
||||||
|
# Build for OS X in ARC mode and for default deployment target
|
||||||
rm -rf "$ARC_BUILD_DIR"
|
rm -rf "$ARC_BUILD_DIR"
|
||||||
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
|
xcodebuild -sdk "$OSX_SDK" -target "$OSX_TARGET" -configuration "$CONFIGURATION" build "SYMROOT=$ARC_BUILD_DIR" "CLANG_ENABLE_OBJC_ARC=YES" > /dev/null
|
||||||
|
|
||||||
|
|||||||
@@ -30,15 +30,12 @@
|
|||||||
|
|
||||||
@interface AppDelegate () <GCDWebUploaderDelegate> {
|
@interface AppDelegate () <GCDWebUploaderDelegate> {
|
||||||
@private
|
@private
|
||||||
UIWindow* _window;
|
|
||||||
GCDWebUploader* _webServer;
|
GCDWebUploader* _webServer;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
@synthesize window=_window;
|
|
||||||
|
|
||||||
#if !__has_feature(objc_arc)
|
#if !__has_feature(objc_arc)
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
@@ -50,7 +47,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||||
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
CGRect bounds = ([UIScreen instancesRespondToSelector:@selector(nativeBounds)] ? [[UIScreen mainScreen] nativeBounds] : [[UIScreen mainScreen] bounds]);
|
||||||
|
_window = [[UIWindow alloc] initWithFrame:bounds];
|
||||||
_window.backgroundColor = [UIColor whiteColor];
|
_window.backgroundColor = [UIColor whiteColor];
|
||||||
[_window makeKeyAndVisible];
|
[_window makeKeyAndVisible];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user