Automatically map HEAD requests to GET ones

This commit is contained in:
Pierre-Olivier Latour
2014-04-09 00:53:06 -07:00
parent 62ee560d51
commit 157b683082
5 changed files with 44 additions and 20 deletions

View File

@ -40,6 +40,7 @@
@end @end
@interface GCDWebServerConnection (Subclassing) @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)open;
- (void)didUpdateBytesRead; // Called from arbitrary thread after @totalBytesRead is updated - Default implementation does nothing - (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)didUpdateBytesWritten; // Called from arbitrary thread after @totalBytesWritten is updated - Default implementation does nothing

View File

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

View File

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

View File

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

View File

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