Automatically map HEAD requests to GET ones
This commit is contained in:
@ -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
|
||||||
|
|||||||
@ -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];
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user