Browse Source

Added GCDWebServerMIMEStreamParser class

Pierre-Olivier Latour 11 năm trước cách đây
mục cha
commit
f7d6da55cd
1 tập tin đã thay đổi với 117 bổ sung76 xóa
  1. 117 76
      GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m

+ 117 - 76
GCDWebServer/Requests/GCDWebServerMultiPartFormRequest.m

@@ -29,13 +29,19 @@
 
 #define kMultiPartBufferSize (256 * 1024)
 
-enum {
+typedef enum {
   kParserState_Undefined = 0,
   kParserState_Start,
   kParserState_Headers,
   kParserState_Content,
   kParserState_End
-};
+} ParserState;
+
+@interface GCDWebServerMIMEStreamParser : NSObject
+- (instancetype)initWithBoundary:(NSString*)boundary arguments:(NSMutableDictionary*)arguments files:(NSMutableDictionary*)files;
+- (BOOL)appendData:(NSData*)data;
+- (BOOL)isAtEnd;
+@end
 
 static NSData* _newlineData = nil;
 static NSData* _newlinesData = nil;
@@ -139,26 +145,23 @@ static NSData* _dashNewlineData = nil;
 
 @end
 
-@interface GCDWebServerMultiPartFormRequest () {
+@interface GCDWebServerMIMEStreamParser () {
 @private
   NSData* _boundary;
+  ParserState _state;
+  NSMutableData* _data;
+  NSMutableDictionary* _arguments;
+  NSMutableDictionary* _files;
   
-  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;
+@implementation GCDWebServerMIMEStreamParser
 
 + (void)initialize {
   if (_newlineData == nil) {
@@ -175,49 +178,47 @@ static NSData* _dashNewlineData = nil;
   }
 }
 
-+ (NSString*)mimeType {
-  return @"multipart/form-data";
-}
-
-- (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])) {
-    NSString* boundary = GCDWebServerExtractHeaderValueParameter(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];
+- (instancetype)initWithBoundary:(NSString*)boundary arguments:(NSMutableDictionary*)arguments files:(NSMutableDictionary*)files {
+  NSData* data = boundary.length ? [[NSString stringWithFormat:@"--%@", boundary] dataUsingEncoding:NSASCIIStringEncoding] : nil;
+  if (data == nil) {
+    DNOT_REACHED();
+    ARC_RELEASE(self);
+    return nil;
+  }
+  if ((self = [super init])) {
+    _boundary = ARC_RETAIN(data);
+    _data = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
+    _arguments = ARC_RETAIN(arguments);
+    _files = ARC_RETAIN(files);
+    _state = kParserState_Start;
   }
   return self;
 }
 
 - (void)dealloc {
+  ARC_RELEASE(_boundary);
+  ARC_RELEASE(_data);
   ARC_RELEASE(_arguments);
   ARC_RELEASE(_files);
-  ARC_RELEASE(_boundary);
+  
+  ARC_RELEASE(_controlName);
+  ARC_RELEASE(_fileName);
+  ARC_RELEASE(_contentType);
+  if (_tmpFile > 0) {
+    close(_tmpFile);
+    unlink([_tmpPath fileSystemRepresentation]);
+  }
+  ARC_RELEASE(_tmpPath);
   
   ARC_DEALLOC(super);
 }
 
-- (BOOL)open:(NSError**)error {
-  _parserData = [[NSMutableData alloc] initWithCapacity:kMultiPartBufferSize];
-  _parserState = kParserState_Start;
-  return YES;
-}
-
 // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
 - (BOOL)_parseData {
   BOOL success = YES;
   
-  if (_parserState == kParserState_Headers) {
-    NSRange range = [_parserData rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _parserData.length)];
+  if (_state == kParserState_Headers) {
+    NSRange range = [_data rangeOfData:_newlinesData options:0 range:NSMakeRange(0, _data.length)];
     if (range.location != NSNotFound) {
       
       ARC_RELEASE(_controlName);
@@ -228,7 +229,7 @@ static NSData* _dashNewlineData = nil;
       _contentType = nil;
       ARC_RELEASE(_tmpPath);
       _tmpPath = nil;
-      NSString* headers = [[NSString alloc] initWithData:[_parserData subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding];
+      NSString* headers = [[NSString alloc] initWithData:[_data subdataWithRange:NSMakeRange(0, range.location)] encoding:NSUTF8StringEncoding];
       if (headers) {
         for (NSString* header in [headers componentsSeparatedByString:@"\r\n"]) {
           NSRange subRange = [header rangeOfString:@":"];
@@ -272,21 +273,21 @@ static NSData* _dashNewlineData = nil;
         success = NO;
       }
       
-      [_parserData replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
-      _parserState = kParserState_Content;
+      [_data replaceBytesInRange:NSMakeRange(0, range.location + range.length) withBytes:NULL length:0];
+      _state = kParserState_Content;
     }
   }
   
-  if ((_parserState == kParserState_Start) || (_parserState == kParserState_Content)) {
-    NSRange range = [_parserData rangeOfData:_boundary options:0 range:NSMakeRange(0, _parserData.length)];
+  if ((_state == kParserState_Start) || (_state == kParserState_Content)) {
+    NSRange range = [_data rangeOfData:_boundary options:0 range:NSMakeRange(0, _data.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];
+      NSRange subRange = NSMakeRange(range.location + range.length, _data.length - range.location - range.length);
+      NSRange subRange1 = [_data rangeOfData:_newlineData options:NSDataSearchAnchored range:subRange];
+      NSRange subRange2 = [_data rangeOfData:_dashNewlineData options:NSDataSearchAnchored range:subRange];
       if ((subRange1.location != NSNotFound) || (subRange2.location != NSNotFound)) {
         
-        if (_parserState == kParserState_Content) {
-          const void* dataBytes = _parserData.bytes;
+        if (_state == kParserState_Content) {
+          const void* dataBytes = _data.bytes;
           NSUInteger dataLength = range.location - 2;
           if (_tmpPath) {
             ssize_t result = write(_tmpFile, dataBytes, dataLength);
@@ -316,20 +317,20 @@ static NSData* _dashNewlineData = nil;
         }
         
         if (subRange1.location != NSNotFound) {
-          [_parserData replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
-          _parserState = kParserState_Headers;
+          [_data replaceBytesInRange:NSMakeRange(0, subRange1.location + subRange1.length) withBytes:NULL length:0];
+          _state = kParserState_Headers;
           success = [self _parseData];
         } else {
-          _parserState = kParserState_End;
+          _state = 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 (_tmpPath && (_data.length > margin)) {
+        NSUInteger length = _data.length - margin;
+        ssize_t result = write(_tmpFile, _data.bytes, length);
         if (result == (ssize_t)length) {
-          [_parserData replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
+          [_data replaceBytesInRange:NSMakeRange(0, length) withBytes:NULL length:0];
         } else {
           DNOT_REACHED();
           success = NO;
@@ -337,36 +338,76 @@ static NSData* _dashNewlineData = nil;
       }
     }
   }
+  
   return success;
 }
 
+- (BOOL)appendData:(NSData*)data {
+  [_data appendBytes:data.bytes length:data.length];
+  return [self _parseData];
+}
+
+- (BOOL)isAtEnd {
+  return (_state == kParserState_End);
+}
+
+@end
+
+@interface GCDWebServerMultiPartFormRequest () {
+@private
+  GCDWebServerMIMEStreamParser* _parser;
+  NSMutableDictionary* _arguments;
+  NSMutableDictionary* _files;
+}
+@end
+
+@implementation GCDWebServerMultiPartFormRequest
+
+@synthesize arguments=_arguments, files=_files;
+
++ (NSString*)mimeType {
+  return @"multipart/form-data";
+}
+
+- (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])) {
+    _arguments = [[NSMutableDictionary alloc] init];
+    _files = [[NSMutableDictionary alloc] init];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  ARC_RELEASE(_arguments);
+  ARC_RELEASE(_files);
+  
+  ARC_DEALLOC(super);
+}
+
+- (BOOL)open:(NSError**)error {
+  NSString* boundary = GCDWebServerExtractHeaderValueParameter(self.contentType, @"boundary");
+  _parser = [[GCDWebServerMIMEStreamParser alloc] initWithBoundary:boundary arguments:_arguments files:_files];
+  if (_parser == nil) {
+    *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed starting to parse multipart form data"}];
+    return NO;
+  }
+  return YES;
+}
+
 - (BOOL)writeData:(NSData*)data error:(NSError**)error {
-  [_parserData appendBytes:data.bytes length:data.length];
-  if (![self _parseData]) {
-    *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed parsing multipart form data"}];
+  if (![_parser appendData:data]) {
+    *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed continuing to parse multipart form data"}];
     return NO;
   }
   return YES;
 }
 
 - (BOOL)close:(NSError**)error {
-  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;
-  if (_parserState != kParserState_End) {
-    *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed parsing multipart form data"}];
+  BOOL atEnd = [_parser isAtEnd];
+  ARC_RELEASE(_parser);
+  _parser = nil;
+  if (!atEnd) {
+    *error = [NSError errorWithDomain:kGCDWebServerErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Failed finishing to parse multipart form data"}];
     return NO;
   }
   return YES;