mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-21 05:45:13 +00:00
264 lines
12 KiB
Objective-C
Executable file
264 lines
12 KiB
Objective-C
Executable file
//
|
|
// NSMutableURLRequest+OSKUtilities.m
|
|
// Overshare
|
|
//
|
|
// Created by Jared Sinclair on 10/24/13.
|
|
// Copyright (c) 2013 Overshare Kit. All rights reserved.
|
|
//
|
|
|
|
#import "NSMutableURLRequest+OSKUtilities.h"
|
|
|
|
#import "OSKLogger.h"
|
|
|
|
static NSString * OSKBoundaryString = @"OvershareKit-nT6YdBLrnos4eaUY";
|
|
static NSString * OSKCRLF = @"\r\n";
|
|
static NSString * kOSKCharactersToBeEscapedInQueryString = @":/?&=;+!@#$()',*";
|
|
|
|
static NSString * OSKPercentEscapedQueryStringKeyFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
|
|
static NSString * const kOSKCharactersToLeaveUnescapedInQueryStringPairKey = @"[].";
|
|
|
|
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kOSKCharactersToLeaveUnescapedInQueryStringPairKey, (__bridge CFStringRef)kOSKCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
|
|
}
|
|
|
|
static NSString * OSKPercentEscapedQueryStringValueFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
|
|
return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, (__bridge CFStringRef)kOSKCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
|
|
}
|
|
|
|
|
|
// ====================================================================================================
|
|
|
|
|
|
@interface OSKQueryStringPair : NSObject
|
|
|
|
@property (readwrite, nonatomic, strong) id field;
|
|
@property (readwrite, nonatomic, strong) id value;
|
|
|
|
- (id)initWithField:(id)field value:(id)value;
|
|
|
|
- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding;
|
|
|
|
@end
|
|
|
|
@implementation OSKQueryStringPair
|
|
|
|
- (id)initWithField:(id)field value:(id)value {
|
|
self = [super init];
|
|
if (!self) {
|
|
return nil;
|
|
}
|
|
|
|
self.field = field;
|
|
self.value = value;
|
|
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding {
|
|
if (!self.value || [self.value isEqual:[NSNull null]]) {
|
|
return OSKPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding);
|
|
} else {
|
|
return [NSString stringWithFormat:@"%@=%@", OSKPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding), OSKPercentEscapedQueryStringValueFromStringWithEncoding([self.value description], stringEncoding)];
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
// ====================================================================================================
|
|
|
|
|
|
extern NSArray * OSKQueryStringPairsFromDictionary(NSDictionary *dictionary);
|
|
extern NSArray * OSKQueryStringPairsFromKeyAndValue(NSString *key, id value);
|
|
|
|
NSArray * OSKQueryStringPairsFromDictionary(NSDictionary *dictionary) {
|
|
return OSKQueryStringPairsFromKeyAndValue(nil, dictionary);
|
|
}
|
|
|
|
NSArray * OSKQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
|
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
|
|
|
|
if ([value isKindOfClass:[NSDictionary class]]) {
|
|
NSDictionary *dictionary = value;
|
|
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
|
|
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(caseInsensitiveCompare:)];
|
|
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
|
|
id nestedValue = [dictionary objectForKey:nestedKey];
|
|
if (nestedValue) {
|
|
[mutableQueryStringComponents addObjectsFromArray:OSKQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
|
|
}
|
|
}
|
|
} else if ([value isKindOfClass:[NSArray class]]) {
|
|
NSArray *array = value;
|
|
for (id nestedValue in array) {
|
|
[mutableQueryStringComponents addObjectsFromArray:OSKQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
|
|
}
|
|
} else if ([value isKindOfClass:[NSSet class]]) {
|
|
NSSet *set = value;
|
|
for (id obj in set) {
|
|
[mutableQueryStringComponents addObjectsFromArray:OSKQueryStringPairsFromKeyAndValue(key, obj)];
|
|
}
|
|
} else {
|
|
[mutableQueryStringComponents addObject:[[OSKQueryStringPair alloc] initWithField:key value:value]];
|
|
}
|
|
|
|
return mutableQueryStringComponents;
|
|
}
|
|
|
|
static NSString * OSKQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding stringEncoding) {
|
|
NSMutableArray *mutablePairs = [NSMutableArray array];
|
|
for (OSKQueryStringPair *pair in OSKQueryStringPairsFromDictionary(parameters)) {
|
|
[mutablePairs addObject:[pair URLEncodedStringValueWithEncoding:stringEncoding]];
|
|
}
|
|
|
|
return [mutablePairs componentsJoinedByString:@"&"];
|
|
}
|
|
|
|
|
|
// ====================================================================================================
|
|
|
|
|
|
@implementation NSMutableURLRequest (OSKUtilities)
|
|
|
|
+ (NSMutableURLRequest *)osk_requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters serialization:(OSKParameterSerializationType)serialization {
|
|
NSParameterAssert(method);
|
|
NSParameterAssert(URLString);
|
|
|
|
NSURL *url = [NSURL URLWithString:URLString];
|
|
|
|
NSParameterAssert(url);
|
|
|
|
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
|
|
[request setHTTPMethod:method];
|
|
|
|
if (serialization == OSKParameterSerializationType_HTTPBody_JSON) {
|
|
request = [[self osk_requestByJSONSerializingRequest:request withParameters:parameters error:nil] mutableCopy];
|
|
}
|
|
else if (serialization == OSKParameterSerializationType_HTTPBody_FormData) {
|
|
request = [[self osk_requestByHTTPSerializingRequest:request withParameters:parameters error:nil] mutableCopy];
|
|
}
|
|
else {
|
|
request = [[self osk_requestByQueryStringSerializingRequest:request withParameters:parameters error:nil] mutableCopy];
|
|
}
|
|
|
|
return request;
|
|
}
|
|
|
|
+ (NSURLRequest *)osk_requestByQueryStringSerializingRequest:(NSURLRequest *)request withParameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error {
|
|
NSParameterAssert(request);
|
|
|
|
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
|
|
|
if (!parameters) {
|
|
return mutableRequest;
|
|
}
|
|
|
|
NSString *query = OSKQueryStringFromParametersWithEncoding(parameters, NSUTF8StringEncoding);
|
|
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
|
|
|
|
return mutableRequest;
|
|
}
|
|
|
|
+ (NSURLRequest *)osk_requestByHTTPSerializingRequest:(NSURLRequest *)request withParameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error {
|
|
NSParameterAssert(request);
|
|
|
|
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
|
|
|
if (!parameters) {
|
|
return mutableRequest;
|
|
}
|
|
|
|
NSString *query = OSKQueryStringFromParametersWithEncoding(parameters, NSUTF8StringEncoding);
|
|
[mutableRequest setHTTPBody:[query dataUsingEncoding:NSUTF8StringEncoding]];
|
|
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
|
|
|
|
return mutableRequest;
|
|
}
|
|
|
|
+ (NSURLRequest *)osk_requestByJSONSerializingRequest:(NSURLRequest *)request withParameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error {
|
|
NSParameterAssert(request);
|
|
|
|
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
|
|
|
if (!parameters) {
|
|
return mutableRequest;
|
|
}
|
|
|
|
NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
|
|
|
|
[mutableRequest setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"];
|
|
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:0 error:error]];
|
|
|
|
return mutableRequest;
|
|
}
|
|
|
|
+ (NSMutableURLRequest *)osk_MultipartFormUploadRequestWithMethod:(NSString *)method URLString:(NSString *)URLstring parameters:(NSDictionary *)parameters uploadData:(NSData *)uploadData filename:(NSString *)filename formName:(NSString *)formName mimeType:(NSString *)mimeType serialization:(OSKParameterSerializationType)serialization bodyData:(NSData **)outputData{
|
|
|
|
NSMutableData *mutableBodyData = [NSMutableData data];
|
|
|
|
NSString *openingBoundary = [NSString stringWithFormat:@"%@--%@%@", OSKCRLF, OSKBoundaryString, OSKCRLF];
|
|
[mutableBodyData appendData:[openingBoundary dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
if (parameters) {
|
|
if (serialization == OSKParameterSerializationType_HTTPBody_JSON) {
|
|
NSString *contentDisposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"metadata\"; filename=\"metadata.json\"%@", OSKCRLF];
|
|
[mutableBodyData appendData:[contentDisposition dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
NSString *contentType = [NSString stringWithFormat:@"Content-Type: application/json%@%@", OSKCRLF, OSKCRLF];
|
|
[mutableBodyData appendData:[contentType dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];
|
|
if (jsonData) {
|
|
[mutableBodyData appendData:jsonData];
|
|
}
|
|
}
|
|
else if (serialization == OSKParameterSerializationType_HTTPBody_FormData) {
|
|
for (OSKQueryStringPair *pair in OSKQueryStringPairsFromDictionary(parameters)) {
|
|
NSString *escapedKey = OSKPercentEscapedQueryStringKeyFromStringWithEncoding(pair.field, NSUTF8StringEncoding);
|
|
NSString *escapedValue = OSKPercentEscapedQueryStringValueFromStringWithEncoding(pair.value, NSUTF8StringEncoding);
|
|
NSString *contentDisposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"%@", escapedKey, OSKCRLF];
|
|
[mutableBodyData appendData:[contentDisposition dataUsingEncoding:NSUTF8StringEncoding]];
|
|
[mutableBodyData appendData:[escapedValue dataUsingEncoding:NSUTF8StringEncoding]];
|
|
}
|
|
}
|
|
else {
|
|
NSAssert(NO, @"Multipart form data requests cannot use the query string parameter serialization type.");
|
|
}
|
|
NSString *closingBoundary = [NSString stringWithFormat:@"%@--%@", OSKCRLF, OSKBoundaryString];
|
|
[mutableBodyData appendData:[closingBoundary dataUsingEncoding:NSUTF8StringEncoding]];
|
|
}
|
|
|
|
if (uploadData) {
|
|
NSString *contentDisposition = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"%@", formName, filename, OSKCRLF];
|
|
[mutableBodyData appendData:[contentDisposition dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
NSString *contentType = [NSString stringWithFormat:@"Content-Type: %@%@%@", mimeType, OSKCRLF, OSKCRLF];
|
|
[mutableBodyData appendData:[contentType dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
[mutableBodyData appendData:[NSData dataWithData:uploadData]];
|
|
|
|
NSString *closingBoundary = [NSString stringWithFormat:@"%@--%@", OSKCRLF, OSKBoundaryString];
|
|
[mutableBodyData appendData:[closingBoundary dataUsingEncoding:NSUTF8StringEncoding]];
|
|
}
|
|
|
|
NSString *closingDashes = @"--";
|
|
[mutableBodyData appendData:[closingDashes dataUsingEncoding:NSUTF8StringEncoding]];
|
|
|
|
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
|
|
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", OSKBoundaryString];
|
|
[request setValue:contentType forHTTPHeaderField:@"Content-Type"];
|
|
[request setURL:[NSURL URLWithString:URLstring]];
|
|
[request setHTTPMethod:method];
|
|
|
|
*outputData = [NSData dataWithData:mutableBodyData];
|
|
|
|
return request;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|