mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-02-11 00:00:06 +08:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d99c7bb154 | ||
|
|
336a56be1c | ||
|
|
8b962cc350 | ||
|
|
13bf4666b0 | ||
|
|
1fc3d6230c | ||
|
|
85346e0381 | ||
|
|
c9f8c8b66a | ||
|
|
9b26a0f031 | ||
|
|
298c031433 | ||
|
|
eab6acf85c | ||
|
|
8cf0d21a7a | ||
|
|
f91727e14a | ||
|
|
7b485507dc | ||
|
|
03b0abb74e | ||
|
|
c3d60c37bf | ||
|
|
54ef8ae169 | ||
|
|
c83940a8f6 | ||
|
|
ccabbf2a29 | ||
|
|
7ba0852698 | ||
|
|
4f3ff9097f | ||
|
|
620ce3f81c | ||
|
|
6e68bf4dfe | ||
|
|
87f0f3600c | ||
|
|
0bf39650e1 | ||
|
|
d444363a8e | ||
|
|
13976bbe43 | ||
|
|
70c8b9bb32 | ||
|
|
4b964e8b7c | ||
|
|
56f8b41f4f | ||
|
|
e1e720fe2d | ||
|
|
a1b6ea94f0 | ||
|
|
d977392a49 | ||
|
|
8d28f4ab80 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
node_modules/**
|
||||
test/app-template/www/certificates/*.cer
|
||||
test/e2e-app-template/www/certificates/*.cer
|
||||
test/e2e-app-template/www/certificates/*.pkcs
|
||||
tags
|
||||
.zedstate
|
||||
npm-debug.log
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,5 +1,35 @@
|
||||
# Changelog
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Feature #216: Support for response type `arraybuffer`
|
||||
- Feature #171: Support for response type `blob`
|
||||
- Feature #205: Add preference for configuring OKHTTP version (thanks RougeCiel)
|
||||
|
||||
## 2.0.11
|
||||
|
||||
- Fixed #221: headers not set on Android when request fails due to non-success status code
|
||||
|
||||
## 2.0.10
|
||||
|
||||
- Fixed #218: headers are used as params on browser platform
|
||||
|
||||
## 2.0.9
|
||||
|
||||
- Fixed #204: broken support for cordova-android < 7.0
|
||||
|
||||
- :warning: **Deprecation**: Deprecated "disableRedirect" in favor of "setFollowRedirect"
|
||||
|
||||
## 2.0.8
|
||||
|
||||
- Fixed #198: cookie header is always passed even if there is no cookie
|
||||
- Fixed #201: browser implementation is broken due to broken dependency
|
||||
- Fixed #197: iOS crashes when multiple request are done simultaneously (reverted a8e3637)
|
||||
- Fixed #189: error code mappings are not precise
|
||||
- Fixed #200: compatibility with Java 6 is broken due to string switch on Android
|
||||
|
||||
- :warning: **Deprecation**: Deprecated "setSSLCertMode" in favor of "setServerTrustMode"
|
||||
|
||||
## 2.0.7
|
||||
|
||||
- Fixed #195: URLs are double-encoded on Android
|
||||
|
||||
53
README.md
53
README.md
@@ -93,7 +93,7 @@ You can choose one of these:
|
||||
* `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")
|
||||
|
||||
You can also override the default content type headers by specifying your own headers (see [setHeader](#setHeader)).
|
||||
This defaults to `urlencoded`. You can also override the default content type headers by specifying your own headers (see [setHeader](#setHeader)).
|
||||
|
||||
__Caution__: `urlencoded` does not support serializing deep structures whereas `json` does.
|
||||
|
||||
@@ -104,6 +104,13 @@ Set how long to wait for a request to respond, in seconds.
|
||||
cordova.plugin.http.setRequestTimeout(5.0);
|
||||
```
|
||||
|
||||
### setFollowRedirect<a name="setFollowRedirect"></a>
|
||||
Configure if it should follow redirects automatically. This defaults to true.
|
||||
|
||||
```js
|
||||
cordova.plugin.setFollowRedirect(true);
|
||||
```
|
||||
|
||||
### getCookieString
|
||||
Returns saved cookies (as string) matching given URL.
|
||||
|
||||
@@ -128,13 +135,13 @@ cordova.plugin.http.clearCookies();
|
||||
## Asynchronous Functions
|
||||
These functions all take success and error callbacks as their last 2 arguments.
|
||||
|
||||
### setSSLCertMode<a name="setSSLCertMode"></a>
|
||||
Set SSL Cert handling mode, being one of the following values:
|
||||
### setServerTrustMode<a name="setServerTrustMode"></a>
|
||||
Set server trust mode, being one of the following values:
|
||||
|
||||
* `default`: default SSL cert handling using system's CA certs
|
||||
* `default`: default SSL trustship and hostname verification handling using system's CA certs
|
||||
* `legacy`: use legacy default behavior (< 2.0.3), excluding user installed CA certs (only for Android)
|
||||
* `nocheck`: disable SSL cert checking, trusting all certs (meant to be used only for testing purposes)
|
||||
* `pinned`: trust only provided certs
|
||||
* `nocheck`: disable SSL certificate checking and hostname verification, trusting all certs (meant to be used only for testing purposes)
|
||||
* `pinned`: trust only provided certificates
|
||||
|
||||
To use SSL pinning you must include at least one `.cer` SSL certificate in your app project. You can pin to your server certificate or to one of the issuing CA certificates. Include your certificate in the `www/certificates` folder. All `.cer` files found there will be loaded automatically.
|
||||
|
||||
@@ -142,46 +149,41 @@ To use SSL pinning you must include at least one `.cer` SSL certificate in your
|
||||
|
||||
```js
|
||||
// enable SSL pinning
|
||||
cordova.plugin.http.setSSLCertMode('pinned', function() {
|
||||
cordova.plugin.http.setServerTrustMode('pinned', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
|
||||
// use system's default CA certs
|
||||
cordova.plugin.http.setSSLCertMode('default', function() {
|
||||
cordova.plugin.http.setServerTrustMode('default', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
|
||||
// disable SSL cert checking, only meant for testing purposes, do NOT use in production!
|
||||
cordova.plugin.http.setSSLCertMode('nocheck', function() {
|
||||
cordova.plugin.http.setServerTrustMode('nocheck', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
```
|
||||
|
||||
### disableRedirect (deprecated)
|
||||
This function was deprecated in 2.0.9. Use ["setFollowRedirect"](#setFollowRedirect) instead.
|
||||
|
||||
### setSSLCertMode (deprecated)
|
||||
This function was deprecated in 2.0.8. Use ["setServerTrustMode"](#setServerTrustMode) instead.
|
||||
|
||||
### enableSSLPinning (obsolete)
|
||||
This function was removed in 2.0.0. Use ["setSSLCertMode"](#setSSLCertMode) to enable SSL pinning (mode "pinned").
|
||||
This function was removed in 2.0.0. Use ["setServerTrustMode"](#setServerTrustMode) to enable SSL pinning (mode "pinned").
|
||||
|
||||
### acceptAllCerts (obsolete)
|
||||
This function was removed in 2.0.0. Use ["setSSLCertMode"](#setSSLCertMode) to disable checking certs (mode "nocheck").
|
||||
|
||||
### disableRedirect
|
||||
If set to `true`, it won't follow redirects automatically. This defaults to false.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.disableRedirect(true, function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
```
|
||||
This function was removed in 2.0.0. Use ["setServerTrustMode"](#setServerTrustMode) to disable checking certs (mode "nocheck").
|
||||
|
||||
### validateDomainName (obsolete)
|
||||
This function was removed in v1.6.2. Domain name validation is disabled automatically when you set SSL cert mode to "nocheck".
|
||||
This function was removed in v1.6.2. Domain name validation is disabled automatically when you set server trust mode to "nocheck".
|
||||
|
||||
### removeCookies
|
||||
Remove all cookies associated with a given URL.
|
||||
@@ -200,7 +202,12 @@ The options object contains following keys:
|
||||
* `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
|
||||
* `responseType`: expected response type, defaults to `text`, needs to be one of the following values:
|
||||
* `text`: data is returned as decoded string, use this for all kinds of string responses (e.g. JSON, XML, HTML, plain text, etc.)
|
||||
* `arraybuffer`: data is returned as [ArrayBuffer instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
|
||||
* `blob`: data is returned as [Blob instance](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
|
||||
* `timeout`: timeout value for the request in seconds, defaults to global timeout value
|
||||
* `followRedirect`: enable or disable automatically following redirects
|
||||
* `headers`: headers object (key value pair), will be merged with global values
|
||||
* `filePath`: filePath to be used during upload and download see [uploadFile](#uploadFile) and [downloadFile](#downloadFile) for detailed information
|
||||
* `name`: name to be used during upload see [uploadFile](#uploadFile) for detailed information
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "2.0.7",
|
||||
"version": "2.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "2.0.7",
|
||||
"version": "2.1.0",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"updatecert": "node ./scripts/update-test-cert.js",
|
||||
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
"buildbrowser": "./scripts/build-test-app.sh --browser",
|
||||
"testandroid": "npm run updatecert && ./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
|
||||
"testios": "npm run updatecert && ./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator",
|
||||
"testapp": "npm run testandroid && npm run testios",
|
||||
"testjs": "mocha ./test/js-mocha-specs.js",
|
||||
"testjs": "mocha ./test/js-specs.js",
|
||||
"test": "npm run testjs && npm run testapp",
|
||||
"release": "npm run test && ./scripts/release.sh"
|
||||
},
|
||||
|
||||
14
plugin.xml
14
plugin.xml
@@ -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.0.7">
|
||||
<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.1.0">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
@@ -11,6 +11,7 @@
|
||||
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
|
||||
<js-module src="www/global-configs.js" name="global-configs"/>
|
||||
<js-module src="www/helpers.js" name="helpers"/>
|
||||
<js-module src="www/js-util.js" name="js-util"/>
|
||||
<js-module src="www/local-storage-store.js" name="local-storage-store"/>
|
||||
<js-module src="www/lodash.js" name="lodash"/>
|
||||
<js-module src="www/messages.js" name="messages"/>
|
||||
@@ -27,6 +28,7 @@
|
||||
</feature>
|
||||
</config-file>
|
||||
<header-file src="src/ios/CordovaHttpPlugin.h"/>
|
||||
<header-file src="src/ios/BinaryResponseSerializer.h"/>
|
||||
<header-file src="src/ios/TextResponseSerializer.h"/>
|
||||
<header-file src="src/ios/TextRequestSerializer.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFHTTPSessionManager.h"/>
|
||||
@@ -38,6 +40,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/BinaryResponseSerializer.m"/>
|
||||
<source-file src="src/ios/TextResponseSerializer.m"/>
|
||||
<source-file src="src/ios/TextRequestSerializer.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFHTTPSessionManager.m"/>
|
||||
@@ -59,20 +62,23 @@
|
||||
<config-file target="AndroidManifest.xml" parent="/manifest">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</config-file>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaClientAuth.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpBase.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/http/HostnameVerifierFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaServerTrust.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/http/HttpBodyDecoder.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/HttpRequest.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/JsonUtils.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/KeyChainKeyManager.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/OkConnectionFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSConfiguration.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TrustManagersFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<framework src="com.squareup.okhttp3:okhttp-urlconnection:3.10.0"/>
|
||||
<preference name="OKHTTP_VERSION" default="3.10.0"/>
|
||||
<framework src="com.squareup.okhttp3:okhttp-urlconnection:$OKHTTP_VERSION"/>
|
||||
</platform>
|
||||
<platform name="browser">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
|
||||
@@ -35,10 +35,11 @@ while :; do
|
||||
shift
|
||||
done
|
||||
|
||||
printf 'Building test app for %s\n' $PLATFORM
|
||||
rm -rf $ROOT/temp
|
||||
mkdir $ROOT/temp
|
||||
cp -r $ROOT/test/app-template/. $ROOT/temp/
|
||||
cp $ROOT/test/app-test-definitions.js $ROOT/temp/www/
|
||||
cp -r $ROOT/test/e2e-app-template/. $ROOT/temp/
|
||||
cp $ROOT/test/e2e-specs.js $ROOT/temp/www/
|
||||
rsync -ax --exclude node_modules --exclude scripts --exclude temp --exclude test $ROOT/. $WORKINGCOPY
|
||||
cd $ROOT/temp
|
||||
$CDV prepare
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
while getopts a:b: option; do
|
||||
case "${option}" in
|
||||
a) API_LEVEL=${OPTARG};;
|
||||
b) BUILD_TOOLS_VERSION=${OPTARG};;
|
||||
esac
|
||||
done
|
||||
|
||||
curl http://dl.google.com/android/android-sdk_r24.4-macosx.zip -o android-sdk-macosx.zip
|
||||
tar -xvf android-sdk-macosx.zip
|
||||
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter platform-tools
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter build-tools-${BUILD_TOOLS_VERSION}
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter android-${API_LEVEL}
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-support
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-m2repository
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-google-m2repository
|
||||
@@ -8,6 +8,7 @@ if [ $CI == "true" ] && ([ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]); th
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
printf 'Running e2e tests\n'
|
||||
pushd $ROOT
|
||||
./node_modules/.bin/mocha ./test/app-mocha-specs/test.js "$@"
|
||||
./node_modules/.bin/mocha ./test/e2e-tooling/test.js "$@"
|
||||
popd
|
||||
|
||||
29
scripts/update-e2e-client-cert.js
Normal file
29
scripts/update-e2e-client-cert.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const path = require('path');
|
||||
|
||||
const SOURCE_URL = 'https://badssl.com/certs/badssl.com-client.p12';
|
||||
const TARGET_PATH = path.join(__dirname, '../test/e2e-app-template/www/certificates/badssl-client-cert.pkcs');
|
||||
|
||||
const downloadPkcsContainer = (source, target) => new Promise((resolve, reject) => {
|
||||
const file = fs.createWriteStream(target);
|
||||
|
||||
const req = https.get(source, response => {
|
||||
response.pipe(file)
|
||||
resolve(target);
|
||||
});
|
||||
|
||||
req.on('error', error => {
|
||||
return reject(error)
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
console.log(`Updating client certificate from ${SOURCE_URL}`);
|
||||
|
||||
downloadPkcsContainer(SOURCE_URL, TARGET_PATH)
|
||||
.catch(error => {
|
||||
console.error(`Updating client certificate failed: ${error}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -3,7 +3,7 @@ const https = require('https');
|
||||
const path = require('path');
|
||||
|
||||
const SOURCE_HOST = 'httpbin.org';
|
||||
const TARGET_PATH = path.join(__dirname, '../test/app-template/www/certificates/httpbin.org.cer');
|
||||
const TARGET_PATH = path.join(__dirname, '../test/e2e-app-template/www/certificates/httpbin.org.cer');
|
||||
|
||||
const getCert = hostname => new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
@@ -30,13 +30,11 @@ const getCert = hostname => new Promise((resolve, reject) => {
|
||||
req.end();
|
||||
});
|
||||
|
||||
console.log(`Updating test certificate from ${SOURCE_HOST}`);
|
||||
console.log(`Updating server certificate from ${SOURCE_HOST}`);
|
||||
|
||||
getCert(SOURCE_HOST)
|
||||
.then(cert => {
|
||||
fs.writeFileSync(TARGET_PATH, cert.raw);
|
||||
})
|
||||
.then(cert => fs.writeFileSync(TARGET_PATH, cert.raw))
|
||||
.catch(error => {
|
||||
console.error(`Updating test cert failed: ${error}`);
|
||||
console.error(`Updating server certificate failed: ${error}`);
|
||||
process.exit(1);
|
||||
});
|
||||
113
src/android/com/silkimen/cordovahttp/CordovaClientAuth.java
Normal file
113
src/android/com/silkimen/cordovahttp/CordovaClientAuth.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyChainAliasCallback;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import com.silkimen.http.KeyChainKeyManager;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
class CordovaClientAuth implements Runnable, KeyChainAliasCallback {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private String mode;
|
||||
private String aliasString;
|
||||
private byte[] rawPkcs;
|
||||
private String pkcsPassword;
|
||||
private Activity activity;
|
||||
private Context context;
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
public CordovaClientAuth(final String mode, final String aliasString, final byte[] rawPkcs,
|
||||
final String pkcsPassword, final Activity activity, final Context context, final TLSConfiguration configContainer,
|
||||
final CallbackContext callbackContext) {
|
||||
|
||||
this.mode = mode;
|
||||
this.aliasString = aliasString;
|
||||
this.rawPkcs = rawPkcs;
|
||||
this.pkcsPassword = pkcsPassword;
|
||||
this.activity = activity;
|
||||
this.tlsConfiguration = configContainer;
|
||||
this.context = context;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ("systemstore".equals(this.mode)) {
|
||||
this.loadFromSystemStore();
|
||||
} else if ("buffer".equals(this.mode)) {
|
||||
this.loadFromBuffer();
|
||||
} else {
|
||||
this.disableClientAuth();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromSystemStore() {
|
||||
if (this.aliasString == null) {
|
||||
KeyChain.choosePrivateKeyAlias(this.activity, this, null, null, null, -1, null);
|
||||
} else {
|
||||
this.alias(this.aliasString);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromBuffer() {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(this.rawPkcs);
|
||||
|
||||
keyStore.load(stream, this.pkcsPassword.toCharArray());
|
||||
keyManagerFactory.init(keyStore, this.pkcsPassword.toCharArray());
|
||||
|
||||
this.tlsConfiguration.setKeyManagers(keyManagerFactory.getKeyManagers());
|
||||
this.callbackContext.success();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Couldn't load given PKCS12 container for authentication", e);
|
||||
this.callbackContext.error("Couldn't load given PKCS12 container for authentication");
|
||||
}
|
||||
}
|
||||
|
||||
private void disableClientAuth() {
|
||||
this.tlsConfiguration.setKeyManagers(null);
|
||||
this.callbackContext.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alias(final String alias) {
|
||||
try {
|
||||
if (alias == null) {
|
||||
throw new Exception("Couldn't get a consent for private key access");
|
||||
}
|
||||
|
||||
PrivateKey key = KeyChain.getPrivateKey(this.context, alias);
|
||||
X509Certificate[] chain = KeyChain.getCertificateChain(this.context, alias);
|
||||
KeyManager keyManager = new KeyChainKeyManager(alias, key, chain);
|
||||
|
||||
this.tlsConfiguration.setKeyManagers(new KeyManager[] { keyManager });
|
||||
|
||||
this.callbackContext.success(alias);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Couldn't load private key and certificate pair with given alias \"" + alias + "\" for authentication",
|
||||
e);
|
||||
this.callbackContext.error(
|
||||
"Couldn't load private key and certificate pair with given alias \"" + alias + "\" for authentication");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
@@ -7,15 +8,14 @@ import java.net.UnknownHostException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import com.silkimen.http.HttpBodyDecoder;
|
||||
import com.silkimen.http.HttpRequest;
|
||||
import com.silkimen.http.HttpRequest.HttpRequestException;
|
||||
import com.silkimen.http.JsonUtils;
|
||||
import com.silkimen.http.OkConnectionFactory;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
@@ -30,16 +30,16 @@ 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;
|
||||
protected boolean followRedirects;
|
||||
protected SSLSocketFactory customSSLSocketFactory;
|
||||
protected HostnameVerifier customHostnameVerifier;
|
||||
protected TLSConfiguration tlsConfiguration;
|
||||
protected CallbackContext callbackContext;
|
||||
|
||||
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout,
|
||||
boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
|
||||
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
@@ -49,22 +49,21 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
this.headers = headers;
|
||||
this.timeout = timeout;
|
||||
this.followRedirects = followRedirects;
|
||||
this.customSSLSocketFactory = customSSLSocketFactory;
|
||||
this.customHostnameVerifier = customHostnameVerifier;
|
||||
this.responseType = responseType;
|
||||
this.tlsConfiguration = tlsConfiguration;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
public CordovaHttpBase(String method, String url, JSONObject headers, int timeout,
|
||||
boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
|
||||
CallbackContext callbackContext) {
|
||||
public CordovaHttpBase(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
|
||||
String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
this.headers = headers;
|
||||
this.timeout = timeout;
|
||||
this.followRedirects = followRedirects;
|
||||
this.customSSLSocketFactory = customSSLSocketFactory;
|
||||
this.customHostnameVerifier = customHostnameVerifier;
|
||||
this.responseType = responseType;
|
||||
this.tlsConfiguration = tlsConfiguration;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
@@ -78,10 +77,10 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
this.sendBody(request);
|
||||
this.processResponse(request, response);
|
||||
} catch (HttpRequestException e) {
|
||||
if (e.getCause() instanceof SSLHandshakeException) {
|
||||
if (e.getCause() instanceof SSLException) {
|
||||
response.setStatus(-2);
|
||||
response.setErrorMessage("SSL handshake failed: " + e.getMessage());
|
||||
Log.w(TAG, "SSL handshake failed", e);
|
||||
response.setErrorMessage("TLS connection could not be established: " + e.getMessage());
|
||||
Log.w(TAG, "TLS connection could not be established", e);
|
||||
} else if (e.getCause() instanceof UnknownHostException) {
|
||||
response.setStatus(-3);
|
||||
response.setErrorMessage("Host could not be resolved: " + e.getMessage());
|
||||
@@ -116,20 +115,18 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
return new HttpRequest(this.url, this.method);
|
||||
}
|
||||
|
||||
protected void prepareRequest(HttpRequest request) throws JSONException {
|
||||
protected void prepareRequest(HttpRequest request) throws JSONException, IOException {
|
||||
request.followRedirects(this.followRedirects);
|
||||
request.readTimeout(this.timeout);
|
||||
request.acceptCharset("UTF-8");
|
||||
request.uncompress(true);
|
||||
request.setConnectionFactory(new OkConnectionFactory());
|
||||
|
||||
if (this.customHostnameVerifier != null) {
|
||||
request.setHostnameVerifier(this.customHostnameVerifier);
|
||||
if (this.tlsConfiguration.getHostnameVerifier() != null) {
|
||||
request.setHostnameVerifier(this.tlsConfiguration.getHostnameVerifier());
|
||||
}
|
||||
|
||||
if (this.customSSLSocketFactory != null) {
|
||||
request.setSSLSocketFactory(this.customSSLSocketFactory);
|
||||
}
|
||||
request.setSSLSocketFactory(this.tlsConfiguration.getTLSSocketFactory());
|
||||
|
||||
// setup content type before applying headers, so user can override it
|
||||
this.setContentType(request);
|
||||
@@ -138,16 +135,12 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
}
|
||||
|
||||
protected void setContentType(HttpRequest request) {
|
||||
switch (this.serializer) {
|
||||
case "json":
|
||||
if ("json".equals(this.serializer)) {
|
||||
request.contentType("application/json", "UTF-8");
|
||||
break;
|
||||
case "utf8":
|
||||
} else if ("utf8".equals(this.serializer)) {
|
||||
request.contentType("text/plain", "UTF-8");
|
||||
break;
|
||||
case "urlencoded":
|
||||
} else if ("urlencoded".equals(this.serializer)) {
|
||||
// intentionally left blank, because content type is set in HttpRequest.form()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,16 +149,12 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.serializer) {
|
||||
case "json":
|
||||
if ("json".equals(this.serializer)) {
|
||||
request.send(this.data.toString());
|
||||
break;
|
||||
case "utf8":
|
||||
} else if ("utf8".equals(this.serializer)) {
|
||||
request.send(((JSONObject) this.data).getString("text"));
|
||||
break;
|
||||
case "urlencoded":
|
||||
} else if ("urlencoded".equals(this.serializer)) {
|
||||
request.form(JsonUtils.getObjectMap((JSONObject) this.data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.silkimen.http.HttpRequest;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.file.FileUtils;
|
||||
@@ -15,12 +16,10 @@ import org.json.JSONObject;
|
||||
class CordovaHttpDownload extends CordovaHttpBase {
|
||||
private String filePath;
|
||||
|
||||
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout,
|
||||
boolean followRedirects, SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
|
||||
CallbackContext callbackContext) {
|
||||
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout, boolean followRedirects,
|
||||
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
|
||||
super("GET", url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
|
||||
callbackContext);
|
||||
super("GET", url, headers, timeout, followRedirects, "text", tlsConfiguration, callbackContext);
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,23 +3,23 @@ package com.silkimen.cordovahttp;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpOperation extends CordovaHttpBase {
|
||||
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
|
||||
int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory,
|
||||
HostnameVerifier customHostnameVerifier, CallbackContext callbackContext) {
|
||||
int timeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CallbackContext callbackContext) {
|
||||
|
||||
super(method, url, serializer, data, headers, timeout, followRedirects, customSSLSocketFactory,
|
||||
customHostnameVerifier, callbackContext);
|
||||
super(method, url, serializer, data, headers, timeout, followRedirects, responseType, tlsConfiguration,
|
||||
callbackContext);
|
||||
}
|
||||
|
||||
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
|
||||
SSLSocketFactory customSSLSocketFactory, HostnameVerifier customHostnameVerifier,
|
||||
CallbackContext callbackContext) {
|
||||
String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
|
||||
super(method, url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
|
||||
callbackContext);
|
||||
super(method, url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,8 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStore.TrustedCertificateEntry;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import com.silkimen.http.HostnameVerifierFactory;
|
||||
import com.silkimen.http.TLSSocketFactory;
|
||||
import com.silkimen.http.TrustManagersFactory;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
@@ -31,26 +12,32 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Log;
|
||||
import android.util.Base64;
|
||||
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private final TrustManagersFactory trustManagersFactory = new TrustManagersFactory();
|
||||
private final HostnameVerifierFactory hostnameVerifierFactory = new HostnameVerifierFactory();
|
||||
|
||||
private boolean followRedirects = true;
|
||||
private SSLSocketFactory customSSLSocketFactory;
|
||||
private HostnameVerifier customHostnameVerifier;
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
|
||||
this.tlsConfiguration = new TLSConfiguration();
|
||||
|
||||
try {
|
||||
this.customSSLSocketFactory = this.createSocketFactory(
|
||||
this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
|
||||
KeyStore store = KeyStore.getInstance("AndroidCAStore");
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
|
||||
store.load(null);
|
||||
tmf.init(store);
|
||||
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while loading system's CA certificates", e);
|
||||
}
|
||||
@@ -64,28 +51,27 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case "get":
|
||||
if ("get".equals(action)) {
|
||||
return this.executeHttpRequestWithoutData(action, args, callbackContext);
|
||||
case "post":
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
case "put":
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
case "patch":
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
case "head":
|
||||
} else if ("head".equals(action)) {
|
||||
return this.executeHttpRequestWithoutData(action, args, callbackContext);
|
||||
case "delete":
|
||||
} else if ("delete".equals(action)) {
|
||||
return this.executeHttpRequestWithoutData(action, args, callbackContext);
|
||||
case "uploadFile":
|
||||
} else if ("post".equals(action)) {
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
} else if ("put".equals(action)) {
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
} else if ("patch".equals(action)) {
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
} else if ("uploadFile".equals(action)) {
|
||||
return this.uploadFile(args, callbackContext);
|
||||
case "downloadFile":
|
||||
} else if ("downloadFile".equals(action)) {
|
||||
return this.downloadFile(args, callbackContext);
|
||||
case "setSSLCertMode":
|
||||
return this.setSSLCertMode(args, callbackContext);
|
||||
case "disableRedirect":
|
||||
return this.disableRedirect(args, callbackContext);
|
||||
default:
|
||||
} else if ("setServerTrustMode".equals(action)) {
|
||||
return this.setServerTrustMode(args, callbackContext);
|
||||
} else if ("setClientAuthMode".equals(action)) {
|
||||
return this.setClientAuthMode(args, callbackContext);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -96,9 +82,11 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
String url = args.getString(0);
|
||||
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,
|
||||
this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(request);
|
||||
|
||||
@@ -113,9 +101,11 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
String serializer = args.getString(2);
|
||||
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, this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
|
||||
timeout, followRedirect, responseType, this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(request);
|
||||
|
||||
@@ -128,9 +118,11 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
String filePath = args.getString(2);
|
||||
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,
|
||||
this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
|
||||
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePath, uploadName, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(upload);
|
||||
|
||||
@@ -142,107 +134,34 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
JSONObject headers = args.getJSONObject(1);
|
||||
String filePath = args.getString(2);
|
||||
int timeout = args.getInt(3) * 1000;
|
||||
boolean followRedirect = args.getBoolean(4);
|
||||
|
||||
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout,
|
||||
this.followRedirects, this.customSSLSocketFactory, this.customHostnameVerifier, callbackContext);
|
||||
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout, followRedirect,
|
||||
this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(download);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setSSLCertMode(final JSONArray args, final CallbackContext callbackContext) {
|
||||
try {
|
||||
switch (args.getString(0)) {
|
||||
case "legacy":
|
||||
this.customHostnameVerifier = null;
|
||||
this.customSSLSocketFactory = null;
|
||||
break;
|
||||
case "nocheck":
|
||||
this.customHostnameVerifier = this.hostnameVerifierFactory.getNoOpVerifier();
|
||||
this.customSSLSocketFactory = this.createSocketFactory(this.trustManagersFactory.getNoopTrustManagers());
|
||||
break;
|
||||
case "pinned":
|
||||
this.customHostnameVerifier = null;
|
||||
this.customSSLSocketFactory = this.createSocketFactory(
|
||||
this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromBundle("www/certificates")));
|
||||
break;
|
||||
default:
|
||||
this.customHostnameVerifier = null;
|
||||
this.customSSLSocketFactory = this.createSocketFactory(
|
||||
this.trustManagersFactory.getPinnedTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
|
||||
break;
|
||||
}
|
||||
private boolean setServerTrustMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
CordovaServerTrust runnable = new CordovaServerTrust(args.getString(0), this.cordova.getActivity(),
|
||||
this.tlsConfiguration, callbackContext);
|
||||
|
||||
callbackContext.success();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while configuring SSL cert mode", e);
|
||||
callbackContext.error("An error occured while configuring SSL cert mode");
|
||||
}
|
||||
cordova.getThreadPool().execute(runnable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean disableRedirect(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
this.followRedirects = !args.getBoolean(0);
|
||||
private boolean setClientAuthMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
byte[] pkcs = args.isNull(2) ? null : Base64.decode(args.getString(2), Base64.DEFAULT);
|
||||
|
||||
callbackContext.success();
|
||||
CordovaClientAuth runnable = new CordovaClientAuth(args.getString(0), args.isNull(1) ? null : args.getString(1),
|
||||
pkcs, args.getString(3), this.cordova.getActivity(), this.cordova.getActivity().getApplicationContext(),
|
||||
this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(runnable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ArrayList<Certificate> getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
|
||||
ArrayList<Certificate> certList = new ArrayList<Certificate>();
|
||||
KeyStore keyStore = KeyStore.getInstance(storeType);
|
||||
keyStore.load(null);
|
||||
|
||||
Enumeration<String> aliases = keyStore.aliases();
|
||||
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = aliases.nextElement();
|
||||
TrustedCertificateEntry certEntry = (TrustedCertificateEntry) keyStore.getEntry(alias, null);
|
||||
Certificate cert = certEntry.getTrustedCertificate();
|
||||
certList.add(cert);
|
||||
}
|
||||
|
||||
return certList;
|
||||
}
|
||||
|
||||
private ArrayList<Certificate> getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
String[] files = assetManager.list(path);
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
ArrayList<Certificate> certList = new ArrayList<Certificate>();
|
||||
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
int index = files[i].lastIndexOf('.');
|
||||
|
||||
if (index == -1 || !files[i].substring(index).equals(".cer")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
certList.add(cf.generateCertificate(assetManager.open(path + "/" + files[i])));
|
||||
}
|
||||
|
||||
return certList;
|
||||
}
|
||||
|
||||
private SSLSocketFactory createSocketFactory(TrustManager[] trustManagers) throws IOException {
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
|
||||
/* @TODO implement custom KeyManager */
|
||||
context.init(null, trustManagers, new SecureRandom());
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 20) {
|
||||
return new TLSSocketFactory(context);
|
||||
} else {
|
||||
return context.getSocketFactory();
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
IOException ioException = new IOException("Security exception occured while configuring SSL context");
|
||||
ioException.initCause(e);
|
||||
throw ioException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, List<String>> 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;
|
||||
@@ -56,13 +66,17 @@ class CordovaHttpResponse {
|
||||
json.put("status", this.status);
|
||||
json.put("url", this.url);
|
||||
|
||||
if (this.headers != null && !this.headers.isEmpty()) {
|
||||
json.put("headers", new JSONObject(getFilteredHeaders()));
|
||||
}
|
||||
|
||||
if (this.hasFailed) {
|
||||
json.put("error", this.error);
|
||||
} else if (this.isFileOperation) {
|
||||
json.put("headers", new JSONObject(getFilteredHeaders()));
|
||||
json.put("file", this.fileEntry);
|
||||
} else if (this.isRawResponse) {
|
||||
json.put("data", Base64.encodeToString(this.rawData, Base64.DEFAULT));
|
||||
} else {
|
||||
json.put("headers", new JSONObject(getFilteredHeaders()));
|
||||
json.put("data", this.body);
|
||||
}
|
||||
|
||||
@@ -72,10 +86,6 @@ class CordovaHttpResponse {
|
||||
private Map<String, String> getFilteredHeaders() throws JSONException {
|
||||
Map<String, String> filteredHeaders = new HashMap<String, String>();
|
||||
|
||||
if (this.headers == null || this.headers.isEmpty()) {
|
||||
return filteredHeaders;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
List<String> value = entry.getValue();
|
||||
|
||||
@@ -10,6 +10,8 @@ import java.net.URI;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -17,12 +19,11 @@ class CordovaHttpUpload extends CordovaHttpBase {
|
||||
private String filePath;
|
||||
private String uploadName;
|
||||
|
||||
public CordovaHttpUpload(String url, JSONObject headers, String filePath, String uploadName,
|
||||
int timeout, boolean followRedirects, SSLSocketFactory customSSLSocketFactory,
|
||||
HostnameVerifier customHostnameVerifier, CallbackContext callbackContext) {
|
||||
public CordovaHttpUpload(String url, JSONObject headers, String filePath, String uploadName, int timeout,
|
||||
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CallbackContext callbackContext) {
|
||||
|
||||
super("POST", url, headers, timeout, followRedirects, customSSLSocketFactory, customHostnameVerifier,
|
||||
callbackContext);
|
||||
super("POST", url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
|
||||
this.filePath = filePath;
|
||||
this.uploadName = uploadName;
|
||||
}
|
||||
|
||||
124
src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
Normal file
124
src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
class CordovaServerTrust implements Runnable {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private final TrustManager[] noOpTrustManagers;
|
||||
private final HostnameVerifier noOpVerifier;
|
||||
|
||||
private String mode;
|
||||
private Activity activity;
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
public CordovaServerTrust(final String mode, final Activity activity, final TLSConfiguration configContainer,
|
||||
final CallbackContext callbackContext) {
|
||||
|
||||
this.mode = mode;
|
||||
this.activity = activity;
|
||||
this.tlsConfiguration = configContainer;
|
||||
this.callbackContext = callbackContext;
|
||||
|
||||
this.noOpTrustManagers = new TrustManager[] { new X509TrustManager() {
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
// intentionally left blank
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
// intentionally left blank
|
||||
}
|
||||
} };
|
||||
|
||||
this.noOpVerifier = new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if ("legacy".equals(this.mode)) {
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(null);
|
||||
} else if ("nocheck".equals(this.mode)) {
|
||||
this.tlsConfiguration.setHostnameVerifier(this.noOpVerifier);
|
||||
this.tlsConfiguration.setTrustManagers(this.noOpTrustManagers);
|
||||
} else if ("pinned".equals(this.mode)) {
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromBundle("www/certificates")));
|
||||
} else {
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while configuring SSL cert mode", e);
|
||||
callbackContext.error("An error occured while configuring SSL cert mode");
|
||||
}
|
||||
}
|
||||
|
||||
private TrustManager[] getTrustManagers(KeyStore store) throws GeneralSecurityException {
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
tmf.init(store);
|
||||
|
||||
return tmf.getTrustManagers();
|
||||
}
|
||||
|
||||
private KeyStore getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
|
||||
AssetManager assetManager = this.activity.getAssets();
|
||||
String[] files = assetManager.list(path);
|
||||
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
|
||||
keyStore.load(null, null);
|
||||
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
int index = files[i].lastIndexOf('.');
|
||||
|
||||
if (index == -1 || !files[i].substring(index).equals(".cer")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
keyStore.setCertificateEntry("CA" + i, cf.generateCertificate(assetManager.open(path + "/" + files[i])));
|
||||
}
|
||||
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private KeyStore getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
|
||||
KeyStore store = KeyStore.getInstance(storeType);
|
||||
store.load(null);
|
||||
|
||||
return store;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
public class HostnameVerifierFactory {
|
||||
private final HostnameVerifier noOpVerifier;
|
||||
|
||||
public HostnameVerifierFactory() {
|
||||
this.noOpVerifier = new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public HostnameVerifier getNoOpVerifier() {
|
||||
return this.noOpVerifier;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
57
src/android/com/silkimen/http/KeyChainKeyManager.java
Normal file
57
src/android/com/silkimen/http/KeyChainKeyManager.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import android.content.Context;
|
||||
import android.security.KeyChain;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
|
||||
public class KeyChainKeyManager extends X509ExtendedKeyManager {
|
||||
private final String alias;
|
||||
private final X509Certificate[] chain;
|
||||
private final PrivateKey key;
|
||||
|
||||
public KeyChainKeyManager(String alias, PrivateKey key, X509Certificate[] chain) {
|
||||
this.alias = alias;
|
||||
this.key = key;
|
||||
this.chain = chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
|
||||
return this.alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getCertificateChain(String alias) {
|
||||
return chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey(String alias) {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
|
||||
// not a client SSLSocket callback
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String[] getClientAliases(String keyType, Principal[] issuers) {
|
||||
// not a client SSLSocket callback
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String[] getServerAliases(String keyType, Principal[] issuers) {
|
||||
// not a client SSLSocket callback
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
63
src/android/com/silkimen/http/TLSConfiguration.java
Normal file
63
src/android/com/silkimen/http/TLSConfiguration.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import com.silkimen.http.TLSSocketFactory;
|
||||
|
||||
public class TLSConfiguration {
|
||||
private TrustManager[] trustManagers;
|
||||
private KeyManager[] keyManagers;
|
||||
private HostnameVerifier hostnameVerifier;
|
||||
|
||||
private SSLSocketFactory socketFactory;
|
||||
|
||||
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
}
|
||||
|
||||
public void setKeyManagers(KeyManager[] keyManagers) {
|
||||
this.keyManagers = keyManagers;
|
||||
this.socketFactory = null;
|
||||
}
|
||||
|
||||
public void setTrustManagers(TrustManager[] trustManagers) {
|
||||
this.trustManagers = trustManagers;
|
||||
this.socketFactory = null;
|
||||
}
|
||||
|
||||
public HostnameVerifier getHostnameVerifier() {
|
||||
return this.hostnameVerifier;
|
||||
}
|
||||
|
||||
public SSLSocketFactory getTLSSocketFactory() throws IOException {
|
||||
if (this.socketFactory != null) {
|
||||
return this.socketFactory;
|
||||
}
|
||||
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
|
||||
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 20) {
|
||||
this.socketFactory = new TLSSocketFactory(context);
|
||||
} else {
|
||||
this.socketFactory = context.getSocketFactory();
|
||||
}
|
||||
|
||||
return this.socketFactory;
|
||||
} catch (GeneralSecurityException e) {
|
||||
IOException ioException = new IOException("Security exception occured while configuring TLS context");
|
||||
ioException.initCause(e);
|
||||
throw ioException;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,8 @@ public class TLSSocketFactory extends SSLSocketFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
|
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(socket, host, port, autoClose));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
public class TrustManagersFactory {
|
||||
private final TrustManager[] noOpTrustManager;
|
||||
|
||||
public TrustManagersFactory() {
|
||||
this.noOpTrustManager = new TrustManager[] { new X509TrustManager() {
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
// intentionally left blank
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
// intentionally left blank
|
||||
}
|
||||
} };
|
||||
}
|
||||
|
||||
public TrustManager[] getNoopTrustManagers() {
|
||||
return this.noOpTrustManager;
|
||||
}
|
||||
|
||||
public TrustManager[] getPinnedTrustManagers(ArrayList<Certificate> pinnedCerts) throws IOException {
|
||||
if (pinnedCerts == null || pinnedCerts.size() == 0) {
|
||||
throw new IOException("You must add at least 1 certificate in order to pin to certificates");
|
||||
}
|
||||
|
||||
try {
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
keyStore.load(null, null);
|
||||
|
||||
for (int i = 0; i < pinnedCerts.size(); i++) {
|
||||
keyStore.setCertificateEntry("CA" + i, pinnedCerts.get(i));
|
||||
}
|
||||
|
||||
// Create a TrustManager that trusts the CAs in our KeyStore
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
tmf.init(keyStore);
|
||||
|
||||
return tmf.getTrustManagers();
|
||||
} catch (GeneralSecurityException e) {
|
||||
IOException ioException = new IOException("Security exception configuring SSL trust managers");
|
||||
ioException.initCause(e);
|
||||
|
||||
throw ioException;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/browser/cordova-http-plugin.js
vendored
62
src/browser/cordova-http-plugin.js
vendored
@@ -1,7 +1,7 @@
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
|
||||
var cordovaProxy = require('cordova/exec/proxy');
|
||||
var helpers = require(pluginId + '.helpers');
|
||||
var jsUtil = require(pluginId + '.js-util');
|
||||
|
||||
function serializeJsonData(data) {
|
||||
try {
|
||||
@@ -29,7 +29,7 @@ function serializeParams(params) {
|
||||
if (params === null) return '';
|
||||
|
||||
return Object.keys(params).map(function(key) {
|
||||
if (helpers.getTypeOf(params[key]) === 'Array') {
|
||||
if (jsUtil.getTypeOf(params[key]) === 'Array') {
|
||||
return serializeArray(key, params[key]);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ function createXhrSuccessObject(xhr) {
|
||||
return {
|
||||
url: xhr.responseURL,
|
||||
status: xhr.status,
|
||||
data: helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response,
|
||||
data: jsUtil.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response,
|
||||
headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
|
||||
};
|
||||
}
|
||||
@@ -65,7 +65,7 @@ function createXhrFailureObject(xhr) {
|
||||
var obj = {};
|
||||
|
||||
obj.headers = xhr.getAllResponseHeaders();
|
||||
obj.error = helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response;
|
||||
obj.error = jsUtil.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response;
|
||||
obj.error = obj.error || 'advanced-http: please check browser console for error messages';
|
||||
|
||||
if (xhr.responseURL) obj.url = xhr.responseURL;
|
||||
@@ -101,12 +101,23 @@ function setHeaders(xhr, headers) {
|
||||
}
|
||||
|
||||
function sendRequest(method, withData, opts, success, failure) {
|
||||
var data = withData ? opts[1] : null;
|
||||
var params = withData ? null : serializeParams(opts[1]);
|
||||
var serializer = withData ? opts[2] : null;
|
||||
var headers = withData ? opts[3] : opts[2];
|
||||
var timeout = withData ? opts[4] : opts[3];
|
||||
var url = params ? opts[0] + '?' + params : opts[0];
|
||||
var data, serializer, headers, timeout, followRedirect, responseType;
|
||||
var url = opts[0];
|
||||
|
||||
if (withData) {
|
||||
data = opts[1];
|
||||
serializer = opts[2];
|
||||
headers = opts[3];
|
||||
timeout = opts[4];
|
||||
followRedirect = opts[5];
|
||||
responseType = opts[6];
|
||||
} else {
|
||||
headers = opts[1];
|
||||
timeout = opts[2];
|
||||
followRedirect = opts[3];
|
||||
responseType = opts[4];
|
||||
|
||||
}
|
||||
|
||||
var processedData = null;
|
||||
var xhr = new XMLHttpRequest();
|
||||
@@ -117,6 +128,10 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
return failure('advanced-http: custom cookies not supported on browser platform');
|
||||
}
|
||||
|
||||
if (!followRedirect) {
|
||||
return failure('advanced-http: disabling follow redirect not supported on browser platform');
|
||||
}
|
||||
|
||||
switch (serializer) {
|
||||
case 'json':
|
||||
setDefaultContentType(headers, 'application/json; charset=utf8');
|
||||
@@ -140,6 +155,7 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
}
|
||||
|
||||
xhr.timeout = timeout * 1000;
|
||||
xhr.responseType = responseType;
|
||||
setHeaders(xhr, headers);
|
||||
|
||||
xhr.onerror = xhr.ontimeout = function () {
|
||||
@@ -160,35 +176,35 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
}
|
||||
|
||||
var browserInterface = {
|
||||
post: function (success, failure, opts) {
|
||||
return sendRequest('post', true, opts, success, failure);
|
||||
},
|
||||
get: function (success, failure, opts) {
|
||||
return sendRequest('get', false, opts, success, failure);
|
||||
},
|
||||
head: function (success, failure, opts) {
|
||||
return sendRequest('head', false, opts, success, failure);
|
||||
},
|
||||
delete: function (success, failure, opts) {
|
||||
return sendRequest('delete', false, opts, success, failure);
|
||||
},
|
||||
post: function (success, failure, opts) {
|
||||
return sendRequest('post', true, opts, success, failure);
|
||||
},
|
||||
put: function (success, failure, opts) {
|
||||
return sendRequest('put', true, opts, success, failure);
|
||||
},
|
||||
patch: function (success, failure, opts) {
|
||||
return sendRequest('patch', true, opts, success, failure);
|
||||
},
|
||||
delete: function (success, failure, opts) {
|
||||
return sendRequest('delete', false, opts, success, failure);
|
||||
},
|
||||
head: function (success, failure, opts) {
|
||||
return sendRequest('head', false, opts, success, failure);
|
||||
},
|
||||
uploadFile: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "uploadFile" not supported on browser platform');
|
||||
},
|
||||
downloadFile: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "downloadFile" not supported on browser platform');
|
||||
},
|
||||
setSSLCertMode: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "setSSLCertMode" not supported on browser platform');
|
||||
setServerTrustMode: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "setServerTrustMode" not supported on browser platform');
|
||||
},
|
||||
disableRedirect: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "disableRedirect" not supported on browser platform');
|
||||
setClientAuthMode: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "setClientAuthMode" not supported on browser platform');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
8
src/ios/BinaryResponseSerializer.h
Normal file
8
src/ios/BinaryResponseSerializer.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFURLResponseSerialization.h"
|
||||
|
||||
@interface BinaryResponseSerializer : AFHTTPResponseSerializer
|
||||
|
||||
+ (instancetype)serializer;
|
||||
|
||||
@end
|
||||
126
src/ios/BinaryResponseSerializer.m
Normal file
126
src/ios/BinaryResponseSerializer.m
Normal file
@@ -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
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
@interface CordovaHttpPlugin : CDVPlugin
|
||||
|
||||
- (void)setSSLCertMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)disableRedirect:(CDVInvokedUrlCommand*)command;
|
||||
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)post:(CDVInvokedUrlCommand*)command;
|
||||
- (void)get:(CDVInvokedUrlCommand*)command;
|
||||
- (void)put:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#import "CordovaHttpPlugin.h"
|
||||
#import "CDVFile.h"
|
||||
#import "BinaryResponseSerializer.h"
|
||||
#import "TextResponseSerializer.h"
|
||||
#import "TextRequestSerializer.h"
|
||||
#import "AFHTTPSessionManager.h"
|
||||
@@ -13,20 +14,16 @@
|
||||
- (NSNumber*)getStatusCode:(NSError*) error;
|
||||
- (NSMutableDictionary*)copyHeaderFields:(NSDictionary*)headerFields;
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager;
|
||||
- (void)setRedirect:(AFHTTPSessionManager*)manager;
|
||||
- (void)setRedirect:(bool)redirect forManager:(AFHTTPSessionManager*)manager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CordovaHttpPlugin {
|
||||
AFSecurityPolicy *securityPolicy;
|
||||
bool redirect;
|
||||
AFHTTPSessionManager *manager;
|
||||
}
|
||||
|
||||
- (void)pluginInitialize {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
manager = [AFHTTPSessionManager manager];
|
||||
redirect = true;
|
||||
}
|
||||
|
||||
- (void)setRequestSerializer:(NSString*)serializerName forManager:(AFHTTPSessionManager*)manager {
|
||||
@@ -45,9 +42,11 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setRedirect:(AFHTTPSessionManager*)manager {
|
||||
[manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest * _Nonnull(NSURLSession * _Nonnull session, NSURLSessionTask * _Nonnull task, NSURLResponse * _Nonnull response, NSURLRequest * _Nonnull request) {
|
||||
if (redirect) {
|
||||
- (void)setRedirect:(bool)followRedirect forManager:(AFHTTPSessionManager*)manager {
|
||||
[manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest * _Nonnull(NSURLSession * _Nonnull session,
|
||||
NSURLSessionTask * _Nonnull task, NSURLResponse * _Nonnull response, NSURLRequest * _Nonnull request) {
|
||||
|
||||
if (followRedirect) {
|
||||
return request;
|
||||
} else {
|
||||
return nil;
|
||||
@@ -55,6 +54,19 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
|
||||
[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"];
|
||||
@@ -72,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"];
|
||||
@@ -106,8 +118,12 @@
|
||||
case -1009:
|
||||
// no connection
|
||||
return [NSNumber numberWithInt:-6];
|
||||
case -1202:
|
||||
// untrusted SSL certificate
|
||||
case -1200: // secure connection failed
|
||||
case -1201: // certificate has bad date
|
||||
case -1202: // certificate untrusted
|
||||
case -1203: // certificate has unknown root
|
||||
case -1204: // certificate is not yet valid
|
||||
// configuring SSL failed
|
||||
return [NSNumber numberWithInt:-2];
|
||||
default:
|
||||
return [NSNumber numberWithInt:-1];
|
||||
@@ -126,7 +142,7 @@
|
||||
return headerFieldsCopy;
|
||||
}
|
||||
|
||||
- (void)setSSLCertMode:(CDVInvokedUrlCommand*)command {
|
||||
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command {
|
||||
NSString *certMode = [command.arguments objectAtIndex:0];
|
||||
|
||||
if ([certMode isEqualToString: @"default"] || [certMode isEqualToString: @"legacy"]) {
|
||||
@@ -147,34 +163,23 @@
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
|
||||
[manager.requestSerializer setTimeoutInterval:timeout];
|
||||
}
|
||||
|
||||
- (void)disableRedirect:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
bool disable = [[command.arguments objectAtIndex:0] boolValue];
|
||||
|
||||
redirect = !disable;
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)get:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
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: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
@@ -201,18 +206,20 @@
|
||||
}
|
||||
|
||||
- (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];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
@@ -240,19 +247,22 @@
|
||||
}
|
||||
|
||||
- (void)delete:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
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: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
@@ -279,6 +289,7 @@
|
||||
}
|
||||
|
||||
- (void)post:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
@@ -286,14 +297,16 @@
|
||||
NSString *serializerName = [command.arguments objectAtIndex:2];
|
||||
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: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
@@ -320,6 +333,7 @@
|
||||
}
|
||||
|
||||
- (void)put:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
@@ -327,14 +341,16 @@
|
||||
NSString *serializerName = [command.arguments objectAtIndex:2];
|
||||
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: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
@@ -361,6 +377,7 @@
|
||||
}
|
||||
|
||||
- (void)patch:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
@@ -368,14 +385,16 @@
|
||||
NSString *serializerName = [command.arguments objectAtIndex:2];
|
||||
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: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
@@ -402,6 +421,7 @@
|
||||
}
|
||||
|
||||
- (void)uploadFile:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
@@ -409,15 +429,17 @@
|
||||
NSString *filePath = [command.arguments objectAtIndex: 2];
|
||||
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: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
@@ -456,23 +478,25 @@
|
||||
}
|
||||
|
||||
- (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];
|
||||
NSString *filePath = [command.arguments objectAtIndex: 2];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
|
||||
if ([filePath hasPrefix:@"file://"]) {
|
||||
filePath = [filePath substringFromIndex:7];
|
||||
}
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
|
||||
@@ -5,6 +5,4 @@
|
||||
|
||||
+ (instancetype)serializer;
|
||||
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyKey;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
const wd = require("wd");
|
||||
|
||||
require('colors');
|
||||
|
||||
const chai = require('chai');
|
||||
const chaiAsPromised = require('chai-as-promised');
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
const should = chai.should();
|
||||
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
|
||||
|
||||
exports.should = should;
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com https://self-signed.badssl.com http://httpbin.org http://www.columbia.edu 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com https://self-signed.badssl.com http://httpbin.org http://www.columbia.edu 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content: blob:;">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
|
||||
@@ -15,7 +15,7 @@
|
||||
<button id="nextBtn">Run next test</button>
|
||||
|
||||
<script type="text/javascript" src="cordova.js"></script>
|
||||
<script type="text/javascript" src="app-test-definitions.js"></script>
|
||||
<script type="text/javascript" src="e2e-specs.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,17 +3,17 @@ const app = {
|
||||
|
||||
lastResult: null,
|
||||
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
document.getElementById('nextBtn').addEventListener('click', app.onNextBtnClick);
|
||||
},
|
||||
|
||||
printResult: function(prefix, content) {
|
||||
printResult: function (prefix, content) {
|
||||
const text = prefix + ': ' + JSON.stringify(content);
|
||||
|
||||
document.getElementById('resultTextarea').value += text;
|
||||
},
|
||||
|
||||
reject: function(content) {
|
||||
reject: function (content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - rejected', content);
|
||||
|
||||
@@ -23,7 +23,7 @@ const app = {
|
||||
};
|
||||
},
|
||||
|
||||
resolve: function(content) {
|
||||
resolve: function (content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - resolved', content);
|
||||
|
||||
@@ -33,7 +33,7 @@ const app = {
|
||||
};
|
||||
},
|
||||
|
||||
throw: function(error) {
|
||||
throw: function (error) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - throwed', error.message);
|
||||
|
||||
@@ -43,11 +43,11 @@ const app = {
|
||||
};
|
||||
},
|
||||
|
||||
getResult: function(cb) {
|
||||
getResult: function (cb) {
|
||||
cb(app.lastResult);
|
||||
},
|
||||
|
||||
runTest: function(index) {
|
||||
runTest: function (index) {
|
||||
const testDefinition = tests[index];
|
||||
const titleText = app.testIndex + ': ' + testDefinition.description;
|
||||
const expectedText = 'expected - ' + testDefinition.expected;
|
||||
@@ -57,32 +57,68 @@ const app = {
|
||||
document.getElementById('resultTextarea').value = '';
|
||||
document.getElementById('descriptionLbl').innerText = titleText;
|
||||
|
||||
try {
|
||||
testDefinition.func(app.resolve, app.reject);
|
||||
} catch (error) {
|
||||
app.throw(error);
|
||||
}
|
||||
},
|
||||
const onSuccessFactory = function (cbChain) {
|
||||
return function () {
|
||||
cbChain.shift()(cbChain);
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeTest: function(testIndex, cb) {
|
||||
app.lastResult = null;
|
||||
const onFailFactory = function (prefix) {
|
||||
return function (errorMessage) {
|
||||
app.reject(prefix + ': ' + errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
if (hooks && hooks.onBeforeEachTest) {
|
||||
return hooks.onBeforeEachTest(function() {
|
||||
const testDefinition = tests[testIndex];
|
||||
const onThrowedHandler = function (prefix, error) {
|
||||
app.throw(new Error(prefix + ': ' + error.message));
|
||||
};
|
||||
|
||||
if (testDefinition.before) {
|
||||
testDefinition.before(cb);
|
||||
} else {
|
||||
cb();
|
||||
const execBeforeEachTest = function (cbChain) {
|
||||
const prefix = 'in before each hook';
|
||||
|
||||
try {
|
||||
if (!hooks || !hooks.onBeforeEachTest) {
|
||||
return onSuccessFactory(cbChain)();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
|
||||
hooks.onBeforeEachTest(
|
||||
onSuccessFactory(cbChain),
|
||||
onFailFactory(prefix)
|
||||
);
|
||||
} catch (error) {
|
||||
onThrowedHandler(prefix, error);
|
||||
}
|
||||
};
|
||||
|
||||
const execBeforeTest = function (cbChain) {
|
||||
const prefix = 'in before hook';
|
||||
|
||||
try {
|
||||
if (!testDefinition.before) {
|
||||
return onSuccessFactory(cbChain)();
|
||||
}
|
||||
|
||||
testDefinition.before(
|
||||
onSuccessFactory(cbChain),
|
||||
onFailFactory(prefix)
|
||||
);
|
||||
} catch (error) {
|
||||
onThrowedHandler(prefix, error);
|
||||
}
|
||||
};
|
||||
|
||||
const execTest = function () {
|
||||
try {
|
||||
testDefinition.func(app.resolve, app.reject);
|
||||
} catch (error) {
|
||||
app.throw(error);
|
||||
}
|
||||
};
|
||||
|
||||
onSuccessFactory([execBeforeEachTest, execBeforeTest, execTest])();
|
||||
},
|
||||
|
||||
onFinishedAllTests: function() {
|
||||
onFinishedAllTests: function () {
|
||||
const titleText = 'No more tests';
|
||||
const expectedText = 'You have run all available tests.';
|
||||
|
||||
@@ -91,13 +127,11 @@ const app = {
|
||||
document.getElementById('descriptionLbl').innerText = titleText;
|
||||
},
|
||||
|
||||
onNextBtnClick: function() {
|
||||
onNextBtnClick: function () {
|
||||
app.testIndex += 1;
|
||||
|
||||
if (app.testIndex < tests.length) {
|
||||
app.onBeforeTest(app.testIndex, function() {
|
||||
app.runTest(app.testIndex);
|
||||
});
|
||||
app.runTest(app.testIndex);
|
||||
} else {
|
||||
app.onFinishedAllTests();
|
||||
}
|
||||
@@ -1,24 +1,52 @@
|
||||
const hooks = {
|
||||
onBeforeEachTest: function(done) {
|
||||
onBeforeEachTest: function (resolve, reject) {
|
||||
cordova.plugin.http.clearCookies();
|
||||
helpers.setDefaultCertMode(done);
|
||||
|
||||
helpers.enableFollowingRedirect(function() {
|
||||
// server trust mode is not supported on brpwser platform
|
||||
if (cordova.platformId === 'browser') {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
helpers.setDefaultServerTrustMode(function () {
|
||||
// @TODO: not ready yet
|
||||
// helpers.setNoneClientAuthMode(resolve, reject);
|
||||
resolve();
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const helpers = {
|
||||
setDefaultCertMode: function(done) { cordova.plugin.http.setSSLCertMode('default', done, done); },
|
||||
setNoCheckCertMode: function(done) { cordova.plugin.http.setSSLCertMode('nocheck', done, done); },
|
||||
setPinnedCertMode: function(done) { cordova.plugin.http.setSSLCertMode('pinned', done, done); },
|
||||
setJsonSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('json')); },
|
||||
setUtf8StringSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('utf8')); },
|
||||
setUrlEncodedSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('urlencoded')); },
|
||||
getWithXhr: function(done, url) {
|
||||
setDefaultServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('default', resolve, reject); },
|
||||
setNoCheckServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('nocheck', resolve, reject); },
|
||||
setPinnedServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('pinned', resolve, reject); },
|
||||
setNoneClientAuthMode: function (resolve, reject) { cordova.plugin.http.setClientAuthMode('none', resolve, reject); },
|
||||
setBufferClientAuthMode: function (resolve, reject) {
|
||||
helpers.getWithXhr(function(pkcs) {
|
||||
cordova.plugin.http.setClientAuthMode('buffer', {
|
||||
rawPkcs: pkcs,
|
||||
pkcsPassword: 'badssl.com'
|
||||
}, resolve, reject);
|
||||
}, './certificates/badssl-client-cert.pkcs', 'arraybuffer');
|
||||
},
|
||||
setJsonSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('json')); },
|
||||
setUtf8StringSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('utf8')); },
|
||||
setUrlEncodedSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('urlencoded')); },
|
||||
disableFollowingRedirect: function (resolve) { resolve(cordova.plugin.http.setFollowRedirect(false)); },
|
||||
enableFollowingRedirect: function(resolve) { resolve(cordova.plugin.http.setFollowRedirect(true)); },
|
||||
getWithXhr: function (done, url, type) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.addEventListener('load', function() {
|
||||
done(this.responseText);
|
||||
xhr.addEventListener('load', function () {
|
||||
if (!type || type === 'text') {
|
||||
done(this.responseText);
|
||||
} else {
|
||||
done(this.response);
|
||||
}
|
||||
});
|
||||
|
||||
xhr.responseType = type;
|
||||
xhr.open('GET', url);
|
||||
xhr.send();
|
||||
},
|
||||
@@ -26,7 +54,7 @@ const helpers = {
|
||||
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function (directoryEntry) {
|
||||
directoryEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
|
||||
fileEntry.createWriter(function (fileWriter) {
|
||||
var blob = new Blob([ content ], { type: 'text/plain' });
|
||||
var blob = new Blob([content], { type: 'text/plain' });
|
||||
|
||||
fileWriter.onwriteend = done;
|
||||
fileWriter.onerror = done;
|
||||
@@ -34,20 +62,32 @@ 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;
|
||||
}
|
||||
};
|
||||
|
||||
const messageFactory = {
|
||||
sslTrustAnchor: function() { return 'SSL handshake failed: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
|
||||
invalidCertificate: function(domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
|
||||
sslTrustAnchor: function () { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
|
||||
invalidCertificate: function (domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
|
||||
}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
description: 'should reject self signed cert (GET)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
@@ -55,8 +95,8 @@ const tests = [
|
||||
{
|
||||
description: 'should reject self signed cert (PUT)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
@@ -64,8 +104,8 @@ const tests = [
|
||||
{
|
||||
description: 'should reject self signed cert (POST)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
@@ -73,8 +113,8 @@ const tests = [
|
||||
{
|
||||
description: 'should reject self signed cert (PATCH)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
@@ -82,8 +122,8 @@ const tests = [
|
||||
{
|
||||
description: 'should reject self signed cert (DELETE)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
@@ -91,9 +131,9 @@ const tests = [
|
||||
{
|
||||
description: 'should accept bad cert (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.should.include({ status: 200 });
|
||||
}
|
||||
@@ -101,9 +141,9 @@ const tests = [
|
||||
{
|
||||
description: 'should accept bad cert (PUT)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PUT is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
@@ -111,9 +151,9 @@ const tests = [
|
||||
{
|
||||
description: 'should accept bad cert (POST)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because POST is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
@@ -121,9 +161,9 @@ const tests = [
|
||||
{
|
||||
description: 'should accept bad cert (PATCH)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PATCH is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
@@ -131,9 +171,9 @@ const tests = [
|
||||
{
|
||||
description: 'should accept bad cert (DELETE)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because DELETE is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
@@ -141,9 +181,9 @@ const tests = [
|
||||
{
|
||||
description: 'should fetch data from http://httpbin.org/ (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.should.include({ status: 200 });
|
||||
}
|
||||
@@ -152,8 +192,8 @@ const tests = [
|
||||
description: 'should send JSON object correctly (POST)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
@@ -162,8 +202,8 @@ const tests = [
|
||||
description: 'should send JSON object correctly (PUT)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
@@ -172,8 +212,8 @@ const tests = [
|
||||
description: 'should send JSON object correctly (PATCH)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
@@ -182,39 +222,39 @@ const tests = [
|
||||
description: 'should send JSON array correctly (POST) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', [ 1, 2, 3 ], {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
|
||||
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON array correctly (PUT) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', [ 1, 2, 3 ], {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
|
||||
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON array correctly (PATCH) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', [ 1, 2, 3 ], {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
|
||||
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send url encoded data correctly (POST) #41',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
@@ -223,8 +263,8 @@ const tests = [
|
||||
description: 'should send url encoded data correctly (PUT)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
@@ -233,8 +273,8 @@ const tests = [
|
||||
description: 'should send url encoded data correctly (PATCH)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
@@ -242,21 +282,31 @@ const tests = [
|
||||
{
|
||||
description: 'should resolve correct URL after redirect (GET) #33',
|
||||
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.url.should.be.equal('http://httpbin.org/anything');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not follow 302 redirect when following redirects is disabled',
|
||||
expected: 'rejected: {"status": 302, ...',
|
||||
before: function(resolve, reject) { cordova.plugin.http.disableRedirect(true, resolve, reject)},
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(302);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should download a file from given URL to given path in local filesystem',
|
||||
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
var sourceUrl = 'http://httpbin.org/xml';
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function(entry) {
|
||||
helpers.getWithXhr(function(content) {
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
@@ -267,7 +317,7 @@ const tests = [
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('test.xml');
|
||||
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
|
||||
@@ -276,17 +326,17 @@ const tests = [
|
||||
{
|
||||
description: 'should upload a file from given path in local filesystem to given URL #27',
|
||||
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
var sourcePath = cordova.file.cacheDirectory + fileName;
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
|
||||
helpers.writeToFile(function() {
|
||||
helpers.writeToFile(function () {
|
||||
cordova.plugin.http.uploadFile(targetUrl, {}, {}, sourcePath, fileName, resolve, reject);
|
||||
}, fileName, fileContent);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
|
||||
@@ -302,10 +352,10 @@ const tests = [
|
||||
{
|
||||
description: 'should encode HTTP array params correctly (GET) #45',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3\\"}\" ...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', { myArray: [ 'val1', 'val2', 'val3' ], myString: 'testString' }, {}, resolve, reject);
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', { myArray: ['val1', 'val2', 'val3'], myString: 'testString' }, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
@@ -318,10 +368,10 @@ const tests = [
|
||||
{
|
||||
description: 'should throw on non-string values in local header object #54',
|
||||
expected: 'throwed: {"message": "advanced-http: header values must be strings"}',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, { myTestHeader: 1 }, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('throwed');
|
||||
result.message.should.be.equal('advanced-http: header values must be strings');
|
||||
}
|
||||
@@ -329,10 +379,10 @@ const tests = [
|
||||
{
|
||||
description: 'should throw an error while setting non-string value as global header #54',
|
||||
expected: 'throwed: "advanced-http: header values must be strings"',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.setHeader('myTestHeader', 2);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('throwed');
|
||||
result.message.should.be.equal('advanced-http: header values must be strings');
|
||||
}
|
||||
@@ -340,10 +390,10 @@ const tests = [
|
||||
{
|
||||
description: 'should accept content-type "application/xml" #58',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/xml', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
}
|
||||
@@ -351,12 +401,12 @@ const tests = [
|
||||
{
|
||||
description: 'should send programmatically set cookies correctly (GET)',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
@@ -370,35 +420,34 @@ const tests = [
|
||||
{
|
||||
description: 'should not send any cookies after running "clearCookies" (GET) #59',
|
||||
expected: 'resolved: {"status": 200, "data": "{\"headers\": {\"Cookie\": \"\"...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
cordova.plugin.http.clearCookies();
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.headers
|
||||
.Cookie
|
||||
.should.be.equal('');
|
||||
.should.not.have.property('Cookie');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send programmatically set cookies correctly (DOWNLOAD) #57',
|
||||
expected: 'resolved: {"content":{"cookies":{"myCookie":"myValue ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
var sourceUrl = 'http://httpbin.org/cookies';
|
||||
var targetPath = cordova.file.cacheDirectory + 'cookies.json';
|
||||
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function(entry) {
|
||||
helpers.getWithXhr(function(content) {
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
@@ -409,7 +458,7 @@ const tests = [
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('cookies.json');
|
||||
result.data.content.should.be.a('string');
|
||||
@@ -424,10 +473,10 @@ const tests = [
|
||||
description: 'should send UTF-8 encoded raw string correctly (POST) #34',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"this is a test string\\"...',
|
||||
before: helpers.setUtf8StringSerializer,
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.post('http://httpbin.org/anything', 'this is a test string', {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).data.should.be.equal('this is a test string');
|
||||
}
|
||||
@@ -435,10 +484,10 @@ const tests = [
|
||||
{
|
||||
description: 'should encode spaces in query string (params object) correctly (GET) #71',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"args\\": \\"query param\\": \\"and value with spaces\\"...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', { 'query param': 'and value with spaces' }, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).args['query param'].should.be.equal('and value with spaces');
|
||||
}
|
||||
@@ -446,10 +495,10 @@ const tests = [
|
||||
{
|
||||
description: 'should decode latin1 (iso-8859-1) encoded body correctly (GET) #72',
|
||||
expected: 'resolved: {"status": 200, "data": "<!DOCTYPE HTML PUBLIC \\"-//W3C//DTD HTML 4.01 Transitional//EN\\"> ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://www.columbia.edu/kermit/latin1.html', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.include('[¡] 161 10/01 241 A1 INVERTED EXCLAMATION MARK\n[¢] 162 10/02 242 A2 CENT SIGN');
|
||||
}
|
||||
@@ -457,10 +506,10 @@ const tests = [
|
||||
{
|
||||
description: 'should return empty body string correctly (GET)',
|
||||
expected: 'resolved: {"status": 200, "data": "" ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/stream/0', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.equal('');
|
||||
}
|
||||
@@ -468,11 +517,11 @@ const tests = [
|
||||
{
|
||||
description: 'should pin SSL cert correctly (GET)',
|
||||
expected: 'resolved: {"status": 200 ...',
|
||||
before: helpers.setPinnedCertMode,
|
||||
func: function(resolve, reject) {
|
||||
before: helpers.setPinnedServerTrustMode,
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('https://httpbin.org', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
}
|
||||
@@ -480,11 +529,11 @@ const tests = [
|
||||
{
|
||||
description: 'should reject when pinned cert does not match received server cert (GET)',
|
||||
expected: 'rejected: {"status": -2 ...',
|
||||
before: helpers.setPinnedCertMode,
|
||||
func: function(resolve, reject) {
|
||||
before: helpers.setPinnedServerTrustMode,
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('https://sha512.badssl.com/', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('sha512.badssl.com') });
|
||||
}
|
||||
@@ -493,18 +542,18 @@ const tests = [
|
||||
description: 'should send deeply structured JSON object correctly (POST) #65',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"{\\\\"outerObj\\\\":{\\\\"innerStr\\\\":\\\\"testString\\\\",\\\\"innerArr\\\\":[1,2,3]}}\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] }}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] } }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] }});
|
||||
JSON.parse(result.data.data).json.should.eql({ outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] } });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should override header "content-type" correctly (POST) #78',
|
||||
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Type\\": \\"text/plain\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', {}, { 'Content-Type': 'text/plain' }, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', {}, { 'Content-Type': 'text/plain' }, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).headers['Content-Type'].should.be.equal('text/plain');
|
||||
}
|
||||
@@ -512,8 +561,8 @@ const tests = [
|
||||
{
|
||||
description: 'should handle error during file download correctly (DOWNLOAD) #83',
|
||||
expected: 'rejected: {"status": 403, "error": "There was an error downloading the file" ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.downloadFile('http://httpbin.org/status/403', {}, {}, cordova.file.tempDirectory + 'testfile.txt', resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.downloadFile('http://httpbin.org/status/403', {}, {}, cordova.file.tempDirectory + 'testfile.txt', resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(403);
|
||||
result.data.error.should.be.equal('There was an error downloading the file');
|
||||
@@ -522,8 +571,8 @@ const tests = [
|
||||
{
|
||||
description: 'should handle gzip encoded response correctly',
|
||||
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Encoding\\": \\"gzip\\" ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/gzip', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/gzip', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
JSON.parse(result.data.data).gzipped.should.be.equal(true);
|
||||
@@ -533,8 +582,8 @@ const tests = [
|
||||
description: 'should send empty string correctly',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"\\" ...',
|
||||
before: helpers.setUtf8StringSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', '', {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', '', {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).data.should.be.equal('');
|
||||
}
|
||||
@@ -543,8 +592,8 @@ const tests = [
|
||||
description: 'shouldn\'t escape forward slashes #184',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"/\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { testString: '/' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { testString: '/' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.testString.should.be.equal('/');
|
||||
}
|
||||
@@ -552,8 +601,8 @@ const tests = [
|
||||
{
|
||||
description: 'should not double encode spaces in url path #195',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"https://httpbin.org/anything/containing spaces in url\\" ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything/containing%20spaces%20in%20url', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything/containing%20spaces%20in%20url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything/containing spaces in url');
|
||||
}
|
||||
@@ -561,8 +610,8 @@ const tests = [
|
||||
{
|
||||
description: 'should encode spaces in url query correctly',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"https://httpbin.org/anything?query key=very long query value with spaces\\" ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything', { 'query key': 'very long query value with spaces' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything', { 'query key': 'very long query value with spaces' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything?query key=very long query value with spaces');
|
||||
}
|
||||
@@ -570,12 +619,12 @@ const tests = [
|
||||
{
|
||||
description: 'should download a file from given HTTPS URL to given path in local filesystem #197',
|
||||
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
|
||||
func: function(resolve, reject) {
|
||||
func: function (resolve, reject) {
|
||||
var sourceUrl = 'https://httpbin.org/xml';
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function(entry) {
|
||||
helpers.getWithXhr(function(content) {
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
@@ -586,12 +635,135 @@ const tests = [
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('test.xml');
|
||||
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return header object when request failed due to non-success response from server #221',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/status/418', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.headers.should.be.an('object');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return status code when request failed due to non-success response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/status/418', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(418);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return url string when request failed due to non-success response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/status/418', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.url.should.be.equal('https://httpbin.org/status/418');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'shouldn\'t return header object when request failed before receiving response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://not_existing_url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
should.equal(result.data.headers, undefined);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return status code when request failed before receiving response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://not_existing_url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.a('number');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'shouldn\'t return url string when request failed before receiving response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://not_existing_url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
should.equal(result.data.url, undefined);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should fetch binary correctly when response type is "arraybuffer"',
|
||||
expected: 'resolved: {"isArrayBuffer:true,"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({
|
||||
isArrayBuffer: response.data.constructor === ArrayBuffer,
|
||||
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.isArrayBuffer.should.be.equal(true);
|
||||
result.data.hash.should.be.equal(-1032603775);
|
||||
result.data.byteLength.should.be.equal(35588);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should fetch binary correctly when response type is "blob"',
|
||||
expected: 'resolved: {"isBlob":true,byteLength":35588}',
|
||||
func: function (resolve, reject) {
|
||||
var url = 'https://httpbin.org/image/jpeg';
|
||||
var options = { method: 'get', responseType: 'blob' };
|
||||
var success = function (response) {
|
||||
resolve({
|
||||
isBlob: response.data.constructor === Blob,
|
||||
type: response.data.type,
|
||||
byteLength: response.data.size
|
||||
});
|
||||
};
|
||||
cordova.plugin.http.sendRequest(url, options, success, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.isBlob.should.be.equal(true);
|
||||
result.data.type.should.be.equal('image/jpeg');
|
||||
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
|
||||
// {
|
||||
// description: 'should authenticate correctly when client cert auth is configured with a PKCS12 container',
|
||||
// expected: 'resolved: {"status": 200, ...',
|
||||
// before: helpers.setBufferClientAuthMode,
|
||||
// func: function (resolve, reject) { cordova.plugin.http.get('https://client.badssl.com/', {}, {}, resolve, reject); },
|
||||
// validationFunc: function (driver, result) {
|
||||
// result.type.should.be.equal('resolved');
|
||||
// result.data.data.should.include('TLS handshake');
|
||||
// }
|
||||
// }
|
||||
];
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
@@ -26,7 +26,7 @@ const local = {
|
||||
const sauce = {
|
||||
iosDevice: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
'appium-version': '1.9.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
deviceName: 'iPhone 6',
|
||||
@@ -35,7 +35,7 @@ const sauce = {
|
||||
},
|
||||
iosEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
'appium-version': '1.9.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
deviceName: 'iPhone Simulator',
|
||||
@@ -44,7 +44,7 @@ const sauce = {
|
||||
},
|
||||
androidEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
'appium-version': '1.9.1',
|
||||
platformName: 'Android',
|
||||
platformVersion: '5.1',
|
||||
deviceName: 'Android Emulator',
|
||||
@@ -1,12 +1,18 @@
|
||||
require('./helpers/setup');
|
||||
|
||||
const wd = require('wd');
|
||||
const chai = require('chai');
|
||||
const chaiAsPromised = require('chai-as-promised');
|
||||
const apps = require('./helpers/apps');
|
||||
const caps = Object.assign({}, require('./helpers/caps'));
|
||||
const serverConfig = require('./helpers/server');
|
||||
const testDefinitions = require('../app-test-definitions');
|
||||
const testDefinitions = require('../e2e-specs');
|
||||
const pkgjson = require('../../package.json');
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
|
||||
global.should = chai.should();
|
||||
|
||||
require('colors');
|
||||
|
||||
describe('Advanced HTTP', function() {
|
||||
const isDevice = process.argv.includes('--device');
|
||||
const isAndroid = process.argv.includes('--android');
|
||||
@@ -49,7 +55,7 @@ describe('Advanced HTTP', function() {
|
||||
if (value === 'finished') {
|
||||
resolve();
|
||||
} else if (Date.now() > timeoutTimestamp) {
|
||||
reject('Test function timed out!');
|
||||
reject(new Error('Test function timed out!'));
|
||||
} else {
|
||||
setTimeout(checkIfFinished, 500);
|
||||
}
|
||||
@@ -3,19 +3,20 @@ const mock = require('mock-require');
|
||||
const should = chai.should();
|
||||
|
||||
describe('Advanced HTTP public interface', function () {
|
||||
const messages = require('../www/messages');
|
||||
let http = {};
|
||||
|
||||
const noop = () => { /* intentionally doing nothing */ };
|
||||
|
||||
const getDependenciesBlueprint = () => {
|
||||
const messages = require('../www/messages');
|
||||
const globalConfigs = require('../www/global-configs');
|
||||
const jsUtil = require('../www/js-util');
|
||||
const ToughCookie = require('../www/umd-tough-cookie');
|
||||
const lodash = require('../www/lodash');
|
||||
const WebStorageCookieStore = require('../www/local-storage-store')(ToughCookie, lodash);
|
||||
const cookieHandler = require('../www/cookie-handler')(null, ToughCookie, WebStorageCookieStore);
|
||||
const helpers = require('../www/helpers')(cookieHandler, messages);
|
||||
const urlUtil = require('../www/url-util')(helpers);
|
||||
const helpers = require('../www/helpers')(jsUtil, cookieHandler, messages);
|
||||
const urlUtil = require('../www/url-util')(jsUtil);
|
||||
|
||||
return { exec: noop, cookieHandler, urlUtil: urlUtil, helpers, globalConfigs };
|
||||
};
|
||||
@@ -126,13 +127,31 @@ describe('Advanced HTTP public interface', function () {
|
||||
});
|
||||
|
||||
it('throws an Error when you try to add a cookie by using "setHeader" #46', () => {
|
||||
(function () { http.setHeader('*', 'cookie', 'value') }).should.throw();
|
||||
(function () { http.setHeader('*', 'cookie', 'value'); }).should.throw();
|
||||
});
|
||||
|
||||
it('configures global timeout value correctly with given valid value', () => {
|
||||
http.setRequestTimeout(10);
|
||||
http.getRequestTimeout().should.equal(10);
|
||||
});
|
||||
|
||||
it('throws an Error when you try to configure global timeout with a string', () => {
|
||||
(function () { http.setRequestTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
|
||||
});
|
||||
|
||||
it('sets global option for following redirects correctly', () => {
|
||||
http.setFollowRedirect(false);
|
||||
http.getFollowRedirect().should.equal(false);
|
||||
});
|
||||
|
||||
it('throws an Error when you try to configure global option for following redirects with a string', () => {
|
||||
(function () { http.setFollowRedirect('myString'); }).should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('URL util', function () {
|
||||
const helpers = require('../www/helpers')(null, null);
|
||||
const util = require('../www/url-util')(helpers);
|
||||
const jsUtil = require('../www/js-util');
|
||||
const util = require('../www/url-util')(jsUtil);
|
||||
|
||||
it('parses URL with protocol, hostname and path correctly', () => {
|
||||
util.parseUrl('http://ilkimen.net/test').should.include({
|
||||
@@ -234,3 +253,124 @@ describe('URL util', function () {
|
||||
.should.equal('http://ilkimen.net/?myParam=myValue¶m1=value1#myHash');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Common helpers', function () {
|
||||
describe('mergeHeaders(globalHeaders, localHeaders)', function () {
|
||||
const init = require('../www/helpers');
|
||||
init.debug = true;
|
||||
|
||||
const helpers = init(null, null, null);
|
||||
|
||||
it('merges empty header sets correctly', () => {
|
||||
helpers.mergeHeaders({}, {}).should.eql({});
|
||||
});
|
||||
|
||||
it('merges ssimple header sets without collision correctly', () => {
|
||||
helpers.mergeHeaders({ a: 1 }, { b: 2 }).should.eql({ a: 1, b: 2 });
|
||||
});
|
||||
|
||||
it('merges header sets with collision correctly', () => {
|
||||
helpers.mergeHeaders({ a: 1 }, { a: 2 }).should.eql({ a: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCookieHeader(url)', function () {
|
||||
it('resolves cookie header correctly when no cookie is set #198', () => {
|
||||
const helpers = require('../www/helpers')(null, { getCookieString: () => '' }, null);
|
||||
|
||||
helpers.getCookieHeader('http://ilkimen.net').should.eql({});
|
||||
});
|
||||
|
||||
it('resolves cookie header correctly when a cookie is set', () => {
|
||||
const helpers = require('../www/helpers')(null, { getCookieString: () => 'cookie=value' }, null);
|
||||
|
||||
helpers.getCookieHeader('http://ilkimen.net').should.eql({ Cookie: 'cookie=value' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkClientAuthOptions()', function () {
|
||||
const jsUtil = require('../www/js-util');
|
||||
const messages = require('../www/messages');
|
||||
const helpers = require('../www/helpers')(jsUtil, null, messages);
|
||||
|
||||
it('returns options object with empty values when mode is "none" and no options are given', () => {
|
||||
helpers.checkClientAuthOptions('none').should.eql({
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('returns options object with empty values when mode is "none" and random options are given', () => {
|
||||
helpers.checkClientAuthOptions('none', {
|
||||
alias: 'myAlias',
|
||||
pkcsPath: 'myPath'
|
||||
}).should.eql({
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an error when mode is "systemstore" and alias is not a string or undefined', () => {
|
||||
(() => helpers.checkClientAuthOptions('systemstore', { alias: 1 }))
|
||||
.should.throw(messages.INVALID_CLIENT_AUTH_ALIAS);
|
||||
|
||||
(() => helpers.checkClientAuthOptions('systemstore', { alias: undefined }))
|
||||
.should.not.throw();
|
||||
});
|
||||
|
||||
it('returns an object with null alias when mode is "systemstore" and no options object is given', () => {
|
||||
helpers.checkClientAuthOptions('systemstore').should.eql({
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an error when mode is "buffer" and rawPkcs is not an array buffer', () => {
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
rawPkcs: undefined,
|
||||
pkcsPassword: 'password'
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
|
||||
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
pkcsPath: 1,
|
||||
pkcsPassword: 'password'
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
|
||||
});
|
||||
|
||||
it('throws an error when mode is "buffer" and pkcsPassword is not a string', () => {
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
rawPkcs: new ArrayBuffer(),
|
||||
pkcsPassword: undefined
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
|
||||
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
rawPkcs: new ArrayBuffer(),
|
||||
pkcsPassword: 1
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleMissingOptions()', function () {
|
||||
const jsUtil = require('../www/js-util');
|
||||
const messages = require('../www/messages');
|
||||
const helpers = require('../www/helpers')(jsUtil, null, messages);
|
||||
const mockGlobals = {
|
||||
headers: {},
|
||||
serializer: 'urlencoded',
|
||||
followRedirect: true,
|
||||
timeout: 60.0,
|
||||
}
|
||||
|
||||
it('adds missing "followRedirect" option correctly', () => {
|
||||
helpers.handleMissingOptions({}, mockGlobals).should.include({ followRedirect: true });
|
||||
});
|
||||
|
||||
it('throws an error when "followRedirect" option is not a boolean', () => {
|
||||
(() => helpers.handleMissingOptions({ followRedirect: 1 }, mockGlobals))
|
||||
.should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -5,14 +5,16 @@
|
||||
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');
|
||||
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')(cookieHandler, messages);
|
||||
var urlUtil = require(pluginId + '.url-util')(helpers);
|
||||
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);
|
||||
|
||||
module.exports = publicInterface;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var globalConfigs = {
|
||||
headers: {},
|
||||
serializer: 'urlencoded',
|
||||
followRedirect: true,
|
||||
timeout: 60.0,
|
||||
};
|
||||
|
||||
|
||||
166
www/helpers.js
166
www/helpers.js
@@ -1,16 +1,22 @@
|
||||
module.exports = function init(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', 'blob'];
|
||||
|
||||
return {
|
||||
var interface = {
|
||||
b64EncodeUnicode: b64EncodeUnicode,
|
||||
getTypeOf: getTypeOf,
|
||||
checkSerializer: checkSerializer,
|
||||
checkSSLCertMode: checkSSLCertMode,
|
||||
checkClientAuthMode: checkClientAuthMode,
|
||||
checkClientAuthOptions: checkClientAuthOptions,
|
||||
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
|
||||
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
|
||||
checkTimeoutValue: checkTimeoutValue,
|
||||
checkFollowRedirectValue: checkFollowRedirectValue,
|
||||
injectCookieHandler: injectCookieHandler,
|
||||
injectRawResponseHandler: injectRawResponseHandler,
|
||||
injectFileEntryHandler: injectFileEntryHandler,
|
||||
getMergedHeaders: getMergedHeaders,
|
||||
getProcessedData: getProcessedData,
|
||||
@@ -18,6 +24,24 @@ module.exports = function init(cookieHandler, messages) {
|
||||
handleMissingOptions: handleMissingOptions
|
||||
};
|
||||
|
||||
// expose all functions for testing purposes
|
||||
if (init.debug) {
|
||||
interface.mergeHeaders = mergeHeaders;
|
||||
interface.checkForValidStringValue = checkForValidStringValue;
|
||||
interface.checkKeyValuePairObject = checkKeyValuePairObject;
|
||||
interface.checkHttpMethod = checkHttpMethod;
|
||||
interface.checkResponseType = checkResponseType;
|
||||
interface.checkHeadersObject = checkHeadersObject;
|
||||
interface.checkParamsObject = checkParamsObject;
|
||||
interface.resolveCookieString = resolveCookieString;
|
||||
interface.createFileEntry = createFileEntry;
|
||||
interface.getCookieHeader = getCookieHeader;
|
||||
interface.getMatchingHostHeaders = getMatchingHostHeaders;
|
||||
interface.getAllowedDataTypes = getAllowedDataTypes;
|
||||
}
|
||||
|
||||
return interface;
|
||||
|
||||
// Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
|
||||
function b64EncodeUnicode(str) {
|
||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
||||
@@ -41,7 +65,7 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
|
||||
function checkForValidStringValue(list, value, onInvalidValueMessage) {
|
||||
if (getTypeOf(value) !== 'String') {
|
||||
if (jsUtil.getTypeOf(value) !== 'String') {
|
||||
throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
|
||||
}
|
||||
|
||||
@@ -55,14 +79,14 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
|
||||
function checkKeyValuePairObject(obj, allowedChildren, onInvalidValueMessage) {
|
||||
if (getTypeOf(obj) !== 'Object') {
|
||||
if (jsUtil.getTypeOf(obj) !== 'Object') {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (allowedChildren.indexOf(getTypeOf(obj[keys[i]])) === -1) {
|
||||
if (allowedChildren.indexOf(jsUtil.getTypeOf(obj[keys[i]])) === -1) {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
}
|
||||
@@ -74,6 +98,10 @@ module.exports = function init(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);
|
||||
}
|
||||
@@ -82,6 +110,58 @@ module.exports = function init(cookieHandler, messages) {
|
||||
return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
|
||||
}
|
||||
|
||||
function checkClientAuthMode(mode) {
|
||||
return checkForValidStringValue(validClientAuthModes, mode, messages.INVALID_CLIENT_AUTH_MODE);
|
||||
}
|
||||
|
||||
function checkClientAuthOptions(mode, options) {
|
||||
options = options || {};
|
||||
|
||||
// none
|
||||
if (mode === validClientAuthModes[0]) {
|
||||
return {
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
};
|
||||
}
|
||||
|
||||
if (jsUtil.getTypeOf(options) !== 'Object') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_OPTIONS);
|
||||
}
|
||||
|
||||
// systemstore
|
||||
if (mode === validClientAuthModes[1]) {
|
||||
if (jsUtil.getTypeOf(options.alias) !== 'String'
|
||||
&& jsUtil.getTypeOf(options.alias) !== 'Undefined') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_ALIAS);
|
||||
}
|
||||
|
||||
return {
|
||||
alias: jsUtil.getTypeOf(options.alias) === 'Undefined' ? null : options.alias,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
};
|
||||
}
|
||||
|
||||
// buffer
|
||||
if (mode === validClientAuthModes[2]) {
|
||||
if (jsUtil.getTypeOf(options.rawPkcs) !== 'ArrayBuffer') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
|
||||
}
|
||||
|
||||
if (jsUtil.getTypeOf(options.pkcsPassword) !== 'String') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
|
||||
}
|
||||
|
||||
return {
|
||||
alias: null,
|
||||
rawPkcs: options.rawPkcs,
|
||||
pkcsPassword: options.pkcsPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkForBlacklistedHeaderKey(key) {
|
||||
if (key.toLowerCase() === 'cookie') {
|
||||
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
|
||||
@@ -91,7 +171,7 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
|
||||
function checkForInvalidHeaderValue(value) {
|
||||
if (getTypeOf(value) !== 'String') {
|
||||
if (jsUtil.getTypeOf(value) !== 'String') {
|
||||
throw new Error(messages.INVALID_HEADERS_VALUE);
|
||||
}
|
||||
|
||||
@@ -99,13 +179,21 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
|
||||
function checkTimeoutValue(timeout) {
|
||||
if (getTypeOf(timeout) !== 'Number' || timeout < 0) {
|
||||
if (jsUtil.getTypeOf(timeout) !== 'Number' || timeout < 0) {
|
||||
throw new Error(messages.INVALID_TIMEOUT_VALUE);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
function checkFollowRedirectValue(follow) {
|
||||
if (jsUtil.getTypeOf(follow) !== 'Boolean') {
|
||||
throw new Error(messages.INVALID_FOLLOW_REDIRECT_VALUE);
|
||||
}
|
||||
|
||||
return follow;
|
||||
}
|
||||
|
||||
function checkHeadersObject(headers) {
|
||||
return checkKeyValuePairObject(headers, ['String'], messages.INVALID_HEADERS_VALUE);
|
||||
}
|
||||
@@ -146,6 +234,26 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
}
|
||||
|
||||
function injectRawResponseHandler(responseType, cb) {
|
||||
return function (response) {
|
||||
// arraybuffer
|
||||
if (responseType === validResponseTypes[1]) {
|
||||
var buffer = base64.toArrayBuffer(response.data);
|
||||
response.data = buffer;
|
||||
}
|
||||
|
||||
// blob
|
||||
if (responseType === validResponseTypes[2]) {
|
||||
var buffer = base64.toArrayBuffer(response.data);
|
||||
var type = response.headers['content-type'] || '';
|
||||
var blob = new Blob([ buffer ], { type: type });
|
||||
response.data = blob;
|
||||
}
|
||||
|
||||
cb(response);
|
||||
}
|
||||
}
|
||||
|
||||
function injectFileEntryHandler(cb) {
|
||||
return function (response) {
|
||||
cb(createFileEntry(response.file));
|
||||
@@ -153,7 +261,13 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
|
||||
function getCookieHeader(url) {
|
||||
return { Cookie: cookieHandler.getCookieString(url) };
|
||||
var cookieString = cookieHandler.getCookieString(url);
|
||||
|
||||
if (cookieString.length) {
|
||||
return { Cookie: cookieHandler.getCookieString(url) };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function getMatchingHostHeaders(url, headersList) {
|
||||
@@ -174,30 +288,6 @@ module.exports = function init(cookieHandler, messages) {
|
||||
return mergedHeaders;
|
||||
}
|
||||
|
||||
// typeof is not working reliably in JS
|
||||
function getTypeOf(object) {
|
||||
switch (Object.prototype.toString.call(object)) {
|
||||
case '[object Array]':
|
||||
return 'Array';
|
||||
case '[object Boolean]':
|
||||
return 'Boolean';
|
||||
case '[object Function]':
|
||||
return 'Function';
|
||||
case '[object Null]':
|
||||
return 'Null';
|
||||
case '[object Number]':
|
||||
return 'Number';
|
||||
case '[object Object]':
|
||||
return 'Object';
|
||||
case '[object String]':
|
||||
return 'String';
|
||||
case '[object Undefined]':
|
||||
return 'Undefined';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
function getAllowedDataTypes(dataSerializer) {
|
||||
switch (dataSerializer) {
|
||||
case 'utf8':
|
||||
@@ -210,7 +300,7 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
|
||||
function getProcessedData(data, dataSerializer) {
|
||||
var currentDataType = getTypeOf(data);
|
||||
var currentDataType = jsUtil.getTypeOf(data);
|
||||
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
|
||||
|
||||
if (allowedDataTypes.indexOf(currentDataType) === -1) {
|
||||
@@ -225,11 +315,11 @@ module.exports = function init(cookieHandler, messages) {
|
||||
}
|
||||
|
||||
function handleMissingCallbacks(successFn, failFn) {
|
||||
if (getTypeOf(successFn) !== 'Function') {
|
||||
if (jsUtil.getTypeOf(successFn) !== 'Function') {
|
||||
throw new Error(messages.MANDATORY_SUCCESS);
|
||||
}
|
||||
|
||||
if (getTypeOf(failFn) !== 'Function') {
|
||||
if (jsUtil.getTypeOf(failFn) !== 'Function') {
|
||||
throw new Error(messages.MANDATORY_FAIL);
|
||||
}
|
||||
}
|
||||
@@ -239,11 +329,13 @@ module.exports = function init(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),
|
||||
headers: checkHeadersObject(options.headers || {}),
|
||||
params: checkParamsObject(options.params || {}),
|
||||
data: getTypeOf(options.data) === 'Undefined' ? null : options.data,
|
||||
data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data,
|
||||
filePath: options.filePath || '',
|
||||
name: options.name || ''
|
||||
};
|
||||
|
||||
28
www/js-util.js
Normal file
28
www/js-util.js
Normal file
@@ -0,0 +1,28 @@
|
||||
module.exports = {
|
||||
// typeof is not working reliably in JS
|
||||
getTypeOf: function (object) {
|
||||
switch (Object.prototype.toString.call(object)) {
|
||||
case '[object Array]':
|
||||
return 'Array';
|
||||
case '[object ArrayBuffer]':
|
||||
return 'ArrayBuffer';
|
||||
case '[object Boolean]':
|
||||
return 'Boolean';
|
||||
case '[object Function]':
|
||||
return 'Function';
|
||||
case '[object Null]':
|
||||
return 'Null';
|
||||
case '[object Number]':
|
||||
return 'Number';
|
||||
case '[object Object]':
|
||||
return 'Object';
|
||||
case '[object String]':
|
||||
return 'String';
|
||||
case '[object Undefined]':
|
||||
return 'Undefined';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +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_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_PKCS_PASSWORD: 'advanced-http: invalid PKCS12 container password, needs to be a string',
|
||||
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_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
|
||||
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value',
|
||||
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',
|
||||
INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings'
|
||||
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
|
||||
MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
|
||||
};
|
||||
|
||||
@@ -12,8 +12,14 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
getCookieString: getCookieString,
|
||||
getRequestTimeout: getRequestTimeout,
|
||||
setRequestTimeout: setRequestTimeout,
|
||||
setSSLCertMode: setSSLCertMode,
|
||||
getFollowRedirect: getFollowRedirect,
|
||||
setFollowRedirect: setFollowRedirect,
|
||||
// @DEPRECATED
|
||||
disableRedirect: disableRedirect,
|
||||
// @DEPRECATED
|
||||
setSSLCertMode: setServerTrustMode,
|
||||
setServerTrustMode: setServerTrustMode,
|
||||
setClientAuthMode: setClientAuthMode,
|
||||
sendRequest: sendRequest,
|
||||
post: post,
|
||||
get: get,
|
||||
@@ -85,15 +91,49 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
}
|
||||
|
||||
function setRequestTimeout(timeout) {
|
||||
globalConfigs.timeout = timeout;
|
||||
globalConfigs.timeout = helpers.checkTimeoutValue(timeout);
|
||||
}
|
||||
|
||||
function setSSLCertMode(mode, success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'setSSLCertMode', [helpers.checkSSLCertMode(mode)]);
|
||||
function getFollowRedirect() {
|
||||
return globalConfigs.followRedirect;
|
||||
}
|
||||
|
||||
function setFollowRedirect(follow) {
|
||||
globalConfigs.followRedirect = helpers.checkFollowRedirectValue(follow);
|
||||
}
|
||||
|
||||
// @DEPRECATED
|
||||
function disableRedirect(disable, success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [!!disable]);
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
setFollowRedirect(!disable);
|
||||
success();
|
||||
}
|
||||
|
||||
function setServerTrustMode(mode, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'setServerTrustMode', [helpers.checkSSLCertMode(mode)]);
|
||||
}
|
||||
|
||||
function setClientAuthMode() {
|
||||
var mode = arguments[0];
|
||||
var options = null;
|
||||
var success = arguments[1];
|
||||
var failure = arguments[2];
|
||||
|
||||
if (arguments.length === 4) {
|
||||
options = arguments[1];
|
||||
success = arguments[2];
|
||||
failure = arguments[3];
|
||||
}
|
||||
|
||||
mode = helpers.checkClientAuthMode(mode);
|
||||
options = helpers.checkClientAuthOptions(mode, options);
|
||||
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'setClientAuthMode', [mode, options.alias, options.rawPkcs, options.pkcsPassword]);
|
||||
}
|
||||
|
||||
function sendRequest(url, options, success, failure) {
|
||||
@@ -103,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]);
|
||||
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]);
|
||||
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]);
|
||||
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]);
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect, options.responseType]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function init(helpers) {
|
||||
module.exports = function init(jsUtil) {
|
||||
return {
|
||||
parseUrl: parseUrl,
|
||||
appendQueryParamsString: appendQueryParamsString,
|
||||
@@ -48,10 +48,10 @@ module.exports = function init(helpers) {
|
||||
|
||||
var identifier = parentKey.length ? parentKey + '[' + key + ']' : key;
|
||||
|
||||
if (helpers.getTypeOf(object[key]) === 'Array') {
|
||||
if (jsUtil.getTypeOf(object[key]) === 'Array') {
|
||||
parts.push(serializeArray(identifier, object[key], encode));
|
||||
continue;
|
||||
} else if (helpers.getTypeOf(object[key]) === 'Object') {
|
||||
} else if (jsUtil.getTypeOf(object[key]) === 'Object') {
|
||||
parts.push(serializeObject(identifier, object[key], encode));
|
||||
continue;
|
||||
}
|
||||
@@ -66,10 +66,10 @@ module.exports = function init(helpers) {
|
||||
var parts = [];
|
||||
|
||||
for (var i = 0; i < array.length; ++i) {
|
||||
if (helpers.getTypeOf(array[i]) === 'Array') {
|
||||
if (jsUtil.getTypeOf(array[i]) === 'Array') {
|
||||
parts.push(serializeArray(parentKey + '[]', array[i], encode));
|
||||
continue;
|
||||
} else if (helpers.getTypeOf(array[i]) === 'Object') {
|
||||
} else if (jsUtil.getTypeOf(array[i]) === 'Object') {
|
||||
parts.push(serializeObject(parentKey + '[]' + array[i], encode));
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user