diff --git a/AFNetworking/AFHTTPClient.h b/AFNetworking/AFHTTPClient.h index ea3f4f9..5b1b716 100644 --- a/AFNetworking/AFHTTPClient.h +++ b/AFNetworking/AFHTTPClient.h @@ -35,6 +35,30 @@ typedef enum { AFPropertyListParameterEncoding, } AFHTTPClientParameterEncoding; +/** + Returns a string, replacing certain characters with the equivalent percent escape sequence based on the specified encoding. + + @param string The string to URL encode + @param encoding The encoding to use for the replacement. If you are uncertain of the correct encoding, you should use UTF-8 (NSUTF8StringEncoding), which is the encoding designated by RFC 3986 as the correct encoding for use in URLs. + + @discussion The characters escaped are all characters that are not legal URL characters (based on RFC 3986), including any whitespace, punctuation, or special characters. + + @return A URL-encoded string. If it does not need to be modified (no percent escape sequences are missing), this function may merely return string argument. + */ +extern NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEncoding encoding); + +/** + Returns a query string constructed by a set of parameters, using the specified encoding. + + @param parameters The parameters used to construct the query string + @param encoding The encoding to use in constructing the query string. If you are uncertain of the correct encoding, you should use UTF-8 (NSUTF8StringEncoding), which is the encoding designated by RFC 3986 as the correct encoding for use in URLs. + + @discussion Query strings are constructed by collecting each key-value pair, URL-encoding the string value of the key and value (by sending `-description` to each), constructing a string in the form "key=value", and then joining the components with "&". The constructed query string does not include the ? character used to delimit the query component. + + @return A URL-encoded query string + */ +extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding); + /** `AFHTTPClient` captures the common patterns of communicating with an web application over HTTP. It encapsulates information like base URL, authorization credentials, and HTTP headers, and uses them to construct and manage the execution of HTTP request operations. diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 7ff8e3a..e2ac85a 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -80,16 +80,17 @@ static NSString * AFBase64EncodedStringFromString(NSString *string) { return [[[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding] autorelease]; } -static NSString * AFURLEncodedStringFromString(NSString *string) { +NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+,:;='\"`<>()[]{}/\\|~ "; - return [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)) autorelease]; + // Following the suggestion in documentation for `CFURLCreateStringByAddingPercentEscapes` to "pre-process" URL strings (using stringByReplacingPercentEscapesUsingEncoding) with unpredictable sequences that may already contain percent escapes. + return [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)[string stringByReplacingPercentEscapesUsingEncoding:encoding], NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding)) autorelease]; } -static NSString * AFQueryStringFromParameters(NSDictionary *parameters) { +NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding) { NSMutableArray *mutableParameterComponents = [NSMutableArray array]; for (id key in [parameters allKeys]) { - NSString *component = [NSString stringWithFormat:@"%@=%@", AFURLEncodedStringFromString([key description]), AFURLEncodedStringFromString([[parameters valueForKey:key] description])]; + NSString *component = [NSString stringWithFormat:@"%@=%@", AFURLEncodedStringFromStringWithEncoding([key description], encoding), AFURLEncodedStringFromStringWithEncoding([[parameters valueForKey:key] description], encoding)]; [mutableParameterComponents addObject:component]; } @@ -235,14 +236,14 @@ static NSString * AFPropertyListStringFromParameters(NSDictionary *parameters) { if (parameters) { if ([method isEqualToString:@"GET"]) { - url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", AFQueryStringFromParameters(parameters)]]; + url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding)]]; [request setURL:url]; } else { NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(self.stringEncoding)); switch (self.parameterEncoding) { case AFFormURLParameterEncoding:; [request setValue:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; - [request setHTTPBody:[AFQueryStringFromParameters(parameters) dataUsingEncoding:self.stringEncoding]]; + [request setHTTPBody:[AFQueryStringFromParametersWithEncoding(parameters, self.stringEncoding) dataUsingEncoding:self.stringEncoding]]; break; case AFJSONParameterEncoding:; [request setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"];