mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-05-13 00:02:02 +08:00
Split class files
This commit is contained in:
@@ -88,6 +88,33 @@ void GCDLogMessage(long level, NSString* format, ...) {
|
||||
|
||||
#endif
|
||||
|
||||
NSString* GCDWebServerExtractHeaderParameter(NSString* header, NSString* attribute) {
|
||||
NSString* value = nil;
|
||||
if (header) {
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:header];
|
||||
NSString* string = [NSString stringWithFormat:@"%@=", attribute];
|
||||
if ([scanner scanUpToString:string intoString:NULL]) {
|
||||
[scanner scanString:string intoString:NULL];
|
||||
if ([scanner scanString:@"\"" intoString:NULL]) {
|
||||
[scanner scanUpToString:@"\"" intoString:&value];
|
||||
} else {
|
||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&value];
|
||||
}
|
||||
}
|
||||
ARC_RELEASE(scanner);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// http://www.w3schools.com/tags/ref_charactersets.asp
|
||||
NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset) {
|
||||
NSStringEncoding encoding = kCFStringEncodingInvalidId;
|
||||
if (charset) {
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset));
|
||||
}
|
||||
return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding);
|
||||
}
|
||||
|
||||
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
||||
static NSDictionary* _overrides = nil;
|
||||
if (_overrides == nil) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
@interface GCDWebServerDataRequest : GCDWebServerRequest
|
||||
@property(nonatomic, readonly) NSData* data; // Only valid after open / write / close sequence
|
||||
@end
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerDataRequest () {
|
||||
@private
|
||||
NSMutableData* _data;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataRequest
|
||||
|
||||
@synthesize data=_data;
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_data != nil);
|
||||
ARC_RELEASE(_data);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open {
|
||||
DCHECK(_data == nil);
|
||||
_data = [[NSMutableData alloc] initWithCapacity:self.contentLength];
|
||||
return _data ? YES : NO;
|
||||
}
|
||||
|
||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
|
||||
DCHECK(_data != nil);
|
||||
[_data appendBytes:buffer length:length];
|
||||
return length;
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
DCHECK(_data != nil);
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
@interface GCDWebServerDataResponse : GCDWebServerResponse
|
||||
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type;
|
||||
- (id)initWithData:(NSData*)data contentType:(NSString*)type;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataResponse (Extensions)
|
||||
+ (GCDWebServerDataResponse*)responseWithText:(NSString*)text;
|
||||
+ (GCDWebServerDataResponse*)responseWithHTML:(NSString*)html;
|
||||
+ (GCDWebServerDataResponse*)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables;
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object;
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
- (id)initWithText:(NSString*)text; // Encodes using UTF-8
|
||||
- (id)initWithHTML:(NSString*)html; // 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)
|
||||
- (id)initWithJSONObject:(id)object;
|
||||
- (id)initWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
@end
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerDataResponse () {
|
||||
@private
|
||||
NSData* _data;
|
||||
BOOL _done;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataResponse
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithData:data contentType:type]);
|
||||
}
|
||||
|
||||
- (id)initWithData:(NSData*)data contentType:(NSString*)type {
|
||||
if (data == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_data = ARC_RETAIN(data);
|
||||
|
||||
self.contentType = type;
|
||||
self.contentLength = data.length;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_data);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
NSData* data;
|
||||
if (_done) {
|
||||
data = [NSData data];
|
||||
} else {
|
||||
data = _data;
|
||||
_done = YES;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataResponse (Extensions)
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithText:(NSString*)text {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithText:text]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithHTML:(NSString*)html {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithHTML:html]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithHTMLTemplate:path variables:variables]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithJSONObject:object]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithJSONObject:object contentType:type]);
|
||||
}
|
||||
|
||||
- (id)initWithText:(NSString*)text {
|
||||
NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/plain; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (id)initWithHTML:(NSString*)html {
|
||||
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/html; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (id)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||
NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
||||
[variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) {
|
||||
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];
|
||||
}];
|
||||
id response = [self initWithHTML:html];
|
||||
ARC_RELEASE(html);
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id)initWithJSONObject:(id)object {
|
||||
return [self initWithJSONObject:object contentType:@"application/json"];
|
||||
}
|
||||
|
||||
- (id)initWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL];
|
||||
if (data == nil) {
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:type];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
@interface GCDWebServerFileRequest : GCDWebServerRequest
|
||||
@property(nonatomic, readonly) NSString* filePath; // Only valid after open / write / close sequence
|
||||
@end
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerFileRequest () {
|
||||
@private
|
||||
NSString* _filePath;
|
||||
int _file;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerFileRequest
|
||||
|
||||
@synthesize filePath=_filePath;
|
||||
|
||||
- (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])) {
|
||||
_filePath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_file < 0);
|
||||
unlink([_filePath fileSystemRepresentation]);
|
||||
ARC_RELEASE(_filePath);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open {
|
||||
DCHECK(_file == 0);
|
||||
_file = open([_filePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
return (_file > 0 ? YES : NO);
|
||||
}
|
||||
|
||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
|
||||
DCHECK(_file > 0);
|
||||
return write(_file, buffer, length);
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
DCHECK(_file > 0);
|
||||
int result = close(_file);
|
||||
_file = -1;
|
||||
return (result == 0 ? YES : NO);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerResponse.h"
|
||||
|
||||
@interface GCDWebServerFileResponse : GCDWebServerResponse
|
||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path;
|
||||
+ (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 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
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <sys/stat.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kFileReadBufferSize (32 * 1024)
|
||||
|
||||
@interface GCDWebServerFileResponse () {
|
||||
@private
|
||||
NSString* _path;
|
||||
NSUInteger _offset;
|
||||
NSUInteger _size;
|
||||
int _file;
|
||||
}
|
||||
@end
|
||||
|
||||
static inline NSError* _MakePosixError(int code) {
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%s", strerror(code)]}];
|
||||
}
|
||||
|
||||
@implementation GCDWebServerFileResponse
|
||||
|
||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||
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 {
|
||||
return [self initWithFile:path byteRange:NSMakeRange(NSNotFound, 0) isAttachment:NO];
|
||||
}
|
||||
|
||||
- (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;
|
||||
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
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, (NSUInteger)info.st_size - range.location);
|
||||
} else {
|
||||
range.length = MIN(range.length, (NSUInteger)info.st_size);
|
||||
range.location = (NSUInteger)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
|
||||
}
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_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 = (NSUInteger)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
|
||||
NSData* data = [[path lastPathComponent] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
||||
NSString* fileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
|
||||
if (fileName) {
|
||||
[self setValue:[NSString stringWithFormat:@"attachment; filename=\"%@\"", fileName] forAdditionalHeader:@"Content-Disposition"];
|
||||
ARC_RELEASE(fileName);
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
self.contentType = GCDWebServerGetMimeTypeForExtension([path pathExtension]);
|
||||
self.contentLength = (range.location != NSNotFound ? range.length : (NSUInteger)info.st_size);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_file <= 0);
|
||||
ARC_RELEASE(_path);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
DCHECK(_file <= 0);
|
||||
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
||||
if (_file <= 0) {
|
||||
*error = _MakePosixError(errno);
|
||||
return NO;
|
||||
}
|
||||
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
||||
*error = _MakePosixError(errno);
|
||||
close(_file);
|
||||
_file = 0;
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
DCHECK(_file > 0);
|
||||
size_t length = MIN((NSUInteger)kFileReadBufferSize, _size);
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
|
||||
ssize_t result = read(_file, data.mutableBytes, length);
|
||||
if (result < 0) {
|
||||
*error = _MakePosixError(errno);
|
||||
return nil;
|
||||
}
|
||||
if (result > 0) {
|
||||
[data setLength:result];
|
||||
_size -= result;
|
||||
}
|
||||
return ARC_AUTORELEASE(data);
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
DCHECK(_file > 0);
|
||||
close(_file);
|
||||
_file = 0;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerRequest.h"
|
||||
|
||||
@interface GCDWebServerMultiPart : NSObject
|
||||
@property(nonatomic, readonly) NSString* contentType; // May be nil
|
||||
@property(nonatomic, readonly) NSString* mimeType; // Defaults to "text/plain" per specifications if undefined
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart
|
||||
@property(nonatomic, readonly) NSData* data;
|
||||
@property(nonatomic, readonly) NSString* string; // May be nil (only valid for text mime types)
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart
|
||||
@property(nonatomic, readonly) NSString* fileName; // May be nil
|
||||
@property(nonatomic, readonly) NSString* temporaryPath;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest
|
||||
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
||||
@property(nonatomic, readonly) NSDictionary* files; // Only valid after open / write / close sequence
|
||||
+ (NSString*)mimeType;
|
||||
@end
|
||||
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kMultiPartBufferSize (256 * 1024)
|
||||
|
||||
enum {
|
||||
kParserState_Undefined = 0,
|
||||
kParserState_Start,
|
||||
kParserState_Headers,
|
||||
kParserState_Content,
|
||||
kParserState_End
|
||||
};
|
||||
|
||||
static NSData* _newlineData = nil;
|
||||
static NSData* _newlinesData = nil;
|
||||
static NSData* _dashNewlineData = nil;
|
||||
|
||||
@interface GCDWebServerMultiPart () {
|
||||
@private
|
||||
NSString* _contentType;
|
||||
NSString* _mimeType;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPart
|
||||
|
||||
@synthesize contentType=_contentType, mimeType=_mimeType;
|
||||
|
||||
- (id)initWithContentType:(NSString*)contentType {
|
||||
if ((self = [super init])) {
|
||||
_contentType = [contentType copy];
|
||||
NSArray* components = [_contentType componentsSeparatedByString:@";"];
|
||||
if (components.count) {
|
||||
_mimeType = ARC_RETAIN([[components objectAtIndex:0] lowercaseString]);
|
||||
}
|
||||
if (_mimeType == nil) {
|
||||
_mimeType = @"text/plain";
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_contentType);
|
||||
ARC_RELEASE(_mimeType);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartArgument () {
|
||||
@private
|
||||
NSData* _data;
|
||||
NSString* _string;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartArgument
|
||||
|
||||
@synthesize data=_data, string=_string;
|
||||
|
||||
- (id)initWithContentType:(NSString*)contentType data:(NSData*)data {
|
||||
if ((self = [super initWithContentType:contentType])) {
|
||||
_data = ARC_RETAIN(data);
|
||||
|
||||
if ([self.mimeType hasPrefix:@"text/"]) {
|
||||
NSString* charset = GCDWebServerExtractHeaderParameter(self.contentType, @"charset");
|
||||
_string = [[NSString alloc] initWithData:_data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_data);
|
||||
ARC_RELEASE(_string);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | %i bytes>", [self class], self.mimeType, (int)_data.length];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartFile () {
|
||||
@private
|
||||
NSString* _fileName;
|
||||
NSString* _temporaryPath;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartFile
|
||||
|
||||
@synthesize fileName=_fileName, temporaryPath=_temporaryPath;
|
||||
|
||||
- (id)initWithContentType:(NSString*)contentType fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath {
|
||||
if ((self = [super initWithContentType:contentType])) {
|
||||
_fileName = [fileName copy];
|
||||
_temporaryPath = [temporaryPath copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
|
||||
ARC_RELEASE(_fileName);
|
||||
ARC_RELEASE(_temporaryPath);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName];
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@implementation GCDWebServerMultiPartFormRequest
|
||||
|
||||
@synthesize arguments=_arguments, files=_files;
|
||||
|
||||
+ (void)initialize {
|
||||
if (_newlineData == nil) {
|
||||
_newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2];
|
||||
DCHECK(_newlineData);
|
||||
}
|
||||
if (_newlinesData == nil) {
|
||||
_newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
||||
DCHECK(_newlinesData);
|
||||
}
|
||||
if (_dashNewlineData == nil) {
|
||||
_dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4];
|
||||
DCHECK(_dashNewlineData);
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"multipart/form-data";
|
||||
}
|
||||
|
||||
- (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])) {
|
||||
NSString* boundary = GCDWebServerExtractHeaderParameter(self.contentType, @"boundary");
|
||||
if (boundary) {
|
||||
NSData* data = [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding];
|
||||
_boundary = ARC_RETAIN(data);
|
||||
}
|
||||
if (_boundary == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
|
||||
_arguments = [[NSMutableDictionary alloc] init];
|
||||
_files = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open {
|
||||
DCHECK(_parserData == nil);
|
||||
_parserData = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
|
||||
_parserState = kParserState_Start;
|
||||
return YES;
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
|
||||
- (BOOL)_parseData {
|
||||
BOOL success = YES;
|
||||
|
||||
if (_parserState == kParserState_Headers) {
|
||||
NSRange range = [_parserData rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _parserData.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
|
||||
ARC_RELEASE(_controlName);
|
||||
_controlName = nil;
|
||||
ARC_RELEASE(_fileName);
|
||||
_fileName = nil;
|
||||
ARC_RELEASE(_contentType);
|
||||
_contentType = nil;
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
||||
const char* temp = "GET / HTTP/1.0\r\n";
|
||||
CFHTTPMessageAppendBytes(message, (const UInt8*)temp, strlen(temp));
|
||||
CFHTTPMessageAppendBytes(message, _parserData.bytes, range.location + range.length);
|
||||
if (CFHTTPMessageIsHeaderComplete(message)) {
|
||||
NSString* controlName = nil;
|
||||
NSString* fileName = nil;
|
||||
NSDictionary* headers = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(message));
|
||||
NSString* contentDisposition = [headers objectForKey:@"Content-Disposition"];
|
||||
if ([[contentDisposition lowercaseString] hasPrefix:@"form-data;"]) {
|
||||
controlName = GCDWebServerExtractHeaderParameter(contentDisposition, @"name");
|
||||
fileName = GCDWebServerExtractHeaderParameter(contentDisposition, @"filename");
|
||||
}
|
||||
_controlName = [controlName copy];
|
||||
_fileName = [fileName copy];
|
||||
_contentType = ARC_RETAIN([headers objectForKey:@"Content-Type"]);
|
||||
}
|
||||
CFRelease(message);
|
||||
if (_controlName) {
|
||||
if (_fileName) {
|
||||
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (_tmpFile > 0) {
|
||||
_tmpPath = [path copy];
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
|
||||
[_parserData replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
|
||||
_parserState = kParserState_Content;
|
||||
}
|
||||
}
|
||||
|
||||
if ((_parserState == kParserState_Start) || (_parserState == kParserState_Content)) {
|
||||
NSRange range = [_parserData rangeOfData:_boundary options:0 range:NSMakeRange(0, _parserData.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
NSRange subRange = NSMakeRange(range.location + range.length, _parserData.length - range.location - range.length);
|
||||
NSRange subRange1 = [_parserData rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
|
||||
NSRange subRange2 = [_parserData rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
|
||||
if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
|
||||
|
||||
if (_parserState == kParserState_Content) {
|
||||
const void* dataBytes = _parserData.bytes;
|
||||
NSUInteger dataLength = range.location - 2;
|
||||
if (_tmpPath) {
|
||||
ssize_t result = write(_tmpFile, dataBytes, dataLength);
|
||||
if (result == (ssize_t)dataLength) {
|
||||
if (close(_tmpFile) == 0) {
|
||||
_tmpFile = 0;
|
||||
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithContentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
|
||||
[_files setObject:file forKey:_controlName];
|
||||
ARC_RELEASE(file);
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
} else {
|
||||
NSData* data = [[NSData alloc] initWithBytesNoCopy:(void*)dataBytes length:dataLength freeWhenDone:NO];
|
||||
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithContentType:_contentType data:data];
|
||||
[_arguments setObject:argument forKey:_controlName];
|
||||
ARC_RELEASE(argument);
|
||||
ARC_RELEASE(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (subRange1.location != NSNotFound) {
|
||||
[_parserData replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
|
||||
_parserState = kParserState_Headers;
|
||||
success = [self _parseData];
|
||||
} else {
|
||||
_parserState = kParserState_End;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NSUInteger margin = 2 * _boundary.length;
|
||||
if (_tmpPath && (_parserData.length > margin)) {
|
||||
NSUInteger length = _parserData.length - margin;
|
||||
ssize_t result = write(_tmpFile, _parserData.bytes, length);
|
||||
if (result == (ssize_t)length) {
|
||||
[_parserData replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
|
||||
DCHECK(_parserData != nil);
|
||||
[_parserData appendBytes:buffer length:length];
|
||||
return ([self _parseData] ? length : -1);
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
DCHECK(_parserData != nil);
|
||||
ARC_RELEASE(_parserData);
|
||||
_parserData = nil;
|
||||
ARC_RELEASE(_controlName);
|
||||
_controlName = nil;
|
||||
ARC_RELEASE(_fileName);
|
||||
_fileName = nil;
|
||||
ARC_RELEASE(_contentType);
|
||||
_contentType = nil;
|
||||
if (_tmpFile > 0) {
|
||||
close(_tmpFile);
|
||||
unlink([_tmpPath fileSystemRepresentation]);
|
||||
_tmpFile = 0;
|
||||
}
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
return (_parserState == kParserState_End ? YES : NO);
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_parserData == nil);
|
||||
ARC_RELEASE(_arguments);
|
||||
ARC_RELEASE(_files);
|
||||
ARC_RELEASE(_boundary);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -52,6 +52,15 @@
|
||||
|
||||
#import "GCDWebServerConnection.h"
|
||||
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
#import "GCDWebServerFileRequest.h"
|
||||
#import "GCDWebServerMultiPartFormRequest.h"
|
||||
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerFileResponse.h"
|
||||
#import "GCDWebServerStreamResponse.h"
|
||||
|
||||
#ifdef __GCDWEBSERVER_LOGGING_HEADER__
|
||||
|
||||
// Define __GCDWEBSERVER_LOGGING_HEADER__ as a preprocessor constant to redirect GCDWebServer logging to your own system
|
||||
@@ -91,6 +100,9 @@ extern void GCDLogMessage(long level, NSString* format, ...) NS_FORMAT_FUNCTION(
|
||||
#define kGCDWebServerDefaultMimeType @"application/octet-stream"
|
||||
#define kGCDWebServerGCDQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||
|
||||
extern NSString* GCDWebServerExtractHeaderParameter(NSString* header, NSString* attribute);
|
||||
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
|
||||
|
||||
@interface GCDWebServerConnection ()
|
||||
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
|
||||
@end
|
||||
|
||||
@@ -45,37 +45,3 @@
|
||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length; // Implementation required
|
||||
- (BOOL)close; // Implementation required
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataRequest : GCDWebServerRequest
|
||||
@property(nonatomic, readonly) NSData* data; // Only valid after open / write / close sequence
|
||||
@end
|
||||
|
||||
@interface GCDWebServerFileRequest : GCDWebServerRequest
|
||||
@property(nonatomic, readonly) NSString* filePath; // Only valid after open / write / close sequence
|
||||
@end
|
||||
|
||||
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest
|
||||
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
||||
+ (NSString*)mimeType;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPart : NSObject
|
||||
@property(nonatomic, readonly) NSString* contentType; // May be nil
|
||||
@property(nonatomic, readonly) NSString* mimeType; // Defaults to "text/plain" per specifications if undefined
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartArgument : GCDWebServerMultiPart
|
||||
@property(nonatomic, readonly) NSData* data;
|
||||
@property(nonatomic, readonly) NSString* string; // May be nil (only valid for text mime types)
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartFile : GCDWebServerMultiPart
|
||||
@property(nonatomic, readonly) NSString* fileName; // May be nil
|
||||
@property(nonatomic, readonly) NSString* temporaryPath;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerMultiPartFormRequest : GCDWebServerRequest
|
||||
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
||||
@property(nonatomic, readonly) NSDictionary* files; // Only valid after open / write / close sequence
|
||||
+ (NSString*)mimeType;
|
||||
@end
|
||||
|
||||
@@ -27,16 +27,6 @@
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kMultiPartBufferSize (256 * 1024)
|
||||
|
||||
enum {
|
||||
kParserState_Undefined = 0,
|
||||
kParserState_Start,
|
||||
kParserState_Headers,
|
||||
kParserState_Content,
|
||||
kParserState_End
|
||||
};
|
||||
|
||||
@interface GCDWebServerRequest () {
|
||||
@private
|
||||
NSString* _method;
|
||||
@@ -50,94 +40,6 @@ enum {
|
||||
}
|
||||
@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* _newlinesData = nil;
|
||||
static NSData* _dashNewlineData = nil;
|
||||
|
||||
static NSString* _ExtractHeaderParameter(NSString* header, NSString* attribute) {
|
||||
NSString* value = nil;
|
||||
if (header) {
|
||||
NSScanner* scanner = [[NSScanner alloc] initWithString:header];
|
||||
NSString* string = [NSString stringWithFormat:@"%@=", attribute];
|
||||
if ([scanner scanUpToString:string intoString:NULL]) {
|
||||
[scanner scanString:string intoString:NULL];
|
||||
if ([scanner scanString:@"\"" intoString:NULL]) {
|
||||
[scanner scanUpToString:@"\"" intoString:&value];
|
||||
} else {
|
||||
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&value];
|
||||
}
|
||||
}
|
||||
ARC_RELEASE(scanner);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// http://www.w3schools.com/tags/ref_charactersets.asp
|
||||
static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
||||
NSStringEncoding encoding = kCFStringEncodingInvalidId;
|
||||
if (charset) {
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset));
|
||||
}
|
||||
return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding);
|
||||
}
|
||||
|
||||
@implementation GCDWebServerRequest : NSObject
|
||||
|
||||
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, byteRange=_range;
|
||||
@@ -234,388 +136,3 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataRequest
|
||||
|
||||
@synthesize data=_data;
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_data != nil);
|
||||
ARC_RELEASE(_data);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open {
|
||||
DCHECK(_data == nil);
|
||||
_data = [[NSMutableData alloc] initWithCapacity:self.contentLength];
|
||||
return _data ? YES : NO;
|
||||
}
|
||||
|
||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
|
||||
DCHECK(_data != nil);
|
||||
[_data appendBytes:buffer length:length];
|
||||
return length;
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
DCHECK(_data != nil);
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerFileRequest
|
||||
|
||||
@synthesize filePath=_filePath;
|
||||
|
||||
- (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])) {
|
||||
_filePath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_file < 0);
|
||||
unlink([_filePath fileSystemRepresentation]);
|
||||
ARC_RELEASE(_filePath);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)open {
|
||||
DCHECK(_file == 0);
|
||||
_file = open([_filePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
return (_file > 0 ? YES : NO);
|
||||
}
|
||||
|
||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
|
||||
DCHECK(_file > 0);
|
||||
return write(_file, buffer, length);
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
DCHECK(_file > 0);
|
||||
int result = close(_file);
|
||||
_file = -1;
|
||||
return (result == 0 ? YES : NO);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerURLEncodedFormRequest
|
||||
|
||||
@synthesize arguments=_arguments;
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"application/x-www-form-urlencoded";
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_arguments);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
if (![super close]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:self.data encoding:_StringEncodingFromCharset(charset)];
|
||||
_arguments = ARC_RETAIN(GCDWebServerParseURLEncodedForm(string));
|
||||
ARC_RELEASE(string);
|
||||
|
||||
return (_arguments ? YES : NO);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPart
|
||||
|
||||
@synthesize contentType=_contentType, mimeType=_mimeType;
|
||||
|
||||
- (id)initWithContentType:(NSString*)contentType {
|
||||
if ((self = [super init])) {
|
||||
_contentType = [contentType copy];
|
||||
NSArray* components = [_contentType componentsSeparatedByString:@";"];
|
||||
if (components.count) {
|
||||
_mimeType = ARC_RETAIN([[components objectAtIndex:0] lowercaseString]);
|
||||
}
|
||||
if (_mimeType == nil) {
|
||||
_mimeType = @"text/plain";
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_contentType);
|
||||
ARC_RELEASE(_mimeType);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartArgument
|
||||
|
||||
@synthesize data=_data, string=_string;
|
||||
|
||||
- (id)initWithContentType:(NSString*)contentType data:(NSData*)data {
|
||||
if ((self = [super initWithContentType:contentType])) {
|
||||
_data = ARC_RETAIN(data);
|
||||
|
||||
if ([self.mimeType hasPrefix:@"text/"]) {
|
||||
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
|
||||
_string = [[NSString alloc] initWithData:_data encoding:_StringEncodingFromCharset(charset)];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_data);
|
||||
ARC_RELEASE(_string);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | %i bytes>", [self class], self.mimeType, (int)_data.length];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartFile
|
||||
|
||||
@synthesize fileName=_fileName, temporaryPath=_temporaryPath;
|
||||
|
||||
- (id)initWithContentType:(NSString*)contentType fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath {
|
||||
if ((self = [super initWithContentType:contentType])) {
|
||||
_fileName = [fileName copy];
|
||||
_temporaryPath = [temporaryPath copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
unlink([_temporaryPath fileSystemRepresentation]);
|
||||
|
||||
ARC_RELEASE(_fileName);
|
||||
ARC_RELEASE(_temporaryPath);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSString*)description {
|
||||
return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerMultiPartFormRequest
|
||||
|
||||
@synthesize arguments=_arguments, files=_files;
|
||||
|
||||
+ (void)initialize {
|
||||
if (_newlineData == nil) {
|
||||
_newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2];
|
||||
DCHECK(_newlineData);
|
||||
}
|
||||
if (_newlinesData == nil) {
|
||||
_newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
|
||||
DCHECK(_newlinesData);
|
||||
}
|
||||
if (_dashNewlineData == nil) {
|
||||
_dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4];
|
||||
DCHECK(_dashNewlineData);
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"multipart/form-data";
|
||||
}
|
||||
|
||||
- (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])) {
|
||||
NSString* boundary = _ExtractHeaderParameter(self.contentType, @"boundary");
|
||||
if (boundary) {
|
||||
NSData* data = [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding];
|
||||
_boundary = ARC_RETAIN(data);
|
||||
}
|
||||
if (_boundary == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
|
||||
_arguments = [[NSMutableDictionary alloc] init];
|
||||
_files = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)open {
|
||||
DCHECK(_parserData == nil);
|
||||
_parserData = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
|
||||
_parserState = kParserState_Start;
|
||||
return YES;
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
|
||||
- (BOOL)_parseData {
|
||||
BOOL success = YES;
|
||||
|
||||
if (_parserState == kParserState_Headers) {
|
||||
NSRange range = [_parserData rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _parserData.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
|
||||
ARC_RELEASE(_controlName);
|
||||
_controlName = nil;
|
||||
ARC_RELEASE(_fileName);
|
||||
_fileName = nil;
|
||||
ARC_RELEASE(_contentType);
|
||||
_contentType = nil;
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
|
||||
const char* temp = "GET / HTTP/1.0\r\n";
|
||||
CFHTTPMessageAppendBytes(message, (const UInt8*)temp, strlen(temp));
|
||||
CFHTTPMessageAppendBytes(message, _parserData.bytes, range.location + range.length);
|
||||
if (CFHTTPMessageIsHeaderComplete(message)) {
|
||||
NSString* controlName = nil;
|
||||
NSString* fileName = nil;
|
||||
NSDictionary* headers = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(message));
|
||||
NSString* contentDisposition = [headers objectForKey:@"Content-Disposition"];
|
||||
if ([[contentDisposition lowercaseString] hasPrefix:@"form-data;"]) {
|
||||
controlName = _ExtractHeaderParameter(contentDisposition, @"name");
|
||||
fileName = _ExtractHeaderParameter(contentDisposition, @"filename");
|
||||
}
|
||||
_controlName = [controlName copy];
|
||||
_fileName = [fileName copy];
|
||||
_contentType = ARC_RETAIN([headers objectForKey:@"Content-Type"]);
|
||||
}
|
||||
CFRelease(message);
|
||||
if (_controlName) {
|
||||
if (_fileName) {
|
||||
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||
_tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (_tmpFile > 0) {
|
||||
_tmpPath = [path copy];
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
|
||||
[_parserData replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
|
||||
_parserState = kParserState_Content;
|
||||
}
|
||||
}
|
||||
|
||||
if ((_parserState == kParserState_Start) || (_parserState == kParserState_Content)) {
|
||||
NSRange range = [_parserData rangeOfData:_boundary options:0 range:NSMakeRange(0, _parserData.length)];
|
||||
if (range.location != NSNotFound) {
|
||||
NSRange subRange = NSMakeRange(range.location + range.length, _parserData.length - range.location - range.length);
|
||||
NSRange subRange1 = [_parserData rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
|
||||
NSRange subRange2 = [_parserData rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
|
||||
if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
|
||||
|
||||
if (_parserState == kParserState_Content) {
|
||||
const void* dataBytes = _parserData.bytes;
|
||||
NSUInteger dataLength = range.location - 2;
|
||||
if (_tmpPath) {
|
||||
ssize_t result = write(_tmpFile, dataBytes, dataLength);
|
||||
if (result == (ssize_t)dataLength) {
|
||||
if (close(_tmpFile) == 0) {
|
||||
_tmpFile = 0;
|
||||
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithContentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
|
||||
[_files setObject:file forKey:_controlName];
|
||||
ARC_RELEASE(file);
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
} else {
|
||||
NSData* data = [[NSData alloc] initWithBytesNoCopy:(void*)dataBytes length:dataLength freeWhenDone:NO];
|
||||
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithContentType:_contentType data:data];
|
||||
[_arguments setObject:argument forKey:_controlName];
|
||||
ARC_RELEASE(argument);
|
||||
ARC_RELEASE(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (subRange1.location != NSNotFound) {
|
||||
[_parserData replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
|
||||
_parserState = kParserState_Headers;
|
||||
success = [self _parseData];
|
||||
} else {
|
||||
_parserState = kParserState_End;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NSUInteger margin = 2 * _boundary.length;
|
||||
if (_tmpPath && (_parserData.length > margin)) {
|
||||
NSUInteger length = _parserData.length - margin;
|
||||
ssize_t result = write(_tmpFile, _parserData.bytes, length);
|
||||
if (result == (ssize_t)length) {
|
||||
[_parserData replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
|
||||
DCHECK(_parserData != nil);
|
||||
[_parserData appendBytes:buffer length:length];
|
||||
return ([self _parseData] ? length : -1);
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
DCHECK(_parserData != nil);
|
||||
ARC_RELEASE(_parserData);
|
||||
_parserData = nil;
|
||||
ARC_RELEASE(_controlName);
|
||||
_controlName = nil;
|
||||
ARC_RELEASE(_fileName);
|
||||
_fileName = nil;
|
||||
ARC_RELEASE(_contentType);
|
||||
_contentType = nil;
|
||||
if (_tmpFile > 0) {
|
||||
close(_tmpFile);
|
||||
unlink([_tmpPath fileSystemRepresentation]);
|
||||
_tmpFile = 0;
|
||||
}
|
||||
ARC_RELEASE(_tmpPath);
|
||||
_tmpPath = nil;
|
||||
return (_parserState == kParserState_End ? YES : NO);
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_parserData == nil);
|
||||
ARC_RELEASE(_arguments);
|
||||
ARC_RELEASE(_files);
|
||||
ARC_RELEASE(_boundary);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
||||
|
||||
@protocol GCDWebServerBodyReader <NSObject>
|
||||
- (BOOL)open:(NSError**)error;
|
||||
- (NSData*)readData:(NSError**)error; // Return nil on error or empty NSData if at end
|
||||
@@ -54,37 +52,3 @@ typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
||||
- (id)initWithStatusCode:(NSInteger)statusCode;
|
||||
- (id)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataResponse : GCDWebServerResponse
|
||||
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type;
|
||||
- (id)initWithData:(NSData*)data contentType:(NSString*)type;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataResponse (Extensions)
|
||||
+ (GCDWebServerDataResponse*)responseWithText:(NSString*)text;
|
||||
+ (GCDWebServerDataResponse*)responseWithHTML:(NSString*)html;
|
||||
+ (GCDWebServerDataResponse*)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables;
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object;
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
- (id)initWithText:(NSString*)text; // Encodes using UTF-8
|
||||
- (id)initWithHTML:(NSString*)html; // 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)
|
||||
- (id)initWithJSONObject:(id)object;
|
||||
- (id)initWithJSONObject:(id)object contentType:(NSString*)type;
|
||||
@end
|
||||
|
||||
@interface GCDWebServerFileResponse : GCDWebServerResponse
|
||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path;
|
||||
+ (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 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
|
||||
|
||||
@interface GCDWebServerStreamResponse : GCDWebServerResponse // Forces chunked transfer encoding
|
||||
+ (GCDWebServerStreamResponse*)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||
- (id)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; // Block must return empty NSData when done or nil on error
|
||||
@end
|
||||
|
||||
@@ -25,14 +25,12 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <sys/stat.h>
|
||||
#import <zlib.h>
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
#define kZlibErrorDomain @"ZlibErrorDomain"
|
||||
#define kGZipInitialBufferSize (256 * 1024)
|
||||
#define kFileReadBufferSize (32 * 1024)
|
||||
|
||||
@interface GCDWebServerBodyEncoder : NSObject <GCDWebServerBodyReader>
|
||||
- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader;
|
||||
@@ -332,293 +330,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerDataResponse () {
|
||||
@private
|
||||
NSData* _data;
|
||||
BOOL _done;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataResponse
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithData:(NSData*)data contentType:(NSString*)type {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithData:data contentType:type]);
|
||||
}
|
||||
|
||||
- (id)initWithData:(NSData*)data contentType:(NSString*)type {
|
||||
if (data == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_data = ARC_RETAIN(data);
|
||||
|
||||
self.contentType = type;
|
||||
self.contentLength = data.length;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_data);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
NSData* data;
|
||||
if (_done) {
|
||||
data = [NSData data];
|
||||
} else {
|
||||
data = _data;
|
||||
_done = YES;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerDataResponse (Extensions)
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithText:(NSString*)text {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithText:text]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithHTML:(NSString*)html {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithHTML:html]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithHTMLTemplate:path variables:variables]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithJSONObject:object]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerDataResponse*)responseWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
return ARC_AUTORELEASE([[self alloc] initWithJSONObject:object contentType:type]);
|
||||
}
|
||||
|
||||
- (id)initWithText:(NSString*)text {
|
||||
NSData* data = [text dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/plain; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (id)initWithHTML:(NSString*)html {
|
||||
NSData* data = [html dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (data == nil) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:@"text/html; charset=utf-8"];
|
||||
}
|
||||
|
||||
- (id)initWithHTMLTemplate:(NSString*)path variables:(NSDictionary*)variables {
|
||||
NSMutableString* html = [[NSMutableString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
|
||||
[variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) {
|
||||
[html replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, html.length)];
|
||||
}];
|
||||
id response = [self initWithHTML:html];
|
||||
ARC_RELEASE(html);
|
||||
return response;
|
||||
}
|
||||
|
||||
- (id)initWithJSONObject:(id)object {
|
||||
return [self initWithJSONObject:object contentType:@"application/json"];
|
||||
}
|
||||
|
||||
- (id)initWithJSONObject:(id)object contentType:(NSString*)type {
|
||||
NSData* data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL];
|
||||
if (data == nil) {
|
||||
ARC_RELEASE(self);
|
||||
return nil;
|
||||
}
|
||||
return [self initWithData:data contentType:type];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerFileResponse () {
|
||||
@private
|
||||
NSString* _path;
|
||||
NSUInteger _offset;
|
||||
NSUInteger _size;
|
||||
int _file;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerFileResponse
|
||||
|
||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithFile:path]);
|
||||
}
|
||||
|
||||
+ (GCDWebServerFileResponse*)responseWithFile:(NSString*)path isAttachment:(BOOL)attachment {
|
||||
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 {
|
||||
return [self initWithFile:path byteRange:NSMakeRange(NSNotFound, 0) isAttachment:NO];
|
||||
}
|
||||
|
||||
- (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;
|
||||
if (lstat([path fileSystemRepresentation], &info) || !(info.st_mode & S_IFREG)) {
|
||||
DNOT_REACHED();
|
||||
ARC_RELEASE(self);
|
||||
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, (NSUInteger)info.st_size - range.location);
|
||||
} else {
|
||||
range.length = MIN(range.length, (NSUInteger)info.st_size);
|
||||
range.location = (NSUInteger)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
|
||||
}
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_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 = (NSUInteger)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
|
||||
NSData* data = [[path lastPathComponent] dataUsingEncoding:NSISOLatin1StringEncoding allowLossyConversion:YES];
|
||||
NSString* fileName = data ? [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding] : nil;
|
||||
if (fileName) {
|
||||
[self setValue:[NSString stringWithFormat:@"attachment; filename=\"%@\"", fileName] forAdditionalHeader:@"Content-Disposition"];
|
||||
ARC_RELEASE(fileName);
|
||||
} else {
|
||||
DNOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
self.contentType = GCDWebServerGetMimeTypeForExtension([path pathExtension]);
|
||||
self.contentLength = (range.location != NSNotFound ? range.length : (NSUInteger)info.st_size);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DCHECK(_file <= 0);
|
||||
ARC_RELEASE(_path);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
static inline NSError* _MakePosixError(int code) {
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%s", strerror(code)]}];
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSError**)error {
|
||||
DCHECK(_file <= 0);
|
||||
_file = open([_path fileSystemRepresentation], O_NOFOLLOW | O_RDONLY);
|
||||
if (_file <= 0) {
|
||||
*error = _MakePosixError(errno);
|
||||
return NO;
|
||||
}
|
||||
if (lseek(_file, _offset, SEEK_SET) != (off_t)_offset) {
|
||||
*error = _MakePosixError(errno);
|
||||
close(_file);
|
||||
_file = 0;
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
DCHECK(_file > 0);
|
||||
size_t length = MIN((NSUInteger)kFileReadBufferSize, _size);
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithLength:length];
|
||||
ssize_t result = read(_file, data.mutableBytes, length);
|
||||
if (result < 0) {
|
||||
*error = _MakePosixError(errno);
|
||||
return nil;
|
||||
}
|
||||
if (result > 0) {
|
||||
[data setLength:result];
|
||||
_size -= result;
|
||||
}
|
||||
return ARC_AUTORELEASE(data);
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
DCHECK(_file > 0);
|
||||
close(_file);
|
||||
_file = 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GCDWebServerStreamResponse () {
|
||||
@private
|
||||
GCDWebServerStreamBlock _block;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerStreamResponse
|
||||
|
||||
+ (GCDWebServerStreamResponse*)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]);
|
||||
}
|
||||
|
||||
- (id)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
if ((self = [super init])) {
|
||||
_block = [block copy];
|
||||
|
||||
self.contentType = type;
|
||||
self.chunkedTransferEncoding = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_block);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
return _block(error);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerStreamResponse.h"
|
||||
|
||||
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
||||
|
||||
@interface GCDWebServerStreamResponse : GCDWebServerResponse // Forces chunked transfer encoding
|
||||
+ (GCDWebServerStreamResponse*)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block;
|
||||
- (id)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block; // Block must return empty NSData when done or nil on error
|
||||
@end
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerStreamResponse () {
|
||||
@private
|
||||
GCDWebServerStreamBlock _block;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerStreamResponse
|
||||
|
||||
+ (GCDWebServerStreamResponse*)responseWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
return ARC_AUTORELEASE([[[self class] alloc] initWithContentType:type streamBlock:block]);
|
||||
}
|
||||
|
||||
- (id)initWithContentType:(NSString*)type streamBlock:(GCDWebServerStreamBlock)block {
|
||||
if ((self = [super init])) {
|
||||
_block = [block copy];
|
||||
|
||||
self.contentType = type;
|
||||
self.chunkedTransferEncoding = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_block);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (NSData*)readData:(NSError**)error {
|
||||
return _block(error);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
|
||||
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest
|
||||
@property(nonatomic, readonly) NSDictionary* arguments; // Only valid after open / write / close sequence
|
||||
+ (NSString*)mimeType;
|
||||
@end
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright (c) 2012-2014, Pierre-Olivier Latour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The name of Pierre-Olivier Latour may not be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL PIERRE-OLIVIER LATOUR BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@interface GCDWebServerURLEncodedFormRequest () {
|
||||
@private
|
||||
NSDictionary* _arguments;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation GCDWebServerURLEncodedFormRequest
|
||||
|
||||
@synthesize arguments=_arguments;
|
||||
|
||||
+ (NSString*)mimeType {
|
||||
return @"application/x-www-form-urlencoded";
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
ARC_RELEASE(_arguments);
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
- (BOOL)close {
|
||||
if (![super close]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString* charset = GCDWebServerExtractHeaderParameter(self.contentType, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:self.data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
_arguments = ARC_RETAIN(GCDWebServerParseURLEncodedForm(string));
|
||||
ARC_RELEASE(string);
|
||||
|
||||
return (_arguments ? YES : NO);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -38,6 +38,20 @@
|
||||
E22112991690B7AA0048D2B2 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; };
|
||||
E221129B1690B7B10048D2B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129A1690B7B10048D2B2 /* UIKit.framework */; };
|
||||
E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */; };
|
||||
E2A0E7ED18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */; };
|
||||
E2A0E7EE18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */; };
|
||||
E2A0E7F118F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */; };
|
||||
E2A0E7F218F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */; };
|
||||
E2A0E7F518F1D1E500C580B1 /* GCDWebServerStreamResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamResponse.m */; };
|
||||
E2A0E7F618F1D1E500C580B1 /* GCDWebServerStreamResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamResponse.m */; };
|
||||
E2A0E7F918F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */; };
|
||||
E2A0E7FA18F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */; };
|
||||
E2A0E7FD18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */; };
|
||||
E2A0E7FE18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */; };
|
||||
E2A0E80118F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */; };
|
||||
E2A0E80218F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */; };
|
||||
E2A0E80518F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */; };
|
||||
E2A0E80618F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */; };
|
||||
E2B0D4A718F13495009A7927 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B0D4A618F13495009A7927 /* libz.dylib */; };
|
||||
E2B0D4A918F134A8009A7927 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B0D4A818F134A8009A7927 /* libz.dylib */; };
|
||||
E2BE850A18E77ECA0061360B /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */; };
|
||||
@@ -99,6 +113,20 @@
|
||||
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; };
|
||||
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; };
|
||||
E2A0E7EB18F1D03700C580B1 /* GCDWebServerDataResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataResponse.h; sourceTree = "<group>"; };
|
||||
E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataResponse.m; sourceTree = "<group>"; };
|
||||
E2A0E7EF18F1D12E00C580B1 /* GCDWebServerFileResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileResponse.h; sourceTree = "<group>"; };
|
||||
E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileResponse.m; sourceTree = "<group>"; };
|
||||
E2A0E7F318F1D1E500C580B1 /* GCDWebServerStreamResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerStreamResponse.h; sourceTree = "<group>"; };
|
||||
E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerStreamResponse.m; sourceTree = "<group>"; };
|
||||
E2A0E7F718F1D24700C580B1 /* GCDWebServerDataRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerDataRequest.h; sourceTree = "<group>"; };
|
||||
E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerDataRequest.m; sourceTree = "<group>"; };
|
||||
E2A0E7FB18F1D36C00C580B1 /* GCDWebServerFileRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerFileRequest.h; sourceTree = "<group>"; };
|
||||
E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerFileRequest.m; sourceTree = "<group>"; };
|
||||
E2A0E7FF18F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerMultiPartFormRequest.h; sourceTree = "<group>"; };
|
||||
E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerMultiPartFormRequest.m; sourceTree = "<group>"; };
|
||||
E2A0E80318F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServerURLEncodedFormRequest.h; sourceTree = "<group>"; };
|
||||
E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDWebServerURLEncodedFormRequest.m; sourceTree = "<group>"; };
|
||||
E2B0D4A618F13495009A7927 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
|
||||
E2B0D4A818F134A8009A7927 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
|
||||
E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = GCDWebUploader.bundle; sourceTree = "<group>"; };
|
||||
@@ -163,11 +191,25 @@
|
||||
E221127D1690B63A0048D2B2 /* GCDWebServer.m */,
|
||||
E221127E1690B63A0048D2B2 /* GCDWebServerConnection.h */,
|
||||
E221127F1690B63A0048D2B2 /* GCDWebServerConnection.m */,
|
||||
E2A0E7F718F1D24700C580B1 /* GCDWebServerDataRequest.h */,
|
||||
E2A0E7F818F1D24700C580B1 /* GCDWebServerDataRequest.m */,
|
||||
E2A0E7EB18F1D03700C580B1 /* GCDWebServerDataResponse.h */,
|
||||
E2A0E7EC18F1D03700C580B1 /* GCDWebServerDataResponse.m */,
|
||||
E2A0E7FB18F1D36C00C580B1 /* GCDWebServerFileRequest.h */,
|
||||
E2A0E7FC18F1D36C00C580B1 /* GCDWebServerFileRequest.m */,
|
||||
E2A0E7EF18F1D12E00C580B1 /* GCDWebServerFileResponse.h */,
|
||||
E2A0E7F018F1D12E00C580B1 /* GCDWebServerFileResponse.m */,
|
||||
E2A0E7FF18F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.h */,
|
||||
E2A0E80018F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m */,
|
||||
E22112801690B63A0048D2B2 /* GCDWebServerPrivate.h */,
|
||||
E22112811690B63A0048D2B2 /* GCDWebServerRequest.h */,
|
||||
E22112821690B63A0048D2B2 /* GCDWebServerRequest.m */,
|
||||
E22112831690B63A0048D2B2 /* GCDWebServerResponse.h */,
|
||||
E22112841690B63A0048D2B2 /* GCDWebServerResponse.m */,
|
||||
E2A0E7F318F1D1E500C580B1 /* GCDWebServerStreamResponse.h */,
|
||||
E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamResponse.m */,
|
||||
E2A0E80318F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.h */,
|
||||
E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */,
|
||||
);
|
||||
path = CGDWebServer;
|
||||
sourceTree = "<group>";
|
||||
@@ -308,11 +350,18 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E22112851690B63A0048D2B2 /* GCDWebServer.m in Sources */,
|
||||
E2A0E7FD18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */,
|
||||
E22112871690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
|
||||
E2A0E80518F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||
E2A0E7F918F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */,
|
||||
E22112891690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */,
|
||||
E2A0E7ED18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */,
|
||||
E2A0E7F518F1D1E500C580B1 /* GCDWebServerStreamResponse.m in Sources */,
|
||||
E2A0E80118F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */,
|
||||
E221128B1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */,
|
||||
E2BE850C18E785940061360B /* GCDWebUploader.m in Sources */,
|
||||
E221128F1690B6470048D2B2 /* main.m in Sources */,
|
||||
E2A0E7F118F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -321,11 +370,18 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E22112861690B63A0048D2B2 /* GCDWebServer.m in Sources */,
|
||||
E2A0E7EE18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */,
|
||||
E2A0E80218F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */,
|
||||
E22112881690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
|
||||
E221128A1690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */,
|
||||
E221128C1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */,
|
||||
E2A0E7FA18F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */,
|
||||
E2A0E80618F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||
E2BE850B18E77ECA0061360B /* GCDWebUploader.m in Sources */,
|
||||
E22112951690B64F0048D2B2 /* AppDelegate.m in Sources */,
|
||||
E2A0E7FE18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */,
|
||||
E2A0E7F618F1D1E500C580B1 /* GCDWebServerStreamResponse.m in Sources */,
|
||||
E2A0E7F218F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */,
|
||||
E22112971690B64F0048D2B2 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
#endif
|
||||
|
||||
#import "GCDWebUploader.h"
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
#import "GCDWebServerMultiPartFormRequest.h"
|
||||
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
#import "GCDWebServerFileResponse.h"
|
||||
|
||||
@interface GCDWebUploader () {
|
||||
@private
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
*/
|
||||
|
||||
#import "GCDWebUploader.h"
|
||||
#import "GCDWebServerDataRequest.h"
|
||||
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
BOOL success = NO;
|
||||
|
||||
@@ -2,7 +2,7 @@ Overview
|
||||
========
|
||||
|
||||
GCDWebServer is a lightweight GCD based HTTP 1.1 server designed to be embedded in Mac & iOS apps. It was written from scratch with the following goals in mind:
|
||||
* Easy to use and understand: only 4 main classes and less than 10 source code files
|
||||
* Easy to use and understand: only 4 core classes to deal with
|
||||
* Well designed API for easy integration and customization
|
||||
* Entirely built with an event-driven design using [Grand Central Dispatch](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) for maximum performance and concurrency
|
||||
* Support for streaming large HTTP bodies for requests and responses to minimize memory usage
|
||||
|
||||
Reference in New Issue
Block a user