Modified GCDWebServerMultiPart to allow duplicate control names

This commit is contained in:
Pierre-Olivier Latour
2014-04-24 19:01:46 -07:00
parent f7d6da55cd
commit c8d2b225ba
4 changed files with 64 additions and 30 deletions
@@ -33,6 +33,11 @@
*/ */
@interface GCDWebServerMultiPart : NSObject @interface GCDWebServerMultiPart : NSObject
/**
* Returns the control name retrieved from the part headers.
*/
@property(nonatomic, readonly) NSString* controlName;
/** /**
* Returns the content type retrieved from the part headers or "text/plain" * Returns the content type retrieved from the part headers or "text/plain"
* if not available (per HTTP specifications). * if not available (per HTTP specifications).
@@ -100,13 +105,13 @@
* Returns the argument parts from the multipart encoded form as * Returns the argument parts from the multipart encoded form as
* name / GCDWebServerMultiPartArgument pairs. * name / GCDWebServerMultiPartArgument pairs.
*/ */
@property(nonatomic, readonly) NSDictionary* arguments; @property(nonatomic, readonly) NSArray* arguments;
/** /**
* Returns the files parts from the multipart encoded form as * Returns the files parts from the multipart encoded form as
* name / GCDWebServerMultiPartFile pairs. * name / GCDWebServerMultiPartFile pairs.
*/ */
@property(nonatomic, readonly) NSDictionary* files; @property(nonatomic, readonly) NSArray* files;
/** /**
* Returns the MIME type for multipart encoded forms * Returns the MIME type for multipart encoded forms
@@ -114,4 +119,14 @@
*/ */
+ (NSString*)mimeType; + (NSString*)mimeType;
/**
* Returns the first argument for a given control name or nil if not found.
*/
- (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name;
/**
* Returns the first file for a given control name or nil if not found.
*/
- (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name;
@end @end
@@ -38,7 +38,7 @@ typedef enum {
} ParserState; } ParserState;
@interface GCDWebServerMIMEStreamParser : NSObject @interface GCDWebServerMIMEStreamParser : NSObject
- (instancetype)initWithBoundary:(NSString*)boundary arguments:(NSMutableDictionary*)arguments files:(NSMutableDictionary*)files; - (instancetype)initWithBoundary:(NSString*)boundary arguments:(NSMutableArray*)arguments files:(NSMutableArray*)files;
- (BOOL)appendData:(NSData*)data; - (BOOL)appendData:(NSData*)data;
- (BOOL)isAtEnd; - (BOOL)isAtEnd;
@end @end
@@ -49,6 +49,7 @@ static NSData* _dashNewlineData = nil;
@interface GCDWebServerMultiPart () { @interface GCDWebServerMultiPart () {
@private @private
NSString* _controlName;
NSString* _contentType; NSString* _contentType;
NSString* _mimeType; NSString* _mimeType;
} }
@@ -56,17 +57,19 @@ static NSData* _dashNewlineData = nil;
@implementation GCDWebServerMultiPart @implementation GCDWebServerMultiPart
@synthesize contentType=_contentType, mimeType=_mimeType; @synthesize controlName=_controlName, contentType=_contentType, mimeType=_mimeType;
- (id)initWithContentType:(NSString*)contentType { - (id)initWithControlName:(NSString*)name contentType:(NSString*)type {
if ((self = [super init])) { if ((self = [super init])) {
_contentType = [contentType copy]; _controlName = [name copy];
_contentType = [type copy];
_mimeType = ARC_RETAIN(GCDWebServerTruncateHeaderValue(_contentType)); _mimeType = ARC_RETAIN(GCDWebServerTruncateHeaderValue(_contentType));
} }
return self; return self;
} }
- (void)dealloc { - (void)dealloc {
ARC_RELEASE(_controlName);
ARC_RELEASE(_contentType); ARC_RELEASE(_contentType);
ARC_RELEASE(_mimeType); ARC_RELEASE(_mimeType);
@@ -86,8 +89,8 @@ static NSData* _dashNewlineData = nil;
@synthesize data=_data, string=_string; @synthesize data=_data, string=_string;
- (id)initWithContentType:(NSString*)contentType data:(NSData*)data { - (id)initWithControlName:(NSString*)name contentType:(NSString*)type data:(NSData*)data {
if ((self = [super initWithContentType:contentType])) { if ((self = [super initWithControlName:name contentType:type])) {
_data = ARC_RETAIN(data); _data = ARC_RETAIN(data);
if ([self.contentType hasPrefix:@"text/"]) { if ([self.contentType hasPrefix:@"text/"]) {
@@ -122,8 +125,8 @@ static NSData* _dashNewlineData = nil;
@synthesize fileName=_fileName, temporaryPath=_temporaryPath; @synthesize fileName=_fileName, temporaryPath=_temporaryPath;
- (id)initWithContentType:(NSString*)contentType fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath { - (id)initWithControlName:(NSString*)name contentType:(NSString*)type fileName:(NSString*)fileName temporaryPath:(NSString*)temporaryPath {
if ((self = [super initWithContentType:contentType])) { if ((self = [super initWithControlName:name contentType:type])) {
_fileName = [fileName copy]; _fileName = [fileName copy];
_temporaryPath = [temporaryPath copy]; _temporaryPath = [temporaryPath copy];
} }
@@ -150,8 +153,8 @@ static NSData* _dashNewlineData = nil;
NSData* _boundary; NSData* _boundary;
ParserState _state; ParserState _state;
NSMutableData* _data; NSMutableData* _data;
NSMutableDictionary* _arguments; NSMutableArray* _arguments;
NSMutableDictionary* _files; NSMutableArray* _files;
NSString* _controlName; NSString* _controlName;
NSString* _fileName; NSString* _fileName;
@@ -178,7 +181,7 @@ static NSData* _dashNewlineData = nil;
} }
} }
- (instancetype)initWithBoundary:(NSString*)boundary arguments:(NSMutableDictionary*)arguments files:(NSMutableDictionary*)files { - (instancetype)initWithBoundary:(NSString*)boundary arguments:(NSMutableArray*)arguments files:(NSMutableArray*)files {
NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil; NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil;
if (data == nil) { if (data == nil) {
DNOT_REACHED(); DNOT_REACHED();
@@ -294,8 +297,8 @@ static NSData* _dashNewlineData = nil;
if (result == (ssize_t)dataLength) { if (result == (ssize_t)dataLength) {
if (close(_tmpFile) == 0) { if (close(_tmpFile) == 0) {
_tmpFile = 0; _tmpFile = 0;
GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithContentType:_contentType fileName:_fileName temporaryPath:_tmpPath]; GCDWebServerMultiPartFile* file = [[GCDWebServerMultiPartFile alloc] initWithControlName:_controlName contentType:_contentType fileName:_fileName temporaryPath:_tmpPath];
[_files setObject:file forKey:_controlName]; [_files addObject:file];
ARC_RELEASE(file); ARC_RELEASE(file);
} else { } else {
DNOT_REACHED(); DNOT_REACHED();
@@ -309,8 +312,8 @@ static NSData* _dashNewlineData = nil;
_tmpPath = nil; _tmpPath = nil;
} else { } else {
NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength]; NSData* data = [[NSData alloc] initWithBytes:(void*)dataBytes length:dataLength];
GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithContentType:_contentType data:data]; GCDWebServerMultiPartArgument* argument = [[GCDWebServerMultiPartArgument alloc] initWithControlName:_controlName contentType:_contentType data:data];
[_arguments setObject:argument forKey:_controlName]; [_arguments addObject:argument];
ARC_RELEASE(argument); ARC_RELEASE(argument);
ARC_RELEASE(data); ARC_RELEASE(data);
} }
@@ -356,8 +359,8 @@ static NSData* _dashNewlineData = nil;
@interface GCDWebServerMultiPartFormRequest () { @interface GCDWebServerMultiPartFormRequest () {
@private @private
GCDWebServerMIMEStreamParser* _parser; GCDWebServerMIMEStreamParser* _parser;
NSMutableDictionary* _arguments; NSMutableArray* _arguments;
NSMutableDictionary* _files; NSMutableArray* _files;
} }
@end @end
@@ -371,8 +374,8 @@ static NSData* _dashNewlineData = nil;
- (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query { - (instancetype)initWithMethod:(NSString*)method url:(NSURL*)url headers:(NSDictionary*)headers path:(NSString*)path query:(NSDictionary*)query {
if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) { if ((self = [super initWithMethod:method url:url headers:headers path:path query:query])) {
_arguments = [[NSMutableDictionary alloc] init]; _arguments = [[NSMutableArray alloc] init];
_files = [[NSMutableDictionary alloc] init]; _files = [[NSMutableArray alloc] init];
} }
return self; return self;
} }
@@ -413,21 +416,37 @@ static NSData* _dashNewlineData = nil;
return YES; return YES;
} }
- (GCDWebServerMultiPartArgument*)firstArgumentForControlName:(NSString*)name {
for (GCDWebServerMultiPartArgument* argument in _arguments) {
if ([argument.controlName isEqualToString:name]) {
return argument;
}
}
return nil;
}
- (GCDWebServerMultiPartFile*)firstFileForControlName:(NSString*)name {
for (GCDWebServerMultiPartFile* file in _files) {
if ([file.controlName isEqualToString:name]) {
return file;
}
}
return nil;
}
- (NSString*)description { - (NSString*)description {
NSMutableString* description = [NSMutableString stringWithString:[super description]]; NSMutableString* description = [NSMutableString stringWithString:[super description]];
if (_arguments.count) { if (_arguments.count) {
[description appendString:@"\n"]; [description appendString:@"\n"];
for (NSString* key in [[_arguments allKeys] sortedArrayUsingSelector:@selector(compare:)]) { for (GCDWebServerMultiPartArgument* argument in _arguments) {
GCDWebServerMultiPartArgument* argument = [_arguments objectForKey:key]; [description appendFormat:@"\n%@ (%@)\n", argument.controlName, argument.contentType];
[description appendFormat:@"\n%@ (%@)\n", key, argument.contentType];
[description appendString:GCDWebServerDescribeData(argument.data, argument.contentType)]; [description appendString:GCDWebServerDescribeData(argument.data, argument.contentType)];
} }
} }
if (_files.count) { if (_files.count) {
[description appendString:@"\n"]; [description appendString:@"\n"];
for (NSString* key in [[_files allKeys] sortedArrayUsingSelector:@selector(compare:)]) { for (GCDWebServerMultiPartFile* file in _files) {
GCDWebServerMultiPartFile* file = [_files objectForKey:key]; [description appendFormat:@"\n%@ (%@): %@\n{%@}", file.controlName, file.contentType, file.fileName, file.temporaryPath];
[description appendFormat:@"\n%@ (%@): %@\n{%@}", key, file.contentType, file.fileName, file.temporaryPath];
} }
} }
return description; return description;
@@ -35,7 +35,7 @@
@interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest @interface GCDWebServerURLEncodedFormRequest : GCDWebServerDataRequest
/** /**
* Returns the unescaped names and values for the URL encoded form. * Returns the unescaped control names and values for the URL encoded form.
* *
* The text encoding used to interpret the data is extracted from the * The text encoding used to interpret the data is extracted from the
* "Content-Type" header or defaults to UTF-8. * "Content-Type" header or defaults to UTF-8.
+2 -2
View File
@@ -154,11 +154,11 @@
NSRange range = [[request.headers objectForKey:@"Accept"] rangeOfString:@"application/json" options:NSCaseInsensitiveSearch]; NSRange range = [[request.headers objectForKey:@"Accept"] rangeOfString:@"application/json" options:NSCaseInsensitiveSearch];
NSString* contentType = (range.location != NSNotFound ? @"application/json" : @"text/plain; charset=utf-8"); // Required when using iFrame transport (see https://github.com/blueimp/jQuery-File-Upload/wiki/Setup) NSString* contentType = (range.location != NSNotFound ? @"application/json" : @"text/plain; charset=utf-8"); // Required when using iFrame transport (see https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
GCDWebServerMultiPartFile* file = [request.files objectForKey:@"files[]"]; GCDWebServerMultiPartFile* file = [request firstFileForControlName:@"files[]"];
if ((!_allowHidden && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) { if ((!_allowHidden && [file.fileName hasPrefix:@"."]) || ![self _checkFileExtension:file.fileName]) {
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName]; return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Uploaded file name \"%@\" is not allowed", file.fileName];
} }
NSString* relativePath = [(GCDWebServerMultiPartArgument*)[request.arguments objectForKey:@"path"] string]; NSString* relativePath = [[request firstArgumentForControlName:@"path"] string];
NSString* absolutePath = [self _uniquePathForPath:[[_uploadDirectory stringByAppendingPathComponent:relativePath] stringByAppendingPathComponent:file.fileName]]; NSString* absolutePath = [self _uniquePathForPath:[[_uploadDirectory stringByAppendingPathComponent:relativePath] stringByAppendingPathComponent:file.fileName]];
if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) { if (![self shouldUploadFileAtPath:absolutePath withTemporaryFile:file.temporaryPath]) {