Split class files

This commit is contained in:
Pierre-Olivier Latour
2014-04-06 11:32:07 -07:00
parent 81638ad086
commit 1f9a0d38d0
24 changed files with 1316 additions and 846 deletions
-483
View File
@@ -27,16 +27,6 @@
#import "GCDWebServerPrivate.h"
#define kMultiPartBufferSize (256 * 1024)
enum {
kParserState_Undefined = 0,
kParserState_Start,
kParserState_Headers,
kParserState_Content,
kParserState_End
};
@interface GCDWebServerRequest () {
@private
NSString* _method;
@@ -50,94 +40,6 @@ enum {
}
@end
@interface GCDWebServerDataRequest () {
@private
NSMutableData* _data;
}
@end
@interface GCDWebServerFileRequest () {
@private
NSString* _filePath;
int _file;
}
@end
@interface GCDWebServerURLEncodedFormRequest () {
@private
NSDictionary* _arguments;
}
@end
@interface GCDWebServerMultiPart () {
@private
NSString* _contentType;
NSString* _mimeType;
}
@end
@interface GCDWebServerMultiPartArgument () {
@private
NSData* _data;
NSString* _string;
}
@end
@interface GCDWebServerMultiPartFile () {
@private
NSString* _fileName;
NSString* _temporaryPath;
}
@end
@interface GCDWebServerMultiPartFormRequest () {
@private
NSData* _boundary;
NSUInteger _parserState;
NSMutableData* _parserData;
NSString* _controlName;
NSString* _fileName;
NSString* _contentType;
NSString* _tmpPath;
int _tmpFile;
NSMutableDictionary* _arguments;
NSMutableDictionary* _files;
}
@end
static NSData* _newlineData = nil;
static NSData* _newlinesData = nil;
static NSData* _dashNewlineData = nil;
static NSString* _ExtractHeaderParameter(NSString* header, NSString* attribute) {
NSString* value = nil;
if (header) {
NSScanner* scanner = [[NSScanner alloc] initWithString:header];
NSString* string = [NSString stringWithFormat:@"%@=", attribute];
if ([scanner scanUpToString:string intoString:NULL]) {
[scanner scanString:string intoString:NULL];
if ([scanner scanString:@"\"" intoString:NULL]) {
[scanner scanUpToString:@"\"" intoString:&value];
} else {
[scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceCharacterSet] intoString:&value];
}
}
ARC_RELEASE(scanner);
}
return value;
}
// http://www.w3schools.com/tags/ref_charactersets.asp
static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
NSStringEncoding encoding = kCFStringEncodingInvalidId;
if (charset) {
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)charset));
}
return (encoding != kCFStringEncodingInvalidId ? encoding : NSUTF8StringEncoding);
}
@implementation GCDWebServerRequest : NSObject
@synthesize method=_method, URL=_url, headers=_headers, path=_path, query=_query, contentType=_type, contentLength=_length, byteRange=_range;
@@ -234,388 +136,3 @@ static NSStringEncoding _StringEncodingFromCharset(NSString* charset) {
}
@end
@implementation GCDWebServerDataRequest
@synthesize data=_data;
- (void)dealloc {
DCHECK(_data != nil);
ARC_RELEASE(_data);
ARC_DEALLOC(super);
}
- (BOOL)open {
DCHECK(_data == nil);
_data = [[NSMutableData alloc] initWithCapacity:self.contentLength];
return _data ? YES : NO;
}
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
DCHECK(_data != nil);
[_data appendBytes:buffer length:length];
return length;
}
- (BOOL)close {
DCHECK(_data != nil);
return YES;
}
@end
@implementation GCDWebServerFileRequest
@synthesize filePath=_filePath;
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
_filePath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
}
return self;
}
- (void)dealloc {
DCHECK(_file < 0);
unlink([_filePath fileSystemRepresentation]);
ARC_RELEASE(_filePath);
ARC_DEALLOC(super);
}
- (BOOL)open {
DCHECK(_file == 0);
_file = open([_filePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
return (_file > 0 ? YES : NO);
}
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
DCHECK(_file > 0);
return write(_file, buffer, length);
}
- (BOOL)close {
DCHECK(_file > 0);
int result = close(_file);
_file = -1;
return (result == 0 ? YES : NO);
}
@end
@implementation GCDWebServerURLEncodedFormRequest
@synthesize arguments=_arguments;
+ (NSString*)mimeType {
return @"application/x-www-form-urlencoded";
}
- (void)dealloc {
ARC_RELEASE(_arguments);
ARC_DEALLOC(super);
}
- (BOOL)close {
if (![super close]) {
return NO;
}
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
NSString* string = [[NSString alloc] initWithData:self.data encoding:_StringEncodingFromCharset(charset)];
_arguments = ARC_RETAIN(GCDWebServerParseURLEncodedForm(string));
ARC_RELEASE(string);
return (_arguments ? YES : NO);
}
@end
@implementation GCDWebServerMultiPart
@synthesize contentType=_contentType, mimeType=_mimeType;
- (id)initWithContentType:(NSString*)contentType {
if ((self = [super init])) {
_contentType = [contentType copy];
NSArray* components = [_contentType componentsSeparatedByString:@";"];
if (components.count) {
_mimeType = ARC_RETAIN([[components objectAtIndex:0] lowercaseString]);
}
if (_mimeType == nil) {
_mimeType = @"text/plain";
}
}
return self;
}
- (void)dealloc {
ARC_RELEASE(_contentType);
ARC_RELEASE(_mimeType);
ARC_DEALLOC(super);
}
@end
@implementation GCDWebServerMultiPartArgument
@synthesize data=_data, string=_string;
- (id)initWithContentType:(NSString*)contentType data:(NSData*)data {
if ((self = [super initWithContentType:contentType])) {
_data = ARC_RETAIN(data);
if ([self.mimeType hasPrefix:@"text/"]) {
NSString* charset = _ExtractHeaderParameter(self.contentType, @"charset");
_string = [[NSString alloc] initWithData:_data encoding:_StringEncodingFromCharset(charset)];
}
}
return self;
}
- (void)dealloc {
ARC_RELEASE(_data);
ARC_RELEASE(_string);
ARC_DEALLOC(super);
}
- (NSString*)description {
return [NSString stringWithFormat:@"<%@ | '%@' | %i bytes>", [self class], self.mimeType, (int)_data.length];
}
@end
@implementation GCDWebServerMultiPartFile
@synthesize fileName=_fileName, temporaryPath=_temporaryPath;
- (id)initWithContentType:(NSString*)contentType fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath {
if ((self = [super initWithContentType:contentType])) {
_fileName = [fileName copy];
_temporaryPath = [temporaryPath copy];
}
return self;
}
- (void)dealloc {
unlink([_temporaryPath fileSystemRepresentation]);
ARC_RELEASE(_fileName);
ARC_RELEASE(_temporaryPath);
ARC_DEALLOC(super);
}
- (NSString*)description {
return [NSString stringWithFormat:@"<%@ | '%@' | '%@>'", [self class], self.mimeType, _fileName];
}
@end
@implementation GCDWebServerMultiPartFormRequest
@synthesize arguments=_arguments, files=_files;
+ (void)initialize {
if (_newlineData == nil) {
_newlineData = [[NSData alloc] initWithBytes:"\r\n" length:2];
DCHECK(_newlineData);
}
if (_newlinesData == nil) {
_newlinesData = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
DCHECK(_newlinesData);
}
if (_dashNewlineData == nil) {
_dashNewlineData = [[NSData alloc] initWithBytes:"--\r\n" length:4];
DCHECK(_dashNewlineData);
}
}
+ (NSString*)mimeType {
return @"multipart/form-data";
}
- (id)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
NSString* boundary = _ExtractHeaderParameter(self.contentType, @"boundary");
if (boundary) {
NSData* data = [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding];
_boundary = ARC_RETAIN(data);
}
if (_boundary == nil) {
DNOT_REACHED();
ARC_RELEASE(self);
return nil;
}
_arguments = [[NSMutableDictionary alloc] init];
_files = [[NSMutableDictionary alloc] init];
}
return self;
}
- (BOOL)open {
DCHECK(_parserData == nil);
_parserData = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
_parserState = kParserState_Start;
return YES;
}
// http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
- (BOOL)_parseData {
BOOL success = YES;
if (_parserState == kParserState_Headers) {
NSRange range = [_parserData rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _parserData.length)];
if (range.location != NSNotFound) {
ARC_RELEASE(_controlName);
_controlName = nil;
ARC_RELEASE(_fileName);
_fileName = nil;
ARC_RELEASE(_contentType);
_contentType = nil;
ARC_RELEASE(_tmpPath);
_tmpPath = nil;
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
const char* temp = "GET / HTTP/1.0\r\n";
CFHTTPMessageAppendBytes(message, (const UInt8*)temp, strlen(temp));
CFHTTPMessageAppendBytes(message, _parserData.bytes, range.location + range.length);
if (CFHTTPMessageIsHeaderComplete(message)) {
NSString* controlName = nil;
NSString* fileName = nil;
NSDictionary* headers = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(message));
NSString* contentDisposition = [headers objectForKey:@"Content-Disposition"];
if ([[contentDisposition lowercaseString] hasPrefix:@"form-data;"]) {
controlName = _ExtractHeaderParameter(contentDisposition, @"name");
fileName = _ExtractHeaderParameter(contentDisposition, @"filename");
}
_controlName = [controlName copy];
_fileName = [fileName copy];
_contentType = ARC_RETAIN([headers objectForKey:@"Content-Type"]);
}
CFRelease(message);
if (_controlName) {
if (_fileName) {
NSString* path = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
_tmpFile = open([path fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (_tmpFile > 0) {
_tmpPath = [path copy];
} else {
DNOT_REACHED();
success = NO;
}
}
} else {
DNOT_REACHED();
success = NO;
}
[_parserData replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
_parserState = kParserState_Content;
}
}
if ((_parserState == kParserState_Start) || (_parserState == kParserState_Content)) {
NSRange range = [_parserData rangeOfData:_boundary options:0 range:NSMakeRange(0, _parserData.length)];
if (range.location != NSNotFound) {
NSRange subRange = NSMakeRange(range.location + range.length, _parserData.length - range.location - range.length);
NSRange subRange1 = [_parserData rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
NSRange subRange2 = [_parserData rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
if (_parserState == kParserState_Content) {
const void* dataBytes = _parserData.bytes;
NSUInteger dataLength = range.location - 2;
if (_tmpPath) {
ssize_t result = write(_tmpFile, dataBytes, dataLength);
if (result == (ssize_t)dataLength) {
if (close(_tmpFile) == 0) {
_tmpFile = 0;
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithContentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
[_files setObject:file forKey:_controlName];
ARC_RELEASE(file);
} else {
DNOT_REACHED();
success = NO;
}
} else {
DNOT_REACHED();
success = NO;
}
ARC_RELEASE(_tmpPath);
_tmpPath = nil;
} else {
NSData* data = [[NSData alloc] initWithBytesNoCopy:(void*)dataBytes length:dataLength freeWhenDone:NO];
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithContentType:_contentType data:data];
[_arguments setObject:argument forKey:_controlName];
ARC_RELEASE(argument);
ARC_RELEASE(data);
}
}
if (subRange1.location != NSNotFound) {
[_parserData replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
_parserState = kParserState_Headers;
success = [self _parseData];
} else {
_parserState = kParserState_End;
}
}
} else {
NSUInteger margin = 2 * _boundary.length;
if (_tmpPath && (_parserData.length > margin)) {
NSUInteger length = _parserData.length - margin;
ssize_t result = write(_tmpFile, _parserData.bytes, length);
if (result == (ssize_t)length) {
[_parserData replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
} else {
DNOT_REACHED();
success = NO;
}
}
}
}
return success;
}
- (NSInteger)write:(const void*)buffer maxLength:(NSUInteger)length {
DCHECK(_parserData != nil);
[_parserData appendBytes:buffer length:length];
return ([self _parseData] ? length : -1);
}
- (BOOL)close {
DCHECK(_parserData != nil);
ARC_RELEASE(_parserData);
_parserData = nil;
ARC_RELEASE(_controlName);
_controlName = nil;
ARC_RELEASE(_fileName);
_fileName = nil;
ARC_RELEASE(_contentType);
_contentType = nil;
if (_tmpFile > 0) {
close(_tmpFile);
unlink([_tmpPath fileSystemRepresentation]);
_tmpFile = 0;
}
ARC_RELEASE(_tmpPath);
_tmpPath = nil;
return (_parserState == kParserState_End ? YES : NO);
}
- (void)dealloc {
DCHECK(_parserData == nil);
ARC_RELEASE(_arguments);
ARC_RELEASE(_files);
ARC_RELEASE(_boundary);
ARC_DEALLOC(super);
}
@end