Skip to content

Commit 53df68f

Browse files
author
Sefa Ilkimen
committed
fix silkimen#72 for iOS: support latin1 (iso-8859-1) encoded data
1 parent 4412685 commit 53df68f

File tree

5 files changed

+121
-35
lines changed

5 files changed

+121
-35
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 1.10.1
4+
5+
- Fixed #71: does not encode query string in URL correctly on Android
6+
37
## 1.10.0
48

59
- Feature #34: add new serializer "utf8" sending utf-8 encoded plain text (thanks robertocapuano)

src/ios/CordovaHttpPlugin.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ - (void)setRedirect:(AFHTTPSessionManager*)manager {
5555
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data {
5656
if (response != nil) {
5757
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
58-
[dictionary setObject:[NSNumber numberWithInt:response.statusCode] forKey:@"status"];
58+
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
5959
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
6060
}
6161

@@ -67,9 +67,9 @@ - (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLRe
6767
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error {
6868
if (response != nil) {
6969
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
70-
[dictionary setObject:[NSNumber numberWithInt:response.statusCode] forKey:@"status"];
70+
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
7171
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
72-
[dictionary setObject:[[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding] forKey:@"error"];
72+
[dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyKey] forKey:@"error"];
7373
} else {
7474
[dictionary setObject:[self getStatusCode:error] forKey:@"status"];
7575
[dictionary setObject:[error localizedDescription] forKey:@"error"];

src/ios/TextResponseSerializer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55

66
+ (instancetype)serializer;
77

8-
@end
8+
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyKey;
9+
10+
@end

src/ios/TextResponseSerializer.m

Lines changed: 101 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,115 @@
11
#import "TextResponseSerializer.h"
22

3+
NSString * const AFNetworkingOperationFailingURLResponseBodyKey = @"com.alamofire.serialization.response.error.body";
4+
NSStringEncoding const SupportedEncodings[6] = { NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding, NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding };
5+
6+
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
7+
if (!error) {
8+
return underlyingError;
9+
}
10+
11+
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
12+
return error;
13+
}
14+
15+
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
16+
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
17+
18+
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
19+
}
20+
321
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
4-
if ([error.domain isEqualToString:domain] && error.code == code) {
5-
return YES;
6-
} else if (error.userInfo[NSUnderlyingErrorKey]) {
7-
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
8-
}
22+
if ([error.domain isEqualToString:domain] && error.code == code) {
23+
return YES;
24+
} else if (error.userInfo[NSUnderlyingErrorKey]) {
25+
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
26+
}
927

10-
return NO;
28+
return NO;
1129
}
1230

1331
@implementation TextResponseSerializer
1432

1533
+ (instancetype)serializer {
16-
TextResponseSerializer *serializer = [[self alloc] init];
17-
return serializer;
34+
TextResponseSerializer *serializer = [[self alloc] init];
35+
return serializer;
1836
}
1937

2038
- (instancetype)init {
21-
self = [super init];
39+
self = [super init];
40+
41+
if (!self) {
42+
return nil;
43+
}
44+
45+
self.acceptableContentTypes = nil;
46+
47+
return self;
48+
}
49+
50+
- (NSString*)decodeResponseData:(NSData*)rawResponseData withEncoding:(CFStringEncoding)cfEncoding {
51+
NSStringEncoding nsEncoding;
52+
NSString* decoded = nil;
53+
54+
if (cfEncoding != kCFStringEncodingInvalidId) {
55+
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
56+
}
57+
58+
for (int i = 0; i < sizeof(SupportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
59+
if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == SupportedEncodings[i]) {
60+
decoded = [[NSString alloc] initWithData:rawResponseData encoding:SupportedEncodings[i]];
61+
}
62+
}
2263

23-
if (!self) {
24-
return nil;
64+
if (!decoded) {
65+
decoded = @"Could not decode response data due to invalid or unknown charset encoding";
66+
}
67+
68+
return decoded;
69+
}
70+
71+
- (CFStringEncoding) getEncoding:(NSURLResponse *)response {
72+
CFStringEncoding encoding = kCFStringEncodingInvalidId;
73+
74+
if (response.textEncodingName) {
75+
encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
76+
}
77+
78+
return encoding;
79+
}
80+
81+
#pragma mark -
82+
83+
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
84+
data:(NSData *)data
85+
error:(NSError * __autoreleasing *)error
86+
{
87+
BOOL responseIsValid = YES;
88+
NSError *validationError = nil;
89+
90+
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
91+
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
92+
NSMutableDictionary *mutableUserInfo = [@{
93+
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
94+
NSURLErrorFailingURLErrorKey:[response URL],
95+
AFNetworkingOperationFailingURLResponseErrorKey: response,
96+
} mutableCopy];
97+
98+
if (data) {
99+
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
100+
mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
101+
}
102+
103+
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
104+
responseIsValid = NO;
25105
}
106+
}
26107

27-
self.acceptableContentTypes = nil;
108+
if (error && !responseIsValid) {
109+
*error = validationError;
110+
}
28111

29-
return self;
112+
return responseIsValid;
30113
}
31114

32115
#pragma mark - AFURLResponseSerialization
@@ -35,25 +118,13 @@ - (id)responseObjectForResponse:(NSURLResponse *)response
35118
data:(NSData *)data
36119
error:(NSError *__autoreleasing *)error
37120
{
38-
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
39-
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
40-
return nil;
41-
}
121+
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
122+
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
123+
return nil;
42124
}
125+
}
43126

44-
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
45-
// See https://github.com/rails/rails/issues/1742
46-
NSStringEncoding stringEncoding = self.stringEncoding;
47-
if (response.textEncodingName) {
48-
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
49-
if (encoding != kCFStringEncodingInvalidId) {
50-
stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
51-
}
52-
}
53-
54-
NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding];
55-
56-
return responseString;
127+
return [self decodeResponseData:data withEncoding:[self getEncoding:response]];
57128
}
58129

59130
@end

test/app-test-definitions.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,18 @@ const tests = [
402402
},
403403
validationFunc: function(driver, result) {
404404
result.type.should.be.equal('resolved');
405-
console.log(JSON.parse(result.data.data).args);
406405
JSON.parse(result.data.data).args['query param'].should.eql('and value with spaces');
407406
}
407+
},{
408+
description: 'should decode latin1 (iso-8859-1) encoded body correctly (GET) #72',
409+
expected: 'resolved: {"status": 200, "data": "<!DOCTYPE HTML PUBLIC \\"-//W3C//DTD HTML 4.01 Transitional//EN\\"> ...',
410+
func: function(resolve, reject) {
411+
cordova.plugin.http.get('http://www.columbia.edu/kermit/latin1.html', {}, {}, resolve, reject);
412+
},
413+
validationFunc: function(driver, result) {
414+
result.type.should.be.equal('resolved');
415+
result.data.data.should.include('[¡] 161 10/01 241 A1 INVERTED EXCLAMATION MARK\n[¢] 162 10/02 242 A2 CENT SIGN');
416+
}
408417
}
409418
];
410419

0 commit comments

Comments
 (0)