Split class files
This commit is contained in:
@ -88,6 +88,33 @@ void GCDLogMessage(long level, NSString* format, ...) {
|
|||||||
|
|
||||||
#endif
|
#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) {
|
NSString* GCDWebServerGetMimeTypeForExtension(NSString* extension) {
|
||||||
static NSDictionary* _overrides = nil;
|
static NSDictionary* _overrides = nil;
|
||||||
if (_overrides == nil) {
|
if (_overrides == nil) {
|
||||||
|
|||||||
32
CGDWebServer/GCDWebServerDataRequest.h
Normal file
32
CGDWebServer/GCDWebServerDataRequest.h
Normal 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
CGDWebServer/GCDWebServerDataRequest.m
Normal file
64
CGDWebServer/GCDWebServerDataRequest.m
Normal 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
CGDWebServer/GCDWebServerDataResponse.h
Normal file
46
CGDWebServer/GCDWebServerDataResponse.h
Normal 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
CGDWebServer/GCDWebServerDataResponse.m
Normal file
143
CGDWebServer/GCDWebServerDataResponse.m
Normal 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
CGDWebServer/GCDWebServerFileRequest.h
Normal file
32
CGDWebServer/GCDWebServerFileRequest.h
Normal 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
CGDWebServer/GCDWebServerFileRequest.m
Normal file
74
CGDWebServer/GCDWebServerFileRequest.m
Normal 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
CGDWebServer/GCDWebServerFileResponse.h
Normal file
39
CGDWebServer/GCDWebServerFileResponse.h
Normal 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
CGDWebServer/GCDWebServerFileResponse.m
Normal file
173
CGDWebServer/GCDWebServerFileResponse.m
Normal 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
|
||||||
49
CGDWebServer/GCDWebServerMultiPartFormRequest.h
Normal file
49
CGDWebServer/GCDWebServerMultiPartFormRequest.h
Normal file
@ -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
|
||||||
367
CGDWebServer/GCDWebServerMultiPartFormRequest.m
Normal file
367
CGDWebServer/GCDWebServerMultiPartFormRequest.m
Normal file
@ -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 "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__
|
#ifdef __GCDWEBSERVER_LOGGING_HEADER__
|
||||||
|
|
||||||
// Define __GCDWEBSERVER_LOGGING_HEADER__ as a preprocessor constant to redirect GCDWebServer logging to your own system
|
// 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 kGCDWebServerDefaultMimeType @"application/octet-stream"
|
||||||
#define kGCDWebServerGCDQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
#define kGCDWebServerGCDQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
|
||||||
|
|
||||||
|
extern NSString* GCDWebServerExtractHeaderParameter(NSString* header, NSString* attribute);
|
||||||
|
extern NSStringEncoding GCDWebServerStringEncodingFromCharset(NSString* charset);
|
||||||
|
|
||||||
@interface GCDWebServerConnection ()
|
@interface GCDWebServerConnection ()
|
||||||
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
|
- (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -45,37 +45,3 @@
|
|||||||
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length; // Implementation required
|
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length; // Implementation required
|
||||||
- (BOOL)close; // Implementation required
|
- (BOOL)close; // Implementation required
|
||||||
@end
|
@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"
|
#import "GCDWebServerPrivate.h"
|
||||||
|
|
||||||
#define kMultiPartBufferSize (256 * 1024)
|
|
||||||
|
|
||||||
enum {
|
|
||||||
kParserState_Undefined = 0,
|
|
||||||
kParserState_Start,
|
|
||||||
kParserState_Headers,
|
|
||||||
kParserState_Content,
|
|
||||||
kParserState_End
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface GCDWebServerRequest () {
|
@interface GCDWebServerRequest () {
|
||||||
@private
|
@private
|
||||||
NSString* _method;
|
NSString* _method;
|
||||||
@ -50,94 +40,6 @@ enum {
|
|||||||
}
|
}
|
||||||
@end
|
@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
|
@implementation GCDWebServerRequest : NSObject
|
||||||
|
|
||||||
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, byteRange=_range;
|
@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
|
@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>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
typedef NSData* (^GCDWebServerStreamBlock)(NSError** error);
|
|
||||||
|
|
||||||
@protocol GCDWebServerBodyReader <NSObject>
|
@protocol GCDWebServerBodyReader <NSObject>
|
||||||
- (BOOL)open:(NSError**)error;
|
- (BOOL)open:(NSError**)error;
|
||||||
- (NSData*)readData:(NSError**)error; // Return nil on error or empty NSData if at end
|
- (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)initWithStatusCode:(NSInteger)statusCode;
|
||||||
- (id)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
- (id)initWithRedirect:(NSURL*)location permanent:(BOOL)permanent;
|
||||||
@end
|
@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.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <sys/stat.h>
|
|
||||||
#import <zlib.h>
|
#import <zlib.h>
|
||||||
|
|
||||||
#import "GCDWebServerPrivate.h"
|
#import "GCDWebServerPrivate.h"
|
||||||
|
|
||||||
#define kZlibErrorDomain @"ZlibErrorDomain"
|
#define kZlibErrorDomain @"ZlibErrorDomain"
|
||||||
#define kGZipInitialBufferSize (256 * 1024)
|
#define kGZipInitialBufferSize (256 * 1024)
|
||||||
#define kFileReadBufferSize (32 * 1024)
|
|
||||||
|
|
||||||
@interface GCDWebServerBodyEncoder : NSObject <GCDWebServerBodyReader>
|
@interface GCDWebServerBodyEncoder : NSObject <GCDWebServerBodyReader>
|
||||||
- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader;
|
- (id)initWithResponse:(GCDWebServerResponse*)response reader:(id<GCDWebServerBodyReader>)reader;
|
||||||
@ -332,293 +330,3 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@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
CGDWebServer/GCDWebServerStreamResponse.h
Normal file
35
CGDWebServer/GCDWebServerStreamResponse.h
Normal 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
CGDWebServer/GCDWebServerStreamResponse.m
Normal file
62
CGDWebServer/GCDWebServerStreamResponse.m
Normal 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
|
||||||
33
CGDWebServer/GCDWebServerURLEncodedFormRequest.h
Normal file
33
CGDWebServer/GCDWebServerURLEncodedFormRequest.h
Normal file
@ -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
|
||||||
63
CGDWebServer/GCDWebServerURLEncodedFormRequest.m
Normal file
63
CGDWebServer/GCDWebServerURLEncodedFormRequest.m
Normal file
@ -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 */; };
|
E22112991690B7AA0048D2B2 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E22112981690B7AA0048D2B2 /* CFNetwork.framework */; };
|
||||||
E221129B1690B7B10048D2B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129A1690B7B10048D2B2 /* UIKit.framework */; };
|
E221129B1690B7B10048D2B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129A1690B7B10048D2B2 /* UIKit.framework */; };
|
||||||
E221129D1690B7BA0048D2B2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E221129C1690B7BA0048D2B2 /* MobileCoreServices.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 */; };
|
E2B0D4A718F13495009A7927 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B0D4A618F13495009A7927 /* libz.dylib */; };
|
||||||
E2B0D4A918F134A8009A7927 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B0D4A818F134A8009A7927 /* libz.dylib */; };
|
E2B0D4A918F134A8009A7927 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E2B0D4A818F134A8009A7927 /* libz.dylib */; };
|
||||||
E2BE850A18E77ECA0061360B /* GCDWebUploader.bundle in Resources */ = {isa = PBXBuildFile; fileRef = E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */; };
|
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; };
|
E22112981690B7AA0048D2B2 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
E221129A1690B7B10048D2B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
|
E221129A1690B7B10048D2B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; };
|
E221129C1690B7BA0048D2B2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/MobileCoreServices.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
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; };
|
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; };
|
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>"; };
|
E2BE850718E77ECA0061360B /* GCDWebUploader.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = GCDWebUploader.bundle; sourceTree = "<group>"; };
|
||||||
@ -163,11 +191,25 @@
|
|||||||
E221127D1690B63A0048D2B2 /* GCDWebServer.m */,
|
E221127D1690B63A0048D2B2 /* GCDWebServer.m */,
|
||||||
E221127E1690B63A0048D2B2 /* GCDWebServerConnection.h */,
|
E221127E1690B63A0048D2B2 /* GCDWebServerConnection.h */,
|
||||||
E221127F1690B63A0048D2B2 /* GCDWebServerConnection.m */,
|
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 */,
|
E22112801690B63A0048D2B2 /* GCDWebServerPrivate.h */,
|
||||||
E22112811690B63A0048D2B2 /* GCDWebServerRequest.h */,
|
E22112811690B63A0048D2B2 /* GCDWebServerRequest.h */,
|
||||||
E22112821690B63A0048D2B2 /* GCDWebServerRequest.m */,
|
E22112821690B63A0048D2B2 /* GCDWebServerRequest.m */,
|
||||||
E22112831690B63A0048D2B2 /* GCDWebServerResponse.h */,
|
E22112831690B63A0048D2B2 /* GCDWebServerResponse.h */,
|
||||||
E22112841690B63A0048D2B2 /* GCDWebServerResponse.m */,
|
E22112841690B63A0048D2B2 /* GCDWebServerResponse.m */,
|
||||||
|
E2A0E7F318F1D1E500C580B1 /* GCDWebServerStreamResponse.h */,
|
||||||
|
E2A0E7F418F1D1E500C580B1 /* GCDWebServerStreamResponse.m */,
|
||||||
|
E2A0E80318F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.h */,
|
||||||
|
E2A0E80418F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m */,
|
||||||
);
|
);
|
||||||
path = CGDWebServer;
|
path = CGDWebServer;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -308,11 +350,18 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E22112851690B63A0048D2B2 /* GCDWebServer.m in Sources */,
|
E22112851690B63A0048D2B2 /* GCDWebServer.m in Sources */,
|
||||||
|
E2A0E7FD18F1D36C00C580B1 /* GCDWebServerFileRequest.m in Sources */,
|
||||||
E22112871690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
|
E22112871690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
|
||||||
|
E2A0E80518F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||||
|
E2A0E7F918F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */,
|
||||||
E22112891690B63A0048D2B2 /* GCDWebServerRequest.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 */,
|
E221128B1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */,
|
||||||
E2BE850C18E785940061360B /* GCDWebUploader.m in Sources */,
|
E2BE850C18E785940061360B /* GCDWebUploader.m in Sources */,
|
||||||
E221128F1690B6470048D2B2 /* main.m in Sources */,
|
E221128F1690B6470048D2B2 /* main.m in Sources */,
|
||||||
|
E2A0E7F118F1D12E00C580B1 /* GCDWebServerFileResponse.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -321,11 +370,18 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E22112861690B63A0048D2B2 /* GCDWebServer.m in Sources */,
|
E22112861690B63A0048D2B2 /* GCDWebServer.m in Sources */,
|
||||||
|
E2A0E7EE18F1D03700C580B1 /* GCDWebServerDataResponse.m in Sources */,
|
||||||
|
E2A0E80218F1D3DE00C580B1 /* GCDWebServerMultiPartFormRequest.m in Sources */,
|
||||||
E22112881690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
|
E22112881690B63A0048D2B2 /* GCDWebServerConnection.m in Sources */,
|
||||||
E221128A1690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */,
|
E221128A1690B63A0048D2B2 /* GCDWebServerRequest.m in Sources */,
|
||||||
E221128C1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */,
|
E221128C1690B63A0048D2B2 /* GCDWebServerResponse.m in Sources */,
|
||||||
|
E2A0E7FA18F1D24700C580B1 /* GCDWebServerDataRequest.m in Sources */,
|
||||||
|
E2A0E80618F1D4A700C580B1 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||||
E2BE850B18E77ECA0061360B /* GCDWebUploader.m in Sources */,
|
E2BE850B18E77ECA0061360B /* GCDWebUploader.m in Sources */,
|
||||||
E22112951690B64F0048D2B2 /* AppDelegate.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 */,
|
E22112971690B64F0048D2B2 /* main.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@ -33,6 +33,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import "GCDWebUploader.h"
|
#import "GCDWebUploader.h"
|
||||||
|
#import "GCDWebServerDataRequest.h"
|
||||||
|
#import "GCDWebServerMultiPartFormRequest.h"
|
||||||
|
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||||
|
#import "GCDWebServerDataResponse.h"
|
||||||
|
#import "GCDWebServerFileResponse.h"
|
||||||
|
|
||||||
@interface GCDWebUploader () {
|
@interface GCDWebUploader () {
|
||||||
@private
|
@private
|
||||||
|
|||||||
@ -26,6 +26,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import "GCDWebUploader.h"
|
#import "GCDWebUploader.h"
|
||||||
|
#import "GCDWebServerDataRequest.h"
|
||||||
|
#import "GCDWebServerURLEncodedFormRequest.h"
|
||||||
|
#import "GCDWebServerDataResponse.h"
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
int main(int argc, const char* argv[]) {
|
||||||
BOOL success = NO;
|
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:
|
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
|
* 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
|
* 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
|
* Support for streaming large HTTP bodies for requests and responses to minimize memory usage
|
||||||
|
|||||||
Reference in New Issue
Block a user