Ben Copsey

Parse text encoding from Content-Type header when supplied

Thanks to Shaun Harrison for this suggestion!
@@ -166,6 +166,9 @@ typedef enum _ASINetworkErrorType { @@ -166,6 +166,9 @@ typedef enum _ASINetworkErrorType {
166 BOOL haveBuiltPostBody; 166 BOOL haveBuiltPostBody;
167 167
168 unsigned long long uploadBufferSize; 168 unsigned long long uploadBufferSize;
  169 +
  170 + NSStringEncoding defaultResponseEncoding;
  171 + NSStringEncoding responseEncoding;
169 } 172 }
170 173
171 #pragma mark init / dealloc 174 #pragma mark init / dealloc
@@ -305,4 +308,6 @@ typedef enum _ASINetworkErrorType { @@ -305,4 +308,6 @@ typedef enum _ASINetworkErrorType {
305 @property (assign) BOOL showAccurateProgress; 308 @property (assign) BOOL showAccurateProgress;
306 @property (assign,readonly) unsigned long long totalBytesRead; 309 @property (assign,readonly) unsigned long long totalBytesRead;
307 @property (assign) unsigned long long uploadBufferSize; 310 @property (assign) unsigned long long uploadBufferSize;
  311 +@property (assign) NSStringEncoding defaultResponseEncoding;
  312 +@property (assign) NSStringEncoding responseEncoding;
308 @end 313 @end
@@ -72,6 +72,7 @@ static NSError *ASIUnableToCreateRequestError; @@ -72,6 +72,7 @@ static NSError *ASIUnableToCreateRequestError;
72 requestAuthentication = NULL; 72 requestAuthentication = NULL;
73 haveBuiltPostBody = NO; 73 haveBuiltPostBody = NO;
74 request = NULL; 74 request = NULL;
  75 + [self setDefaultResponseEncoding:NSISOLatin1StringEncoding];
75 [self setUploadBufferSize:0]; 76 [self setUploadBufferSize:0];
76 [self setResponseHeaders:nil]; 77 [self setResponseHeaders:nil];
77 [self setTimeOutSeconds:10]; 78 [self setTimeOutSeconds:10];
@@ -171,7 +172,8 @@ static NSError *ASIUnableToCreateRequestError; @@ -171,7 +172,8 @@ static NSError *ASIUnableToCreateRequestError;
171 if (!receivedData) { 172 if (!receivedData) {
172 return nil; 173 return nil;
173 } 174 }
174 - return [[[NSString alloc] initWithBytes:[receivedData bytes] length:[receivedData length] encoding:NSUTF8StringEncoding] autorelease]; 175 +
  176 + return [[[NSString alloc] initWithBytes:[receivedData bytes] length:[receivedData length] encoding:[self responseEncoding]] autorelease];
175 } 177 }
176 178
177 179
@@ -738,6 +740,22 @@ static NSError *ASIUnableToCreateRequestError; @@ -738,6 +740,22 @@ static NSError *ASIUnableToCreateRequestError;
738 } 740 }
739 } 741 }
740 742
  743 + // Handle response text encoding
  744 + // If the Content-Type header specified an encoding, we'll use that, otherwise we use defaultStringEncoding (which defaults to NSISOLatin1StringEncoding)
  745 + NSString *contentType = [[self responseHeaders] objectForKey:@"Content-Type"];
  746 + NSStringEncoding encoding = [self defaultResponseEncoding];
  747 + if (contentType) {
  748 + NSArray *parts = [contentType componentsSeparatedByString:@"="];
  749 + NSString *IANAEncoding = [parts objectAtIndex:[parts count]-1];
  750 + if (IANAEncoding) {
  751 + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)IANAEncoding);
  752 + if (cfEncoding != kCFStringEncodingInvalidId) {
  753 + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
  754 + }
  755 + }
  756 + }
  757 + [self setResponseEncoding:encoding];
  758 +
741 // Handle cookies 759 // Handle cookies
742 NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaders forURL:url]; 760 NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaders forURL:url];
743 [self setResponseCookies:cookies]; 761 [self setResponseCookies:cookies];
@@ -1182,4 +1200,6 @@ static NSError *ASIUnableToCreateRequestError; @@ -1182,4 +1200,6 @@ static NSError *ASIUnableToCreateRequestError;
1182 @synthesize showAccurateProgress; 1200 @synthesize showAccurateProgress;
1183 @synthesize totalBytesRead; 1201 @synthesize totalBytesRead;
1184 @synthesize uploadBufferSize; 1202 @synthesize uploadBufferSize;
  1203 +@synthesize defaultResponseEncoding;
  1204 +@synthesize responseEncoding;
1185 @end 1205 @end
@@ -24,5 +24,6 @@ @@ -24,5 +24,6 @@
24 - (void)testCookies; 24 - (void)testCookies;
25 - (void)testBasicAuthentication; 25 - (void)testBasicAuthentication;
26 - (void)testDigestAuthentication; 26 - (void)testDigestAuthentication;
  27 +- (void)testCharacterEncoding;
27 28
28 @end 29 @end
@@ -53,9 +53,31 @@ @@ -53,9 +53,31 @@
53 STAssertTrue(success,@"Failed to generate an error for a bad host"); 53 STAssertTrue(success,@"Failed to generate an error for a bad host");
54 } 54 }
55 55
  56 +- (void)testCharacterEncoding
  57 +{
  58 +
  59 + NSArray *IANAEncodings = [NSArray arrayWithObjects:@"UTF-8",@"US-ASCII",@"ISO-8859-1",@"UTF-16",nil];
  60 + NSUInteger NSStringEncodings[] = {NSUTF8StringEncoding,NSASCIIStringEncoding,NSISOLatin1StringEncoding,NSUnicodeStringEncoding};
  61 +
  62 + int i;
  63 + for (i=0; i<[IANAEncodings count]; i++) {
  64 + NSURL *url = [[[NSURL alloc] initWithString:[NSString stringWithFormat:@"http://allseeing-i.com/ASIHTTPRequest/tests/Character-Encoding/%@",[IANAEncodings objectAtIndex:i]]] autorelease];
  65 + ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
  66 + [request start];
  67 + BOOL success = [request responseEncoding] == NSStringEncodings[i];
  68 + STAssertTrue(success,[NSString stringWithFormat:@"Failed to use the correct text encoding for %@i",[IANAEncodings objectAtIndex:i]]);
  69 + }
  70 +
  71 + NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/Character-Encoding/Something-else"] autorelease];
  72 + ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
  73 + [request setDefaultResponseEncoding:NSWindowsCP1251StringEncoding];
  74 + [request start];
  75 + BOOL success = [request responseEncoding] == [request defaultResponseEncoding];
  76 + STAssertTrue(success,[NSString stringWithFormat:@"Failed to use the default string encoding"]);
  77 +}
  78 +
56 - (void)testTimeOut 79 - (void)testTimeOut
57 { 80 {
58 - //Grab data  
59 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease]; 81 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease];
60 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 82 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
61 [request setTimeOutSeconds:0.0001]; //It's pretty unlikely we will be able to grab the data this quickly, so the request should timeout 83 [request setTimeOutSeconds:0.0001]; //It's pretty unlikely we will be able to grab the data this quickly, so the request should timeout