diff --git a/CHANGELOG.md b/CHANGELOG.md
index 492604c..f4ef441 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## 2.4.0
+
+- Feature #291: add support for sending 'raw' requests (thanks to jachstet-sea and chuchuva)
+
## 2.3.1
- Fixed #275: getAllCookies() is broken because of a typo (thanks ath0mas)
diff --git a/README.md b/README.md
index c90d745..246709a 100644
--- a/README.md
+++ b/README.md
@@ -91,10 +91,21 @@ cordova.plugin.http.setDataSerializer('urlencoded');
```
You can choose one of these:
-* `urlencoded`: send data as url encoded content in body (content type "application/x-www-form-urlencoded")
-* `json`: send data as JSON encoded content in body (content type "application/json")
-* `utf8`: send data as plain UTF8 encoded string in body (content type "plain/text")
-* `multipart`: send FormData objects as multipart content in body (content type "multipart/form-data")
+* `urlencoded`: send data as url encoded content in body
+ * default content type "application/x-www-form-urlencoded"
+ * data must be an dictionary style `Object`
+* `json`: send data as JSON encoded content in body
+ * default content type "application/json"
+ * data must be an `Array` or an dictionary style `Object`
+* `utf8`: send data as plain UTF8 encoded string in body
+ * default content type "plain/text"
+ * data must be a `String`
+* `multipart`: send FormData objects as multipart content in body
+ * default content type "multipart/form-data"
+ * data must be an `FormData` instance
+* `raw`: send data as is, without any processing
+ * default content type "application/octet-stream"
+ * data must be an `Uint8Array` or an `ArrayBuffer`
This defaults to `urlencoded`. You can also override the default content type headers by specifying your own headers (see [setHeader](#setHeader)).
diff --git a/package.json b/package.json
index 3e9bec9..12de35a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-advanced-http",
- "version": "2.3.1",
+ "version": "2.4.0",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
@@ -50,7 +50,7 @@
"pvsaikrishna",
"cvillerm",
"hideov",
- "Mobisys"
+ "silkimen"
],
"license": "MIT",
"bugs": {
diff --git a/plugin.xml b/plugin.xml
index 78d43a9..1bf0f73 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -31,6 +31,7 @@
+
@@ -43,6 +44,7 @@
+
diff --git a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
index 5617508..a86c46f 100644
--- a/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
+++ b/src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
@@ -143,6 +143,8 @@ abstract class CordovaHttpBase implements Runnable {
request.contentType("application/json", "UTF-8");
} else if ("utf8".equals(this.serializer)) {
request.contentType("text/plain", "UTF-8");
+ } else if ("raw".equals(this.serializer)) {
+ request.contentType("application/octet-stream");
} else if ("urlencoded".equals(this.serializer)) {
// intentionally left blank, because content type is set in HttpRequest.form()
} else if ("multipart".equals(this.serializer)) {
@@ -159,6 +161,8 @@ abstract class CordovaHttpBase implements Runnable {
request.send(this.data.toString());
} else if ("utf8".equals(this.serializer)) {
request.send(((JSONObject) this.data).getString("text"));
+ } else if ("raw".equals(this.serializer)) {
+ request.send(Base64.decode((String)this.data, Base64.DEFAULT));
} else if ("urlencoded".equals(this.serializer)) {
request.form(JsonUtils.getObjectMap((JSONObject) this.data));
} else if ("multipart".equals(this.serializer)) {
diff --git a/src/ios/BinaryRequestSerializer.h b/src/ios/BinaryRequestSerializer.h
new file mode 100644
index 0000000..b9930a8
--- /dev/null
+++ b/src/ios/BinaryRequestSerializer.h
@@ -0,0 +1,8 @@
+#import
+#import "AFURLRequestSerialization.h"
+
+@interface BinaryRequestSerializer : AFHTTPRequestSerializer
+
++ (instancetype)serializer;
+
+@end
diff --git a/src/ios/BinaryRequestSerializer.m b/src/ios/BinaryRequestSerializer.m
new file mode 100644
index 0000000..ada4df8
--- /dev/null
+++ b/src/ios/BinaryRequestSerializer.m
@@ -0,0 +1,53 @@
+#import "BinaryRequestSerializer.h"
+
+@implementation BinaryRequestSerializer
+
++ (instancetype)serializer
+{
+ BinaryRequestSerializer *serializer = [[self alloc] init];
+ return serializer;
+}
+
+#pragma mark - AFURLRequestSerialization
+
+- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
+ withParameters:(id)parameters
+ error:(NSError *__autoreleasing *)error
+{
+ NSParameterAssert(request);
+
+ if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
+ return [super requestBySerializingRequest:request withParameters:parameters error:error];
+ }
+
+ NSMutableURLRequest *mutableRequest = [request mutableCopy];
+
+ [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
+ if (![request valueForHTTPHeaderField:field]) {
+ [mutableRequest setValue:value forHTTPHeaderField:field];
+ }
+ }];
+
+ if (parameters) {
+ if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
+ [mutableRequest setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
+ }
+
+ [mutableRequest setHTTPBody: parameters];
+ }
+
+ return mutableRequest;
+}
+
+#pragma mark - NSSecureCoding
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) {
+ return nil;
+ }
+
+ return self;
+}
+
+@end
diff --git a/src/ios/CordovaHttpPlugin.m b/src/ios/CordovaHttpPlugin.m
index f77f0d9..df31126 100644
--- a/src/ios/CordovaHttpPlugin.m
+++ b/src/ios/CordovaHttpPlugin.m
@@ -1,5 +1,6 @@
#import "CordovaHttpPlugin.h"
#import "CDVFile.h"
+#import "BinaryRequestSerializer.h"
#import "BinaryResponseSerializer.h"
#import "TextResponseSerializer.h"
#import "TextRequestSerializer.h"
@@ -31,6 +32,8 @@
manager.requestSerializer = [AFJSONRequestSerializer serializer];
} else if ([serializerName isEqualToString:@"utf8"]) {
manager.requestSerializer = [TextRequestSerializer serializer];
+ } else if ([serializerName isEqualToString:@"raw"]) {
+ manager.requestSerializer = [BinaryRequestSerializer serializer];
} else {
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
}
diff --git a/test/e2e-specs.js b/test/e2e-specs.js
index 83f753d..02312a2 100644
--- a/test/e2e-specs.js
+++ b/test/e2e-specs.js
@@ -34,6 +34,7 @@ const helpers = {
setUtf8StringSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('utf8')); },
setUrlEncodedSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('urlencoded')); },
setMultipartSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('multipart')); },
+ setRawSerializer: function(resolve) { resolve(cordova.plugin.http.setDataSerializer('raw')); },
disableFollowingRedirect: function (resolve) { resolve(cordova.plugin.http.setFollowRedirect(false)); },
enableFollowingRedirect: function(resolve) { resolve(cordova.plugin.http.setFollowRedirect(true)); },
getWithXhr: function (done, url, type) {
@@ -843,7 +844,31 @@ const tests = [
const b64Logo = rawLogo.toString('base64');
JSON.parse(result.data.data).files.CordovaLogo.should.be.equal('data:image/png;base64,' + b64Logo);
}
- }
+ },
+ {
+ description: 'should send raw byte array correctly (POST) #291',
+ expected: 'resolved: {"status":200,"data:application/octet-stream;base64,iVBORw0KGgoAAAANSUhEUg ...',
+ before: helpers.setRawSerializer,
+ func: function (resolve, reject) {
+ helpers.getWithXhr(function(buffer) {
+ cordova.plugin.http.post('http://httpbin.org/anything', buffer, {}, resolve, reject);
+ }, './res/cordova_logo.png', 'arraybuffer');
+ },
+ validationFunc: function (driver, result) {
+ helpers.checkResult(result, 'resolved');
+ result.data.status.should.be.equal(200);
+
+ // httpbin.org encodes posted binaries in base64 and echoes them back
+ // therefore we need to check for base64 string with mime type prefix
+ const fs = require('fs');
+ const rawLogo = fs.readFileSync('./test/e2e-app-template/www/res/cordova_logo.png');
+ const b64Logo = rawLogo.toString('base64');
+ const parsed = JSON.parse(result.data.data);
+
+ parsed.headers['Content-Type'].should.be.equal('application/octet-stream');
+ parsed.data.should.be.equal('data:application/octet-stream;base64,' + b64Logo);
+ }
+ },
// TODO: not ready yet
// {
diff --git a/test/js-specs.js b/test/js-specs.js
index 8a2698c..2507dd6 100644
--- a/test/js-specs.js
+++ b/test/js-specs.js
@@ -589,6 +589,15 @@ describe('Common helpers', function () {
cb();
});
});
+
+ it('processes data correctly when serializer "raw" is configured', (cb) => {
+ const byteArray = new Uint8Array([1,2,3]);
+ helpers.processData(byteArray, 'raw', (data) => {
+ data.should.be.a('ArrayBuffer');
+ data.should.be.equal(byteArray.buffer);
+ cb();
+ })
+ });
});
});
diff --git a/www/helpers.js b/www/helpers.js
index 4b4ed28..ff6a88f 100644
--- a/www/helpers.js
+++ b/www/helpers.js
@@ -1,5 +1,5 @@
module.exports = function init(global, jsUtil, cookieHandler, messages, base64, errorCodes, dependencyValidator, ponyfills) {
- var validSerializers = ['urlencoded', 'json', 'utf8', 'multipart'];
+ var validSerializers = ['urlencoded', 'json', 'utf8', 'raw', 'multipart'];
var validCertModes = ['default', 'nocheck', 'pinned', 'legacy'];
var validClientAuthModes = ['none', 'systemstore', 'buffer'];
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download'];
@@ -365,6 +365,8 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
return ['Object'];
case 'json':
return ['Array', 'Object'];
+ case 'raw':
+ return ['Uint8Array', 'ArrayBuffer'];
default:
return [];
}
@@ -400,6 +402,8 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
switch (dataSerializer) {
case 'utf8':
return cb({ text: data });
+ case 'raw':
+ return cb(currentDataType === 'Uint8Array' ? data.buffer : data);
case 'multipart':
return processFormData(data, cb);
default:
diff --git a/www/js-util.js b/www/js-util.js
index e379dba..593dcc0 100644
--- a/www/js-util.js
+++ b/www/js-util.js
@@ -6,6 +6,8 @@ module.exports = {
return 'Array';
case '[object Blob]':
return 'Blob';
+ case '[object Uint8Array]':
+ return 'Uint8Array';
case '[object ArrayBuffer]':
return 'ArrayBuffer';
case '[object Boolean]':