浏览代码

Cleaned up connections read / write implementation

Pierre-Olivier Latour 11 年之前
父节点
当前提交
8fe66444ae
共有 2 个文件被更改,包括 71 次插入123 次删除
  1. 2 2
      GCDWebServer/Core/GCDWebServerConnection.h
  2. 69 121
      GCDWebServer/Core/GCDWebServerConnection.m

+ 2 - 2
GCDWebServer/Core/GCDWebServerConnection.h

@@ -41,8 +41,8 @@
 
 @interface GCDWebServerConnection (Subclassing)
 - (BOOL)open;  // Return NO to reject connection e.g. after validating local or remote addresses
-- (void)didUpdateBytesRead;  // Called from arbitrary thread after @totalBytesRead is updated - Default implementation does nothing
-- (void)didUpdateBytesWritten;  // Called from arbitrary thread after @totalBytesWritten is updated - Default implementation does nothing
+- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length;  // Called after data has been read from the connection
+- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length;  // Called after data has been written to the connection
 - (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block;  // Only called if the request can be processed
 - (GCDWebServerResponse*)replaceResponse:(GCDWebServerResponse*)response forRequest:(GCDWebServerRequest*)request;  // Default implementation replaces any response matching the "ETag" or "Last-Modified-Date" header of the request by a barebone "Not-Modified" (304) one
 - (void)abortRequest:(GCDWebServerRequest*)request withStatusCode:(NSInteger)statusCode;  // If request headers was malformed, "request" will be nil

+ 69 - 121
GCDWebServer/Core/GCDWebServerConnection.m

@@ -33,14 +33,13 @@
 
 #import "GCDWebServerPrivate.h"
 
-#define kHeadersReadBuffer 1024
+#define kHeadersReadCapacity (1 * 1024)
+#define kBodyReadCapacity (256 * 1024)
 
-typedef void (^ReadBufferCompletionBlock)(dispatch_data_t buffer);
-typedef void (^ReadDataCompletionBlock)(NSData* data);
+typedef void (^ReadDataCompletionBlock)(BOOL success);
 typedef void (^ReadHeadersCompletionBlock)(NSData* extraData);
 typedef void (^ReadBodyCompletionBlock)(BOOL success);
 
-typedef void (^WriteBufferCompletionBlock)(BOOL success);
 typedef void (^WriteDataCompletionBlock)(BOOL success);
 typedef void (^WriteHeadersCompletionBlock)(BOOL success);
 typedef void (^WriteBodyCompletionBlock)(BOOL success);
@@ -83,87 +82,50 @@ static int32_t _connectionCounter = 0;
 
 @implementation GCDWebServerConnection (Read)
 
-- (void)_readBufferWithLength:(NSUInteger)length completionBlock:(ReadBufferCompletionBlock)block {
+- (void)_readData:(NSMutableData*)data withLength:(NSUInteger)length completionBlock:(ReadDataCompletionBlock)block {
   dispatch_read(_socket, length, kGCDWebServerGCDQueue, ^(dispatch_data_t buffer, int error) {
     
     @autoreleasepool {
       if (error == 0) {
         size_t size = dispatch_data_get_size(buffer);
         if (size > 0) {
-          LOG_DEBUG(@"Connection received %zu bytes on socket %i", size, _socket);
-          _bytesRead += size;
-          [self didUpdateBytesRead];
-#ifdef __GCDWEBSERVER_ENABLE_TESTING__
-          if (_requestFD > 0) {
-            bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
-              return (write(_requestFD, chunkBytes, chunkSize) == (ssize_t)chunkSize);
-            });
-            if (!success) {
-              LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
-              close(_requestFD);
-              _requestFD = 0;
-            }
-          }
-#endif
-          block(buffer);
+          NSUInteger originalLength = data.length;
+          dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
+            [data appendBytes:chunkBytes length:chunkSize];
+            return true;
+          });
+          [self didReadBytes:((char*)data.bytes + originalLength) length:(data.length - originalLength)];
+          block(YES);
         } else {
           if (_bytesRead > 0) {
             LOG_ERROR(@"No more data available on socket %i", _socket);
           } else {
             LOG_WARNING(@"No data received from socket %i", _socket);
           }
-          block(NULL);
+          block(NO);
         }
       } else {
         LOG_ERROR(@"Error while reading from socket %i: %s (%i)", _socket, strerror(error), error);
-        block(NULL);
+        block(NO);
       }
     }
     
   });
 }
 
-- (void)_readDataWithCompletionBlock:(ReadDataCompletionBlock)block {
-  [self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
-    
-    if (buffer) {
-      NSMutableData* data = [[NSMutableData alloc] initWithCapacity:dispatch_data_get_size(buffer)];
-      dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
-        [data appendBytes:chunkBytes length:chunkSize];
-        return true;
-      });
-      block(data);
-      ARC_RELEASE(data);
-    } else {
-      block(nil);
-    }
-    
-  }];
-}
-
-- (void)_readHeadersWithCompletionBlock:(ReadHeadersCompletionBlock)block {
+- (void)_readHeaders:(NSMutableData*)headersData withCompletionBlock:(ReadHeadersCompletionBlock)block {
   DCHECK(_requestMessage);
-  [self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
+  [self _readData:headersData withLength:NSUIntegerMax completionBlock:^(BOOL success) {
     
-    if (buffer) {
-      NSMutableData* data = [NSMutableData dataWithCapacity:kHeadersReadBuffer];
-      dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
-        [data appendBytes:chunkBytes length:chunkSize];
-        return true;
-      });
-      NSRange range = [data rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, data.length)];
+    if (success) {
+      NSRange range = [headersData rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, headersData.length)];
       if (range.location == NSNotFound) {
-        if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, data.length)) {
-          [self _readHeadersWithCompletionBlock:block];
-        } else {
-          LOG_ERROR(@"Failed appending request headers data from socket %i", _socket);
-          block(nil);
-        }
+        [self _readHeaders:headersData withCompletionBlock:block];
       } else {
         NSUInteger length = range.location + range.length;
-        if (CFHTTPMessageAppendBytes(_requestMessage, data.bytes, length)) {
+        if (CFHTTPMessageAppendBytes(_requestMessage, headersData.bytes, length)) {
           if (CFHTTPMessageIsHeaderComplete(_requestMessage)) {
-            block([data subdataWithRange:NSMakeRange(length, data.length - length)]);
+            block([headersData subdataWithRange:NSMakeRange(length, headersData.length - length)]);
           } else {
             LOG_ERROR(@"Failed parsing request headers from socket %i", _socket);
             block(nil);
@@ -182,27 +144,21 @@ static int32_t _connectionCounter = 0;
 
 - (void)_readBodyWithRemainingLength:(NSUInteger)length completionBlock:(ReadBodyCompletionBlock)block {
   DCHECK([_request hasBody] && ![_request usesChunkedTransferEncoding]);
-  [self _readBufferWithLength:length completionBlock:^(dispatch_data_t buffer) {
+  NSMutableData* bodyData = [[NSMutableData alloc] initWithCapacity:kBodyReadCapacity];
+  [self _readData:bodyData withLength:length completionBlock:^(BOOL success) {
     
-    if (buffer) {
-      if (dispatch_data_get_size(buffer) <= length) {
-        bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
-          NSData* data = [NSData dataWithBytesNoCopy:(void*)chunkBytes length:chunkSize freeWhenDone:NO];
-          NSError* error = nil;
-          if (![_request performWriteData:data error:&error]) {
-            LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
-            return false;
-          }
-          return true;
-        });
-        if (success) {
-          NSUInteger remainingLength = length - dispatch_data_get_size(buffer);
+    if (success) {
+      if (bodyData.length <= length) {
+        NSError* error = nil;
+        if ([_request performWriteData:bodyData error:&error]) {
+          NSUInteger remainingLength = length - bodyData.length;
           if (remainingLength) {
             [self _readBodyWithRemainingLength:remainingLength completionBlock:block];
           } else {
             block(YES);
           }
         } else {
+          LOG_ERROR(@"Failed writing request body on socket %i: %@", _socket, error);
           block(NO);
         }
       } else {
@@ -215,6 +171,7 @@ static int32_t _connectionCounter = 0;
     }
     
   }];
+  ARC_RELEASE(bodyData);
 }
 
 static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
@@ -270,13 +227,9 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
     }
   }
   
-  [self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
+  [self _readData:chunkData withLength:NSUIntegerMax completionBlock:^(BOOL success) {
     
-    if (buffer) {
-      dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
-        [chunkData appendBytes:chunkBytes length:chunkSize];
-        return true;
-      });
+    if (success) {
       [self _readNextBodyChunk:chunkData completionBlock:block];
     } else {
       block(NO);
@@ -289,56 +242,31 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
 
 @implementation GCDWebServerConnection (Write)
 
-- (void)_writeBuffer:(dispatch_data_t)buffer withCompletionBlock:(WriteBufferCompletionBlock)block {
-  size_t size = dispatch_data_get_size(buffer);
-#ifdef __GCDWEBSERVER_ENABLE_TESTING__
-  ARC_DISPATCH_RETAIN(buffer);
+- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
+#if !__has_feature(objc_arc)
+  [data retain];
 #endif
-  dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(dispatch_data_t data, int error) {
+  dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, kGCDWebServerGCDQueue, ^{
+#if __has_feature(objc_arc)
+    [data self];  // Keeps ARC from releasing data too early
+#else
+    [data release];
+#endif
+  });
+  dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(dispatch_data_t remainingData, int error) {
     
     @autoreleasepool {
       if (error == 0) {
-        DCHECK(data == NULL);
-        LOG_DEBUG(@"Connection sent %zu bytes on socket %i", size, _socket);
-        _bytesWritten += size;
-        [self didUpdateBytesWritten];
-#ifdef __GCDWEBSERVER_ENABLE_TESTING__
-        if (_responseFD > 0) {
-          bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
-            return (write(_responseFD, chunkBytes, chunkSize) == (ssize_t)chunkSize);
-          });
-          if (!success) {
-            LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
-            close(_responseFD);
-            _responseFD = 0;
-          }
-        }
-#endif
+        DCHECK(remainingData == NULL);
+        [self didWriteBytes:data.bytes length:data.length];
         block(YES);
       } else {
         LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
         block(NO);
       }
     }
-#ifdef __GCDWEBSERVER_ENABLE_TESTING__
-    ARC_DISPATCH_RELEASE(buffer);
-#endif
     
   });
-}
-
-- (void)_writeData:(NSData*)data withCompletionBlock:(WriteDataCompletionBlock)block {
-#if !__has_feature(objc_arc)
-  [data retain];
-#endif
-  dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, kGCDWebServerGCDQueue, ^{
-#if __has_feature(objc_arc)
-    [data self];  // Keeps ARC from releasing data too early
-#else
-    [data release];
-#endif
-  });
-  [self _writeBuffer:buffer withCompletionBlock:block];
   ARC_DISPATCH_RELEASE(buffer);
 }
 
@@ -576,7 +504,8 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
 
 - (void)_readRequestHeaders {
   _requestMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true);
-  [self _readHeadersWithCompletionBlock:^(NSData* extraData) {
+  NSMutableData* headersData = [[NSMutableData alloc] initWithCapacity:kHeadersReadCapacity];
+  [self _readHeaders:headersData withCompletionBlock:^(NSData* extraData) {
     
     if (extraData) {
       NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage));  // Method verbs are case-sensitive and uppercase
@@ -646,6 +575,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
     }
     
   }];
+  ARC_RELEASE(headersData);
 }
 
 - (id)initWithServer:(GCDWebServer*)server localAddress:(NSData*)localAddress remoteAddress:(NSData*)remoteAddress socket:(CFSocketNativeHandle)socket {
@@ -745,12 +675,30 @@ static NSString* _StringFromAddressData(NSData* data) {
   return YES;
 }
 
-- (void)didUpdateBytesRead {
-  ;
+- (void)didReadBytes:(const void*)bytes length:(NSUInteger)length {
+  LOG_DEBUG(@"Connection received %zu bytes on socket %i", length, _socket);
+  _bytesRead += length;
+  
+#ifdef __GCDWEBSERVER_ENABLE_TESTING__
+  if ((_requestFD > 0) && (write(_requestFD, bytes, length) != (ssize_t)length)) {
+    LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
+    close(_requestFD);
+    _requestFD = 0;
+  }
+#endif
 }
 
-- (void)didUpdateBytesWritten {
-  ;
+- (void)didWriteBytes:(const void*)bytes length:(NSUInteger)length {
+  LOG_DEBUG(@"Connection sent %zu bytes on socket %i", length, _socket);
+  _bytesWritten += length;
+  
+#ifdef __GCDWEBSERVER_ENABLE_TESTING__
+  if ((_responseFD > 0) && (write(_responseFD, bytes, length) != (ssize_t)length)) {
+    LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
+    close(_responseFD);
+    _responseFD = 0;
+  }
+#endif
 }
 
 - (GCDWebServerResponse*)processRequest:(GCDWebServerRequest*)request withBlock:(GCDWebServerProcessBlock)block {