2012-12-30 14:23:49 +08:00
/ *
Copyright ( c ) 2012 -2013 , Pierre - Olivier Latour
All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions are met :
* Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
* Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
2013-12-30 11:02:01 +08:00
* The name of Pierre - Olivier Latour may not be used to endorse
or promote products derived from this software without specific
prior written permission .
2012-12-30 14:23:49 +08:00
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2013-12-30 11:02:01 +08:00
DISCLAIMED . IN NO EVENT SHALL PIERRE - OLIVIER LATOUR BE LIABLE FOR ANY
2012-12-30 14:23:49 +08:00
DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
2012-12-31 02:00:41 +08:00
# import < TargetConditionals . h >
# if TARGET_OS _IPHONE
# import < MobileCoreServices / MobileCoreServices . h >
# endif
2012-12-30 14:23:49 +08:00
# import < netinet / in . h >
# import "GCDWebServerPrivate.h"
2013-04-02 06:42:16 +08:00
# define kMaxPendingConnections 16
2012-12-30 14:23:49 +08:00
static BOOL _run ;
NSString * GCDWebServerGetMimeTypeForExtension ( NSString * extension ) {
static NSDictionary * _overrides = nil ;
if ( _overrides = = nil ) {
_overrides = [ [ NSDictionary alloc ] initWithObjectsAndKeys :
@ "text/css" , @ "css" ,
nil ] ;
}
NSString * mimeType = nil ;
extension = [ extension lowercaseString ] ;
if ( extension . length ) {
mimeType = [ _overrides objectForKey : extension ] ;
if ( mimeType = = nil ) {
2014-01-09 14:27:15 +08:00
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag ( kUTTagClassFilenameExtension , ( ARC_BRIDGE CFStringRef ) extension , NULL ) ;
2012-12-30 14:23:49 +08:00
if ( uti ) {
2014-01-09 14:27:15 +08:00
mimeType = ARC_BRIDGE _RELEASE ( UTTypeCopyPreferredTagWithClass ( uti , kUTTagClassMIMEType ) ) ;
2012-12-30 14:23:49 +08:00
CFRelease ( uti ) ;
}
}
}
return mimeType ;
}
2012-12-31 01:42:19 +08:00
NSString * GCDWebServerUnescapeURLString ( NSString * string ) {
2014-01-09 14:27:15 +08:00
return ARC_BRIDGE _RELEASE ( CFURLCreateStringByReplacingPercentEscapesUsingEncoding ( kCFAllocatorDefault , ( CFStringRef ) string , CFSTR ( "" ) ,
kCFStringEncodingUTF8 ) ) ;
2012-12-30 14:23:49 +08:00
}
NSDictionary * GCDWebServerParseURLEncodedForm ( NSString * form ) {
NSMutableDictionary * parameters = [ NSMutableDictionary dictionary ] ;
NSScanner * scanner = [ [ NSScanner alloc ] initWithString : form ] ;
[ scanner setCharactersToBeSkipped : nil ] ;
while ( 1 ) {
NSString * key = nil ;
if ( ! [ scanner scanUpToString : @ "=" intoString : & key ] || [ scanner isAtEnd ] ) {
break ;
}
[ scanner setScanLocation : ( [ scanner scanLocation ] + 1 ) ] ;
NSString * value = nil ;
if ( ! [ scanner scanUpToString : @ "&" intoString : & value ] ) {
break ;
}
key = [ key stringByReplacingOccurrencesOfString : @ "+" withString : @ " " ] ;
value = [ value stringByReplacingOccurrencesOfString : @ "+" withString : @ " " ] ;
2012-12-31 01:42:19 +08:00
[ parameters setObject : GCDWebServerUnescapeURLString ( value ) forKey : GCDWebServerUnescapeURLString ( key ) ] ;
2012-12-30 14:23:49 +08:00
if ( [ scanner isAtEnd ] ) {
break ;
}
[ scanner setScanLocation : ( [ scanner scanLocation ] + 1 ) ] ;
}
2014-01-09 14:27:15 +08:00
ARC_RELEASE ( scanner ) ;
2012-12-30 14:23:49 +08:00
return parameters ;
}
static void _SignalHandler ( int signal ) {
_run = NO ;
printf ( "\n" ) ;
}
@ implementation GCDWebServerHandler
@ synthesize matchBlock = _matchBlock , processBlock = _processBlock ;
2012-12-31 10:48:25 +08:00
- ( id ) initWithMatchBlock : ( GCDWebServerMatchBlock ) matchBlock processBlock : ( GCDWebServerProcessBlock ) processBlock {
2012-12-30 14:23:49 +08:00
if ( ( self = [ super init ] ) ) {
2014-01-09 14:27:15 +08:00
_matchBlock = [ matchBlock copy ] ;
_processBlock = [ processBlock copy ] ;
2012-12-30 14:23:49 +08:00
}
return self ;
}
2012-12-31 10:48:25 +08:00
- ( void ) dealloc {
2014-01-09 14:27:15 +08:00
ARC_RELEASE ( _matchBlock ) ;
ARC_RELEASE ( _processBlock ) ;
2012-12-30 14:23:49 +08:00
2014-01-09 14:27:15 +08:00
ARC_DEALLOC ( super ) ;
2012-12-30 14:23:49 +08:00
}
@ end
@ implementation GCDWebServer
@ synthesize handlers = _handlers , port = _port ;
2012-12-31 10:48:25 +08:00
+ ( void ) initialize {
2012-12-31 01:42:19 +08:00
[ GCDWebServerConnection class ] ; // Initialize class immediately to make sure it happens on the main thread
2012-12-30 14:23:49 +08:00
}
2012-12-31 10:48:25 +08:00
- ( id ) init {
2012-12-30 14:23:49 +08:00
if ( ( self = [ super init ] ) ) {
_handlers = [ [ NSMutableArray alloc ] init ] ;
}
return self ;
}
2012-12-31 10:48:25 +08:00
- ( void ) dealloc {
2013-04-02 06:42:16 +08:00
if ( _source ) {
2012-12-30 14:23:49 +08:00
[ self stop ] ;
}
2014-01-09 14:27:15 +08:00
ARC_RELEASE ( _handlers ) ;
2012-12-30 14:23:49 +08:00
2014-01-09 14:27:15 +08:00
ARC_DEALLOC ( super ) ;
2012-12-30 14:23:49 +08:00
}
2014-01-24 06:17:44 +08:00
- ( NSString * ) bonjourName {
2014-01-24 06:26:31 +08:00
CFStringRef name = _service ? CFNetServiceGetName ( _service ) : NULL ;
return name && CFStringGetLength ( name ) ? ARC_BRIDGE _RELEASE ( CFStringCreateCopy ( kCFAllocatorDefault , name ) ) : nil ;
2014-01-24 06:17:44 +08:00
}
2012-12-31 10:48:25 +08:00
- ( void ) addHandlerWithMatchBlock : ( GCDWebServerMatchBlock ) matchBlock processBlock : ( GCDWebServerProcessBlock ) handlerBlock {
2013-04-02 06:42:16 +08:00
DCHECK ( _source = = NULL ) ;
2012-12-30 14:23:49 +08:00
GCDWebServerHandler * handler = [ [ GCDWebServerHandler alloc ] initWithMatchBlock : matchBlock processBlock : handlerBlock ] ;
[ _handlers insertObject : handler atIndex : 0 ] ;
2014-01-09 14:27:15 +08:00
ARC_RELEASE ( handler ) ;
2012-12-30 14:23:49 +08:00
}
2012-12-31 10:48:25 +08:00
- ( void ) removeAllHandlers {
2013-04-02 06:42:16 +08:00
DCHECK ( _source = = NULL ) ;
2012-12-30 14:23:49 +08:00
[ _handlers removeAllObjects ] ;
}
2012-12-31 10:48:25 +08:00
- ( BOOL ) start {
2013-04-02 06:42:16 +08:00
return [ self startWithPort : 8080 bonjourName : @ "" ] ;
2012-12-30 14:23:49 +08:00
}
static void _NetServiceClientCallBack ( CFNetServiceRef service , CFStreamError * error , void * info ) {
@ autoreleasepool {
if ( error -> error ) {
LOG_ERROR ( @ "Bonjour error %i (domain %i)" , error -> error , ( int ) error -> domain ) ;
} else {
2014-01-24 06:17:44 +08:00
LOG_VERBOSE ( @ "Registered Bonjour service \" % @ \ " in domain \" % @ \ " with type '%@' on port %i" , CFNetServiceGetName ( service ) , CFNetServiceGetDomain ( service ) , CFNetServiceGetType ( service ) , CFNetServiceGetPortNumber ( service ) ) ;
2012-12-30 14:23:49 +08:00
}
}
}
2013-04-02 06:42:16 +08:00
- ( BOOL ) startWithPort : ( NSUInteger ) port bonjourName : ( NSString * ) name {
DCHECK ( _source = = NULL ) ;
int listeningSocket = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ) ;
if ( listeningSocket > 0 ) {
2012-12-30 14:23:49 +08:00
int yes = 1 ;
2013-04-02 06:42:16 +08:00
setsockopt ( listeningSocket , SOL_SOCKET , SO_REUSEADDR , & yes , sizeof ( yes ) ) ;
setsockopt ( listeningSocket , SOL_SOCKET , SO_REUSEPORT , & yes , sizeof ( yes ) ) ;
2012-12-30 14:23:49 +08:00
struct sockaddr_in addr4 ;
bzero ( & addr4 , sizeof ( addr4 ) ) ;
addr4 . sin_len = sizeof ( addr4 ) ;
addr4 . sin_family = AF_INET ;
addr4 . sin_port = htons ( port ) ;
addr4 . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
2013-04-02 06:42:16 +08:00
if ( bind ( listeningSocket , ( void * ) & addr4 , sizeof ( addr4 ) ) = = 0 ) {
if ( listen ( listeningSocket , kMaxPendingConnections ) = = 0 ) {
_source = dispatch_source _create ( DISPATCH_SOURCE _TYPE _READ , listeningSocket , 0 , kGCDWebServerGCDQueue ) ;
dispatch_source _set _cancel _handler ( _source , ^ {
@ autoreleasepool {
int result = close ( listeningSocket ) ;
if ( result ! = 0 ) {
LOG_ERROR ( @ "Failed closing socket (%i): %s" , errno , strerror ( errno ) ) ;
} else {
LOG_DEBUG ( @ "Closed listening socket" ) ;
}
}
} ) ;
dispatch_source _set _event _handler ( _source , ^ {
@ autoreleasepool {
struct sockaddr addr ;
socklen_t addrlen = sizeof ( addr ) ;
int socket = accept ( listeningSocket , & addr , & addrlen ) ;
if ( socket > 0 ) {
int yes = 1 ;
setsockopt ( socket , SOL_SOCKET , SO_NOSIGPIPE , & yes , sizeof ( yes ) ) ; // Make sure this socket cannot generate SIG_PIPE
NSData * data = [ NSData dataWithBytes : & addr length : addrlen ] ;
Class connectionClass = [ [ self class ] connectionClass ] ;
2014-01-09 14:27:15 +08:00
GCDWebServerConnection * connection = [ [ connectionClass alloc ] initWithServer : self address : data socket : socket ] ; // Connection will automatically retain itself while opened
# if __has _feature ( objc_arc )
[ connection self ] ; // Prevent compiler from complaining about unused variable / useless statement
# else
[ connection release ] ;
# endif
2013-04-02 06:42:16 +08:00
} else {
LOG_ERROR ( @ "Failed accepting socket (%i): %s" , errno , strerror ( errno ) ) ;
}
}
} ) ;
if ( port = = 0 ) { // Determine the actual port we are listening on
struct sockaddr addr ;
socklen_t addrlen = sizeof ( addr ) ;
if ( getsockname ( listeningSocket , & addr , & addrlen ) = = 0 ) {
struct sockaddr_in * sockaddr = ( struct sockaddr_in * ) & addr ;
_port = ntohs ( sockaddr -> sin_port ) ;
} else {
LOG_ERROR ( @ "Failed retrieving socket address (%i): %s" , errno , strerror ( errno ) ) ;
}
2012-12-30 14:23:49 +08:00
} else {
2013-04-02 06:42:16 +08:00
_port = port ;
}
if ( name ) {
2014-01-09 14:27:15 +08:00
_service = CFNetServiceCreate ( kCFAllocatorDefault , CFSTR ( "local." ) , CFSTR ( "_http._tcp" ) , ( ARC_BRIDGE CFStringRef ) name , _port ) ;
2013-04-02 06:42:16 +08:00
if ( _service ) {
2014-01-09 14:27:15 +08:00
CFNetServiceClientContext context = { 0 , ( ARC_BRIDGE void * ) self , NULL , NULL , NULL } ;
2013-04-02 06:42:16 +08:00
CFNetServiceSetClient ( _service , _NetServiceClientCallBack , & context ) ;
CFNetServiceScheduleWithRunLoop ( _service , CFRunLoopGetMain ( ) , kCFRunLoopCommonModes ) ;
CFStreamError error = { 0 } ;
CFNetServiceRegisterWithOptions ( _service , 0 , & error ) ;
} else {
LOG_ERROR ( @ "Failed creating CFNetService" ) ;
}
2012-12-30 14:23:49 +08:00
}
2013-04-02 06:42:16 +08:00
dispatch_resume ( _source ) ;
LOG_VERBOSE ( @ "%@ started on port %i" , [ self class ] , ( int ) _port ) ;
} else {
LOG_ERROR ( @ "Failed listening on socket (%i): %s" , errno , strerror ( errno ) ) ;
close ( listeningSocket ) ;
2012-12-30 14:23:49 +08:00
}
} else {
2013-04-02 06:42:16 +08:00
LOG_ERROR ( @ "Failed binding socket (%i): %s" , errno , strerror ( errno ) ) ;
close ( listeningSocket ) ;
2012-12-30 14:23:49 +08:00
}
} else {
2013-04-02 06:42:16 +08:00
LOG_ERROR ( @ "Failed creating socket (%i): %s" , errno , strerror ( errno ) ) ;
2012-12-30 14:23:49 +08:00
}
2013-04-02 06:42:16 +08:00
return ( _source ? YES : NO ) ;
2012-12-30 14:23:49 +08:00
}
2012-12-31 10:48:25 +08:00
- ( BOOL ) isRunning {
2013-04-02 06:42:16 +08:00
return ( _source ? YES : NO ) ;
2012-12-30 14:23:49 +08:00
}
2012-12-31 10:48:25 +08:00
- ( void ) stop {
2013-04-02 06:42:16 +08:00
DCHECK ( _source ! = NULL ) ;
if ( _source ) {
2012-12-30 14:23:49 +08:00
if ( _service ) {
2013-04-02 06:42:16 +08:00
CFNetServiceUnscheduleFromRunLoop ( _service , CFRunLoopGetMain ( ) , kCFRunLoopCommonModes ) ;
2012-12-30 14:23:49 +08:00
CFNetServiceSetClient ( _service , NULL , NULL ) ;
CFRelease ( _service ) ;
2013-04-02 06:42:16 +08:00
_service = NULL ;
2012-12-30 14:23:49 +08:00
}
2013-04-02 06:42:16 +08:00
dispatch_source _cancel ( _source ) ; // This will close the socket
2014-01-24 03:33:33 +08:00
ARC_DISPATCH _RELEASE ( _source ) ;
2013-04-02 06:42:16 +08:00
_source = NULL ;
2012-12-30 14:23:49 +08:00
LOG_VERBOSE ( @ "%@ stopped" , [ self class ] ) ;
}
_port = 0 ;
}
@ end
@ implementation GCDWebServer ( Subclassing )
2012-12-31 10:48:25 +08:00
+ ( Class ) connectionClass {
2012-12-30 14:23:49 +08:00
return [ GCDWebServerConnection class ] ;
}
2012-12-31 10:48:25 +08:00
+ ( NSString * ) serverName {
2012-12-30 14:23:49 +08:00
return NSStringFromClass ( self ) ;
}
@ end
@ implementation GCDWebServer ( Extensions )
2012-12-31 10:48:25 +08:00
- ( BOOL ) runWithPort : ( NSUInteger ) port {
2012-12-30 14:23:49 +08:00
BOOL success = NO ;
_run = YES ;
void * handler = signal ( SIGINT , _SignalHandler ) ;
if ( handler ! = SIG_ERR ) {
2013-04-02 06:42:16 +08:00
if ( [ self startWithPort : port bonjourName : @ "" ] ) {
2012-12-30 14:23:49 +08:00
while ( _run ) {
2013-04-02 06:42:16 +08:00
CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 1.0 , true ) ;
2012-12-30 14:23:49 +08:00
}
[ self stop ] ;
success = YES ;
}
signal ( SIGINT , handler ) ;
}
return success ;
}
@ end
@ implementation GCDWebServer ( Handlers )
2012-12-31 10:48:25 +08:00
- ( void ) addDefaultHandlerForMethod : ( NSString * ) method requestClass : ( Class ) class processBlock : ( GCDWebServerProcessBlock ) block {
2012-12-30 14:23:49 +08:00
[ self addHandlerWithMatchBlock : ^ GCDWebServerRequest * ( NSString * requestMethod , NSURL * requestURL , NSDictionary * requestHeaders , NSString * urlPath , NSDictionary * urlQuery ) {
2014-01-09 14:27:15 +08:00
return ARC_AUTORELEASE ( [ [ class alloc ] initWithMethod : requestMethod url : requestURL headers : requestHeaders path : urlPath query : urlQuery ] ) ;
2012-12-30 14:23:49 +08:00
} processBlock : block ] ;
}
2012-12-31 10:48:25 +08:00
- ( GCDWebServerResponse * ) _responseWithContentsOfFile : ( NSString * ) path {
2012-12-30 14:23:49 +08:00
return [ GCDWebServerFileResponse responseWithFile : path ] ;
}
2012-12-31 10:48:25 +08:00
- ( GCDWebServerResponse * ) _responseWithContentsOfDirectory : ( NSString * ) path {
2012-12-30 14:23:49 +08:00
NSDirectoryEnumerator * enumerator = [ [ NSFileManager defaultManager ] enumeratorAtPath : path ] ;
if ( enumerator = = nil ) {
return nil ;
}
NSMutableString * html = [ NSMutableString string ] ;
[ html appendString : @ "<html><body>\n" ] ;
[ html appendString : @ "<ul>\n" ] ;
for ( NSString * file in enumerator ) {
if ( ! [ file hasPrefix : @ "." ] ) {
NSString * type = [ [ enumerator fileAttributes ] objectForKey : NSFileType ] ;
NSString * escapedFile = [ file stringByAddingPercentEscapesUsingEncoding : NSUTF8StringEncoding ] ;
DCHECK ( escapedFile ) ;
if ( [ type isEqualToString : NSFileTypeRegular ] ) {
[ html appendFormat : @ "<li><a href=\" % @ \ ">%@</a></li>\n" , escapedFile , file ] ;
} else if ( [ type isEqualToString : NSFileTypeDirectory ] ) {
[ html appendFormat : @ "<li><a href=\" % @ / \ ">%@/</a></li>\n" , escapedFile , file ] ;
}
}
[ enumerator skipDescendents ] ;
}
[ html appendString : @ "</ul>\n" ] ;
[ html appendString : @ "</body></html>\n" ] ;
return [ GCDWebServerDataResponse responseWithHTML : html ] ;
}
2012-12-31 10:48:25 +08:00
- ( void ) addHandlerForBasePath : ( NSString * ) basePath localPath : ( NSString * ) localPath indexFilename : ( NSString * ) indexFilename cacheAge : ( NSUInteger ) cacheAge {
2012-12-30 14:23:49 +08:00
if ( [ basePath hasPrefix : @ "/" ] && [ basePath hasSuffix : @ "/" ] ) {
2014-01-09 14:27:15 +08:00
# if __has _feature ( objc_arc )
__unsafe _unretained GCDWebServer * server = self ;
# else
GCDWebServer * server = self ;
# endif
2012-12-30 14:23:49 +08:00
[ self addHandlerWithMatchBlock : ^ GCDWebServerRequest * ( NSString * requestMethod , NSURL * requestURL , NSDictionary * requestHeaders , NSString * urlPath , NSDictionary * urlQuery ) {
if ( ! [ requestMethod isEqualToString : @ "GET" ] ) {
return nil ;
}
if ( ! [ urlPath hasPrefix : basePath ] ) {
return nil ;
}
2014-01-09 14:27:15 +08:00
return ARC_AUTORELEASE ( [ [ GCDWebServerRequest alloc ] initWithMethod : requestMethod url : requestURL headers : requestHeaders path : urlPath query : urlQuery ] ) ;
2012-12-30 14:23:49 +08:00
} processBlock : ^ GCDWebServerResponse * ( GCDWebServerRequest * request ) {
G CDWebServerResponse * response = nil ;
NSString * filePath = [ localPath stringByAppendingPathComponent : [ request . path substringFromIndex : basePath . length ] ] ;
BOOL isDirectory ;
if ( [ [ NSFileManager defaultManager ] fileExistsAtPath : filePath isDirectory : & isDirectory ] ) {
if ( isDirectory ) {
if ( indexFilename ) {
NSString * indexPath = [ filePath stringByAppendingPathComponent : indexFilename ] ;
if ( [ [ NSFileManager defaultManager ] fileExistsAtPath : indexPath isDirectory : & isDirectory ] && ! isDirectory ) {
2014-01-09 14:27:15 +08:00
return [ server _responseWithContentsOfFile : indexPath ] ;
2012-12-30 14:23:49 +08:00
}
}
2014-01-09 14:27:15 +08:00
response = [ server _responseWithContentsOfDirectory : filePath ] ;
2012-12-30 14:23:49 +08:00
} else {
2014-01-09 14:27:15 +08:00
response = [ server _responseWithContentsOfFile : filePath ] ;
2012-12-30 14:23:49 +08:00
}
}
if ( response ) {
response . cacheControlMaxAge = cacheAge ;
} else {
response = [ GCDWebServerResponse responseWithStatusCode : 404 ] ;
}
return response ;
} ] ;
} else {
DNOT_REACHED ( ) ;
}
}
2012-12-31 10:48:25 +08:00
- ( void ) addHandlerForMethod : ( NSString * ) method path : ( NSString * ) path requestClass : ( Class ) class processBlock : ( GCDWebServerProcessBlock ) block {
2012-12-30 14:23:49 +08:00
if ( [ path hasPrefix : @ "/" ] && [ class isSubclassOfClass : [ GCDWebServerRequest class ] ] ) {
[ self addHandlerWithMatchBlock : ^ GCDWebServerRequest * ( NSString * requestMethod , NSURL * requestURL , NSDictionary * requestHeaders , NSString * urlPath , NSDictionary * urlQuery ) {
if ( ! [ requestMethod isEqualToString : method ] ) {
return nil ;
}
if ( [ urlPath caseInsensitiveCompare : path ] ! = NSOrderedSame ) {
return nil ;
}
2014-01-09 14:27:15 +08:00
return ARC_AUTORELEASE ( [ [ class alloc ] initWithMethod : requestMethod url : requestURL headers : requestHeaders path : urlPath query : urlQuery ] ) ;
2012-12-30 14:23:49 +08:00
} processBlock : block ] ;
} else {
DNOT_REACHED ( ) ;
}
}
2012-12-31 10:48:25 +08:00
- ( void ) addHandlerForMethod : ( NSString * ) method pathRegex : ( NSString * ) regex requestClass : ( Class ) class processBlock : ( GCDWebServerProcessBlock ) block {
2012-12-30 14:23:49 +08:00
NSRegularExpression * expression = [ NSRegularExpression regularExpressionWithPattern : regex options : NSRegularExpressionCaseInsensitive error : NULL ] ;
if ( expression && [ class isSubclassOfClass : [ GCDWebServerRequest class ] ] ) {
[ self addHandlerWithMatchBlock : ^ GCDWebServerRequest * ( NSString * requestMethod , NSURL * requestURL , NSDictionary * requestHeaders , NSString * urlPath , NSDictionary * urlQuery ) {
if ( ! [ requestMethod isEqualToString : method ] ) {
return nil ;
}
if ( [ expression firstMatchInString : urlPath options : 0 range : NSMakeRange ( 0 , urlPath . length ) ] = = nil ) {
return nil ;
}
2014-01-09 14:27:15 +08:00
return ARC_AUTORELEASE ( [ [ class alloc ] initWithMethod : requestMethod url : requestURL headers : requestHeaders path : urlPath query : urlQuery ] ) ;
2012-12-30 14:23:49 +08:00
} processBlock : block ] ;
} else {
DNOT_REACHED ( ) ;
}
}
@ end