Index: src/OFHTTPRequest.m ================================================================== --- src/OFHTTPRequest.m +++ src/OFHTTPRequest.m @@ -17,10 +17,11 @@ #include "config.h" #define OF_HTTP_REQUEST_M #include +#include #import "OFHTTPRequest.h" #import "OFString.h" #import "OFURL.h" #import "OFTCPSocket.h" @@ -35,10 +36,30 @@ #import "OFUnsupportedProtocolException.h" #import "macros.h" Class of_http_request_tls_socket_class = Nil; + +static OF_INLINE void +normalize_key(OFString *key) +{ + char *str = (char*)[key cString]; + BOOL firstLetter = YES; + + while (*str != '\0') { + if (!isalnum(*str)) { + firstLetter = YES; + str++; + continue; + } + + *str = (firstLetter ? toupper(*str) : tolower(*str)); + + firstLetter = NO; + str++; + } +} @implementation OFHTTPRequest + request { return [[[self alloc] init] autorelease]; @@ -184,18 +205,19 @@ autorelease]; } @try { OFString *line, *path; - OFMutableDictionary *s_headers; + OFMutableDictionary *serverHeaders; OFDataArray *data; OFEnumerator *enumerator; OFString *key; int status; const char *t = NULL; char *buf; size_t bytesReceived; + OFString *contentLengthHeader; [sock connectToHost: [URL host] onPort: [URL port]]; /* @@ -269,11 +291,11 @@ @throw [OFHTTPRequestFailedException newWithClass: isa HTTPRequest: self statusCode: status]; - s_headers = [OFMutableDictionary dictionary]; + serverHeaders = [OFMutableDictionary dictionary]; while ((line = [sock readLine]) != nil) { OFString *key, *value; const char *line_c = [line cString], *tmp; @@ -284,20 +306,20 @@ @throw [OFInvalidServerReplyException newWithClass: isa]; key = [OFString stringWithCString: line_c length: tmp - line_c]; + normalize_key(key); do { tmp++; } while (*tmp == ' '); value = [OFString stringWithCString: tmp]; if ((redirects > 0 && (status == 301 || status == 302 || - status == 303) && [key caseInsensitiveCompare: - @"Location"] == OF_ORDERED_SAME) && + status == 303) && [key isEqual: @"Location"]) && (redirectsFromHTTPSToHTTPAllowed || [scheme isEqual: @"http"] || ![value hasPrefix: @"http://"])) { OFURL *new; BOOL follow; @@ -307,12 +329,12 @@ follow = [delegate request: self willFollowRedirectTo: new]; if (!follow && delegate != nil) { - [s_headers setObject: value - forKey: key]; + [serverHeaders setObject: value + forKey: key]; continue; } new = [new retain]; [URL release]; @@ -329,16 +351,16 @@ return [self performWithRedirects: redirects - 1]; } - [s_headers setObject: value - forKey: key]; + [serverHeaders setObject: value + forKey: key]; } [delegate request: self - didReceiveHeaders: s_headers + didReceiveHeaders: serverHeaders withStatusCode: status]; if (storesData) data = [OFDataArray dataArrayWithItemSize: 1]; else @@ -361,15 +383,13 @@ } } @finally { [self freeMemory: buf]; } - if ([s_headers objectForKey: @"Content-Length"] != nil) { - intmax_t cl; - - cl = [[s_headers objectForKey: @"Content-Length"] - decimalValue]; + if ((contentLengthHeader = + [serverHeaders objectForKey: @"Content-Length"]) != nil) { + intmax_t cl = [contentLengthHeader decimalValue]; if (cl > SIZE_MAX) @throw [OFOutOfRangeException newWithClass: isa]; @@ -382,15 +402,15 @@ * Class swizzle the dictionary to be immutable. We pass it as * OFDictionary*, so it can't be modified anyway. But not * swizzling it would create a real copy each time -[copy] is * called. */ - s_headers->isa = [OFDictionary class]; + serverHeaders->isa = [OFDictionary class]; result = [[OFHTTPRequestResult alloc] initWithStatusCode: status - headers: s_headers + headers: serverHeaders data: data]; } @finally { [pool release]; } Index: tests/OFHTTPRequestTests.m ================================================================== --- tests/OFHTTPRequestTests.m +++ tests/OFHTTPRequestTests.m @@ -25,10 +25,11 @@ #import "OFHTTPRequest.h" #import "OFString.h" #import "OFThread.h" #import "OFTCPSocket.h" #import "OFURL.h" +#import "OFDictionary.h" #import "OFAutoreleasePool.h" #import "TestsAppDelegate.h" static OFString *module = @"OFHTTPRequest"; @@ -70,11 +71,11 @@ if (![[client readLine] isEqual: @""]) assert(0); [client writeString: @"HTTP/1.0 200 OK\r\n" - @"Content-Length: 7\r\n" + @"cONTeNT-lENgTH: 7\r\n" @"\r\n" @"foo\n" @"bar"]; [client close]; @@ -106,10 +107,13 @@ TEST(@"+[requestWithURL]", (req = [OFHTTPRequest requestWithURL: url])) TEST(@"-[perform]", (res = [req perform])) + TEST(@"Normalization of server header keys", + ([[res headers] objectForKey: @"Content-Length"] != nil)) + [server join]; [pool drain]; } @end