Make header parsing more robust

This commit is contained in:
Pierre-Olivier Latour
2014-04-14 19:08:08 -03:00
parent 252c38c42a
commit c8cd771697
+43 -46
View File
@@ -579,69 +579,66 @@ 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);
if ([[_server class] shouldAutomaticallyMapHEADToGET] && [requestMethod isEqualToString:@"HEAD"]) { if ([[_server class] shouldAutomaticallyMapHEADToGET] && [requestMethod isEqualToString:@"HEAD"]) {
requestMethod = @"GET"; requestMethod = @"GET";
_virtualHEAD = YES; _virtualHEAD = YES;
} }
NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(_requestMessage)); NSURL* requestURL = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(_requestMessage));
DCHECK(requestURL); NSString* requestPath = requestURL ? GCDWebServerUnescapeURLString(ARC_BRIDGE_RELEASE(CFURLCopyPath((CFURLRef)requestURL))) : nil; // 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 NSString* queryString = requestURL ? ARC_BRIDGE_RELEASE(CFURLCopyQueryString((CFURLRef)requestURL, NULL)) : nil; // Don't use -[NSURL query] to make sure query is not unescaped;
DCHECK(requestPath); NSDictionary* requestQuery = queryString ? GCDWebServerParseURLEncodedForm(queryString) : @{};
NSDictionary* requestQuery = nil;
NSString* queryString = ARC_BRIDGE_RELEASE(CFURLCopyQueryString((CFURLRef)requestURL, NULL)); // Don't use -[NSURL query] to make sure query is not unescaped;
if (queryString.length) {
requestQuery = GCDWebServerParseURLEncodedForm(queryString);
DCHECK(requestQuery);
}
NSDictionary* requestHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones NSDictionary* requestHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(_requestMessage)); // Header names are case-insensitive but CFHTTPMessageCopyAllHeaderFields() will standardize the common ones
DCHECK(requestHeaders); if (requestMethod && requestURL && requestHeaders && requestPath && requestQuery) {
for (_handler in _server.handlers) { for (_handler in _server.handlers) {
_request = ARC_RETAIN(_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery)); _request = ARC_RETAIN(_handler.matchBlock(requestMethod, requestURL, requestHeaders, requestPath, requestQuery));
if (_request) { if (_request) {
break; break;
}
} }
} if (_request) {
if (_request) { if ([_request hasBody]) {
if ([_request hasBody]) { [_request prepareForWriting];
[_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) { if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) {
if ([expectHeader caseInsensitiveCompare:@"100-continue"] == NSOrderedSame) { [self _writeData:_continueData withCompletionBlock:^(BOOL success) {
[self _writeData:_continueData withCompletionBlock:^(BOOL success) {
if (success) {
if (success) { if (_request.usesChunkedTransferEncoding) {
if (_request.usesChunkedTransferEncoding) { [self _readChunkedBodyWithInitialData:extraData];
[self _readChunkedBodyWithInitialData:extraData]; } else {
} else { [self _readBodyWithLength:_request.contentLength initialData:extraData];
[self _readBodyWithLength:_request.contentLength initialData:extraData]; }
} }
}
}];
}]; } else {
LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed];
}
} else { } else {
LOG_ERROR(@"Unsupported 'Expect' / 'Content-Length' header combination on socket %i", _socket); if (_request.usesChunkedTransferEncoding) {
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_ExpectationFailed]; [self _readChunkedBodyWithInitialData:extraData];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
}
} }
} else { } else {
if (_request.usesChunkedTransferEncoding) { LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket);
[self _readChunkedBodyWithInitialData:extraData]; [self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
} else {
[self _readBodyWithLength:_request.contentLength initialData:extraData];
}
} }
} else { } else {
LOG_ERROR(@"Unexpected 'Content-Length' header value on socket %i", _socket); [self _processRequest];
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_BadRequest];
} }
} else { } else {
[self _processRequest]; _request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery];
DCHECK(_request);
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed];
} }
} else { } else {
_request = [[GCDWebServerRequest alloc] initWithMethod:requestMethod url:requestURL headers:requestHeaders path:requestPath query:requestQuery]; [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];
DCHECK(_request); DNOT_REACHED();
[self abortRequest:_request withStatusCode:kGCDWebServerHTTPStatusCode_MethodNotAllowed];
} }
} else { } else {
[self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError]; [self abortRequest:nil withStatusCode:kGCDWebServerHTTPStatusCode_InternalServerError];