mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-02-11 00:00:07 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e9fe4c4c1 | ||
|
|
95bccff2f7 | ||
|
|
780a608d6c | ||
|
|
18d93bbf47 | ||
|
|
b35ebd7d58 | ||
|
|
a11b047233 | ||
|
|
4eac9d4f8e | ||
|
|
d1e2a1a12f | ||
|
|
54d5abd3a8 | ||
|
|
a9fee8d7e2 | ||
|
|
6b15bdaa4e | ||
|
|
3771cf4e92 |
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'GCDWebServer'
|
s.name = 'GCDWebServer'
|
||||||
s.version = '2.5.2'
|
s.version = '3.0'
|
||||||
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'
|
||||||
|
|||||||
@@ -370,7 +370,7 @@
|
|||||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 0600;
|
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 +490,6 @@
|
|||||||
1DEB928608733DD80010E9CD /* Debug */ = {
|
1DEB928608733DD80010E9CD /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD)";
|
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
};
|
};
|
||||||
@@ -499,7 +498,6 @@
|
|||||||
1DEB928708733DD80010E9CD /* Release */ = {
|
1DEB928708733DD80010E9CD /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD)";
|
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
};
|
};
|
||||||
@@ -548,7 +546,6 @@
|
|||||||
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;
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
@@ -561,7 +558,6 @@
|
|||||||
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;
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
|
|||||||
@@ -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).
|
||||||
*
|
*
|
||||||
@@ -289,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.
|
||||||
@@ -298,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.
|
||||||
*
|
*
|
||||||
@@ -392,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)
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -193,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
|
||||||
@@ -221,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);
|
||||||
@@ -273,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];
|
||||||
@@ -329,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];
|
||||||
}
|
}
|
||||||
@@ -349,9 +345,15 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@@ -592,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];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -757,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]) {
|
||||||
@@ -764,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) {
|
||||||
|
|
||||||
@@ -779,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) {
|
||||||
@@ -793,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,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 ()
|
||||||
@@ -153,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];
|
||||||
|
|
||||||
|
|||||||
23
Mac/main.m
23
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");
|
||||||
|
|||||||
43
README.md
43
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
|
||||||
===========================================
|
===========================================
|
||||||
|
|||||||
Reference in New Issue
Block a user