浏览代码

Automatically map HEAD requests to GET ones

Pierre-Olivier Latour 11 年之前
父节点
当前提交
157b683082

+ 1 - 0
CGDWebServer/GCDWebServerConnection.h

@@ -40,6 +40,7 @@
 @end
 
 @interface GCDWebServerConnection (Subclassing)
++ (BOOL)shouldAutomaticallyMapHEADToGET;  // Default is YES which means HEAD requests are mapped to GET requests with the response body being discarded
 - (void)open;
 - (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

+ 19 - 4
CGDWebServer/GCDWebServerConnection.m

@@ -54,6 +54,7 @@ static NSData* _lastChunkData = nil;
   CFSocketNativeHandle _socket;
   NSUInteger _bytesRead;
   NSUInteger _bytesWritten;
+  BOOL _virtualHEAD;
   
   CFHTTPMessageRef _requestMessage;
   GCDWebServerRequest* _request;
@@ -395,13 +396,18 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
 - (void)_processRequest {
   DCHECK(_responseMessage == NULL);
+  BOOL hasBody = NO;
   
   GCDWebServerResponse* response = [self processRequest:_request withBlock:_handler.processBlock];
   if (response) {
     response = [self replaceResponse:response forRequest:_request];
     if (response) {
+      if ([response hasBody]) {
+        [response prepareForReading];
+        hasBody = !_virtualHEAD;
+      }
       NSError* error = nil;
-      if ([response hasBody] && ![response performOpen:&error]) {
+      if (hasBody && ![response performOpen:&error]) {
         LOG_ERROR(@"Failed opening response body for socket %i: %@", _socket, error);
       } else {
         _response = ARC_RETAIN(response);
@@ -437,14 +443,14 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
     [self _writeHeadersWithCompletionBlock:^(BOOL success) {
       
       if (success) {
-        if ([_response hasBody]) {
+        if (hasBody) {
           [self _writeBodyWithCompletionBlock:^(BOOL successInner) {
             
             [_response performClose];  // TODO: There's nothing we can do on failure as headers have already been sent
             
           }];
         }
-      } else if ([_response hasBody]) {
+      } else if (hasBody) {
         [_response performClose];
       }
       
@@ -527,6 +533,10 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
     if (extraData) {
       NSString* requestMethod = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(_requestMessage));  // Method verbs are case-sensitive and uppercase
       DCHECK(requestMethod);
+      if ([[self class] shouldAutomaticallyMapHEADToGET] && [requestMethod isEqualToString:@"HEAD"]) {
+        requestMethod = @"GET";
+        _virtualHEAD = YES;
+      }
       NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(_requestMessage));
       DCHECK(requestURL);
       NSString* requestPath = GCDWebServerUnescapeURLString(ARC_BRIDGE_RELEASE(CFURLCopyPath((CFURLRef)requestURL)));  // Don't use -[NSURL path] which strips the ending slash
@@ -546,7 +556,8 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
         }
       }
       if (_request) {
-        if (_request.hasBody) {
+        if ([_request hasBody]) {
+          [_request prepareForWriting];
           if (_request.usesChunkedTransferEncoding || (extraData.length <= _request.contentLength)) {
             NSString* expectHeader = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyHeaderFieldValue(_requestMessage, CFSTR("Expect")));
             if (expectHeader) {
@@ -649,6 +660,10 @@ static NSString* _StringFromAddressData(NSData* data) {
 
 @implementation GCDWebServerConnection (Subclassing)
 
++ (BOOL)shouldAutomaticallyMapHEADToGET {
+  return YES;
+}
+
 - (void)open {
   LOG_DEBUG(@"Did open connection on socket %i", _socket);
   [self _readRequestHeaders];

+ 2 - 0
CGDWebServer/GCDWebServerPrivate.h

@@ -129,6 +129,7 @@ extern NSDate* GCDWebServerParseHTTPDate(NSString* string);
 
 @interface GCDWebServerRequest ()
 @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
+- (void)prepareForWriting;
 - (BOOL)performOpen:(NSError**)error;
 - (BOOL)performWriteData:(NSData*)data error:(NSError**)error;
 - (BOOL)performClose:(NSError**)error;
@@ -137,6 +138,7 @@ extern NSDate* GCDWebServerParseHTTPDate(NSString* string);
 @interface GCDWebServerResponse ()
 @property(nonatomic, readonly) NSDictionary* additionalHeaders;
 @property(nonatomic, readonly) BOOL usesChunkedTransferEncoding;
+- (void)prepareForReading;
 - (BOOL)performOpen:(NSError**)error;
 - (NSData*)performReadData:(NSError**)error;
 - (void)performClose;

+ 11 - 8
CGDWebServer/GCDWebServerRequest.m

@@ -271,14 +271,7 @@
   return YES;
 }
 
-- (BOOL)performOpen:(NSError**)error {
-  DCHECK(_type);
-  if (_opened) {
-    DNOT_REACHED();
-    return NO;
-  }
-  _opened = YES;
-  
+- (void)prepareForWriting {
   _writer = self;
   if ([[[self.headers objectForKey:@"Content-Encoding"] lowercaseString] isEqualToString:@"gzip"]) {
     GCDWebServerGZipDecoder* decoder = [[GCDWebServerGZipDecoder alloc] initWithRequest:self writer:_writer];
@@ -286,6 +279,16 @@
     ARC_RELEASE(decoder);
     _writer = decoder;
   }
+}
+
+- (BOOL)performOpen:(NSError**)error {
+  DCHECK(_type);
+  DCHECK(_writer);
+  if (_opened) {
+    DNOT_REACHED();
+    return NO;
+  }
+  _opened = YES;
   return [_writer open:error];
 }
 

+ 11 - 8
CGDWebServer/GCDWebServerResponse.m

@@ -223,14 +223,7 @@
   ;
 }
 
-- (BOOL)performOpen:(NSError**)error {
-  DCHECK(_type);
-  if (_opened) {
-    DNOT_REACHED();
-    return NO;
-  }
-  _opened = YES;
-  
+- (void)prepareForReading {
   _reader = self;
   if (_gzipped) {
     GCDWebServerGZipEncoder* encoder = [[GCDWebServerGZipEncoder alloc] initWithResponse:self reader:_reader];
@@ -238,6 +231,16 @@
     ARC_RELEASE(encoder);
     _reader = encoder;
   }
+}
+
+- (BOOL)performOpen:(NSError**)error {
+  DCHECK(_type);
+  DCHECK(_reader);
+  if (_opened) {
+    DNOT_REACHED();
+    return NO;
+  }
+  _opened = YES;
   return [_reader open:error];
 }