mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-02-11 00:00:07 +08:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47b8ea5f7c | ||
|
|
dc7fe87878 | ||
|
|
cedec20673 | ||
|
|
ed0f3ac68e | ||
|
|
0a48f42ccb | ||
|
|
6c1439405d | ||
|
|
79e041eae5 | ||
|
|
fb02b9f9d1 | ||
|
|
1b163b1b8b | ||
|
|
eb6589e27a | ||
|
|
0a34a0b205 | ||
|
|
1e99e91407 | ||
|
|
096b07a201 | ||
|
|
e65b569ddc | ||
|
|
08e58e4a5a | ||
|
|
111027f413 | ||
|
|
d7e9386272 | ||
|
|
040515aff4 | ||
|
|
bb56e1e808 | ||
|
|
ff7a5c8e0a | ||
|
|
7c24996be3 | ||
|
|
feacf7601e | ||
|
|
36658278f8 | ||
|
|
0f2f22a1b0 | ||
|
|
628cf6833c | ||
|
|
9392ddadb6 | ||
|
|
5dee044caa | ||
|
|
7c8205caa0 | ||
|
|
965e111280 | ||
|
|
8a69050cca | ||
|
|
a15a49240a | ||
|
|
404b46537e | ||
|
|
811e18e2fa | ||
|
|
78480e004a | ||
|
|
2e587919ca | ||
|
|
544e236144 | ||
|
|
1a1ee2869e | ||
|
|
e665d0a778 | ||
|
|
43cc98ad47 | ||
|
|
1d08a8fcc3 | ||
|
|
2cddc8c939 | ||
|
|
1344ad9e04 | ||
|
|
3eb27a4d7b |
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,18 +1,3 @@
|
|||||||
# Xcode
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
build/
|
|
||||||
*.pbxuser
|
|
||||||
!default.pbxuser
|
|
||||||
*.mode1v3
|
|
||||||
!default.mode1v3
|
|
||||||
*.mode2v3
|
|
||||||
!default.mode2v3
|
|
||||||
*.perspectivev3
|
|
||||||
!default.perspectivev3
|
|
||||||
*.xcworkspace
|
|
||||||
!default.xcworkspace
|
|
||||||
xcuserdata
|
xcuserdata
|
||||||
profile
|
project.xcworkspace
|
||||||
*.moved-aside
|
|
||||||
DerivedData
|
|
||||||
.idea/
|
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
* Redistributions of source code must retain the above copyright
|
* Redistributions of source code must retain the above copyright
|
||||||
notice, this list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
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
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "GCDWebServerRequest.h"
|
#import "GCDWebServerRequest.h"
|
||||||
#import "GCDWebServerResponse.h"
|
#import "GCDWebServerResponse.h"
|
||||||
@@ -31,16 +31,22 @@
|
|||||||
typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
|
typedef GCDWebServerRequest* (^GCDWebServerMatchBlock)(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery);
|
||||||
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request);
|
typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* request);
|
||||||
|
|
||||||
@interface GCDWebServer : NSObject {
|
#ifdef __cplusplus
|
||||||
@private
|
extern "C" {
|
||||||
NSMutableArray* _handlers;
|
#endif
|
||||||
|
|
||||||
NSUInteger _port;
|
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension);
|
||||||
dispatch_source_t _source;
|
NSString* GCDWebServerUnescapeURLString(NSString* string);
|
||||||
CFNetServiceRef _service;
|
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@interface GCDWebServer : NSObject
|
||||||
@property(nonatomic, readonly, getter=isRunning) BOOL running;
|
@property(nonatomic, readonly, getter=isRunning) BOOL running;
|
||||||
@property(nonatomic, readonly) NSUInteger port;
|
@property(nonatomic, readonly) NSUInteger port;
|
||||||
|
@property(nonatomic, readonly) NSString* bonjourName; // Only non-nil if Bonjour registration is active
|
||||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
||||||
- (void)removeAllHandlers;
|
- (void)removeAllHandlers;
|
||||||
|
|
||||||
@@ -59,8 +65,13 @@ typedef GCDWebServerResponse* (^GCDWebServerProcessBlock)(GCDWebServerRequest* r
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServer (Handlers)
|
@interface GCDWebServer (Handlers)
|
||||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)class processBlock:(GCDWebServerProcessBlock)block;
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block;
|
||||||
- (void)addHandlerForBasePath:(NSString*)basePath localPath:(NSString*)localPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge; // Base path is recursive and case-sensitive
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; // Path is case-insensitive
|
||||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)class processBlock:(GCDWebServerProcessBlock)block; // Path is case-insensitive
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block; // Regular expression is case-insensitive
|
||||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)class processBlock:(GCDWebServerProcessBlock)block; // Regular expression is case-insensitive
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServer (GETHandlers)
|
||||||
|
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge; // Path is case-insensitive
|
||||||
|
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests; // Path is case-insensitive
|
||||||
|
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests; // Base path is recursive and case-sensitive
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -36,6 +36,23 @@
|
|||||||
|
|
||||||
#define kMaxPendingConnections 16
|
#define kMaxPendingConnections 16
|
||||||
|
|
||||||
|
@interface GCDWebServer () {
|
||||||
|
@private
|
||||||
|
NSMutableArray* _handlers;
|
||||||
|
|
||||||
|
NSUInteger _port;
|
||||||
|
dispatch_source_t _source;
|
||||||
|
CFNetServiceRef _service;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerHandler () {
|
||||||
|
@private
|
||||||
|
GCDWebServerMatchBlock _matchBlock;
|
||||||
|
GCDWebServerProcessBlock _processBlock;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
static BOOL _run;
|
static BOOL _run;
|
||||||
|
|
||||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
||||||
@@ -50,9 +67,9 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
|||||||
if (extension.length) {
|
if (extension.length) {
|
||||||
mimeType = [_overrides objectForKey:extension];
|
mimeType = [_overrides objectForKey:extension];
|
||||||
if (mimeType == nil) {
|
if (mimeType == nil) {
|
||||||
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)extension, NULL);
|
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (ARC_BRIDGE CFStringRef)extension, NULL);
|
||||||
if (uti) {
|
if (uti) {
|
||||||
mimeType = [(id)UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType) autorelease];
|
mimeType = ARC_BRIDGE_RELEASE(UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
|
||||||
CFRelease(uti);
|
CFRelease(uti);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,8 +78,8 @@ NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSString* GCDWebServerUnescapeURLString(NSString* string) {
|
NSString* GCDWebServerUnescapeURLString(NSString* string) {
|
||||||
return [(id)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""),
|
return ARC_BRIDGE_RELEASE(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, (CFStringRef)string, CFSTR(""),
|
||||||
kCFStringEncodingUTF8) autorelease];
|
kCFStringEncodingUTF8));
|
||||||
}
|
}
|
||||||
|
|
||||||
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
||||||
@@ -83,14 +100,18 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form) {
|
|||||||
|
|
||||||
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
key = [key stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||||
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||||
[parameters setObject:GCDWebServerUnescapeURLString(value) forKey:GCDWebServerUnescapeURLString(key)];
|
if (key && value) {
|
||||||
|
[parameters setObject:GCDWebServerUnescapeURLString(value) forKey:GCDWebServerUnescapeURLString(key)];
|
||||||
|
} else {
|
||||||
|
DNOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
if ([scanner isAtEnd]) {
|
if ([scanner isAtEnd]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
[scanner setScanLocation:([scanner scanLocation] + 1)];
|
||||||
}
|
}
|
||||||
[scanner release];
|
ARC_RELEASE(scanner);
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,17 +126,17 @@ static void _SignalHandler(int signal) {
|
|||||||
|
|
||||||
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_matchBlock = Block_copy(matchBlock);
|
_matchBlock = [matchBlock copy];
|
||||||
_processBlock = Block_copy(processBlock);
|
_processBlock = [processBlock copy];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
Block_release(_matchBlock);
|
ARC_RELEASE(_matchBlock);
|
||||||
Block_release(_processBlock);
|
ARC_RELEASE(_processBlock);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -140,16 +161,21 @@ static void _SignalHandler(int signal) {
|
|||||||
[self stop];
|
[self stop];
|
||||||
}
|
}
|
||||||
|
|
||||||
[_handlers release];
|
ARC_RELEASE(_handlers);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)bonjourName {
|
||||||
|
CFStringRef name = _service ? CFNetServiceGetName(_service) : NULL;
|
||||||
|
return name && CFStringGetLength(name) ? ARC_BRIDGE_RELEASE(CFStringCreateCopy(kCFAllocatorDefault, name)) : nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)handlerBlock {
|
- (void)addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)handlerBlock {
|
||||||
DCHECK(_source == NULL);
|
DCHECK(_source == NULL);
|
||||||
GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock processBlock:handlerBlock];
|
GCDWebServerHandler* handler = [[GCDWebServerHandler alloc] initWithMatchBlock:matchBlock processBlock:handlerBlock];
|
||||||
[_handlers insertObject:handler atIndex:0];
|
[_handlers insertObject:handler atIndex:0];
|
||||||
[handler release];
|
ARC_RELEASE(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeAllHandlers {
|
- (void)removeAllHandlers {
|
||||||
@@ -166,7 +192,7 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
if (error->error) {
|
if (error->error) {
|
||||||
LOG_ERROR(@"Bonjour error %i (domain %i)", error->error, (int)error->domain);
|
LOG_ERROR(@"Bonjour error %i (domain %i)", error->error, (int)error->domain);
|
||||||
} else {
|
} else {
|
||||||
LOG_VERBOSE(@"Registered Bonjour service \"%@\" with type '%@' on port %i", CFNetServiceGetName(service), CFNetServiceGetType(service), CFNetServiceGetPortNumber(service));
|
LOG_VERBOSE(@"Registered Bonjour service \"%@\" in domain \"%@\" with type '%@' on port %i", CFNetServiceGetName(service), CFNetServiceGetDomain(service), CFNetServiceGetType(service), CFNetServiceGetPortNumber(service));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,8 +238,12 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
|
|
||||||
NSData* data = [NSData dataWithBytes:&addr length:addrlen];
|
NSData* data = [NSData dataWithBytes:&addr length:addrlen];
|
||||||
Class connectionClass = [[self class] connectionClass];
|
Class connectionClass = [[self class] connectionClass];
|
||||||
GCDWebServerConnection* connection = [[connectionClass alloc] initWithServer:self address:data socket:socket];
|
GCDWebServerConnection* connection = [[connectionClass alloc] initWithServer:self address:data socket:socket]; // Connection will automatically retain itself while opened
|
||||||
[connection release]; // 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
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(@"Failed accepting socket (%i): %s", errno, strerror(errno));
|
LOG_ERROR(@"Failed accepting socket (%i): %s", errno, strerror(errno));
|
||||||
}
|
}
|
||||||
@@ -235,9 +265,9 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
_service = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), CFSTR("_http._tcp"), (CFStringRef)name, _port);
|
_service = CFNetServiceCreate(kCFAllocatorDefault, CFSTR("local."), CFSTR("_http._tcp"), (ARC_BRIDGE CFStringRef)name, (SInt32)_port);
|
||||||
if (_service) {
|
if (_service) {
|
||||||
CFNetServiceClientContext context = {0, self, NULL, NULL, NULL};
|
CFNetServiceClientContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
|
||||||
CFNetServiceSetClient(_service, _NetServiceClientCallBack, &context);
|
CFNetServiceSetClient(_service, _NetServiceClientCallBack, &context);
|
||||||
CFNetServiceScheduleWithRunLoop(_service, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
CFNetServiceScheduleWithRunLoop(_service, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
CFStreamError error = {0};
|
CFStreamError error = {0};
|
||||||
@@ -278,7 +308,7 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch_source_cancel(_source); // This will close the socket
|
dispatch_source_cancel(_source); // This will close the socket
|
||||||
dispatch_release(_source);
|
ARC_DISPATCH_RELEASE(_source);
|
||||||
_source = NULL;
|
_source = NULL;
|
||||||
|
|
||||||
LOG_VERBOSE(@"%@ stopped", [self class]);
|
LOG_VERBOSE(@"%@ stopped", [self class]);
|
||||||
@@ -305,7 +335,7 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
- (BOOL)runWithPort:(NSUInteger)port {
|
- (BOOL)runWithPort:(NSUInteger)port {
|
||||||
BOOL success = NO;
|
BOOL success = NO;
|
||||||
_run = YES;
|
_run = YES;
|
||||||
void* handler = signal(SIGINT, _SignalHandler);
|
void (*handler)(int) = signal(SIGINT, _SignalHandler);
|
||||||
if (handler != SIG_ERR) {
|
if (handler != SIG_ERR) {
|
||||||
if ([self startWithPort:port bonjourName:@""]) {
|
if ([self startWithPort:port bonjourName:@""]) {
|
||||||
while (_run) {
|
while (_run) {
|
||||||
@@ -323,16 +353,79 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
|
|
||||||
@implementation GCDWebServer (Handlers)
|
@implementation GCDWebServer (Handlers)
|
||||||
|
|
||||||
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)class processBlock:(GCDWebServerProcessBlock)block {
|
- (void)addDefaultHandlerForMethod:(NSString*)method requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)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) {
|
||||||
|
|
||||||
return [[[class alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery] autorelease];
|
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||||
|
|
||||||
} processBlock:block];
|
} processBlock:block];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (GCDWebServerResponse*)_responseWithContentsOfFile:(NSString*)path {
|
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||||
return [GCDWebServerFileResponse responseWithFile:path];
|
if ([path hasPrefix:@"/"] && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
||||||
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
|
if (![requestMethod isEqualToString:method]) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if ([urlPath caseInsensitiveCompare:path] != NSOrderedSame) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||||
|
|
||||||
|
} processBlock:block];
|
||||||
|
} else {
|
||||||
|
DNOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)aClass processBlock:(GCDWebServerProcessBlock)block {
|
||||||
|
NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:NULL];
|
||||||
|
if (expression && [aClass isSubclassOfClass:[GCDWebServerRequest class]]) {
|
||||||
|
[self addHandlerWithMatchBlock:^GCDWebServerRequest *(NSString* requestMethod, NSURL* requestURL, NSDictionary* requestHeaders, NSString* urlPath, NSDictionary* urlQuery) {
|
||||||
|
|
||||||
|
if (![requestMethod isEqualToString:method]) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if ([expression firstMatchInString:urlPath options:0 range:NSMakeRange(0, urlPath.length)] == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return ARC_AUTORELEASE([[aClass alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||||
|
|
||||||
|
} processBlock:block];
|
||||||
|
} else {
|
||||||
|
DNOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GCDWebServer (GETHandlers)
|
||||||
|
|
||||||
|
- (void)addGETHandlerForPath:(NSString*)path staticData:(NSData*)staticData contentType:(NSString*)contentType cacheAge:(NSUInteger)cacheAge {
|
||||||
|
GCDWebServerResponse* response = [GCDWebServerDataResponse responseWithData:staticData contentType:contentType];
|
||||||
|
response.cacheControlMaxAge = cacheAge;
|
||||||
|
[self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addGETHandlerForPath:(NSString*)path filePath:(NSString*)filePath isAttachment:(BOOL)isAttachment cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
||||||
|
[self addHandlerForMethod:@"GET" path:path requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
|
GCDWebServerResponse* response = nil;
|
||||||
|
if (allowRangeRequests) {
|
||||||
|
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange isAttachment:isAttachment];
|
||||||
|
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||||
|
} else {
|
||||||
|
response = [GCDWebServerFileResponse responseWithFile:filePath isAttachment:isAttachment];
|
||||||
|
}
|
||||||
|
response.cacheControlMaxAge = cacheAge;
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
|
- (GCDWebServerResponse*)_responseWithContentsOfDirectory:(NSString*)path {
|
||||||
@@ -341,7 +434,8 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
NSMutableString* html = [NSMutableString string];
|
NSMutableString* html = [NSMutableString string];
|
||||||
[html appendString:@"<html><body>\n"];
|
[html appendString:@"<!DOCTYPE html>\n"];
|
||||||
|
[html appendString:@"<html><head><meta charset=\"utf-8\"></head><body>\n"];
|
||||||
[html appendString:@"<ul>\n"];
|
[html appendString:@"<ul>\n"];
|
||||||
for (NSString* file in enumerator) {
|
for (NSString* file in enumerator) {
|
||||||
if (![file hasPrefix:@"."]) {
|
if (![file hasPrefix:@"."]) {
|
||||||
@@ -361,8 +455,13 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
return [GCDWebServerDataResponse responseWithHTML:html];
|
return [GCDWebServerDataResponse responseWithHTML:html];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForBasePath:(NSString*)basePath localPath:(NSString*)localPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge {
|
- (void)addGETHandlerForBasePath:(NSString*)basePath directoryPath:(NSString*)directoryPath indexFilename:(NSString*)indexFilename cacheAge:(NSUInteger)cacheAge allowRangeRequests:(BOOL)allowRangeRequests {
|
||||||
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
|
if ([basePath hasPrefix:@"/"] && [basePath hasSuffix:@"/"]) {
|
||||||
|
#if __has_feature(objc_arc)
|
||||||
|
__unsafe_unretained GCDWebServer* server = self;
|
||||||
|
#else
|
||||||
|
GCDWebServer* server = self;
|
||||||
|
#endif
|
||||||
[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:@"GET"]) {
|
if (![requestMethod isEqualToString:@"GET"]) {
|
||||||
@@ -371,24 +470,29 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
if (![urlPath hasPrefix:basePath]) {
|
if (![urlPath hasPrefix:basePath]) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return [[[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery] autorelease];
|
return ARC_AUTORELEASE([[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery]);
|
||||||
|
|
||||||
} processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
} processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
GCDWebServerResponse* response = nil;
|
GCDWebServerResponse* response = nil;
|
||||||
NSString* filePath = [localPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
NSString* filePath = [directoryPath stringByAppendingPathComponent:[request.path substringFromIndex:basePath.length]];
|
||||||
BOOL isDirectory;
|
BOOL isDirectory;
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory]) {
|
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDirectory]) {
|
||||||
if (isDirectory) {
|
if (isDirectory) {
|
||||||
if (indexFilename) {
|
if (indexFilename) {
|
||||||
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
NSString* indexPath = [filePath stringByAppendingPathComponent:indexFilename];
|
||||||
if ([[NSFileManager defaultManager] fileExistsAtPath:indexPath isDirectory:&isDirectory] && !isDirectory) {
|
if ([[NSFileManager defaultManager] fileExistsAtPath:indexPath isDirectory:&isDirectory] && !isDirectory) {
|
||||||
return [self _responseWithContentsOfFile:indexPath];
|
return [GCDWebServerFileResponse responseWithFile:indexPath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = [self _responseWithContentsOfDirectory:filePath];
|
response = [server _responseWithContentsOfDirectory:filePath];
|
||||||
} else {
|
} else {
|
||||||
response = [self _responseWithContentsOfFile:filePath];
|
if (allowRangeRequests) {
|
||||||
|
response = [GCDWebServerFileResponse responseWithFile:filePath byteRange:request.byteRange];
|
||||||
|
[response setValue:@"bytes" forAdditionalHeader:@"Accept-Ranges"];
|
||||||
|
} else {
|
||||||
|
response = [GCDWebServerFileResponse responseWithFile:filePath];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (response) {
|
if (response) {
|
||||||
@@ -404,41 +508,4 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method path:(NSString*)path requestClass:(Class)class processBlock:(GCDWebServerProcessBlock)block {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return [[[class alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery] autorelease];
|
|
||||||
|
|
||||||
} processBlock:block];
|
|
||||||
} else {
|
|
||||||
DNOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)addHandlerForMethod:(NSString*)method pathRegex:(NSString*)regex requestClass:(Class)class processBlock:(GCDWebServerProcessBlock)block {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return [[[class alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:urlPath query:urlQuery] autorelease];
|
|
||||||
|
|
||||||
} processBlock:block];
|
|
||||||
} else {
|
|
||||||
DNOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,48 +1,35 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
* Redistributions of source code must retain the above copyright
|
* Redistributions of source code must retain the above copyright
|
||||||
notice, this list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
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
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "GCDWebServer.h"
|
#import "GCDWebServer.h"
|
||||||
|
|
||||||
@class GCDWebServerHandler;
|
@class GCDWebServerHandler;
|
||||||
|
|
||||||
@interface GCDWebServerConnection : NSObject {
|
@interface GCDWebServerConnection : NSObject
|
||||||
@private
|
|
||||||
GCDWebServer* _server;
|
|
||||||
NSData* _address;
|
|
||||||
CFSocketNativeHandle _socket;
|
|
||||||
NSUInteger _bytesRead;
|
|
||||||
NSUInteger _bytesWritten;
|
|
||||||
|
|
||||||
CFHTTPMessageRef _requestMessage;
|
|
||||||
GCDWebServerRequest* _request;
|
|
||||||
GCDWebServerHandler* _handler;
|
|
||||||
CFHTTPMessageRef _responseMessage;
|
|
||||||
GCDWebServerResponse* _response;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) GCDWebServer* server;
|
@property(nonatomic, readonly) GCDWebServer* server;
|
||||||
@property(nonatomic, readonly) NSData* address; // struct sockaddr
|
@property(nonatomic, readonly) NSData* address; // struct sockaddr
|
||||||
@property(nonatomic, readonly) NSUInteger totalBytesRead;
|
@property(nonatomic, readonly) NSUInteger totalBytesRead;
|
||||||
@@ -51,6 +38,8 @@
|
|||||||
|
|
||||||
@interface GCDWebServerConnection (Subclassing)
|
@interface GCDWebServerConnection (Subclassing)
|
||||||
- (void)open;
|
- (void)open;
|
||||||
|
- (void)didUpdateBytesRead; // Called from arbitrary thread after @totalBytesRead is updated - Default implementation does nothing
|
||||||
|
- (void)didUpdateBytesWritten; // Called from arbitrary thread after @totalBytesWritten is updated - Default implementation does nothing
|
||||||
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block;
|
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block;
|
||||||
- (void)close;
|
- (void)close;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -45,6 +45,22 @@ static NSData* _continueData = nil;
|
|||||||
static NSDateFormatter* _dateFormatter = nil;
|
static NSDateFormatter* _dateFormatter = nil;
|
||||||
static dispatch_queue_t _formatterQueue = NULL;
|
static dispatch_queue_t _formatterQueue = NULL;
|
||||||
|
|
||||||
|
@interface GCDWebServerConnection () {
|
||||||
|
@private
|
||||||
|
GCDWebServer* _server;
|
||||||
|
NSData* _address;
|
||||||
|
CFSocketNativeHandle _socket;
|
||||||
|
NSUInteger _bytesRead;
|
||||||
|
NSUInteger _bytesWritten;
|
||||||
|
|
||||||
|
CFHTTPMessageRef _requestMessage;
|
||||||
|
GCDWebServerRequest* _request;
|
||||||
|
GCDWebServerHandler* _handler;
|
||||||
|
CFHTTPMessageRef _responseMessage;
|
||||||
|
GCDWebServerResponse* _response;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation GCDWebServerConnection (Read)
|
@implementation GCDWebServerConnection (Read)
|
||||||
|
|
||||||
- (void)_readBufferWithLength:(NSUInteger)length completionBlock:(ReadBufferCompletionBlock)block {
|
- (void)_readBufferWithLength:(NSUInteger)length completionBlock:(ReadBufferCompletionBlock)block {
|
||||||
@@ -56,6 +72,7 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
LOG_DEBUG(@"Connection received %i bytes on socket %i", size, _socket);
|
LOG_DEBUG(@"Connection received %i bytes on socket %i", size, _socket);
|
||||||
_bytesRead += size;
|
_bytesRead += size;
|
||||||
|
[self didUpdateBytesRead];
|
||||||
block(buffer);
|
block(buffer);
|
||||||
} else {
|
} else {
|
||||||
if (_bytesRead > 0) {
|
if (_bytesRead > 0) {
|
||||||
@@ -84,7 +101,7 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
block(data);
|
block(data);
|
||||||
[data release];
|
ARC_RELEASE(data);
|
||||||
} else {
|
} else {
|
||||||
block(nil);
|
block(nil);
|
||||||
}
|
}
|
||||||
@@ -140,7 +157,7 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
if (remainingLength >= 0) {
|
if (remainingLength >= 0) {
|
||||||
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* buffer, size_t size) {
|
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* buffer, size_t size) {
|
||||||
NSInteger result = [_request write:buffer maxLength:size];
|
NSInteger result = [_request write:buffer maxLength:size];
|
||||||
if (result != size) {
|
if (result != (NSInteger)size) {
|
||||||
LOG_ERROR(@"Failed writing request body on socket %i (error %i)", _socket, (int)result);
|
LOG_ERROR(@"Failed writing request body on socket %i (error %i)", _socket, (int)result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -156,8 +173,8 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
block(NO);
|
block(NO);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DNOT_REACHED();
|
|
||||||
block(NO);
|
block(NO);
|
||||||
|
DNOT_REACHED();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
block(NO);
|
block(NO);
|
||||||
@@ -179,6 +196,7 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
DCHECK(data == NULL);
|
DCHECK(data == NULL);
|
||||||
LOG_DEBUG(@"Connection sent %i bytes on socket %i", size, _socket);
|
LOG_DEBUG(@"Connection sent %i bytes on socket %i", size, _socket);
|
||||||
_bytesWritten += size;
|
_bytesWritten += size;
|
||||||
|
[self didUpdateBytesWritten];
|
||||||
block(YES);
|
block(YES);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
|
LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
|
||||||
@@ -190,18 +208,24 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
|
||||||
|
#if !__has_feature(objc_arc)
|
||||||
[data retain];
|
[data retain];
|
||||||
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_current_queue(), ^{
|
#endif
|
||||||
|
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_main_queue(), ^{
|
||||||
|
#if __has_feature(objc_arc)
|
||||||
|
[data self]; // Keeps ARC from releasing data too early
|
||||||
|
#else
|
||||||
[data release];
|
[data release];
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
[self _writeBuffer:buffer withCompletionBlock:block];
|
[self _writeBuffer:buffer withCompletionBlock:block];
|
||||||
dispatch_release(buffer);
|
ARC_DISPATCH_RELEASE(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
- (void)_writeHeadersWithCompletionBlock:(WriteHeadersCompletionBlock)block {
|
||||||
DCHECK(_responseMessage);
|
DCHECK(_responseMessage);
|
||||||
CFDataRef message = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
CFDataRef message = CFHTTPMessageCopySerializedMessage(_responseMessage);
|
||||||
[self _writeData:(NSData*)message withCompletionBlock:block];
|
[self _writeData:(ARC_BRIDGE NSData*)message withCompletionBlock:block];
|
||||||
CFRelease(message);
|
CFRelease(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +244,7 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
}
|
}
|
||||||
|
|
||||||
}];
|
}];
|
||||||
dispatch_release(wrapper);
|
ARC_DISPATCH_RELEASE(wrapper);
|
||||||
} else if (result < 0) {
|
} else if (result < 0) {
|
||||||
LOG_ERROR(@"Failed reading response body on socket %i (error %i)", _socket, (int)result);
|
LOG_ERROR(@"Failed reading response body on socket %i (error %i)", _socket, (int)result);
|
||||||
block(NO);
|
block(NO);
|
||||||
@@ -238,22 +262,26 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
@synthesize server=_server, address=_address, totalBytesRead=_bytesRead, totalBytesWritten=_bytesWritten;
|
@synthesize server=_server, address=_address, totalBytesRead=_bytesRead, totalBytesWritten=_bytesWritten;
|
||||||
|
|
||||||
+ (void)initialize {
|
+ (void)initialize {
|
||||||
DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread
|
|
||||||
if (_separatorData == nil) {
|
if (_separatorData == nil) {
|
||||||
_separatorData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
_separatorData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
||||||
DCHECK(_separatorData);
|
DCHECK(_separatorData);
|
||||||
}
|
}
|
||||||
if (_continueData == nil) {
|
if (_continueData == nil) {
|
||||||
CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1);
|
CFHTTPMessageRef message = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 100, NULL, kCFHTTPVersion1_1);
|
||||||
|
#if __has_feature(objc_arc)
|
||||||
|
_continueData = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message));
|
||||||
|
#else
|
||||||
_continueData = (NSData*)CFHTTPMessageCopySerializedMessage(message);
|
_continueData = (NSData*)CFHTTPMessageCopySerializedMessage(message);
|
||||||
|
#endif
|
||||||
CFRelease(message);
|
CFRelease(message);
|
||||||
DCHECK(_continueData);
|
DCHECK(_continueData);
|
||||||
}
|
}
|
||||||
if (_dateFormatter == nil) {
|
if (_dateFormatter == nil) {
|
||||||
|
DCHECK([NSThread isMainThread]); // NSDateFormatter should be initialized on main thread
|
||||||
_dateFormatter = [[NSDateFormatter alloc] init];
|
_dateFormatter = [[NSDateFormatter alloc] init];
|
||||||
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
_dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
|
||||||
_dateFormatter.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
|
_dateFormatter.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
|
||||||
_dateFormatter.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease];
|
_dateFormatter.locale = ARC_AUTORELEASE([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
|
||||||
DCHECK(_dateFormatter);
|
DCHECK(_dateFormatter);
|
||||||
}
|
}
|
||||||
if (_formatterQueue == NULL) {
|
if (_formatterQueue == NULL) {
|
||||||
@@ -265,10 +293,10 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
|
- (void)_initializeResponseHeadersWithStatusCode:(NSInteger)statusCode {
|
||||||
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
|
_responseMessage = CFHTTPMessageCreateResponse(kCFAllocatorDefault, statusCode, NULL, kCFHTTPVersion1_1);
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Connection"), CFSTR("Close"));
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (CFStringRef)[[_server class] serverName]);
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Server"), (ARC_BRIDGE CFStringRef)[[_server class] serverName]);
|
||||||
dispatch_sync(_formatterQueue, ^{
|
dispatch_sync(_formatterQueue, ^{
|
||||||
NSString* date = [_dateFormatter stringFromDate:[NSDate date]];
|
NSString* date = [_dateFormatter stringFromDate:[NSDate date]];
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (CFStringRef)date);
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Date"), (ARC_BRIDGE CFStringRef)date);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,23 +316,23 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
|
|
||||||
GCDWebServerResponse* response = [self processRequest:_request withBlock:_handler.processBlock];
|
GCDWebServerResponse* response = [self processRequest:_request withBlock:_handler.processBlock];
|
||||||
if (![response hasBody] || [response open]) {
|
if (![response hasBody] || [response open]) {
|
||||||
_response = [response retain];
|
_response = ARC_RETAIN(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_response) {
|
if (_response) {
|
||||||
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
[self _initializeResponseHeadersWithStatusCode:_response.statusCode];
|
||||||
NSUInteger maxAge = _response.cacheControlMaxAge;
|
NSUInteger maxAge = _response.cacheControlMaxAge;
|
||||||
if (maxAge > 0) {
|
if (maxAge > 0) {
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)maxAge]);
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), (ARC_BRIDGE CFStringRef)[NSString stringWithFormat:@"max-age=%i, public", (int)maxAge]);
|
||||||
} else {
|
} else {
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache"));
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Cache-Control"), CFSTR("no-cache"));
|
||||||
}
|
}
|
||||||
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
[_response.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (CFStringRef)key, (CFStringRef)obj);
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, (ARC_BRIDGE CFStringRef)key, (ARC_BRIDGE CFStringRef)obj);
|
||||||
}];
|
}];
|
||||||
if ([_response hasBody]) {
|
if ([_response hasBody]) {
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (CFStringRef)_response.contentType);
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Type"), (ARC_BRIDGE CFStringRef)_response.contentType);
|
||||||
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (CFStringRef)[NSString stringWithFormat:@"%i", (int)_response.contentLength]);
|
CFHTTPMessageSetHeaderFieldValue(_responseMessage, CFSTR("Content-Length"), (ARC_BRIDGE CFStringRef)[NSString stringWithFormat:@"%i", (int)_response.contentLength]);
|
||||||
}
|
}
|
||||||
[self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
[self _writeHeadersWithCompletionBlock:^(BOOL success) {
|
||||||
|
|
||||||
@@ -332,7 +360,7 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
NSInteger length = _request.contentLength;
|
NSInteger length = _request.contentLength;
|
||||||
if (initialData.length) {
|
if (initialData.length) {
|
||||||
NSInteger result = [_request write:initialData.bytes maxLength:initialData.length];
|
NSInteger result = [_request write:initialData.bytes maxLength:initialData.length];
|
||||||
if (result == initialData.length) {
|
if (result == (NSInteger)initialData.length) {
|
||||||
length -= initialData.length;
|
length -= initialData.length;
|
||||||
DCHECK(length >= 0);
|
DCHECK(length >= 0);
|
||||||
} else {
|
} else {
|
||||||
@@ -373,22 +401,22 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
[self _readHeadersWithCompletionBlock:^(NSData* extraData) {
|
[self _readHeadersWithCompletionBlock:^(NSData* extraData) {
|
||||||
|
|
||||||
if (extraData) {
|
if (extraData) {
|
||||||
NSString* requestMethod = [[(id)CFHTTPMessageCopyRequestMethod(_requestMessage) autorelease] uppercaseString];
|
NSString* requestMethod = [ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage)) uppercaseString];
|
||||||
DCHECK(requestMethod);
|
DCHECK(requestMethod);
|
||||||
NSURL* requestURL = [(id)CFHTTPMessageCopyRequestURL(_requestMessage) autorelease];
|
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(_requestMessage));
|
||||||
DCHECK(requestURL);
|
DCHECK(requestURL);
|
||||||
NSString* requestPath = GCDWebServerUnescapeURLString([(id)CFURLCopyPath((CFURLRef)requestURL) autorelease]); // Don't use -[NSURL path] which strips the ending slash
|
NSString* requestPath = GCDWebServerUnescapeURLString(ARC_BRIDGE_RELEASE(CFURLCopyPath((CFURLRef)requestURL))); // Don't use -[NSURL path] which strips the ending slash
|
||||||
DCHECK(requestPath);
|
DCHECK(requestPath);
|
||||||
NSDictionary* requestQuery = nil;
|
NSDictionary* requestQuery = nil;
|
||||||
NSString* queryString = [(id)CFURLCopyQueryString((CFURLRef)requestURL, NULL) autorelease]; // Don't use -[NSURL query] to make sure query is not unescaped;
|
NSString* queryString = ARC_BRIDGE_RELEASE(CFURLCopyQueryString((CFURLRef)requestURL, NULL)); // Don't use -[NSURL query] to make sure query is not unescaped;
|
||||||
if (queryString.length) {
|
if (queryString.length) {
|
||||||
requestQuery = GCDWebServerParseURLEncodedForm(queryString);
|
requestQuery = GCDWebServerParseURLEncodedForm(queryString);
|
||||||
DCHECK(requestQuery);
|
DCHECK(requestQuery);
|
||||||
}
|
}
|
||||||
NSDictionary* requestHeaders = [(id)CFHTTPMessageCopyAllHeaderFields(_requestMessage) autorelease];
|
NSDictionary* requestHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(_requestMessage));
|
||||||
DCHECK(requestHeaders);
|
DCHECK(requestHeaders);
|
||||||
for (_handler in _server.handlers) {
|
for (_handler in _server.handlers) {
|
||||||
_request = [_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery) retain];
|
_request = ARC_RETAIN(_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery));
|
||||||
if (_request) {
|
if (_request) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -396,7 +424,7 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
if (_request) {
|
if (_request) {
|
||||||
if (_request.hasBody) {
|
if (_request.hasBody) {
|
||||||
if (extraData.length <= _request.contentLength) {
|
if (extraData.length <= _request.contentLength) {
|
||||||
NSString* expectHeader = [(id)CFHTTPMessageCopyHeaderFieldValue(_requestMessage, CFSTR("Expect")) autorelease];
|
NSString* expectHeader = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyHeaderFieldValue(_requestMessage, CFSTR("Expect")));
|
||||||
if (expectHeader) {
|
if (expectHeader) {
|
||||||
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) {
|
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) {
|
||||||
[self _writeData:_continueData withCompletionBlock:^(BOOL success) {
|
[self _writeData:_continueData withCompletionBlock:^(BOOL success) {
|
||||||
@@ -432,8 +460,8 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
|
|
||||||
- (id)initWithServer:(GCDWebServer*)server address:(NSData*)address socket:(CFSocketNativeHandle)socket {
|
- (id)initWithServer:(GCDWebServer*)server address:(NSData*)address socket:(CFSocketNativeHandle)socket {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_server = [server retain];
|
_server = ARC_RETAIN(server);
|
||||||
_address = [address retain];
|
_address = ARC_RETAIN(address);
|
||||||
_socket = socket;
|
_socket = socket;
|
||||||
|
|
||||||
[self open];
|
[self open];
|
||||||
@@ -444,20 +472,20 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[self close];
|
[self close];
|
||||||
|
|
||||||
[_server release];
|
ARC_RELEASE(_server);
|
||||||
[_address release];
|
ARC_RELEASE(_address);
|
||||||
|
|
||||||
if (_requestMessage) {
|
if (_requestMessage) {
|
||||||
CFRelease(_requestMessage);
|
CFRelease(_requestMessage);
|
||||||
}
|
}
|
||||||
[_request release];
|
ARC_RELEASE(_request);
|
||||||
|
|
||||||
if (_responseMessage) {
|
if (_responseMessage) {
|
||||||
CFRelease(_responseMessage);
|
CFRelease(_responseMessage);
|
||||||
}
|
}
|
||||||
[_response release];
|
ARC_RELEASE(_response);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -469,6 +497,14 @@ static dispatch_queue_t _formatterQueue = NULL;
|
|||||||
[self _readRequestHeaders];
|
[self _readRequestHeaders];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)didUpdateBytesRead {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didUpdateBytesWritten {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
|
- (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {
|
||||||
LOG_DEBUG(@"Connection on socket %i processing %@ request for \"%@\" (%i bytes body)", _socket, _request.method, _request.path, _request.contentLength);
|
LOG_DEBUG(@"Connection on socket %i processing %@ request for \"%@\" (%i bytes body)", _socket, _request.method, _request.path, _request.contentLength);
|
||||||
GCDWebServerResponse* response = nil;
|
GCDWebServerResponse* response = nil;
|
||||||
|
|||||||
@@ -1,29 +1,54 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
|
or promote products derived from this software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
#import <TargetConditionals.h>
|
||||||
modification, are permitted provided that the following conditions are met:
|
#import <AvailabilityMacros.h>
|
||||||
* 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.
|
|
||||||
* Neither the name of the <organization> nor the
|
|
||||||
names of its contributors may be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
#if __has_feature(objc_arc)
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
#define ARC_BRIDGE __bridge
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
#define ARC_BRIDGE_RELEASE(__OBJECT__) CFBridgingRelease(__OBJECT__)
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
#define ARC_RETAIN(__OBJECT__) __OBJECT__
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
#define ARC_RELEASE(__OBJECT__)
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
#define ARC_AUTORELEASE(__OBJECT__) __OBJECT__
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
#define ARC_DEALLOC(__OBJECT__)
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
#if (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0)) || (!TARGET_OS_IPHONE && (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8))
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
#define ARC_DISPATCH_RELEASE(__OBJECT__)
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
#else
|
||||||
*/
|
#define ARC_DISPATCH_RELEASE(__OBJECT__) dispatch_release(__OBJECT__)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define ARC_BRIDGE
|
||||||
|
#define ARC_BRIDGE_RELEASE(__OBJECT__) [(id)__OBJECT__ autorelease]
|
||||||
|
#define ARC_RETAIN(__OBJECT__) [__OBJECT__ retain]
|
||||||
|
#define ARC_RELEASE(__OBJECT__) [__OBJECT__ release]
|
||||||
|
#define ARC_AUTORELEASE(__OBJECT__) [__OBJECT__ autorelease]
|
||||||
|
#define ARC_DEALLOC(__OBJECT__) [__OBJECT__ dealloc]
|
||||||
|
#define ARC_DISPATCH_RELEASE(__OBJECT__) dispatch_release(__OBJECT__)
|
||||||
|
#endif
|
||||||
|
|
||||||
#import "GCDWebServerConnection.h"
|
#import "GCDWebServerConnection.h"
|
||||||
|
|
||||||
@@ -46,8 +71,8 @@ static inline void __LogMessage(long level, NSString* format, ...) {
|
|||||||
va_start(arguments, format);
|
va_start(arguments, format);
|
||||||
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
|
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
|
||||||
va_end(arguments);
|
va_end(arguments);
|
||||||
printf("[%s] %s\n", levelNames[level], [message UTF8String]);
|
fprintf(stderr, "[%s] %s\n", levelNames[level], [message UTF8String]);
|
||||||
[message release];
|
ARC_RELEASE(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,18 +106,6 @@ static inline void __LogMessage(long level, NSString* format, ...) {
|
|||||||
#define kGCDWebServerDefaultMimeType @"application/octet-stream"
|
#define kGCDWebServerDefaultMimeType @"application/octet-stream"
|
||||||
#define kGCDWebServerGCDQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
#define kGCDWebServerGCDQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension);
|
|
||||||
NSString* GCDWebServerUnescapeURLString(NSString* string);
|
|
||||||
NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@interface GCDWebServerConnection ()
|
@interface GCDWebServerConnection ()
|
||||||
- (id)initWithServer:(GCDWebServer*)server address:(NSData*)address socket:(CFSocketNativeHandle)socket;
|
- (id)initWithServer:(GCDWebServer*)server address:(NSData*)address socket:(CFSocketNativeHandle)socket;
|
||||||
@end
|
@end
|
||||||
@@ -101,11 +114,7 @@ NSDictionary* GCDWebServerParseURLEncodedForm(NSString* form);
|
|||||||
@property(nonatomic, readonly) NSArray* handlers;
|
@property(nonatomic, readonly) NSArray* handlers;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerHandler : NSObject {
|
@interface GCDWebServerHandler : NSObject
|
||||||
@private
|
|
||||||
GCDWebServerMatchBlock _matchBlock;
|
|
||||||
GCDWebServerProcessBlock _processBlock;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock;
|
@property(nonatomic, readonly) GCDWebServerMatchBlock matchBlock;
|
||||||
@property(nonatomic, readonly) GCDWebServerProcessBlock processBlock;
|
@property(nonatomic, readonly) GCDWebServerProcessBlock processBlock;
|
||||||
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
- (id)initWithMatchBlock:(GCDWebServerMatchBlock)matchBlock processBlock:(GCDWebServerProcessBlock)processBlock;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -27,16 +27,7 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@interface GCDWebServerRequest : NSObject {
|
@interface GCDWebServerRequest : NSObject
|
||||||
@private
|
|
||||||
NSString* _method;
|
|
||||||
NSURL* _url;
|
|
||||||
NSDictionary* _headers;
|
|
||||||
NSString* _path;
|
|
||||||
NSDictionary* _query;
|
|
||||||
NSString* _type;
|
|
||||||
NSUInteger _length;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSString* method;
|
@property(nonatomic, readonly) NSString* method;
|
||||||
@property(nonatomic, readonly) NSURL* URL;
|
@property(nonatomic, readonly) NSURL* URL;
|
||||||
@property(nonatomic, readonly) NSDictionary* headers;
|
@property(nonatomic, readonly) NSDictionary* headers;
|
||||||
@@ -44,6 +35,7 @@
|
|||||||
@property(nonatomic, readonly) NSDictionary* query; // May be nil
|
@property(nonatomic, readonly) NSDictionary* query; // May be nil
|
||||||
@property(nonatomic, readonly) NSString* contentType; // Automatically parsed from headers (nil if request has no body)
|
@property(nonatomic, readonly) NSString* contentType; // Automatically parsed from headers (nil if request has no body)
|
||||||
@property(nonatomic, readonly) NSUInteger contentLength; // Automatically parsed from headers
|
@property(nonatomic, readonly) NSUInteger contentLength; // Automatically parsed from headers
|
||||||
|
@property(nonatomic, readonly) NSRange byteRange; // Automatically parsed from headers ([NSNotFound, 0] if request has no "Range" header, [offset, length] for byte range from beginning or [NSNotFound, -bytes] from end)
|
||||||
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query;
|
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query;
|
||||||
- (BOOL)hasBody; // Convenience method
|
- (BOOL)hasBody; // Convenience method
|
||||||
@end
|
@end
|
||||||
@@ -54,71 +46,35 @@
|
|||||||
- (BOOL)close; // Implementation required
|
- (BOOL)close; // Implementation required
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerDataRequest : GCDWebServerRequest {
|
@interface GCDWebServerDataRequest : GCDWebServerRequest
|
||||||
@private
|
|
||||||
NSMutableData* _data;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSData* data; // Only valid after open / write / close sequence
|
@property(nonatomic, readonly) NSData* data; // Only valid after open / write / close sequence
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerFileRequest : GCDWebServerRequest {
|
@interface GCDWebServerFileRequest : GCDWebServerRequest
|
||||||
@private
|
|
||||||
NSString* _filePath;
|
|
||||||
int _file;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSString* filePath; // Only valid after open / write / close sequence
|
@property(nonatomic, readonly) NSString* filePath; // Only valid after open / write / close sequence
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest {
|
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest
|
||||||
@private
|
|
||||||
NSDictionary* _arguments;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
||||||
+ (NSString*)mimeType;
|
+ (NSString*)mimeType;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerMultiPart : NSObject {
|
@interface GCDWebServerMultiPart : NSObject
|
||||||
@private
|
|
||||||
NSString* _contentType;
|
|
||||||
NSString* _mimeType;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSString* contentType; // May be nil
|
@property(nonatomic, readonly) NSString* contentType; // May be nil
|
||||||
@property(nonatomic, readonly) NSString* mimeType; // Defaults to "text/plain" per specifications if undefined
|
@property(nonatomic, readonly) NSString* mimeType; // Defaults to "text/plain" per specifications if undefined
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart {
|
@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart
|
||||||
@private
|
|
||||||
NSData* _data;
|
|
||||||
NSString* _string;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSData* data;
|
@property(nonatomic, readonly) NSData* data;
|
||||||
@property(nonatomic, readonly) NSString* string; // May be nil (only valid for text mime types
|
@property(nonatomic, readonly) NSString* string; // May be nil (only valid for text mime types
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart {
|
@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart
|
||||||
@private
|
|
||||||
NSString* _fileName;
|
|
||||||
NSString* _temporaryPath;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSString* fileName; // May be nil
|
@property(nonatomic, readonly) NSString* fileName; // May be nil
|
||||||
@property(nonatomic, readonly) NSString* temporaryPath;
|
@property(nonatomic, readonly) NSString* temporaryPath;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest {
|
@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest
|
||||||
@private
|
|
||||||
NSData* _boundary;
|
|
||||||
|
|
||||||
NSUInteger _parserState;
|
|
||||||
NSMutableData* _parserData;
|
|
||||||
NSString* _controlName;
|
|
||||||
NSString* _fileName;
|
|
||||||
NSString* _contentType;
|
|
||||||
NSString* _tmpPath;
|
|
||||||
int _tmpFile;
|
|
||||||
|
|
||||||
NSMutableDictionary* _arguments;
|
|
||||||
NSMutableDictionary* _files;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
||||||
@property(nonatomic, readonly) NSDictionary* files; // Only valid after open / write / close sequence
|
@property(nonatomic, readonly) NSDictionary* files; // Only valid after open / write / close sequence
|
||||||
+ (NSString*)mimeType;
|
+ (NSString*)mimeType;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -37,6 +37,76 @@ enum {
|
|||||||
kParserState_End
|
kParserState_End
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@interface GCDWebServerRequest () {
|
||||||
|
@private
|
||||||
|
NSString* _method;
|
||||||
|
NSURL* _url;
|
||||||
|
NSDictionary* _headers;
|
||||||
|
NSString* _path;
|
||||||
|
NSDictionary* _query;
|
||||||
|
NSString* _type;
|
||||||
|
NSUInteger _length;
|
||||||
|
NSRange _range;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerDataRequest () {
|
||||||
|
@private
|
||||||
|
NSMutableData* _data;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerFileRequest () {
|
||||||
|
@private
|
||||||
|
NSString* _filePath;
|
||||||
|
int _file;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerURLEncodedFormRequest () {
|
||||||
|
@private
|
||||||
|
NSDictionary* _arguments;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerMultiPart () {
|
||||||
|
@private
|
||||||
|
NSString* _contentType;
|
||||||
|
NSString* _mimeType;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerMultiPartArgument () {
|
||||||
|
@private
|
||||||
|
NSData* _data;
|
||||||
|
NSString* _string;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerMultiPartFile () {
|
||||||
|
@private
|
||||||
|
NSString* _fileName;
|
||||||
|
NSString* _temporaryPath;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerMultiPartFormRequest () {
|
||||||
|
@private
|
||||||
|
NSData* _boundary;
|
||||||
|
|
||||||
|
NSUInteger _parserState;
|
||||||
|
NSMutableData* _parserData;
|
||||||
|
NSString* _controlName;
|
||||||
|
NSString* _fileName;
|
||||||
|
NSString* _contentType;
|
||||||
|
NSString* _tmpPath;
|
||||||
|
int _tmpFile;
|
||||||
|
|
||||||
|
NSMutableDictionary* _arguments;
|
||||||
|
NSMutableDictionary* _files;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
static NSData* _newlineData = nil;
|
static NSData* _newlineData = nil;
|
||||||
static NSData* _newlinesData = nil;
|
static NSData* _newlinesData = nil;
|
||||||
static NSData* _dashNewlineData = nil;
|
static NSData* _dashNewlineData = nil;
|
||||||
@@ -54,7 +124,7 @@ static NSString* _ExtractHeaderParameter(NSString* header, NSString* attribute)
|
|||||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&value];
|
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[scanner release];
|
ARC_RELEASE(scanner);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -70,41 +140,70 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
|
|
||||||
@implementation GCDWebServerRequest : NSObject
|
@implementation GCDWebServerRequest : NSObject
|
||||||
|
|
||||||
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length;
|
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, byteRange=_range;
|
||||||
|
|
||||||
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_method = [method copy];
|
_method = [method copy];
|
||||||
_url = [url retain];
|
_url = ARC_RETAIN(url);
|
||||||
_headers = [headers retain];
|
_headers = ARC_RETAIN(headers);
|
||||||
_path = [path copy];
|
_path = [path copy];
|
||||||
_query = [query retain];
|
_query = ARC_RETAIN(query);
|
||||||
|
|
||||||
_type = [[_headers objectForKey:@"Content-Type"] retain];
|
_type = ARC_RETAIN([_headers objectForKey:@"Content-Type"]);
|
||||||
NSInteger length = [[_headers objectForKey:@"Content-Length"] integerValue];
|
NSInteger length = [[_headers objectForKey:@"Content-Length"] integerValue];
|
||||||
if (length < 0) {
|
if (length < 0) {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
[self release];
|
ARC_RELEASE(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
_length = length;
|
_length = length;
|
||||||
|
|
||||||
if ((_length > 0) && (_type == nil)) {
|
if ((_length > 0) && (_type == nil)) {
|
||||||
_type = [kGCDWebServerDefaultMimeType copy];
|
_type = [kGCDWebServerDefaultMimeType copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_range = NSMakeRange(NSNotFound, 0);
|
||||||
|
NSString* rangeHeader = [[_headers objectForKey:@"Range"] lowercaseString];
|
||||||
|
if (rangeHeader) {
|
||||||
|
if ([rangeHeader hasPrefix:@"bytes="]) {
|
||||||
|
NSArray* components = [[rangeHeader substringFromIndex:6] componentsSeparatedByString:@","];
|
||||||
|
if (components.count == 1) {
|
||||||
|
components = [[components firstObject] componentsSeparatedByString:@"-"];
|
||||||
|
if (components.count == 2) {
|
||||||
|
NSString* startString = [components objectAtIndex:0];
|
||||||
|
NSInteger startValue = [startString integerValue];
|
||||||
|
NSString* endString = [components objectAtIndex:1];
|
||||||
|
NSInteger endValue = [endString integerValue];
|
||||||
|
if (startString.length && (startValue >= 0) && endString.length && (endValue >= startValue)) { // The second 500 bytes: "500-999"
|
||||||
|
_range.location = startValue;
|
||||||
|
_range.length = endValue - startValue + 1;
|
||||||
|
} else if (startString.length && (startValue >= 0)) { // The bytes after 9500 bytes: "9500-"
|
||||||
|
_range.location = startValue;
|
||||||
|
_range.length = NSUIntegerMax;
|
||||||
|
} else if (endString.length && (endValue > 0)) { // The final 500 bytes: "-500"
|
||||||
|
_range.location = NSNotFound;
|
||||||
|
_range.length = endValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((_range.location == NSNotFound) && (_range.length == 0)) { // Ignore "Range" header if syntactically invalid
|
||||||
|
LOG_WARNING(@"Failed to parse 'Range' header \"%@\" for url: %@", rangeHeader, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_method release];
|
ARC_RELEASE(_method);
|
||||||
[_url release];
|
ARC_RELEASE(_url);
|
||||||
[_headers release];
|
ARC_RELEASE(_headers);
|
||||||
[_path release];
|
ARC_RELEASE(_path);
|
||||||
[_query release];
|
ARC_RELEASE(_query);
|
||||||
[_type release];
|
ARC_RELEASE(_type);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasBody {
|
- (BOOL)hasBody {
|
||||||
@@ -138,9 +237,9 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
DCHECK(_data != nil);
|
DCHECK(_data != nil);
|
||||||
[_data release];
|
ARC_RELEASE(_data);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)open {
|
- (BOOL)open {
|
||||||
@@ -168,7 +267,7 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
|
|
||||||
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
|
||||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||||
_filePath = [[NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]] retain];
|
_filePath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -176,9 +275,9 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
DCHECK(_file < 0);
|
DCHECK(_file < 0);
|
||||||
unlink([_filePath fileSystemRepresentation]);
|
unlink([_filePath fileSystemRepresentation]);
|
||||||
[_filePath release];
|
ARC_RELEASE(_filePath);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)open {
|
- (BOOL)open {
|
||||||
@@ -210,9 +309,9 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_arguments release];
|
ARC_RELEASE(_arguments);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)close {
|
- (BOOL)close {
|
||||||
@@ -222,8 +321,8 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
|
|
||||||
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
|
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
|
||||||
NSString* string = [[NSString alloc] initWithData:self.data encoding:_StringEncodingFromCharset(charset)];
|
NSString* string = [[NSString alloc] initWithData:self.data encoding:_StringEncodingFromCharset(charset)];
|
||||||
_arguments = [GCDWebServerParseURLEncodedForm(string) retain];
|
_arguments = ARC_RETAIN(GCDWebServerParseURLEncodedForm(string));
|
||||||
[string release];
|
ARC_RELEASE(string);
|
||||||
|
|
||||||
return (_arguments ? YES : NO);
|
return (_arguments ? YES : NO);
|
||||||
}
|
}
|
||||||
@@ -239,7 +338,7 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
_contentType = [contentType copy];
|
_contentType = [contentType copy];
|
||||||
NSArray* components = [_contentType componentsSeparatedByString:@";"];
|
NSArray* components = [_contentType componentsSeparatedByString:@";"];
|
||||||
if (components.count) {
|
if (components.count) {
|
||||||
_mimeType = [[[components objectAtIndex:0] lowercaseString] retain];
|
_mimeType = ARC_RETAIN([[components objectAtIndex:0] lowercaseString]);
|
||||||
}
|
}
|
||||||
if (_mimeType == nil) {
|
if (_mimeType == nil) {
|
||||||
_mimeType = @"text/plain";
|
_mimeType = @"text/plain";
|
||||||
@@ -249,10 +348,10 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_contentType release];
|
ARC_RELEASE(_contentType);
|
||||||
[_mimeType release];
|
ARC_RELEASE(_mimeType);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -263,7 +362,7 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
|
|
||||||
- (id)initWithContentType:(NSString*)contentType data:(NSData*)data {
|
- (id)initWithContentType:(NSString*)contentType data:(NSData*)data {
|
||||||
if ((self = [super initWithContentType:contentType])) {
|
if ((self = [super initWithContentType:contentType])) {
|
||||||
_data = [data retain];
|
_data = ARC_RETAIN(data);
|
||||||
|
|
||||||
if ([self.mimeType hasPrefix:@"text/"]) {
|
if ([self.mimeType hasPrefix:@"text/"]) {
|
||||||
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
|
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
|
||||||
@@ -274,10 +373,10 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_data release];
|
ARC_RELEASE(_data);
|
||||||
[_string release];
|
ARC_RELEASE(_string);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)description {
|
- (NSString*)description {
|
||||||
@@ -301,10 +400,10 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
unlink([_temporaryPath fileSystemRepresentation]);
|
unlink([_temporaryPath fileSystemRepresentation]);
|
||||||
|
|
||||||
[_fileName release];
|
ARC_RELEASE(_fileName);
|
||||||
[_temporaryPath release];
|
ARC_RELEASE(_temporaryPath);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)description {
|
- (NSString*)description {
|
||||||
@@ -340,11 +439,12 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
|
||||||
NSString* boundary = _ExtractHeaderParameter(self.contentType, @"boundary");
|
NSString* boundary = _ExtractHeaderParameter(self.contentType, @"boundary");
|
||||||
if (boundary) {
|
if (boundary) {
|
||||||
_boundary = [[[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] retain];
|
NSData* data = [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding];
|
||||||
|
_boundary = ARC_RETAIN(data);
|
||||||
}
|
}
|
||||||
if (_boundary == nil) {
|
if (_boundary == nil) {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
[self release];
|
ARC_RELEASE(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,13 +469,13 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
NSRange range = [_parserData rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _parserData.length)];
|
NSRange range = [_parserData rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _parserData.length)];
|
||||||
if (range.location != NSNotFound) {
|
if (range.location != NSNotFound) {
|
||||||
|
|
||||||
[_controlName release];
|
ARC_RELEASE(_controlName);
|
||||||
_controlName = nil;
|
_controlName = nil;
|
||||||
[_fileName release];
|
ARC_RELEASE(_fileName);
|
||||||
_fileName = nil;
|
_fileName = nil;
|
||||||
[_contentType release];
|
ARC_RELEASE(_contentType);
|
||||||
_contentType = nil;
|
_contentType = nil;
|
||||||
[_tmpPath release];
|
ARC_RELEASE(_tmpPath);
|
||||||
_tmpPath = nil;
|
_tmpPath = nil;
|
||||||
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
||||||
const char* temp = "GET / HTTP/1.0\r\n";
|
const char* temp = "GET / HTTP/1.0\r\n";
|
||||||
@@ -384,7 +484,7 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
if (CFHTTPMessageIsHeaderComplete(message)) {
|
if (CFHTTPMessageIsHeaderComplete(message)) {
|
||||||
NSString* controlName = nil;
|
NSString* controlName = nil;
|
||||||
NSString* fileName = nil;
|
NSString* fileName = nil;
|
||||||
NSDictionary* headers = [(id)CFHTTPMessageCopyAllHeaderFields(message) autorelease];
|
NSDictionary* headers = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(message));
|
||||||
NSString* contentDisposition = [headers objectForKey:@"Content-Disposition"];
|
NSString* contentDisposition = [headers objectForKey:@"Content-Disposition"];
|
||||||
if ([[contentDisposition lowercaseString] hasPrefix:@"form-data;"]) {
|
if ([[contentDisposition lowercaseString] hasPrefix:@"form-data;"]) {
|
||||||
controlName = _ExtractHeaderParameter(contentDisposition, @"name");
|
controlName = _ExtractHeaderParameter(contentDisposition, @"name");
|
||||||
@@ -392,7 +492,7 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
}
|
}
|
||||||
_controlName = [controlName copy];
|
_controlName = [controlName copy];
|
||||||
_fileName = [fileName copy];
|
_fileName = [fileName copy];
|
||||||
_contentType = [[headers objectForKey:@"Content-Type"] retain];
|
_contentType = ARC_RETAIN([headers objectForKey:@"Content-Type"]);
|
||||||
}
|
}
|
||||||
CFRelease(message);
|
CFRelease(message);
|
||||||
if (_controlName) {
|
if (_controlName) {
|
||||||
@@ -428,13 +528,13 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
const void* dataBytes = _parserData.bytes;
|
const void* dataBytes = _parserData.bytes;
|
||||||
NSUInteger dataLength = range.location - 2;
|
NSUInteger dataLength = range.location - 2;
|
||||||
if (_tmpPath) {
|
if (_tmpPath) {
|
||||||
int result = write(_tmpFile, dataBytes, dataLength);
|
ssize_t result = write(_tmpFile, dataBytes, dataLength);
|
||||||
if (result == dataLength) {
|
if (result == (ssize_t)dataLength) {
|
||||||
if (close(_tmpFile) == 0) {
|
if (close(_tmpFile) == 0) {
|
||||||
_tmpFile = 0;
|
_tmpFile = 0;
|
||||||
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithContentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
|
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithContentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
|
||||||
[_files setObject:file forKey:_controlName];
|
[_files setObject:file forKey:_controlName];
|
||||||
[file release];
|
ARC_RELEASE(file);
|
||||||
} else {
|
} else {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
success = NO;
|
success = NO;
|
||||||
@@ -443,14 +543,14 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
success = NO;
|
success = NO;
|
||||||
}
|
}
|
||||||
[_tmpPath release];
|
ARC_RELEASE(_tmpPath);
|
||||||
_tmpPath = nil;
|
_tmpPath = nil;
|
||||||
} else {
|
} else {
|
||||||
NSData* data = [[NSData alloc] initWithBytesNoCopy:(void*)dataBytes length:dataLength freeWhenDone:NO];
|
NSData* data = [[NSData alloc] initWithBytesNoCopy:(void*)dataBytes length:dataLength freeWhenDone:NO];
|
||||||
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithContentType:_contentType data:data];
|
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithContentType:_contentType data:data];
|
||||||
[_arguments setObject:argument forKey:_controlName];
|
[_arguments setObject:argument forKey:_controlName];
|
||||||
[argument release];
|
ARC_RELEASE(argument);
|
||||||
[data release];
|
ARC_RELEASE(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,8 +566,8 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
NSUInteger margin = 2 * _boundary.length;
|
NSUInteger margin = 2 * _boundary.length;
|
||||||
if (_tmpPath && (_parserData.length > margin)) {
|
if (_tmpPath && (_parserData.length > margin)) {
|
||||||
NSUInteger length = _parserData.length - margin;
|
NSUInteger length = _parserData.length - margin;
|
||||||
int result = write(_tmpFile, _parserData.bytes, length);
|
ssize_t result = write(_tmpFile, _parserData.bytes, length);
|
||||||
if (result == length) {
|
if (result == (ssize_t)length) {
|
||||||
[_parserData replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
[_parserData replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
||||||
} else {
|
} else {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
@@ -487,26 +587,31 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
|||||||
|
|
||||||
- (BOOL)close {
|
- (BOOL)close {
|
||||||
DCHECK(_parserData != nil);
|
DCHECK(_parserData != nil);
|
||||||
[_parserData release];
|
ARC_RELEASE(_parserData);
|
||||||
_parserData = nil;
|
_parserData = nil;
|
||||||
[_controlName release];
|
ARC_RELEASE(_controlName);
|
||||||
[_fileName release];
|
_controlName = nil;
|
||||||
[_contentType release];
|
ARC_RELEASE(_fileName);
|
||||||
|
_fileName = nil;
|
||||||
|
ARC_RELEASE(_contentType);
|
||||||
|
_contentType = nil;
|
||||||
if (_tmpFile > 0) {
|
if (_tmpFile > 0) {
|
||||||
close(_tmpFile);
|
close(_tmpFile);
|
||||||
unlink([_tmpPath fileSystemRepresentation]);
|
unlink([_tmpPath fileSystemRepresentation]);
|
||||||
|
_tmpFile = 0;
|
||||||
}
|
}
|
||||||
[_tmpPath release];
|
ARC_RELEASE(_tmpPath);
|
||||||
|
_tmpPath = nil;
|
||||||
return (_parserState == kParserState_End ? YES : NO);
|
return (_parserState == kParserState_End ? YES : NO);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
DCHECK(_parserData == nil);
|
DCHECK(_parserData == nil);
|
||||||
[_arguments release];
|
ARC_RELEASE(_arguments);
|
||||||
[_files release];
|
ARC_RELEASE(_files);
|
||||||
[_boundary release];
|
ARC_RELEASE(_boundary);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -27,14 +27,7 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@interface GCDWebServerResponse : NSObject {
|
@interface GCDWebServerResponse : NSObject
|
||||||
@private
|
|
||||||
NSString* _type;
|
|
||||||
NSUInteger _length;
|
|
||||||
NSInteger _status;
|
|
||||||
NSUInteger _maxAge;
|
|
||||||
NSMutableDictionary* _headers;
|
|
||||||
}
|
|
||||||
@property(nonatomic, readonly) NSString* contentType;
|
@property(nonatomic, readonly) NSString* contentType;
|
||||||
@property(nonatomic, readonly) NSUInteger contentLength;
|
@property(nonatomic, readonly) NSUInteger contentLength;
|
||||||
@property(nonatomic) NSInteger statusCode; // Default is 200
|
@property(nonatomic) NSInteger statusCode; // Default is 200
|
||||||
@@ -60,11 +53,7 @@
|
|||||||
- (id)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
- (id)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerDataResponse : GCDWebServerResponse {
|
@interface GCDWebServerDataResponse : GCDWebServerResponse
|
||||||
@private
|
|
||||||
NSData* _data;
|
|
||||||
NSInteger _offset;
|
|
||||||
}
|
|
||||||
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type;
|
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type;
|
||||||
- (id)initWithData:(NSData*)data contentType:(NSString*)type;
|
- (id)initWithData:(NSData*)data contentType:(NSString*)type;
|
||||||
@end
|
@end
|
||||||
@@ -78,13 +67,13 @@
|
|||||||
- (id)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; // Simple template system that replaces all occurences of "%variable%" with corresponding value (encodes using UTF-8)
|
- (id)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables; // Simple template system that replaces all occurences of "%variable%" with corresponding value (encodes using UTF-8)
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebServerFileResponse : GCDWebServerResponse {
|
@interface GCDWebServerFileResponse : GCDWebServerResponse
|
||||||
@private
|
|
||||||
NSString* _path;
|
|
||||||
int _file;
|
|
||||||
}
|
|
||||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path;
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path;
|
||||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
||||||
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path byteRange:(NSRange)range;
|
||||||
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
|
||||||
- (id)initWithFile:(NSString*)path;
|
- (id)initWithFile:(NSString*)path;
|
||||||
- (id)initWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
- (id)initWithFile:(NSString*)path isAttachment:(BOOL)attachment;
|
||||||
|
- (id)initWithFile:(NSString*)path byteRange:(NSRange)range; // Pass [NSNotFound, 0] to disable byte range entirely, [offset, length] to enable byte range from beginning of file or [NSNotFound, -bytes] from end of file
|
||||||
|
- (id)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -29,12 +29,38 @@
|
|||||||
|
|
||||||
#import "GCDWebServerPrivate.h"
|
#import "GCDWebServerPrivate.h"
|
||||||
|
|
||||||
|
@interface GCDWebServerResponse () {
|
||||||
|
@private
|
||||||
|
NSString* _type;
|
||||||
|
NSUInteger _length;
|
||||||
|
NSInteger _status;
|
||||||
|
NSUInteger _maxAge;
|
||||||
|
NSMutableDictionary* _headers;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerDataResponse () {
|
||||||
|
@private
|
||||||
|
NSData* _data;
|
||||||
|
NSInteger _offset;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GCDWebServerFileResponse () {
|
||||||
|
@private
|
||||||
|
NSString* _path;
|
||||||
|
NSUInteger _offset;
|
||||||
|
NSUInteger _size;
|
||||||
|
int _file;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation GCDWebServerResponse
|
@implementation GCDWebServerResponse
|
||||||
|
|
||||||
@synthesize contentType=_type, contentLength=_length, statusCode=_status, cacheControlMaxAge=_maxAge, additionalHeaders=_headers;
|
@synthesize contentType=_type, contentLength=_length, statusCode=_status, cacheControlMaxAge=_maxAge, additionalHeaders=_headers;
|
||||||
|
|
||||||
+(GCDWebServerResponse*) response {
|
+ (GCDWebServerResponse*)response {
|
||||||
return [[[[self class] alloc] init] autorelease];
|
return ARC_AUTORELEASE([[[self class] alloc] init]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init {
|
- (id)init {
|
||||||
@@ -57,10 +83,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_type release];
|
ARC_RELEASE(_type);
|
||||||
[_headers release];
|
ARC_RELEASE(_headers);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header {
|
- (void)setValue:(NSString*)value forAdditionalHeader:(NSString*)header {
|
||||||
@@ -95,11 +121,11 @@
|
|||||||
@implementation GCDWebServerResponse (Extensions)
|
@implementation GCDWebServerResponse (Extensions)
|
||||||
|
|
||||||
+ (GCDWebServerResponse*)responseWithStatusCode:(NSInteger)statusCode {
|
+ (GCDWebServerResponse*)responseWithStatusCode:(NSInteger)statusCode {
|
||||||
return [[[self alloc] initWithStatusCode:statusCode] autorelease];
|
return ARC_AUTORELEASE([[self alloc] initWithStatusCode:statusCode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (GCDWebServerResponse*)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
|
+ (GCDWebServerResponse*)responseWithRedirect:(NSURL*)location permanent:(BOOL)permanent {
|
||||||
return [[[self alloc] initWithRedirect:location permanent:permanent] autorelease];
|
return ARC_AUTORELEASE([[self alloc] initWithRedirect:location permanent:permanent]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithStatusCode:(NSInteger)statusCode {
|
- (id)initWithStatusCode:(NSInteger)statusCode {
|
||||||
@@ -122,18 +148,18 @@
|
|||||||
@implementation GCDWebServerDataResponse
|
@implementation GCDWebServerDataResponse
|
||||||
|
|
||||||
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type {
|
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type {
|
||||||
return [[[[self class] alloc] initWithData:data contentType:type] autorelease];
|
return ARC_AUTORELEASE([[[self class] alloc] initWithData:data contentType:type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithData:(NSData*)data contentType:(NSString*)type {
|
- (id)initWithData:(NSData*)data contentType:(NSString*)type {
|
||||||
if (data == nil) {
|
if (data == nil) {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
[self release];
|
ARC_RELEASE(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((self = [super initWithContentType:type contentLength:data.length])) {
|
if ((self = [super initWithContentType:type contentLength:data.length])) {
|
||||||
_data = [data retain];
|
_data = ARC_RETAIN(data);
|
||||||
_offset = -1;
|
_offset = -1;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -141,9 +167,9 @@
|
|||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
DCHECK(_offset < 0);
|
DCHECK(_offset < 0);
|
||||||
[_data release];
|
ARC_RELEASE(_data);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)open {
|
- (BOOL)open {
|
||||||
@@ -155,7 +181,7 @@
|
|||||||
- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
|
- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
|
||||||
DCHECK(_offset >= 0);
|
DCHECK(_offset >= 0);
|
||||||
NSInteger size = 0;
|
NSInteger size = 0;
|
||||||
if (_offset < _data.length) {
|
if (_offset < (NSInteger)_data.length) {
|
||||||
size = MIN(_data.length - _offset, length);
|
size = MIN(_data.length - _offset, length);
|
||||||
bcopy((char*)_data.bytes + _offset, buffer, size);
|
bcopy((char*)_data.bytes + _offset, buffer, size);
|
||||||
_offset += size;
|
_offset += size;
|
||||||
@@ -174,22 +200,22 @@
|
|||||||
@implementation GCDWebServerDataResponse (Extensions)
|
@implementation GCDWebServerDataResponse (Extensions)
|
||||||
|
|
||||||
+ (GCDWebServerDataResponse*)responseWithText:(NSString*)text {
|
+ (GCDWebServerDataResponse*)responseWithText:(NSString*)text {
|
||||||
return [[[self alloc] initWithText:text] autorelease];
|
return ARC_AUTORELEASE([[self alloc] initWithText:text]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (GCDWebServerDataResponse*)responseWithHTML:(NSString*)html {
|
+ (GCDWebServerDataResponse*)responseWithHTML:(NSString*)html {
|
||||||
return [[[self alloc] initWithHTML:html] autorelease];
|
return ARC_AUTORELEASE([[self alloc] initWithHTML:html]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (GCDWebServerDataResponse*)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
+ (GCDWebServerDataResponse*)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||||
return [[[self alloc] initWithHTMLTemplate:path variables:variables] autorelease];
|
return ARC_AUTORELEASE([[self alloc] initWithHTMLTemplate:path variables:variables]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithText:(NSString*)text {
|
- (id)initWithText:(NSString*)text {
|
||||||
NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding];
|
NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
if (data == nil) {
|
if (data == nil) {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
[self release];
|
ARC_RELEASE(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return [self initWithData:data contentType:@"text/plain; charset=utf-8"];
|
return [self initWithData:data contentType:@"text/plain; charset=utf-8"];
|
||||||
@@ -199,7 +225,7 @@
|
|||||||
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
|
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
if (data == nil) {
|
if (data == nil) {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
[self release];
|
ARC_RELEASE(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return [self initWithData:data contentType:@"text/html; charset=utf-8"];
|
return [self initWithData:data contentType:@"text/html; charset=utf-8"];
|
||||||
@@ -211,7 +237,7 @@
|
|||||||
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];
|
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];
|
||||||
}];
|
}];
|
||||||
id response = [self initWithHTML:html];
|
id response = [self initWithHTML:html];
|
||||||
[html release];
|
ARC_RELEASE(html);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,37 +246,76 @@
|
|||||||
@implementation GCDWebServerFileResponse
|
@implementation GCDWebServerFileResponse
|
||||||
|
|
||||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path {
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path {
|
||||||
return [[[[self class] alloc] initWithFile:path] autorelease];
|
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path]);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||||
return [[[[self class] alloc] initWithFile:path isAttachment:attachment] autorelease];
|
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path isAttachment:attachment]);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path byteRange:(NSRange)range {
|
||||||
|
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path byteRange:range]);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment {
|
||||||
|
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path byteRange:range isAttachment:attachment]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFile:(NSString*)path {
|
- (id)initWithFile:(NSString*)path {
|
||||||
return [self initWithFile:path isAttachment:NO];
|
return [self initWithFile:path byteRange:NSMakeRange(NSNotFound, 0) isAttachment:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)initWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
- (id)initWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||||
|
return [self initWithFile:path byteRange:NSMakeRange(NSNotFound, 0) isAttachment:attachment];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithFile:(NSString*)path byteRange:(NSRange)range {
|
||||||
|
return [self initWithFile:path byteRange:range isAttachment:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithFile:(NSString*)path byteRange:(NSRange)range isAttachment:(BOOL)attachment {
|
||||||
struct stat info;
|
struct stat info;
|
||||||
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
|
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
[self release];
|
ARC_RELEASE(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
if ((range.location != NSNotFound) || (range.length > 0)) {
|
||||||
|
if (range.location != NSNotFound) {
|
||||||
|
range.location = MIN(range.location, (NSUInteger)info.st_size);
|
||||||
|
range.length = MIN(range.length, info.st_size - range.location);
|
||||||
|
} else {
|
||||||
|
range.length = MIN(range.length, (NSUInteger)info.st_size);
|
||||||
|
range.location = info.st_size - range.length;
|
||||||
|
}
|
||||||
|
if (range.length == 0) {
|
||||||
|
ARC_RELEASE(self);
|
||||||
|
return nil; // TODO: Return 416 status code and "Content-Range: bytes */{file length}" header
|
||||||
|
}
|
||||||
|
}
|
||||||
NSString* type = GCDWebServerGetMimeTypeForExtension([path pathExtension]);
|
NSString* type = GCDWebServerGetMimeTypeForExtension([path pathExtension]);
|
||||||
if (type == nil) {
|
if (type == nil) {
|
||||||
type = kGCDWebServerDefaultMimeType;
|
type = kGCDWebServerDefaultMimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((self = [super initWithContentType:type contentLength:info.st_size])) {
|
if ((self = [super initWithContentType:type contentLength:(range.location != NSNotFound ? range.length : info.st_size)])) {
|
||||||
_path = [path copy];
|
_path = [path copy];
|
||||||
|
if (range.location != NSNotFound) {
|
||||||
|
_offset = range.location;
|
||||||
|
_size = range.length;
|
||||||
|
[self setStatusCode:206];
|
||||||
|
[self setValue:[NSString stringWithFormat:@"bytes %i-%i/%i", (int)range.location, (int)(range.location + range.length - 1), (int)info.st_size] forAdditionalHeader:@"Content-Range"];
|
||||||
|
LOG_DEBUG(@"Using content bytes range [%i-%i] for file \"%@\"", (int)range.location, (int)(range.location + range.length - 1), path);
|
||||||
|
} else {
|
||||||
|
_offset = 0;
|
||||||
|
_size = info.st_size;
|
||||||
|
}
|
||||||
if (attachment) { // TODO: Use http://tools.ietf.org/html/rfc5987 to encode file names with special characters instead of using lossy conversion to ISO 8859-1
|
if (attachment) { // TODO: Use http://tools.ietf.org/html/rfc5987 to encode file names with special characters instead of using lossy conversion to ISO 8859-1
|
||||||
NSData* data = [[path lastPathComponent] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
NSData* data = [[path lastPathComponent] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
||||||
NSString* fileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
|
NSString* fileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
|
||||||
if (fileName) {
|
if (fileName) {
|
||||||
[self setValue:[NSString stringWithFormat:@"attachment; filename=\"%@\"", fileName] forAdditionalHeader:@"Content-Disposition"];
|
[self setValue:[NSString stringWithFormat:@"attachment; filename=\"%@\"", fileName] forAdditionalHeader:@"Content-Disposition"];
|
||||||
[fileName release];
|
ARC_RELEASE(fileName);
|
||||||
} else {
|
} else {
|
||||||
DNOT_REACHED();
|
DNOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -261,20 +326,32 @@
|
|||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
DCHECK(_file <= 0);
|
DCHECK(_file <= 0);
|
||||||
[_path release];
|
ARC_RELEASE(_path);
|
||||||
|
|
||||||
[super dealloc];
|
ARC_DEALLOC(super);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)open {
|
- (BOOL)open {
|
||||||
DCHECK(_file <= 0);
|
DCHECK(_file <= 0);
|
||||||
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
||||||
return (_file > 0 ? YES : NO);
|
if (_file <= 0) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
||||||
|
close(_file);
|
||||||
|
_file = 0;
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
|
- (NSInteger)read:(void*)buffer maxLength:(NSUInteger)length {
|
||||||
DCHECK(_file > 0);
|
DCHECK(_file > 0);
|
||||||
return read(_file, buffer, length);
|
ssize_t result = read(_file, buffer, MIN(length, _size));
|
||||||
|
if (result > 0) {
|
||||||
|
_size -= result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)close {
|
- (BOOL)close {
|
||||||
|
|||||||
@@ -6,6 +6,21 @@
|
|||||||
objectVersion = 46;
|
objectVersion = 46;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXAggregateTarget section */
|
||||||
|
E274F876187E77D8009E0582 /* Build All */ = {
|
||||||
|
isa = PBXAggregateTarget;
|
||||||
|
buildConfigurationList = E274F879187E77D8009E0582 /* Build configuration list for PBXAggregateTarget "Build All" */;
|
||||||
|
buildPhases = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
E274F87D187E77E5009E0582 /* PBXTargetDependency */,
|
||||||
|
E274F87B187E77E3009E0582 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = "Build All";
|
||||||
|
productName = "Build All";
|
||||||
|
};
|
||||||
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
E208D149167B76B700500836 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D148167B76B700500836 /* CFNetwork.framework */; };
|
E208D149167B76B700500836 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D148167B76B700500836 /* CFNetwork.framework */; };
|
||||||
E208D1B3167BB17E00500836 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D1B2167BB17E00500836 /* CoreServices.framework */; };
|
E208D1B3167BB17E00500836 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E208D1B2167BB17E00500836 /* CoreServices.framework */; };
|
||||||
@@ -25,6 +40,23 @@
|
|||||||
E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; };
|
E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
E274F87A187E77E3009E0582 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 8DD76FA90486AB0100D96B5E;
|
||||||
|
remoteInfo = "GCDWebServer (Mac)";
|
||||||
|
};
|
||||||
|
E274F87C187E77E5009E0582 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = E22112591690B4DE0048D2B2;
|
||||||
|
remoteInfo = "GCDWebServer (iOS)";
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
8DD76FB20486AB0100D96B5E /* GCDWebServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = GCDWebServer; sourceTree = BUILT_PRODUCTS_DIR; };
|
8DD76FB20486AB0100D96B5E /* GCDWebServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = GCDWebServer; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
E208D148167B76B700500836 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
|
E208D148167B76B700500836 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
|
||||||
@@ -47,6 +79,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; };
|
||||||
|
E263213318DB688E00D9DC0C /* index.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = index.html; sourceTree = "<group>"; };
|
||||||
|
E263213418DB7F7900D9DC0C /* filedrop-min.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "filedrop-min.js"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -113,6 +147,8 @@
|
|||||||
E221128D1690B6470048D2B2 /* Mac */ = {
|
E221128D1690B6470048D2B2 /* Mac */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E263213418DB7F7900D9DC0C /* filedrop-min.js */,
|
||||||
|
E263213318DB688E00D9DC0C /* index.html */,
|
||||||
E221128E1690B6470048D2B2 /* main.m */,
|
E221128E1690B6470048D2B2 /* main.m */,
|
||||||
);
|
);
|
||||||
path = Mac;
|
path = Mac;
|
||||||
@@ -189,6 +225,9 @@
|
|||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 0510;
|
||||||
|
};
|
||||||
buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "GCDWebServer" */;
|
buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "GCDWebServer" */;
|
||||||
compatibilityVersion = "Xcode 3.2";
|
compatibilityVersion = "Xcode 3.2";
|
||||||
developmentRegion = English;
|
developmentRegion = English;
|
||||||
@@ -204,6 +243,7 @@
|
|||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
|
E274F876187E77D8009E0582 /* Build All */,
|
||||||
8DD76FA90486AB0100D96B5E /* GCDWebServer (Mac) */,
|
8DD76FA90486AB0100D96B5E /* GCDWebServer (Mac) */,
|
||||||
E22112591690B4DE0048D2B2 /* GCDWebServer (iOS) */,
|
E22112591690B4DE0048D2B2 /* GCDWebServer (iOS) */,
|
||||||
);
|
);
|
||||||
@@ -238,12 +278,35 @@
|
|||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
E274F87B187E77E3009E0582 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 8DD76FA90486AB0100D96B5E /* GCDWebServer (Mac) */;
|
||||||
|
targetProxy = E274F87A187E77E3009E0582 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
E274F87D187E77E5009E0582 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = E22112591690B4DE0048D2B2 /* GCDWebServer (iOS) */;
|
||||||
|
targetProxy = E274F87C187E77E5009E0582 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
1DEB928608733DD80010E9CD /* Debug */ = {
|
1DEB928608733DD80010E9CD /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
ARCHS = "$(ARCHS_STANDARD)";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
_index_html_,
|
||||||
|
"\"Mac/index.html\"",
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
_filedrop_js_,
|
||||||
|
"\"Mac/filedrop-min.js\"",
|
||||||
|
);
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
};
|
};
|
||||||
@@ -252,8 +315,18 @@
|
|||||||
1DEB928708733DD80010E9CD /* Release */ = {
|
1DEB928708733DD80010E9CD /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
ARCHS = "$(ARCHS_STANDARD)";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
_index_html_,
|
||||||
|
"\"Mac/index.html\"",
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
_filedrop_js_,
|
||||||
|
"\"Mac/filedrop-min.js\"",
|
||||||
|
);
|
||||||
PRODUCT_NAME = GCDWebServer;
|
PRODUCT_NAME = GCDWebServer;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
};
|
};
|
||||||
@@ -262,19 +335,28 @@
|
|||||||
1DEB928A08733DD80010E9CD /* Debug */ = {
|
1DEB928A08733DD80010E9CD /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
WARNING_CFLAGS = "-Wall";
|
WARNING_CFLAGS = (
|
||||||
|
"-Wall",
|
||||||
|
"-Weverything",
|
||||||
|
"-Wno-gnu-statement-expression",
|
||||||
|
"-Wno-direct-ivar-access",
|
||||||
|
"-Wno-implicit-retain-self",
|
||||||
|
"-Wno-assign-enum",
|
||||||
|
"-Wno-format-nonliteral",
|
||||||
|
"-Wno-cast-align",
|
||||||
|
);
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
1DEB928B08733DD80010E9CD /* Release */ = {
|
1DEB928B08733DD80010E9CD /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
NDEBUG,
|
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
|
||||||
NS_BLOCK_ASSERTIONS,
|
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||||
);
|
|
||||||
WARNING_CFLAGS = "-Wall";
|
WARNING_CFLAGS = "-Wall";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -282,6 +364,7 @@
|
|||||||
E22112761690B4DF0048D2B2 /* Debug */ = {
|
E22112761690B4DF0048D2B2 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
INFOPLIST_FILE = iOS/Info.plist;
|
INFOPLIST_FILE = iOS/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||||
@@ -295,6 +378,7 @@
|
|||||||
E22112771690B4DF0048D2B2 /* Release */ = {
|
E22112771690B4DF0048D2B2 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
INFOPLIST_FILE = iOS/Info.plist;
|
INFOPLIST_FILE = iOS/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
||||||
@@ -305,6 +389,20 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
E274F877187E77D8009E0582 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
E274F878187E77D8009E0582 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
@@ -333,6 +431,16 @@
|
|||||||
E22112771690B4DF0048D2B2 /* Release */,
|
E22112771690B4DF0048D2B2 /* Release */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
E274F879187E77D8009E0582 /* Build configuration list for PBXAggregateTarget "Build All" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
E274F877187E77D8009E0582 /* Debug */,
|
||||||
|
E274F878187E77D8009E0582 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
|
|||||||
10
LICENSE
10
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -8,14 +8,14 @@ modification, are permitted provided that the following conditions are met:
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
|||||||
11
Mac/filedrop-min.js
vendored
Executable file
11
Mac/filedrop-min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
91
Mac/index.html
Executable file
91
Mac/index.html
Executable file
@@ -0,0 +1,91 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Basic FileDrop example</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="filedrop-min.js"></script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
/* Essential FileDrop zone element configuration: */
|
||||||
|
.fd-zone {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
/* The following are not required but create a pretty box: */
|
||||||
|
width: 15em;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hides <input type="file"> while simulating "Browse" button: */
|
||||||
|
.fd-file {
|
||||||
|
opacity: 0;
|
||||||
|
font-size: 118px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
filter: alpha(opacity=0);
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Provides visible feedback when use drags a file over the drop zone: */
|
||||||
|
.fd-zone.over { border-color: maroon; background: #eee; }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<noscript style="color: maroon">
|
||||||
|
<h2>JavaScript is disabled in your browser. How do you expect FileDrop to work?</h2>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
<h2 style="text-align: center">
|
||||||
|
<a href="http://filedropjs.org">FileDrop</a> basic sample
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<!-- A FileDrop area. Can contain any text or elements, or be empty.
|
||||||
|
Can be of any HTML tag too, not necessary fieldset. -->
|
||||||
|
<fieldset id="zone">
|
||||||
|
<legend>Drop a file inside…</legend>
|
||||||
|
<p>Or click here to <em>Browse</em>..</p>
|
||||||
|
|
||||||
|
<!-- Putting another element on top of file input so it overlays it
|
||||||
|
and user can interact with it freely. -->
|
||||||
|
<p style="z-index: 10; position: relative">
|
||||||
|
<input type="checkbox" id="multiple">
|
||||||
|
<label for="multiple">Allow multiple selection</label>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// Attach FileDrop to an area ('zone' is an ID but you can also give a DOM node):
|
||||||
|
var zone = new FileDrop('zone', {});
|
||||||
|
|
||||||
|
// Do something when a user chooses or drops a file:
|
||||||
|
zone.event('send', function (files) {
|
||||||
|
// Depending on browser support files (FileList) might contain multiple items.
|
||||||
|
files.each(function (file) {
|
||||||
|
// React on successful AJAX upload:
|
||||||
|
file.event('done', function (xhr) {
|
||||||
|
// 'this' here points to fd.File instance that has triggered the event.
|
||||||
|
alert('Done uploading ' + this.name + ', response:\n\n' + xhr.responseText);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send the file:
|
||||||
|
file.sendTo('ajax-upload');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// A bit of sugar - toggling multiple selection:
|
||||||
|
fd.addEvent(fd.byID('multiple'), 'change', function (e) {
|
||||||
|
zone.multiple(e.currentTarget || e.srcElement.checked);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
58
Mac/main.m
58
Mac/main.m
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -25,19 +25,33 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#import <mach-o/getsect.h>
|
||||||
|
|
||||||
#import "GCDWebServer.h"
|
#import "GCDWebServer.h"
|
||||||
|
|
||||||
|
static NSData* _DataFromTEXTSection(const char* name) {
|
||||||
|
unsigned long size = 0;
|
||||||
|
char* ptr = getsectdata("__TEXT", name, &size);
|
||||||
|
if (!ptr || !size) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return [NSData dataWithBytesNoCopy:ptr length:size freeWhenDone:NO];
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
BOOL success = NO;
|
BOOL success = NO;
|
||||||
|
int mode = (argc == 2 ? MIN(MAX(atoi(argv[1]), 0), 3) : 0);
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
GCDWebServer* webServer = [[GCDWebServer alloc] init];
|
GCDWebServer* webServer = [[GCDWebServer alloc] init];
|
||||||
switch (0) {
|
switch (mode) {
|
||||||
|
|
||||||
|
// Simply serve contents of home directory
|
||||||
case 0: {
|
case 0: {
|
||||||
[webServer addHandlerForBasePath:@"/" localPath:NSHomeDirectory() indexFilename:nil cacheAge:0];
|
[webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:0 allowRangeRequests:YES];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Renders a HTML page
|
||||||
case 1: {
|
case 1: {
|
||||||
[webServer addDefaultHandlerForMethod:@"GET"
|
[webServer addDefaultHandlerForMethod:@"GET"
|
||||||
requestClass:[GCDWebServerRequest class]
|
requestClass:[GCDWebServerRequest class]
|
||||||
@@ -49,6 +63,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implements an HTML form
|
||||||
case 2: {
|
case 2: {
|
||||||
[webServer addHandlerForMethod:@"GET"
|
[webServer addHandlerForMethod:@"GET"
|
||||||
path:@"/"
|
path:@"/"
|
||||||
@@ -79,9 +94,38 @@ int main(int argc, const char* argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implements drag & drop file upload using http://filedropjs.org (requires Chrome 13+, Firefox 3.6+, IE 10+ or Safari 6+)
|
||||||
|
case 3: {
|
||||||
|
[webServer addGETHandlerForPath:@"/"
|
||||||
|
staticData:_DataFromTEXTSection("_index_html_")
|
||||||
|
contentType:@"text/html; charset=utf-8"
|
||||||
|
cacheAge:0];
|
||||||
|
[webServer addGETHandlerForPath:@"/filedrop-min.js"
|
||||||
|
staticData:_DataFromTEXTSection("_filedrop_js_")
|
||||||
|
contentType:@"application/javascript; charset=utf-8"
|
||||||
|
cacheAge:0];
|
||||||
|
[webServer addHandlerForMethod:@"POST" path:@"/ajax-upload" requestClass:[GCDWebServerFileRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
|
NSString* fileName = GCDWebServerUnescapeURLString([request.headers objectForKey:@"X-File-Name"]);
|
||||||
|
NSString* inPath = [(GCDWebServerFileRequest*)request filePath];
|
||||||
|
NSString* outPath = [@"/tmp" stringByAppendingPathComponent:fileName];
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:outPath error:NULL];
|
||||||
|
if ([[NSFileManager defaultManager] moveItemAtPath:inPath toPath:outPath error:NULL]) {
|
||||||
|
NSString* message = [NSString stringWithFormat:@"File uploaded to \"%@\"", outPath];
|
||||||
|
return [GCDWebServerDataResponse responseWithText:message];
|
||||||
|
} else {
|
||||||
|
return [GCDWebServerResponse responseWithStatusCode:500];
|
||||||
|
}
|
||||||
|
|
||||||
|
}];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
success = [webServer runWithPort:8080];
|
success = [webServer runWithPort:8080];
|
||||||
|
#if !__has_feature(objc_arc)
|
||||||
[webServer release];
|
[webServer release];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return success ? 0 : -1;
|
return success ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -7,10 +7,10 @@ GCDWebServer is a lightweight GCD based HTTP 1.1 server designed to be embedded
|
|||||||
* Support for streaming large HTTP bodies for requests and responses to minimize memory usage
|
* Support for streaming large HTTP bodies for requests and responses to minimize memory usage
|
||||||
* Built-in parser for web forms submitted using "application/x-www-form-urlencoded" or "multipart/form-data" encodings (including file uploads)
|
* Built-in parser for web forms submitted using "application/x-www-form-urlencoded" or "multipart/form-data" encodings (including file uploads)
|
||||||
* Minimal number of source files and no dependencies on third-party source code
|
* Minimal number of source files and no dependencies on third-party source code
|
||||||
* Available under a friendly [New BSD License](GCDWebServer/blob/master/LICENSE)
|
* Available under a friendly [New BSD License](LICENSE)
|
||||||
|
|
||||||
What's not available out of the box but can be implemented on top of the API:
|
What's not available out of the box but can be implemented on top of the API:
|
||||||
* Authentication like Basic Authentication
|
* Authentication like [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
||||||
* Web forms submitted using "multipart/mixed"
|
* Web forms submitted using "multipart/mixed"
|
||||||
|
|
||||||
What's not supported (but not really required from an embedded HTTP server):
|
What's not supported (but not really required from an embedded HTTP server):
|
||||||
@@ -18,8 +18,8 @@ What's not supported (but not really required from an embedded HTTP server):
|
|||||||
* HTTPS
|
* HTTPS
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
* OS X 10.7 or later
|
* OS X 10.7 or later (x86_64)
|
||||||
* iOS 5.0 or later
|
* iOS 5.0 or later (armv7, armv7s or arm64)
|
||||||
|
|
||||||
Hello World
|
Hello World
|
||||||
===========
|
===========
|
||||||
@@ -67,7 +67,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
|
|
||||||
GCDWebServer* webServer = [[GCDWebServer alloc] init];
|
GCDWebServer* webServer = [[GCDWebServer alloc] init];
|
||||||
[webServer addHandlerForBasePath:@"/" localPath:NSHomeDirectory() indexFilename:nil cacheAge:3600];
|
[webServer addGETHandlerForBasePath:@"/" directoryPath:NSHomeDirectory() indexFilename:nil cacheAge:3600 allowRangeRequests:YES];
|
||||||
[webServer runWithPort:8080];
|
[webServer runWithPort:8080];
|
||||||
[webServer release];
|
[webServer release];
|
||||||
|
|
||||||
@@ -91,8 +91,8 @@ Understanding GCDWebServer Architecture
|
|||||||
GCDWebServer is made of only 4 core classes:
|
GCDWebServer is made of only 4 core classes:
|
||||||
* 'GCDWebServer' manages the socket that listens for new HTTP connections and the list of handlers used by the server.
|
* 'GCDWebServer' manages the socket that listens for new HTTP connections and the list of handlers used by the server.
|
||||||
* 'GCDWebServerConnection' is instantiated by 'GCDWebServer' to handle each new HTTP connection. Each instance stays alive until the connection is closed. You cannot use this class directly, but it is exposed so you can subclass it to override some hooks.
|
* 'GCDWebServerConnection' is instantiated by 'GCDWebServer' to handle each new HTTP connection. Each instance stays alive until the connection is closed. You cannot use this class directly, but it is exposed so you can subclass it to override some hooks.
|
||||||
* 'GCDWebServerRequest' is created by the 'GCDWebServerConnection' instance after HTTP headers have been received. It wraps the request and handles the HTTP body if any. GCDWebServer comes with several subclasses of 'GCDWebServerRequest' to handle common cases like storing the body in memory or stream it to a file on disk. See [GCDWebServerRequest.h](GCDWebServer/blob/master/CGDWebServer/GCDWebServerRequest.h) for the full list.
|
* 'GCDWebServerRequest' is created by the 'GCDWebServerConnection' instance after HTTP headers have been received. It wraps the request and handles the HTTP body if any. GCDWebServer comes with several subclasses of 'GCDWebServerRequest' to handle common cases like storing the body in memory or stream it to a file on disk. See [GCDWebServerRequest.h](CGDWebServer/GCDWebServerRequest.h) for the full list.
|
||||||
* 'GCDWebServerResponse' is created by the request handler and wraps the response HTTP headers and optional body. GCDWebServer provides several subclasses of 'GCDWebServerResponse' to handle common cases like HTML text in memory or streaming a file from disk. See [GCDWebServerResponse.h](GCDWebServer/blob/master/CGDWebServer/GCDWebServerResponse.h) for the full list.
|
* 'GCDWebServerResponse' is created by the request handler and wraps the response HTTP headers and optional body. GCDWebServer provides several subclasses of 'GCDWebServerResponse' to handle common cases like HTML text in memory or streaming a file from disk. See [GCDWebServerResponse.h](CGDWebServer/GCDWebServerResponse.h) for the full list.
|
||||||
|
|
||||||
Implementing Handlers
|
Implementing Handlers
|
||||||
=====================
|
=====================
|
||||||
@@ -171,7 +171,7 @@ Assuming you have a website directory in your app containing HTML template files
|
|||||||
NSString* websitePath = [[NSBundle mainBundle] pathForResource:@"Website" ofType:nil];
|
NSString* websitePath = [[NSBundle mainBundle] pathForResource:@"Website" ofType:nil];
|
||||||
|
|
||||||
// Add a default handler to serve static files (i.e. anything other than HTML files)
|
// Add a default handler to serve static files (i.e. anything other than HTML files)
|
||||||
[self addHandlerForBasePath:@"/" localPath:websitePath indexFilename:nil cacheAge:3600];
|
[self addGETHandlerForBasePath:@"/" directoryPath:websitePath indexFilename:nil cacheAge:3600 allowRangeRequests:YES];
|
||||||
|
|
||||||
// Add an override handler for all requests to "*.html" URLs to do the special HTML templatization
|
// Add an override handler for all requests to "*.html" URLs to do the special HTML templatization
|
||||||
[self addHandlerForMethod:@"GET"
|
[self addHandlerForMethod:@"GET"
|
||||||
@@ -203,4 +203,4 @@ Final Example: File Downloads and Uploads From iOS App
|
|||||||
|
|
||||||
GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It uses it to provide a web server for people to upload and download comic files directly over WiFi to and from the app.
|
GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It uses it to provide a web server for people to upload and download comic files directly over WiFi to and from the app.
|
||||||
|
|
||||||
ComicFlow is [entirely open-source](https://code.google.com/p/comicflow/) and you can see how it uses GCDWebServer in the [WebServer.m](http://code.google.com/p/comicflow/source/browse/Classes/WebServer.m) file.
|
ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebServer in the [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) file.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -29,10 +29,6 @@
|
|||||||
|
|
||||||
#import "GCDWebServer.h"
|
#import "GCDWebServer.h"
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate> {
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
@private
|
|
||||||
UIWindow* _window;
|
|
||||||
GCDWebServer* _webServer;
|
|
||||||
}
|
|
||||||
@property(retain, nonatomic) UIWindow* window;
|
@property(retain, nonatomic) UIWindow* window;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
@@ -27,14 +27,27 @@
|
|||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
@interface AppDelegate () {
|
||||||
|
@private
|
||||||
|
UIWindow* _window;
|
||||||
|
GCDWebServer* _webServer;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
@synthesize window=_window;
|
||||||
|
|
||||||
|
#if !__has_feature(objc_arc)
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[_window release];
|
[_window release];
|
||||||
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||||
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||||
_window.backgroundColor = [UIColor whiteColor];
|
_window.backgroundColor = [UIColor whiteColor];
|
||||||
|
|||||||
10
iOS/main.m
10
iOS/main.m
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright (c) 2012-2013, Pierre-Olivier Latour
|
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
* Redistributions in binary form must reproduce the above copyright
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
notice, this list of conditions and the following disclaimer in the
|
notice, this list of conditions and the following disclaimer in the
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of the <organization> nor the
|
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||||
names of its contributors may be used to endorse or promote products
|
or promote products derived from this software without specific
|
||||||
derived from this software without specific prior written permission.
|
prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
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
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
|||||||
Reference in New Issue
Block a user