diff --git a/plugin.xml b/plugin.xml
index ac45f0b..8204cdb 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -28,6 +28,7 @@
+
@@ -39,6 +40,7 @@
+
@@ -87,4 +89,4 @@
-
\ No newline at end of file
+
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
index 6905b2f..011b0dc 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
@@ -30,6 +30,7 @@ abstract class CordovaHttpBase implements Runnable {
protected String method;
protected String url;
protected String serializer = "none";
+ protected String responseType;
protected Object data;
protected JSONObject headers;
protected int timeout;
@@ -38,7 +39,8 @@ abstract class CordovaHttpBase implements Runnable {
protected CallbackContext callbackContext;
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout,
- boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
+ boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
+ CallbackContext callbackContext) {
this.method = method;
this.url = url;
@@ -47,18 +49,20 @@ abstract class CordovaHttpBase implements Runnable {
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
+ this.responseType = responseType;
this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
public CordovaHttpBase(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
- TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
+ String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
this.method = method;
this.url = url;
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
+ this.responseType = responseType;
this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
@@ -158,17 +162,19 @@ abstract class CordovaHttpBase implements Runnable {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
request.receive(outputStream);
- ByteBuffer rawOutput = ByteBuffer.wrap(outputStream.toByteArray());
- String decodedBody = HttpBodyDecoder.decodeBody(rawOutput, request.charset());
-
response.setStatus(request.code());
response.setUrl(request.url().toString());
response.setHeaders(request.headers());
if (request.code() >= 200 && request.code() < 300) {
- response.setBody(decodedBody);
+ if ("text".equals(this.responseType)) {
+ String decoded = HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset());
+ response.setBody(decoded);
+ } else {
+ response.setData(outputStream.toByteArray());
+ }
} else {
- response.setErrorMessage(decodedBody);
+ response.setErrorMessage(HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset()));
}
}
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java b/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
index 3b30bfe..d89db82 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
@@ -19,7 +19,7 @@ class CordovaHttpDownload extends CordovaHttpBase {
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout, boolean followRedirects,
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
- super("GET", url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
+ super("GET", url, headers, timeout, followRedirects, "text", tlsConfiguration, callbackContext);
this.filePath = filePath;
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java b/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
index 2a9a34c..5f17e5d 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
@@ -10,14 +10,16 @@ import org.json.JSONObject;
class CordovaHttpOperation extends CordovaHttpBase {
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
- int timeout, boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
+ int timeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
+ CallbackContext callbackContext) {
- super(method, url, serializer, data, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
+ super(method, url, serializer, data, headers, timeout, followRedirects, responseType, tlsConfiguration,
+ callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
- TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
+ String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
- super(method, url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
+ super(method, url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
}
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
index 43ccd56..b7ea1b3 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
@@ -83,9 +83,10 @@ public class CordovaHttpPlugin extends CordovaPlugin {
JSONObject headers = args.getJSONObject(1);
int timeout = args.getInt(2) * 1000;
boolean followRedirect = args.getBoolean(3);
+ String responseType = args.getString(4);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout, followRedirect,
- this.tlsConfiguration, callbackContext);
+ responseType, this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(request);
@@ -101,9 +102,10 @@ public class CordovaHttpPlugin extends CordovaPlugin {
JSONObject headers = args.getJSONObject(3);
int timeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
+ String responseType = args.getString(6);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
- timeout, followRedirect, this.tlsConfiguration, callbackContext);
+ timeout, followRedirect, responseType, this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(request);
@@ -117,9 +119,10 @@ public class CordovaHttpPlugin extends CordovaPlugin {
String uploadName = args.getString(3);
int timeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
+ String responseType = args.getString(6);
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePath, uploadName, timeout, followRedirect,
- this.tlsConfiguration, callbackContext);
+ responseType, this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(upload);
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java b/src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java
index 407af47..e6051bf 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java
@@ -1,5 +1,7 @@
package com.silkimen.cordovahttp;
+import java.nio.ByteBuffer;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -9,15 +11,18 @@ import org.json.JSONObject;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Base64;
class CordovaHttpResponse {
private int status;
private String url;
private Map> headers;
private String body;
+ private byte[] rawData;
private JSONObject fileEntry;
private boolean hasFailed;
private boolean isFileOperation;
+ private boolean isRawResponse;
private String error;
public void setStatus(int status) {
@@ -36,6 +41,11 @@ class CordovaHttpResponse {
this.body = body;
}
+ public void setData(byte[] rawData) {
+ this.isRawResponse = true;
+ this.rawData = rawData;
+ }
+
public void setFileEntry(JSONObject entry) {
this.isFileOperation = true;
this.fileEntry = entry;
@@ -64,6 +74,8 @@ class CordovaHttpResponse {
json.put("error", this.error);
} else if (this.isFileOperation) {
json.put("file", this.fileEntry);
+ } else if (this.isRawResponse) {
+ json.put("data", Base64.encodeToString(this.rawData, Base64.DEFAULT));
} else {
json.put("data", this.body);
}
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
index 623e025..9d74736 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
@@ -20,9 +20,10 @@ class CordovaHttpUpload extends CordovaHttpBase {
private String uploadName;
public CordovaHttpUpload(String url, JSONObject headers, String filePath, String uploadName, int timeout,
- boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
+ boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
+ CallbackContext callbackContext) {
- super("POST", url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
+ super("POST", url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
this.filePath = filePath;
this.uploadName = uploadName;
}
diff --git a/src/android/com/silkimen/http/HttpBodyDecoder.java b/src/android/com/silkimen/http/HttpBodyDecoder.java
index 7aeddc3..92d69e2 100644
--- a/src/android/com/silkimen/http/HttpBodyDecoder.java
+++ b/src/android/com/silkimen/http/HttpBodyDecoder.java
@@ -10,21 +10,28 @@ import java.nio.charset.MalformedInputException;
public class HttpBodyDecoder {
private static final String[] ACCEPTED_CHARSETS = new String[] { "UTF-8", "ISO-8859-1" };
- public static String decodeBody(ByteBuffer rawOutput, String charsetName)
+ public static String decodeBody(byte[] body, String charsetName)
+ throws CharacterCodingException, MalformedInputException {
+
+ return decodeBody(ByteBuffer.wrap(body), charsetName);
+ }
+
+ public static String decodeBody(ByteBuffer body, String charsetName)
throws CharacterCodingException, MalformedInputException {
if (charsetName == null) {
- return tryDecodeByteBuffer(rawOutput);
+ return tryDecodeByteBuffer(body);
+ } else {
+ return decodeByteBuffer(body, charsetName);
}
-
- return decodeByteBuffer(rawOutput, charsetName);
}
- private static String tryDecodeByteBuffer(ByteBuffer rawOutput) throws CharacterCodingException, MalformedInputException {
+ private static String tryDecodeByteBuffer(ByteBuffer buffer)
+ throws CharacterCodingException, MalformedInputException {
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
try {
- return decodeByteBuffer(rawOutput, ACCEPTED_CHARSETS[i]);
+ return decodeByteBuffer(buffer, ACCEPTED_CHARSETS[i]);
} catch (MalformedInputException e) {
continue;
} catch (CharacterCodingException e) {
@@ -32,13 +39,13 @@ public class HttpBodyDecoder {
}
}
- return decodeBody(rawOutput, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
+ return decodeBody(buffer, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
}
- private static String decodeByteBuffer(ByteBuffer rawOutput, String charsetName)
+ private static String decodeByteBuffer(ByteBuffer buffer, String charsetName)
throws CharacterCodingException, MalformedInputException {
- return createCharsetDecoder(charsetName).decode(rawOutput).toString();
+ return createCharsetDecoder(charsetName).decode(buffer).toString();
}
private static CharsetDecoder createCharsetDecoder(String charsetName) {
diff --git a/src/ios/AFNetworking/AFURLResponseSerialization.h b/src/ios/AFNetworking/AFURLResponseSerialization.h
index a9430ad..10e0fef 100644
--- a/src/ios/AFNetworking/AFURLResponseSerialization.h
+++ b/src/ios/AFNetworking/AFURLResponseSerialization.h
@@ -308,4 +308,11 @@ FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorK
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey;
+/**
+`AFNetworkingOperationFailingURLResponseBodyErrorKey`
+The corresponding value is an `NSString` containing the decoded error message.
+ */
+
+FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyErrorKey;
+
NS_ASSUME_NONNULL_END
diff --git a/src/ios/AFNetworking/AFURLResponseSerialization.m b/src/ios/AFNetworking/AFURLResponseSerialization.m
index 5e46799..f88d938 100755
--- a/src/ios/AFNetworking/AFURLResponseSerialization.m
+++ b/src/ios/AFNetworking/AFURLResponseSerialization.m
@@ -34,6 +34,7 @@
NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response";
NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response";
NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data";
+NSString * const AFNetworkingOperationFailingURLResponseBodyErrorKey = @"com.alamofire.serialization.response.error.body";
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
@@ -525,7 +526,7 @@ static NSLock* imageLock = nil;
dispatch_once(&onceToken, ^{
imageLock = [[NSLock alloc] init];
});
-
+
[imageLock lock];
image = [UIImage imageWithData:data];
[imageLock unlock];
@@ -539,7 +540,7 @@ static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
if (image.images) {
return image;
}
-
+
return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
}
diff --git a/src/ios/BinaryResponseSerializer.h b/src/ios/BinaryResponseSerializer.h
new file mode 100644
index 0000000..92af266
--- /dev/null
+++ b/src/ios/BinaryResponseSerializer.h
@@ -0,0 +1,8 @@
+#import
+#import "AFURLResponseSerialization.h"
+
+@interface BinaryResponseSerializer : AFHTTPResponseSerializer
+
++ (instancetype)serializer;
+
+@end
diff --git a/src/ios/BinaryResponseSerializer.m b/src/ios/BinaryResponseSerializer.m
new file mode 100644
index 0000000..a891f8f
--- /dev/null
+++ b/src/ios/BinaryResponseSerializer.m
@@ -0,0 +1,126 @@
+#import "BinaryResponseSerializer.h"
+
+static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
+ if (!error) {
+ return underlyingError;
+ }
+
+ if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
+ return error;
+ }
+
+ NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
+ mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
+
+ return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
+}
+
+static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
+ if ([error.domain isEqualToString:domain] && error.code == code) {
+ return YES;
+ } else if (error.userInfo[NSUnderlyingErrorKey]) {
+ return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
+ }
+
+ return NO;
+}
+
+@implementation BinaryResponseSerializer
+
++ (instancetype)serializer {
+ BinaryResponseSerializer *serializer = [[self alloc] init];
+ return serializer;
+}
+
+- (instancetype)init {
+ self = [super init];
+
+ if (!self) {
+ return nil;
+ }
+
+ self.acceptableContentTypes = nil;
+
+ return self;
+}
+
+- (NSString*)decodeResponseData:(NSData*)rawResponseData withEncoding:(CFStringEncoding)cfEncoding {
+ NSStringEncoding nsEncoding;
+ NSString* decoded = nil;
+
+ if (cfEncoding != kCFStringEncodingInvalidId) {
+ nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
+ }
+
+ NSStringEncoding supportedEncodings[6] = {
+ NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding,
+ NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding
+ };
+
+ for (int i = 0; i < sizeof(supportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
+ if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == supportedEncodings[i]) {
+ decoded = [[NSString alloc] initWithData:rawResponseData encoding:supportedEncodings[i]];
+ }
+ }
+
+ return decoded;
+}
+
+- (CFStringEncoding) getEncoding:(NSURLResponse *)response {
+ CFStringEncoding encoding = kCFStringEncodingInvalidId;
+
+ if (response.textEncodingName) {
+ encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
+ }
+
+ return encoding;
+}
+
+#pragma mark -
+
+- (BOOL)validateResponse:(NSHTTPURLResponse *)response
+ data:(NSData *)data
+ error:(NSError * __autoreleasing *)error
+{
+ if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
+ if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
+ NSMutableDictionary *mutableUserInfo = [@{
+ NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
+ NSURLErrorFailingURLErrorKey: [response URL],
+ AFNetworkingOperationFailingURLResponseErrorKey: response,
+ } mutableCopy];
+
+ if (data) {
+ mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
+
+ // trying to decode error message in body
+ mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
+ }
+
+ if (error) {
+ *error = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo];
+ }
+
+ return NO;
+ }
+ }
+
+ return YES;
+}
+
+#pragma mark - AFURLResponseSerialization
+
+- (id)responseObjectForResponse:(NSURLResponse *)response
+ data:(NSData *)data
+ error:(NSError *__autoreleasing *)error
+{
+ if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
+ if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
+ return nil;
+ }
+ }
+
+ return [data base64EncodedStringWithOptions:0];
+}
+
+@end
diff --git a/src/ios/CordovaHttpPlugin.m b/src/ios/CordovaHttpPlugin.m
index ac1c99b..13d4369 100644
--- a/src/ios/CordovaHttpPlugin.m
+++ b/src/ios/CordovaHttpPlugin.m
@@ -1,5 +1,6 @@
#import "CordovaHttpPlugin.h"
#import "CDVFile.h"
+#import "BinaryResponseSerializer.h"
#import "TextResponseSerializer.h"
#import "TextRequestSerializer.h"
#import "AFHTTPSessionManager.h"
@@ -57,6 +58,15 @@
[manager.requestSerializer setTimeoutInterval:timeout];
}
+- (void)setResponseSerializer:(NSString*)responseType forManager:(AFHTTPSessionManager*)manager {
+ if ([responseType isEqualToString: @"text"]) {
+ manager.responseSerializer = [TextResponseSerializer serializer];
+ } else {
+ manager.responseSerializer = [BinaryResponseSerializer serializer];
+ }
+}
+
+
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data {
if (response != nil) {
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
@@ -74,8 +84,8 @@
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
- if (error.userInfo[AFNetworkingOperationFailingURLResponseBodyKey]) {
- [dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyKey] forKey:@"error"];
+ if (error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey]) {
+ [dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] forKey:@"error"];
}
} else {
[dictionary setObject:[self getStatusCode:error] forKey:@"status"];
@@ -161,14 +171,15 @@
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
+ NSString *responseType = [command.arguments objectAtIndex:4];
[self setRequestSerializer: @"default" forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
+ [self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
@@ -197,6 +208,7 @@
- (void)head:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
+ manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *headers = [command.arguments objectAtIndex:1];
@@ -208,7 +220,6 @@
[self setRedirect:followRedirect forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
@@ -243,14 +254,15 @@
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
+ NSString *responseType = [command.arguments objectAtIndex:4];
[self setRequestSerializer: @"default" forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
+ [self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
@@ -286,14 +298,15 @@
NSDictionary *headers = [command.arguments objectAtIndex:3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
+ NSString *responseType = [command.arguments objectAtIndex:6];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
+ [self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
@@ -329,14 +342,15 @@
NSDictionary *headers = [command.arguments objectAtIndex:3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
+ NSString *responseType = [command.arguments objectAtIndex:6];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
+ [self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
@@ -372,14 +386,15 @@
NSDictionary *headers = [command.arguments objectAtIndex:3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
+ NSString *responseType = [command.arguments objectAtIndex:6];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
+ [self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
@@ -415,15 +430,16 @@
NSString *name = [command.arguments objectAtIndex: 3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
+ NSString *responseType = [command.arguments objectAtIndex:6];
NSURL *fileURL = [NSURL URLWithString: filePath];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
+ [self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
@@ -464,6 +480,7 @@
- (void)downloadFile:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
+ manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *headers = [command.arguments objectAtIndex:1];
@@ -480,7 +497,6 @@
}
CordovaHttpPlugin* __weak weakSelf = self;
- manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
diff --git a/src/ios/TextResponseSerializer.h b/src/ios/TextResponseSerializer.h
index 7bf87db..d086a8c 100644
--- a/src/ios/TextResponseSerializer.h
+++ b/src/ios/TextResponseSerializer.h
@@ -5,6 +5,4 @@
+ (instancetype)serializer;
-FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyKey;
-
@end
diff --git a/src/ios/TextResponseSerializer.m b/src/ios/TextResponseSerializer.m
index 762f7c3..76b6530 100644
--- a/src/ios/TextResponseSerializer.m
+++ b/src/ios/TextResponseSerializer.m
@@ -1,8 +1,5 @@
#import "TextResponseSerializer.h"
-NSString * const AFNetworkingOperationFailingURLResponseBodyKey = @"com.alamofire.serialization.response.error.body";
-NSStringEncoding const SupportedEncodings[6] = { NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding, NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding };
-
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
@@ -55,9 +52,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}
- for (int i = 0; i < sizeof(SupportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
- if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == SupportedEncodings[i]) {
- decoded = [[NSString alloc] initWithData:rawResponseData encoding:SupportedEncodings[i]];
+ NSStringEncoding supportedEncodings[6] = {
+ NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding,
+ NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding
+ };
+
+ for (int i = 0; i < sizeof(supportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
+ if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == supportedEncodings[i]) {
+ decoded = [[NSString alloc] initWithData:rawResponseData encoding:supportedEncodings[i]];
}
}
@@ -94,7 +96,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
AFNetworkingOperationFailingURLResponseDataErrorKey: data,
- AFNetworkingOperationFailingURLResponseBodyKey: @"Could not decode response data due to invalid or unknown charset encoding",
+ AFNetworkingOperationFailingURLResponseBodyErrorKey: @"Could not decode response data due to invalid or unknown charset encoding",
} mutableCopy];
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
@@ -108,7 +110,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
- mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyKey] = *decoded;
+ mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] = *decoded;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
diff --git a/test/e2e-specs.js b/test/e2e-specs.js
index 7092f52..17c46d1 100644
--- a/test/e2e-specs.js
+++ b/test/e2e-specs.js
@@ -62,6 +62,18 @@ const helpers = {
}, done);
}, done);
}, done);
+ },
+ // adopted from: https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
+ hashArrayBuffer: function (buffer) {
+ var hash = 0;
+ var byteArray = new Uint8Array(buffer);
+
+ for (var i = 0; i < byteArray.length; i++) {
+ hash = ((hash << 5) - hash) + byteArray[i];
+ hash |= 0; // Convert to 32bit integer
+ }
+
+ return hash;
}
};
@@ -682,6 +694,40 @@ const tests = [
result.type.should.be.equal('rejected');
should.equal(result.data.url, undefined);
}
+ },
+ {
+ description: 'should fetch binary correctly when response type is "arraybuffer"',
+ expected: 'resolved: {"hash":-1032603775,"byteLength":35588}',
+ func: function (resolve, reject) {
+ var url = 'https://httpbin.org/image/jpeg';
+ var options = { method: 'get', responseType: 'arraybuffer' };
+ var success = function (response) {
+ resolve({
+ hash: helpers.hashArrayBuffer(response.data),
+ byteLength: response.data.byteLength
+ });
+ };
+ cordova.plugin.http.sendRequest(url, options, success, reject);
+ },
+ validationFunc: function (driver, result) {
+ result.type.should.be.equal('resolved');
+ result.data.hash.should.be.equal(-1032603775);
+ result.data.byteLength.should.be.equal(35588);
+ }
+ },
+ {
+ description: 'should decode error body even if response type is "arraybuffer"',
+ expected: 'rejected: {"status": 418, ...',
+ func: function (resolve, reject) {
+ var url = 'https://httpbin.org/status/418';
+ var options = { method: 'get', responseType: 'arraybuffer' };
+ cordova.plugin.http.sendRequest(url, options, resolve, reject);
+ },
+ validationFunc: function (driver, result) {
+ result.type.should.be.equal('rejected');
+ result.data.status.should.be.equal(418);
+ result.data.error.should.be.equal("\n -=[ teapot ]=-\n\n _...._\n .' _ _ `.\n | .\"` ^ `\". _,\n \\_;`\"---\"`|//\n | ;/\n \\_ _/\n `\"\"\"`\n");
+ }
}
// @TODO: not ready yet
// {
diff --git a/www/advanced-http.js b/www/advanced-http.js
index b6732dc..a2a8700 100644
--- a/www/advanced-http.js
+++ b/www/advanced-http.js
@@ -5,6 +5,7 @@
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var exec = require('cordova/exec');
+var base64 = require('cordova/base64');
var messages = require(pluginId + '.messages');
var globalConfigs = require(pluginId + '.global-configs');
var jsUtil = require(pluginId + '.js-util');
@@ -12,7 +13,7 @@ var ToughCookie = require(pluginId + '.tough-cookie');
var lodash = require(pluginId + '.lodash');
var WebStorageCookieStore = require(pluginId + '.local-storage-store')(ToughCookie, lodash);
var cookieHandler = require(pluginId + '.cookie-handler')(window.localStorage, ToughCookie, WebStorageCookieStore);
-var helpers = require(pluginId + '.helpers')(jsUtil, cookieHandler, messages);
+var helpers = require(pluginId + '.helpers')(jsUtil, cookieHandler, messages, base64);
var urlUtil = require(pluginId + '.url-util')(jsUtil);
var publicInterface = require(pluginId + '.public-interface')(exec, cookieHandler, urlUtil, helpers, globalConfigs);
diff --git a/www/helpers.js b/www/helpers.js
index 9e9127c..e6180d5 100644
--- a/www/helpers.js
+++ b/www/helpers.js
@@ -1,8 +1,9 @@
-module.exports = function init(jsUtil, cookieHandler, messages) {
+module.exports = function init(jsUtil, cookieHandler, messages, base64) {
var validSerializers = ['urlencoded', 'json', 'utf8'];
var validCertModes = ['default', 'nocheck', 'pinned', 'legacy'];
var validClientAuthModes = ['none', 'systemstore', 'buffer'];
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download'];
+ var validResponseTypes = ['text','arraybuffer'];
var interface = {
b64EncodeUnicode: b64EncodeUnicode,
@@ -15,6 +16,7 @@ module.exports = function init(jsUtil, cookieHandler, messages) {
checkTimeoutValue: checkTimeoutValue,
checkFollowRedirectValue: checkFollowRedirectValue,
injectCookieHandler: injectCookieHandler,
+ injectRawResponseHandler: injectRawResponseHandler,
injectFileEntryHandler: injectFileEntryHandler,
getMergedHeaders: getMergedHeaders,
getProcessedData: getProcessedData,
@@ -28,6 +30,7 @@ module.exports = function init(jsUtil, cookieHandler, messages) {
interface.checkForValidStringValue = checkForValidStringValue;
interface.checkKeyValuePairObject = checkKeyValuePairObject;
interface.checkHttpMethod = checkHttpMethod;
+ interface.checkResponseType = checkResponseType;
interface.checkHeadersObject = checkHeadersObject;
interface.checkParamsObject = checkParamsObject;
interface.resolveCookieString = resolveCookieString;
@@ -95,6 +98,10 @@ module.exports = function init(jsUtil, cookieHandler, messages) {
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
}
+ function checkResponseType(type) {
+ return checkForValidStringValue(validResponseTypes, type, messages.INVALID_RESPONSE_TYPE);
+ }
+
function checkSerializer(serializer) {
return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER);
}
@@ -227,6 +234,17 @@ module.exports = function init(jsUtil, cookieHandler, messages) {
}
}
+ function injectRawResponseHandler(responseType, cb) {
+ return function (response) {
+ // arraybuffer
+ if (responseType === validResponseTypes[1]) {
+ response.data = base64.toArrayBuffer(response.data);
+ }
+
+ cb(response);
+ }
+ }
+
function injectFileEntryHandler(cb) {
return function (response) {
cb(createFileEntry(response.file));
@@ -302,6 +320,7 @@ module.exports = function init(jsUtil, cookieHandler, messages) {
return {
method: checkHttpMethod(options.method || validHttpMethods[0]),
+ responseType: checkResponseType(options.responseType || validResponseTypes[0]),
serializer: checkSerializer(options.serializer || globals.serializer),
timeout: checkTimeoutValue(options.timeout || globals.timeout),
followRedirect: checkFollowRedirectValue(options.followRedirect || globals.followRedirect),
diff --git a/www/messages.js b/www/messages.js
index 5eebeb4..dd14eaf 100644
--- a/www/messages.js
+++ b/www/messages.js
@@ -1,18 +1,19 @@
module.exports = {
ADDING_COOKIES_NOT_SUPPORTED: 'advanced-http: "setHeader" does not support adding cookies, please use "setCookie" function instead',
DATA_TYPE_MISMATCH: 'advanced-http: "data" argument supports only following data types:',
- MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
- MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
- INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
- INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
- INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
+ INVALID_CLIENT_AUTH_ALIAS: 'advanced-http: invalid client certificate alias, needs to be a string or undefined',
INVALID_CLIENT_AUTH_MODE: 'advanced-http: invalid client certificate authentication mode, supported modes are:',
INVALID_CLIENT_AUTH_OPTIONS: 'advanced-http: invalid client certificate authentication options, needs to be an object',
- INVALID_CLIENT_AUTH_ALIAS: 'advanced-http: invalid client certificate alias, needs to be a string or undefined',
- INVALID_CLIENT_AUTH_RAW_PKCS: 'advanced-http: invalid PKCS12 container, needs to be an array buffer',
INVALID_CLIENT_AUTH_PKCS_PASSWORD: 'advanced-http: invalid PKCS12 container password, needs to be a string',
- INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings',
- INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
+ INVALID_CLIENT_AUTH_RAW_PKCS: 'advanced-http: invalid PKCS12 container, needs to be an array buffer',
+ INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value',
- INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings'
+ INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings',
+ INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
+ INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings',
+ INVALID_RESPONSE_TYPE: 'advanced-http: invalid response type, supported types are:',
+ INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
+ INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
+ MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
+ MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
};
diff --git a/www/public-interface.js b/www/public-interface.js
index 60c850c..371c4da 100644
--- a/www/public-interface.js
+++ b/www/public-interface.js
@@ -143,22 +143,23 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
url = urlUtil.appendQueryParamsString(url, urlUtil.serializeQueryParams(options.params, true));
var headers = helpers.getMergedHeaders(url, options.headers, globalConfigs.headers);
- var onSuccess = helpers.injectCookieHandler(url, success);
+
var onFail = helpers.injectCookieHandler(url, failure);
+ var onSuccess = helpers.injectCookieHandler(url, helpers.injectRawResponseHandler(options.responseType, success));
switch (options.method) {
case 'post':
case 'put':
case 'patch':
var data = helpers.getProcessedData(options.data, options.serializer);
- return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect]);
+ return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect, options.responseType]);
case 'upload':
- return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, headers, options.filePath, options.name, options.timeout, options.followRedirect]);
+ return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, headers, options.filePath, options.name, options.timeout, options.followRedirect, options.responseType]);
case 'download':
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, options.filePath, options.timeout, options.followRedirect]);
default:
- return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect]);
+ return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect, options.responseType]);
}
}