瀏覽代碼

Added support for asynchronous reading in GCDWebServerBodyReader

Pierre-Olivier Latour 11 年之前
父節點
當前提交
c4bf7b11e2

+ 43 - 41
GCDWebServer/Core/GCDWebServerConnection.m

@@ -280,54 +280,56 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
 
 - (void)_writeBodyWithCompletionBlock:(WriteBodyCompletionBlock)block {
   GWS_DCHECK([_response hasBody]);
-  NSError* error = nil;
-  NSData* data = [_response performReadData:&error];
-  if (data) {
-    if (data.length) {
-      if (_response.usesChunkedTransferEncoding) {
-        const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
-        size_t hexLength = strlen(hexString);
-        NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
-        if (chunk == nil) {
-          GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error);
-          block(NO);
-          return;
-        }
-        char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
-        bcopy(hexString, ptr, hexLength);
-        ptr += hexLength;
-        *ptr++ = '\r';
-        *ptr++ = '\n';
-        bcopy(data.bytes, ptr, data.length);
-        ptr += data.length;
-        *ptr++ = '\r';
-        *ptr = '\n';
-        data = chunk;
-      }
-      [self _writeData:data withCompletionBlock:^(BOOL success) {
-        
-        if (success) {
-          [self _writeBodyWithCompletionBlock:block];
-        } else {
-          block(NO);
+  [_response performReadDataWithCompletion:^(NSData* data, NSError* error) {
+    
+    if (data) {
+      if (data.length) {
+        if (_response.usesChunkedTransferEncoding) {
+          const char* hexString = [[NSString stringWithFormat:@"%lx", (unsigned long)data.length] UTF8String];
+          size_t hexLength = strlen(hexString);
+          NSData* chunk = [NSMutableData dataWithLength:(hexLength + 2 + data.length + 2)];
+          if (chunk == nil) {
+            GWS_LOG_ERROR(@"Failed allocating memory for response body chunk for socket %i: %@", _socket, error);
+            block(NO);
+            return;
+          }
+          char* ptr = (char*)[(NSMutableData*)chunk mutableBytes];
+          bcopy(hexString, ptr, hexLength);
+          ptr += hexLength;
+          *ptr++ = '\r';
+          *ptr++ = '\n';
+          bcopy(data.bytes, ptr, data.length);
+          ptr += data.length;
+          *ptr++ = '\r';
+          *ptr = '\n';
+          data = chunk;
         }
-        
-      }];
-    } else {
-      if (_response.usesChunkedTransferEncoding) {
-        [self _writeData:_lastChunkData withCompletionBlock:^(BOOL success) {
+        [self _writeData:data withCompletionBlock:^(BOOL success) {
           
-          block(success);
+          if (success) {
+            [self _writeBodyWithCompletionBlock:block];
+          } else {
+            block(NO);
+          }
           
         }];
       } else {
-        block(YES);
+        if (_response.usesChunkedTransferEncoding) {
+          [self _writeData:_lastChunkData withCompletionBlock:^(BOOL success) {
+            
+            block(success);
+            
+          }];
+        } else {
+          block(YES);
+        }
       }
+    } else {
+      GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
+      block(NO);
     }
-  } else {
-    GWS_LOG_ERROR(@"Failed reading response body for socket %i: %@", _socket, error);
-    block(NO);
-  }
+    
+  }];
 }
 
 @end

+ 1 - 1
GCDWebServer/Core/GCDWebServerPrivate.h

@@ -249,6 +249,6 @@ extern NSString* GCDWebServerStringFromSockAddr(const struct sockaddr* addr, BOO
 @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
 - (void)prepareForReading;
 - (BOOL)performOpen:(NSError**)error;
-- (NSData*)performReadData:(NSError**)error;
+- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
 - (void)performClose;
 @end

+ 19 - 0
GCDWebServer/Core/GCDWebServerResponse.h

@@ -27,6 +27,12 @@
 
 #import <Foundation/Foundation.h>
 
+/**
+ *  The GCDWebServerBodyReaderCompletionBlock is passed by GCDWebServer to the
+ *  GCDWebServerBodyReader object when reading data from it asynchronously.
+ */
+typedef void (^GCDWebServerBodyReaderCompletionBlock)(NSData* data, NSError* error);
+
 /**
  *  This protocol is used by the GCDWebServerConnection to communicate with
  *  the GCDWebServerResponse and read the HTTP body data to send.
@@ -39,6 +45,8 @@
  */
 @protocol GCDWebServerBodyReader <NSObject>
 
+@required
+
 /**
  *  This method is called before any body data is sent.
  *
@@ -61,6 +69,17 @@
  */
 - (void)close;
 
+@optional
+
+/**
+ *  If this method is implemented, it will be preferred over -readData:.
+ *
+ *  It must call the passed block when data is available, passing a non-empty
+ *  NSData if there is body data available, or an empty NSData there is no more
+ *  body data, or nil on error and pass an NSError along.
+ */
+- (void)asyncReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block;
+
 @end
 
 /**

+ 8 - 3
GCDWebServer/Core/GCDWebServerResponse.m

@@ -244,9 +244,14 @@
   return [_reader open:error];
 }
 
-- (NSData*)performReadData:(NSError**)error {
-  GWS_DCHECK(_opened);
-  return [_reader readData:error];
+- (void)performReadDataWithCompletion:(GCDWebServerBodyReaderCompletionBlock)block {
+  if ([_reader respondsToSelector:@selector(asyncReadDataWithCompletion:)]) {
+    [_reader asyncReadDataWithCompletion:block];
+  } else {
+    NSError* error = nil;
+    NSData* data = [_reader readData:&error];
+    block(data, error);
+  }
 }
 
 - (void)performClose {