Compare commits

...

17 Commits

Author SHA1 Message Date
Sefa Ilkimen
192059d34e release v2.4.0 2020-01-27 04:49:11 +01:00
Sefa Ilkimen
7193b636f1 feat #283: improve error message on timeout on browser platform 2020-01-27 04:44:13 +01:00
Sefa Ilkimen
662b4352f5 update readme and changelog 2020-01-27 02:58:45 +01:00
Sefa Ilkimen
e18f9eee51 implement feat #155 for iOS 2020-01-27 02:22:29 +01:00
Sefa Ilkimen
c7eb60e670 implement feat #155 for android 2020-01-27 02:10:32 +01:00
Sefa Ilkimen
6a930de82f implement www interface for #155 2020-01-27 02:10:01 +01:00
Sefa Ilkimen
588e4a0e57 implement e2e specs for #155 2020-01-27 02:09:21 +01:00
Sefa Ilkimen
aded59e3d1 implement raw serializer support for browser target 2020-01-26 18:41:31 +01:00
Sefa Ilkimen
9ef582b37f Merge branch 'chuchuva-raw-request-body' 2020-01-26 18:00:47 +01:00
Sefa Ilkimen
33fea67603 update readme and changelog 2020-01-26 17:55:04 +01:00
Sefa Ilkimen
faffe0e078 fix e2e tests for raw serializer 2020-01-26 17:50:40 +01:00
Sefa Ilkimen
99c7f5d331 add default content type for raw serializer 2020-01-26 17:50:18 +01:00
Pavel Chuchuva
b74ad73742 Add documentation for 'raw' serializer 2020-01-18 08:44:58 +11:00
Pavel Chuchuva
7fd7ab223c Add tests for 'raw' serializer 2020-01-18 08:23:43 +11:00
Pavel Chuchuva
937010bd4e Add support for sending 'raw' requests on iOS too 2020-01-14 16:09:47 +11:00
Pavel Chuchuva
7ab4b634ca Add support for sending 'raw' requests on Android
request.data can be `Uint8Array` or `ArrayBuffer`. The plugin will send it as is, without any processing.
2020-01-12 08:18:02 +11:00
Sefa Ilkimen
32187a12fe chore: fix #281 broken builds in forks 2019-12-15 05:18:58 +01:00
19 changed files with 209 additions and 30 deletions

View File

@@ -58,6 +58,8 @@ jobs:
- name: Build test app
run: scripts/build-test-app.sh --android --device
- name: Upload artifact to BrowserStack
if: env.BROWSERSTACK_USERNAME != ''
run: scripts/upload-browserstack.sh --android
- name: Run e2e tests
if: env.BROWSERSTACK_USERNAME != ''
run: scripts/test-app.sh --android --device

View File

@@ -1,5 +1,11 @@
# Changelog
## 2.4.0
- Feature #291: add support for sending 'raw' requests (thanks to jachstet-sea and chuchuva)
- Feature #155: add OPTIONS method
- Feature #283: improve error message on timeout on browser platform
## 2.3.1
- Fixed #275: getAllCookies() is broken because of a typo (thanks ath0mas)

View File

@@ -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)).
@@ -196,7 +207,7 @@ Execute a HTTP request. Takes a URL and an options object. This is the internal
The options object contains following keys:
* `method`: HTTP method to be used, defaults to `get`, needs to be one of the following values:
* `get`, `post`, `put`, `patch`, `head`, `delete`, `upload`, `download`
* `get`, `post`, `put`, `patch`, `head`, `delete`, `options`, `upload`, `download`
* `data`: payload to be send to the server (only applicable on `post`, `put` or `patch` methods)
* `params`: query params to be appended to the URL (only applicable on `get`, `head`, `delete`, `upload` or `download` methods)
* `serializer`: data serializer to be used (only applicable on `post`, `put` or `patch` methods), defaults to global serializer value, see [setDataSerializer](#setDataSerializer) for supported values
@@ -332,6 +343,9 @@ Execute a DELETE request. Takes a URL, parameters, and headers. See the [post]
### head<a name="head"></a>
Execute a HEAD request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### options<a name="options"></a>
Execute a OPTIONS request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### uploadFile<a name="uploadFile"></a>
Uploads one or more file(s) saved on the device. Takes a URL, parameters, headers, filePath(s), and the name(s) of the parameter to pass the file along as. See the [post](#post) documentation for details on what is returned on success and failure.

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-advanced-http",
"version": "2.3.1",
"version": "2.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -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": {

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.3.1">
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.4.0">
<name>Advanced HTTP plugin</name>
<description>
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
@@ -31,6 +31,7 @@
</feature>
</config-file>
<header-file src="src/ios/CordovaHttpPlugin.h"/>
<header-file src="src/ios/BinaryRequestSerializer.h"/>
<header-file src="src/ios/BinaryResponseSerializer.h"/>
<header-file src="src/ios/TextResponseSerializer.h"/>
<header-file src="src/ios/TextRequestSerializer.h"/>
@@ -43,6 +44,7 @@
<header-file src="src/ios/AFNetworking/AFURLSessionManager.h"/>
<header-file src="src/ios/SDNetworkActivityIndicator/SDNetworkActivityIndicator.h"/>
<source-file src="src/ios/CordovaHttpPlugin.m"/>
<source-file src="src/ios/BinaryRequestSerializer.m"/>
<source-file src="src/ios/BinaryResponseSerializer.m"/>
<source-file src="src/ios/TextResponseSerializer.m"/>
<source-file src="src/ios/TextRequestSerializer.m"/>

View File

@@ -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)) {

View File

@@ -57,6 +57,8 @@ public class CordovaHttpPlugin extends CordovaPlugin {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("delete".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("options".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("post".equals(action)) {
return this.executeHttpRequestWithData(action, args, callbackContext);
} else if ("put".equals(action)) {

View File

@@ -160,16 +160,30 @@ function sendRequest(method, withData, opts, success, failure) {
setDefaultContentType(headers, 'application/x-www-form-urlencoded');
processedData = serializeParams(data);
break;
case 'raw':
setDefaultContentType(headers, 'application/octet-stream');
processedData = data;
break;
}
xhr.timeout = timeout * 1000;
xhr.responseType = responseType;
setHeaders(xhr, headers);
xhr.onerror = xhr.ontimeout = function () {
xhr.onerror = function () {
return failure(createXhrFailureObject(xhr));
};
xhr.ontimeout = function () {
return failure({
status: -4,
error: 'Request timed out',
url: url,
headers: {}
});
};
xhr.onload = function () {
if (xhr.readyState !== xhr.DONE) return;

View File

@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
#import "AFURLRequestSerialization.h"
@interface BinaryRequestSerializer : AFHTTPRequestSerializer
+ (instancetype)serializer;
@end

View File

@@ -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

View File

@@ -6,10 +6,12 @@
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
- (void)post:(CDVInvokedUrlCommand*)command;
- (void)get:(CDVInvokedUrlCommand*)command;
- (void)put:(CDVInvokedUrlCommand*)command;
- (void)patch:(CDVInvokedUrlCommand*)command;
- (void)get:(CDVInvokedUrlCommand*)command;
- (void)delete:(CDVInvokedUrlCommand*)command;
- (void)head:(CDVInvokedUrlCommand*)command;
- (void)options:(CDVInvokedUrlCommand*)command;
- (void)uploadFiles:(CDVInvokedUrlCommand*)command;
- (void)downloadFile:(CDVInvokedUrlCommand*)command;

View File

@@ -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];
}
@@ -299,6 +302,18 @@
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)post:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"POST"];
}
- (void)put:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"PUT"];
}
- (void)patch:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"PATCH"];
}
- (void)get:(CDVInvokedUrlCommand*)command {
[self executeRequestWithoutData: command withMethod:@"GET"];
}
@@ -311,16 +326,8 @@
[self executeRequestWithoutData: command withMethod:@"HEAD"];
}
- (void)post:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"POST"];
}
- (void)put:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"PUT"];
}
- (void)patch:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"PATCH"];
- (void)options:(CDVInvokedUrlCommand*)command {
[self executeRequestWithoutData: command withMethod:@"OPTIONS"];
}
- (void)uploadFiles:(CDVInvokedUrlCommand*)command {

View File

@@ -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,52 @@ 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);
}
},
{
description: 'should perform an OPTIONS request correctly #155',
expected: 'resolved: {"status":200,"headers":{"allow":"GET, PUT, DELETE, HEAD, PATCH, TRACE, POST, OPTIONS" ...',
func: function (resolve, reject) { cordova.plugin.http.options('http://httpbin.org/anything', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.status.should.be.equal(200);
result.data.headers.should.be.an('object');
result.data.headers.allow.should.include('GET');
result.data.headers.allow.should.include('PUT');
result.data.headers.allow.should.include('DELETE');
result.data.headers.allow.should.include('HEAD');
result.data.headers.allow.should.include('PATCH');
result.data.headers.allow.should.include('TRACE');
result.data.headers.allow.should.include('POST');
result.data.headers.allow.should.include('OPTIONS');
result.data.headers['access-control-allow-origin'].should.be.equal('*');
}
},
// TODO: not ready yet
// {

View File

@@ -77,7 +77,6 @@ const configs = {
function getCaps(environment, os, runtime) {
const key = environment.toLowerCase() + capitalize(os) + capitalize(runtime);
console.log(key);
const caps = configs[key];
caps.name = `cordova-plugin-advanced-http (${os})`;

View File

@@ -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();
})
});
});
});

View File

@@ -1,8 +1,8 @@
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'];
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'options', 'upload', 'download'];
var validResponseTypes = ['text', 'json', 'arraybuffer', 'blob'];
var interface = {
@@ -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:

View File

@@ -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]':

View File

@@ -22,11 +22,12 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
setClientAuthMode: setClientAuthMode,
sendRequest: sendRequest,
post: post,
get: get,
put: put,
patch: patch,
get: get,
delete: del,
head: head,
options: options,
uploadFile: uploadFile,
downloadFile: downloadFile,
ErrorCode: errorCodes,
@@ -172,10 +173,6 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
return publicInterface.sendRequest(url, { method: 'post', data: data, headers: headers }, success, failure);
};
function get(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure);
};
function put(url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'put', data: data, headers: headers }, success, failure);
}
@@ -184,6 +181,10 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
return publicInterface.sendRequest(url, { method: 'patch', data: data, headers: headers }, success, failure);
}
function get(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure);
};
function del(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'delete', params: params, headers: headers }, success, failure);
}
@@ -192,6 +193,10 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
return publicInterface.sendRequest(url, { method: 'head', params: params, headers: headers }, success, failure);
}
function options(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'options', params: params, headers: headers }, success, failure);
};
function uploadFile(url, params, headers, filePath, name, success, failure) {
return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure);
}