Split class files

This commit is contained in:
Pierre-Olivier Latour
2014-04-06 11:32:07 -07:00
parent 81638ad086
commit 1f9a0d38d0
24 changed files with 1316 additions and 846 deletions
+27
View File
@@ -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) {
+32
View File
@@ -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
+64
View File
@@ -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
+46
View File
@@ -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
+143
View File
@@ -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
+32
View File
@@ -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
+74
View File
@@ -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
+39
View File
@@ -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
+173
View File
@@ -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
+12
View File
@@ -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
-34
View File
@@ -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
-483
View File
@@ -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
-36
View File
@@ -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
-292
View File
@@ -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
+35
View File
@@ -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
+62
View File
@@ -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
+56
View File
@@ -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;
+5
View File
@@ -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
+3
View File
@@ -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;
+1 -1
View File
@@ -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