mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-02-11 00:00:06 +08:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
516aa6b61d | ||
|
|
a6bf9041a5 | ||
|
|
c41fc11888 | ||
|
|
91515d30bd | ||
|
|
c8638ff204 | ||
|
|
0acf2e2574 | ||
|
|
f4674028b5 | ||
|
|
5919d90698 | ||
|
|
27e959800a | ||
|
|
96f45d7274 | ||
|
|
3dbd07c9df | ||
|
|
cb597c0d30 | ||
|
|
60189a68b3 | ||
|
|
8e4bfdbc70 | ||
|
|
2ae4c7cf39 | ||
|
|
32fdf49d31 | ||
|
|
db0f233737 | ||
|
|
2263950de9 | ||
|
|
2a9d3296cc | ||
|
|
55dd751cec | ||
|
|
af239d3194 | ||
|
|
291e8fe547 | ||
|
|
8b3466e3c3 | ||
|
|
73e76f6333 | ||
|
|
3fcac64597 | ||
|
|
4a437f9435 | ||
|
|
2d15b86cb5 | ||
|
|
63d859ad54 | ||
|
|
6b9ed72b9f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
node_modules/**
|
||||
test/app-template/www/certificates/*.cer
|
||||
tags
|
||||
.zedstate
|
||||
npm-debug.log
|
||||
|
||||
@@ -30,6 +30,7 @@ install:
|
||||
|
||||
script:
|
||||
- npm run testjs
|
||||
- npm run updatecert
|
||||
- travis_wait scripts/build-test-app.sh --$TARGET_PLATFORM --emulator &&
|
||||
scripts/upload-artifact.sh --$TARGET_PLATFORM &&
|
||||
scripts/test-app.sh --$TARGET_PLATFORM --emulator;
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.0
|
||||
|
||||
- Feature #103: implement HTTP SSL cert modes
|
||||
|
||||
- :warning: **Breaking Change**: Removed AngularJS (v1) integration service
|
||||
- :warning: **Breaking Change**: Removed "enableSSLPinning" and "acceptAllCerts", use "setSSLCertMode" instead
|
||||
- :warning: **Breaking Change**: Certificates must be placed in "www/certificates" folder
|
||||
|
||||
## 1.11.1
|
||||
|
||||
- Fixed #92: headers not deserialized on platform "browser"
|
||||
|
||||
## 1.11.0
|
||||
|
||||
- Feature #77: allow overriding global settings for each single request
|
||||
- Feature #11: add support for "browser" platform
|
||||
|
||||
## 1.10.2
|
||||
|
||||
- Fixed #78: overriding header "Content-Type" not working on Android
|
||||
|
||||
144
README.md
144
README.md
@@ -6,7 +6,8 @@ Cordova Advanced HTTP
|
||||
[](https://travis-ci.org/silkimen/cordova-plugin-advanced-http)
|
||||
|
||||
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS and Android.
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS, Android and [Browser](#browserSupport).
|
||||
|
||||
This is a fork of [Wymsee's Cordova-HTTP plugin](https://github.com/wymsee/cordova-HTTP).
|
||||
|
||||
## Advantages over Javascript requests
|
||||
@@ -40,19 +41,6 @@ This plugin registers a global object located at `cordova.plugin.http`.
|
||||
|
||||
Check the [Ionic docs](https://ionicframework.com/docs/native/http/) for how to use this plugin with Ionic-native.
|
||||
|
||||
### With AngularJS (Deprecated)
|
||||
|
||||
:warning: *This feature is deprecated and will be removed anytime soon.* :warning:
|
||||
|
||||
This plugin creates a cordovaHTTP service inside of a cordovaHTTP module. You must load the module when you create your app's module.
|
||||
|
||||
```js
|
||||
var app = angular.module('myApp', ['ngRoute', 'ngAnimate', 'cordovaHTTP']);
|
||||
```
|
||||
|
||||
You can then inject the cordovaHTTP service into your controllers. The functions can then be used identically to the examples shown below except that instead of accepting success and failure callback functions, each function returns a promise. For more information on promises in AngularJS read the [AngularJS docs](http://docs.angularjs.org/api/ng/service/$q). For more info on promises in general check out this article on [html5rocks](http://www.html5rocks.com/en/tutorials/es6/promises/). Make sure that you load cordova.js or phonegap.js after AngularJS is loaded.
|
||||
|
||||
|
||||
## Synchronous Functions
|
||||
|
||||
### getBasicAuthHeader
|
||||
@@ -93,14 +81,7 @@ cordova.plugin.http.setHeader('www.example.com', 'Header', 'Value');
|
||||
cordova.plugin.http.setHeader('www.example.com:8080', 'Header', 'Value');
|
||||
```
|
||||
|
||||
### disableRedirect
|
||||
If set to `true`, it won't follow redirects automatically. This is a global setting.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.disableRedirect(true);
|
||||
```
|
||||
|
||||
### setDataSerializer
|
||||
### setDataSerializer<a name="setDataSerializer"></a>
|
||||
Set the data serializer which will be used for all future PATCH, POST and PUT requests. Takes a string representing the name of the serializer.
|
||||
|
||||
```js
|
||||
@@ -147,34 +128,59 @@ cordova.plugin.http.clearCookies();
|
||||
## Asynchronous Functions
|
||||
These functions all take success and error callbacks as their last 2 arguments.
|
||||
|
||||
### enableSSLPinning
|
||||
Enable or disable SSL pinning. This defaults to false.
|
||||
### setSSLCertMode<a name="setSSLCertMode"></a>
|
||||
Set SSL Cert handling mode, being one of the following values:
|
||||
|
||||
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. For ios include your certificate in the root level of your bundle (just add the .cer file to your project/target at the root level). For android include your certificate in your project's platforms/android/assets folder. In both cases all .cer files found will be loaded automatically. If you only have a .pem certificate see this [stackoverflow answer](http://stackoverflow.com/a/16583429/3182729). You want to convert it to a DER encoded certificate with a .cer extension.
|
||||
* `default`: default SSL cert handling using system's CA certs
|
||||
* `nocheck`: disable SSL cert checking, trusting all certs (meant to be used only for testing purposes)
|
||||
* `pinned`: trust only provided certs
|
||||
|
||||
As an alternative, you can store your .cer files in the www/certificates folder.
|
||||
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.
|
||||
|
||||
:warning: Your certificate must be DER encoded! If you only have a PEM enoceded certificate see this [stackoverflow answer](http://stackoverflow.com/a/16583429/3182729). You want to convert it to a DER encoded certificate with a .cer extension.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.enableSSLPinning(true, function() {
|
||||
// enable SSL pinning
|
||||
cordova.plugin.http.setSSLCertMode('pinned', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
|
||||
// use system's default CA certs
|
||||
cordova.plugin.http.setSSLCertMode('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() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
```
|
||||
|
||||
### acceptAllCerts
|
||||
Accept all SSL certificates. Or disable accepting all certificates. This defaults to false.
|
||||
### enableSSLPinning (obsolete)
|
||||
This function was removed in 2.0.0. Use ["setSSLCertMode"](#setSSLCertMode) 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.acceptAllCerts(true, function() {
|
||||
cordova.plugin.http.disableRedirect(true, function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
```
|
||||
|
||||
### validateDomainName
|
||||
This function was removed in v1.6.2. Domain name validation is disabled automatically when you enable "acceptAllCerts".
|
||||
### validateDomainName (obsolete)
|
||||
This function was removed in v1.6.2. Domain name validation is disabled automatically when you set SSL cert mode to "nocheck".
|
||||
|
||||
### removeCookies
|
||||
Remove all cookies associated with a given URL.
|
||||
@@ -183,11 +189,47 @@ Remove all cookies associated with a given URL.
|
||||
cordova.plugin.http.removeCookies(url, callback);
|
||||
```
|
||||
|
||||
### sendRequest
|
||||
Execute a HTTP request. Takes a URL and an options object. This is the internally used implementation of the following shorthand functions ([post](#post), [get](#get), [put](#put), [patch](#patch), [delete](#delete), [head](#head), [uploadFile](#uploadFile) and [downloadFile](#downloadFile)). You can use this function, if you want to override global settings for each single request.
|
||||
|
||||
The options object contains following keys:
|
||||
|
||||
* `method`: HTTP method to be used, defaults to `get`, needs to be one of the following values:
|
||||
* `get`, `post`, `put`, `patch`, `head`, `delete`, `upload`, `download`
|
||||
* `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
|
||||
* `timeout`: timeout value for the request in seconds, defaults to global timeout value
|
||||
* `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
|
||||
|
||||
Here's a quick example:
|
||||
|
||||
```js
|
||||
const options = {
|
||||
method: 'post',
|
||||
data: { id: 12, message: 'test' },
|
||||
headers: { Authorization: 'OAuth2: token' }
|
||||
};
|
||||
|
||||
cordova.plugin.http.sendRequest('https://google.com/', options, function(response) {
|
||||
// prints 200
|
||||
console.log(response.status);
|
||||
}, function(response) {
|
||||
// prints 403
|
||||
console.log(response.status);
|
||||
|
||||
//prints Permission denied
|
||||
console.log(response.error);
|
||||
});
|
||||
```
|
||||
|
||||
### post<a name="post"></a>
|
||||
Execute a POST request. Takes a URL, data, and headers.
|
||||
|
||||
#### success
|
||||
The success function receives a response object with 3 properties: status, data, and headers. **status** is the HTTP response code as numeric value. **data** is the response from the server as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
|
||||
The success function receives a response object with 4 properties: status, data, url, and headers. **status** is the HTTP response code as numeric value. **data** is the response from the server as a string. **url** is the final URL obtained after any redirects as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
|
||||
|
||||
Here's a quick example:
|
||||
|
||||
@@ -195,6 +237,7 @@ Here's a quick example:
|
||||
{
|
||||
status: 200,
|
||||
data: '{"id": 12, "message": "test"}',
|
||||
url: 'http://example.net/rest'
|
||||
headers: {
|
||||
'content-length': '247'
|
||||
}
|
||||
@@ -227,7 +270,7 @@ cordova.plugin.http.post('https://google.com/', {
|
||||
```
|
||||
|
||||
#### failure
|
||||
The error function receives a response object with 3 properties: status, error and headers. **status** is the HTTP response code as numeric value. **error** is the error response from the server as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
|
||||
The error function receives a response object with 4 properties: status, error, url, and headers (url and headers being optional). **status** is the HTTP response code as numeric value. **error** is the error response from the server as a string. **url** is the final URL obtained after any redirects as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
|
||||
|
||||
Here's a quick example:
|
||||
|
||||
@@ -235,18 +278,19 @@ Here's a quick example:
|
||||
{
|
||||
status: 403,
|
||||
error: 'Permission denied',
|
||||
url: 'http://example.net/noperm'
|
||||
headers: {
|
||||
'content-length': '247'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### get
|
||||
### get<a name="get"></a>
|
||||
Execute a GET request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.get('https://google.com/', {
|
||||
id: 12,
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, function(response) {
|
||||
console.log(response.status);
|
||||
@@ -255,24 +299,24 @@ cordova.plugin.http.get('https://google.com/', {
|
||||
});
|
||||
```
|
||||
|
||||
### put
|
||||
### put<a name="put"></a>
|
||||
Execute a PUT request. Takes a URL, data, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
### patch
|
||||
### patch<a name="patch"></a>
|
||||
Execute a PATCH request. Takes a URL, data, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
### delete
|
||||
### delete<a name="delete"></a>
|
||||
Execute a DELETE request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
### head
|
||||
### head<a name="head"></a>
|
||||
Execute a HEAD request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
### uploadFile
|
||||
### uploadFile<a name="uploadFile"></a>
|
||||
Uploads a file saved on the device. Takes a URL, parameters, headers, filePath, and the name of the parameter to pass the file along as. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.uploadFile("https://google.com/", {
|
||||
id: 12,
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', 'picture', function(response) {
|
||||
console.log(response.status);
|
||||
@@ -281,12 +325,12 @@ cordova.plugin.http.uploadFile("https://google.com/", {
|
||||
});
|
||||
```
|
||||
|
||||
### downloadFile
|
||||
### downloadFile<a name="downloadFile"></a>
|
||||
Downloads a file and saves it to the device. Takes a URL, parameters, headers, and a filePath. See [post](#post) documentation for details on what is returned on failure. On success this function returns a cordova [FileEntry object](http://cordova.apache.org/docs/en/3.3.0/cordova_file_file.md.html#FileEntry).
|
||||
|
||||
```js
|
||||
cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
id: 12,
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry) {
|
||||
// prints the filename
|
||||
@@ -299,6 +343,18 @@ cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
});
|
||||
```
|
||||
|
||||
## Browser support<a name="browserSupport"></a>
|
||||
|
||||
This plugin supports a very restricted set of functions on the browser platform.
|
||||
It's meant for testing purposes, not for production grade usage.
|
||||
|
||||
Following features are *not* supported:
|
||||
|
||||
* Manipulating Cookies
|
||||
* Uploading and Downloading files
|
||||
* Pinning SSL certificate
|
||||
* Disabling SSL certificate check
|
||||
* Disabling transparently following redirects (HTTP codes 3xx)
|
||||
|
||||
## Libraries
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "1.10.2",
|
||||
"version": "2.0.0",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"testandroid": "./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
|
||||
"testios": "./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator",
|
||||
"updatecert": "node ./scripts/update-test-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",
|
||||
"test": "npm run testjs && npm run testapp",
|
||||
|
||||
17
plugin.xml
17
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="1.10.2">
|
||||
<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.0">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
@@ -13,7 +13,6 @@
|
||||
<js-module src="www/messages.js" name="messages"/>
|
||||
<js-module src="www/local-storage-store.js" name="local-storage-store"/>
|
||||
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
|
||||
<js-module src="www/angular-integration.js" name="angular-integration"/>
|
||||
<js-module src="www/helpers.js" name="helpers"/>
|
||||
<js-module src="www/advanced-http.js" name="http">
|
||||
<clobbers target="cordova.plugin.http"/>
|
||||
@@ -34,6 +33,7 @@
|
||||
<header-file src="src/ios/AFNetworking/AFURLRequestSerialization.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFURLResponseSerialization.h"/>
|
||||
<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/TextResponseSerializer.m"/>
|
||||
<source-file src="src/ios/TextRequestSerializer.m"/>
|
||||
@@ -43,6 +43,7 @@
|
||||
<source-file src="src/ios/AFNetworking/AFURLRequestSerialization.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFURLResponseSerialization.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFURLSessionManager.m"/>
|
||||
<source-file src="src/ios/SDNetworkActivityIndicator/SDNetworkActivityIndicator.m"/>
|
||||
<framework src="Security.framework"/>
|
||||
<framework src="SystemConfiguration.framework"/>
|
||||
</platform>
|
||||
@@ -68,6 +69,16 @@
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPut.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPatch.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpUpload.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<framework src="com.squareup.okhttp3:okhttp-urlconnection:3.9.1"/>
|
||||
<framework src="com.squareup.okhttp3:okhttp-urlconnection:3.10.0"/>
|
||||
</platform>
|
||||
<platform name="browser">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="CordovaHttpPlugin">
|
||||
<param name="browser-package" value="CordovaHttpPlugin"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
<js-module src="src/browser/cordova-http-plugin.js" name="http-proxy">
|
||||
<runs/>
|
||||
</js-module>
|
||||
</platform>
|
||||
</plugin>
|
||||
@@ -1,11 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
|
||||
TARGET=$([[ "${@#--device}" = "$@" ]] && echo "emulator" || echo "device")
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
|
||||
CDV=$ROOT/node_modules/.bin/cordova
|
||||
|
||||
PLATFORM=ios
|
||||
TARGET=emulator
|
||||
|
||||
while :; do
|
||||
case $1 in
|
||||
--android)
|
||||
PLATFORM=android
|
||||
;;
|
||||
--browser)
|
||||
PLATFORM=browser
|
||||
;;
|
||||
--ios)
|
||||
PLATFORM=ios
|
||||
;;
|
||||
--device)
|
||||
TARGET=device
|
||||
;;
|
||||
--emulator)
|
||||
TARGET=emulator
|
||||
;;
|
||||
-?*)
|
||||
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
|
||||
;;
|
||||
*)
|
||||
break
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
rm -rf $ROOT/temp
|
||||
mkdir $ROOT/temp
|
||||
cp -r $ROOT/test/app-template/ $ROOT/temp/
|
||||
|
||||
42
scripts/update-test-cert.js
Normal file
42
scripts/update-test-cert.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const fs = require('fs');
|
||||
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 getCert = hostname => new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname,
|
||||
agent: false,
|
||||
rejectUnauthorized: false,
|
||||
ciphers: 'ALL'
|
||||
};
|
||||
|
||||
const req = https.get(options, response => {
|
||||
const certificate = response.socket.getPeerCertificate();
|
||||
|
||||
if (certificate === null) {
|
||||
return reject({ message: 'The website did not provide a certificate' });
|
||||
}
|
||||
|
||||
resolve(certificate);
|
||||
});
|
||||
|
||||
req.on('error', error => {
|
||||
return reject(error)
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
console.log(`Updating test certificate from ${SOURCE_HOST}`);
|
||||
|
||||
getCert(SOURCE_HOST)
|
||||
.then(cert => {
|
||||
fs.writeFileSync(TARGET_PATH, cert.raw);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Updating test cert failed: ${error}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -263,6 +263,12 @@ public class HttpRequest {
|
||||
*/
|
||||
public static final String PARAM_CHARSET = "charset";
|
||||
|
||||
public static final String CERT_MODE_DEFAULT = "default";
|
||||
|
||||
public static final String CERT_MODE_PINNED = "pinned";
|
||||
|
||||
public static final String CERT_MODE_TRUSTALL = "trustall";
|
||||
|
||||
private static final String BOUNDARY = "00content0boundary00";
|
||||
|
||||
private static final String CONTENT_TYPE_MULTIPART = "multipart/form-data; boundary="
|
||||
@@ -272,13 +278,13 @@ public class HttpRequest {
|
||||
|
||||
private static final String[] EMPTY_STRINGS = new String[0];
|
||||
|
||||
private static SSLSocketFactory PINNED_FACTORY;
|
||||
private static SSLSocketFactory SOCKET_FACTORY;
|
||||
|
||||
private static SSLSocketFactory TRUSTED_FACTORY;
|
||||
private static String CURRENT_CERT_MODE = CERT_MODE_DEFAULT;
|
||||
|
||||
private static ArrayList<Certificate> PINNED_CERTS;
|
||||
|
||||
private static HostnameVerifier TRUSTED_VERIFIER;
|
||||
private static HostnameVerifier HOSTNAME_VERIFIER;
|
||||
|
||||
private static String getValidCharset(final String charset) {
|
||||
if (charset != null && charset.length() > 0)
|
||||
@@ -287,63 +293,97 @@ public class HttpRequest {
|
||||
return CHARSET_UTF8;
|
||||
}
|
||||
|
||||
private static SSLSocketFactory getPinnedFactory()
|
||||
throws HttpRequestException {
|
||||
if (PINNED_FACTORY != null) {
|
||||
return PINNED_FACTORY;
|
||||
} else {
|
||||
IOException e = new IOException("You must add at least 1 certificate in order to pin to certificates");
|
||||
throw new HttpRequestException(e);
|
||||
/**
|
||||
* Configure SSL cert handling for all future HTTPS connections
|
||||
*
|
||||
* @param mode
|
||||
*/
|
||||
public static void setSSLCertMode(String mode) {
|
||||
try {
|
||||
if (mode == CERT_MODE_TRUSTALL) {
|
||||
SOCKET_FACTORY = createSocketFactory(getNoopTrustManagers());
|
||||
HOSTNAME_VERIFIER = getTrustedVerifier();
|
||||
} else if (mode == CERT_MODE_PINNED) {
|
||||
SOCKET_FACTORY = createSocketFactory(getPinnedTrustManagers());
|
||||
HOSTNAME_VERIFIER = null;
|
||||
} else {
|
||||
SOCKET_FACTORY = null;
|
||||
HOSTNAME_VERIFIER = null;
|
||||
}
|
||||
|
||||
CURRENT_CERT_MODE = mode;
|
||||
} catch(IOException e) {
|
||||
throw new HttpRequestException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static SSLSocketFactory getTrustedFactory()
|
||||
throws HttpRequestException {
|
||||
if (TRUSTED_FACTORY == null) {
|
||||
final TrustManager[] trustAllCerts = 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
|
||||
}
|
||||
} };
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(null, trustAllCerts, new SecureRandom());
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 20) {
|
||||
TRUSTED_FACTORY = new TLSSocketFactory(context);
|
||||
} else {
|
||||
TRUSTED_FACTORY = context.getSocketFactory();
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
IOException ioException = new IOException(
|
||||
"Security exception configuring SSL context");
|
||||
ioException.initCause(e);
|
||||
throw new HttpRequestException(ioException);
|
||||
}
|
||||
private static TrustManager[] getPinnedTrustManagers() throws IOException {
|
||||
if (PINNED_CERTS == null) {
|
||||
throw new IOException("You must add at least 1 certificate in order to pin to certificates");
|
||||
}
|
||||
|
||||
return TRUSTED_FACTORY;
|
||||
try {
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
keyStore.load(null, null);
|
||||
|
||||
for (int i = 0; i < PINNED_CERTS.size(); i++) {
|
||||
keyStore.setCertificateEntry("CA" + i, PINNED_CERTS.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 new HttpRequestException(ioException);
|
||||
}
|
||||
}
|
||||
|
||||
private static TrustManager[] getNoopTrustManagers() {
|
||||
return 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
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
private static SSLSocketFactory createSocketFactory(TrustManager[] trustManagers)
|
||||
throws HttpRequestException {
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
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 configuring SSL context");
|
||||
ioException.initCause(e);
|
||||
throw new HttpRequestException(ioException);
|
||||
}
|
||||
}
|
||||
|
||||
private static HostnameVerifier getTrustedVerifier() {
|
||||
if (TRUSTED_VERIFIER == null)
|
||||
TRUSTED_VERIFIER = new HostnameVerifier() {
|
||||
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
return TRUSTED_VERIFIER;
|
||||
return new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static StringBuilder addPathSeparator(final String baseUrl,
|
||||
@@ -453,32 +493,15 @@ public class HttpRequest {
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void addCert(Certificate ca) throws GeneralSecurityException, IOException {
|
||||
if (PINNED_CERTS == null) {
|
||||
PINNED_CERTS = new ArrayList<Certificate>();
|
||||
}
|
||||
PINNED_CERTS.add(ca);
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
keyStore.load(null, null);
|
||||
if (PINNED_CERTS == null) {
|
||||
PINNED_CERTS = new ArrayList<Certificate>();
|
||||
}
|
||||
|
||||
for (int i = 0; i < PINNED_CERTS.size(); i++) {
|
||||
keyStore.setCertificateEntry("CA" + i, PINNED_CERTS.get(i));
|
||||
}
|
||||
PINNED_CERTS.add(ca);
|
||||
|
||||
// Create a TrustManager that trusts the CAs in our KeyStore
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
tmf.init(keyStore);
|
||||
|
||||
// Create an SSLContext that uses our TrustManager
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, tmf.getTrustManagers(), null);
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 20) {
|
||||
PINNED_FACTORY = new TLSSocketFactory(sslContext);
|
||||
} else {
|
||||
PINNED_FACTORY = sslContext.getSocketFactory();
|
||||
}
|
||||
if (CURRENT_CERT_MODE == CERT_MODE_PINNED) {
|
||||
SOCKET_FACTORY = createSocketFactory(getPinnedTrustManagers());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1632,6 +1655,7 @@ public class HttpRequest {
|
||||
throw new HttpRequestException(e);
|
||||
}
|
||||
this.requestMethod = method;
|
||||
this.setupSecurity();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1645,6 +1669,23 @@ public class HttpRequest {
|
||||
throws HttpRequestException {
|
||||
this.url = url;
|
||||
this.requestMethod = method;
|
||||
this.setupSecurity();
|
||||
}
|
||||
|
||||
private void setupSecurity() {
|
||||
final HttpURLConnection connection = getConnection();
|
||||
|
||||
if (!(connection instanceof HttpsURLConnection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SOCKET_FACTORY != null) {
|
||||
((HttpsURLConnection) connection).setSSLSocketFactory(SOCKET_FACTORY);
|
||||
}
|
||||
|
||||
if (HOSTNAME_VERIFIER != null) {
|
||||
((HttpsURLConnection) connection).setHostnameVerifier(HOSTNAME_VERIFIER);
|
||||
}
|
||||
}
|
||||
|
||||
private Proxy createProxy() {
|
||||
@@ -3351,58 +3392,6 @@ public class HttpRequest {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure HTTPS connection to trust only certain certificates
|
||||
* <p>
|
||||
* This method throws an exception if the current request is not a HTTPS request
|
||||
*
|
||||
* @return this request
|
||||
* @throws HttpRequestException
|
||||
*/
|
||||
public HttpRequest pinToCerts() throws HttpRequestException {
|
||||
final HttpURLConnection connection = getConnection();
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection) connection).setSSLSocketFactory(getPinnedFactory());
|
||||
} else {
|
||||
IOException e = new IOException("You must use a https url to use ssl pinning");
|
||||
throw new HttpRequestException(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure HTTPS connection to trust all certificates
|
||||
* <p>
|
||||
* This method does nothing if the current request is not a HTTPS request
|
||||
*
|
||||
* @return this request
|
||||
* @throws HttpRequestException
|
||||
*/
|
||||
public HttpRequest trustAllCerts() throws HttpRequestException {
|
||||
final HttpURLConnection connection = getConnection();
|
||||
if (connection instanceof HttpsURLConnection)
|
||||
((HttpsURLConnection) connection)
|
||||
.setSSLSocketFactory(getTrustedFactory());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure HTTPS connection to trust all hosts using a custom
|
||||
* {@link HostnameVerifier} that always returns <code>true</code> for each
|
||||
* host verified
|
||||
* <p>
|
||||
* This method does nothing if the current request is not a HTTPS request
|
||||
*
|
||||
* @return this request
|
||||
*/
|
||||
public HttpRequest trustAllHosts() {
|
||||
final HttpURLConnection connection = getConnection();
|
||||
if (connection instanceof HttpsURLConnection)
|
||||
((HttpsURLConnection) connection)
|
||||
.setHostnameVerifier(getTrustedVerifier());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link URL} of this request's connection
|
||||
*
|
||||
|
||||
@@ -38,10 +38,6 @@ import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
abstract class CordovaHttp {
|
||||
protected static final String TAG = "CordovaHTTP";
|
||||
protected static final String[] ACCEPTED_CHARSETS = new String[] { HttpRequest.CHARSET_UTF8, HttpRequest.CHARSET_LATIN1 };
|
||||
|
||||
private static AtomicBoolean sslPinning = new AtomicBoolean(false);
|
||||
private static AtomicBoolean acceptAllCerts = new AtomicBoolean(false);
|
||||
private static AtomicBoolean validateDomainName = new AtomicBoolean(true);
|
||||
private static AtomicBoolean disableRedirect = new AtomicBoolean(false);
|
||||
|
||||
private String urlString;
|
||||
@@ -64,24 +60,6 @@ abstract class CordovaHttp {
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
public static void enableSSLPinning(boolean enable) {
|
||||
sslPinning.set(enable);
|
||||
if (enable) {
|
||||
acceptAllCerts.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void acceptAllCerts(boolean accept) {
|
||||
acceptAllCerts.set(accept);
|
||||
if (accept) {
|
||||
sslPinning.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateDomainName(boolean accept) {
|
||||
validateDomainName.set(accept);
|
||||
}
|
||||
|
||||
public static void disableRedirect(boolean disable) {
|
||||
disableRedirect.set(disable);
|
||||
}
|
||||
@@ -122,20 +100,6 @@ abstract class CordovaHttp {
|
||||
return this.callbackContext;
|
||||
}
|
||||
|
||||
protected HttpRequest setupSecurity(HttpRequest request) {
|
||||
if (acceptAllCerts.get()) {
|
||||
request.trustAllCerts();
|
||||
}
|
||||
if (!validateDomainName.get()) {
|
||||
request.trustAllHosts();
|
||||
}
|
||||
if (sslPinning.get()) {
|
||||
request.pinToCerts();
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
protected HttpRequest setupRedirect(HttpRequest request) {
|
||||
if (disableRedirect.get()) {
|
||||
request.followRedirects(false);
|
||||
@@ -222,7 +186,6 @@ abstract class CordovaHttp {
|
||||
|
||||
protected void prepareRequest(HttpRequest request) throws HttpRequestException, JSONException {
|
||||
this.setupRedirect(request);
|
||||
this.setupSecurity(request);
|
||||
|
||||
request.readTimeout(this.getRequestTimeout());
|
||||
request.acceptCharset(ACCEPTED_CHARSETS);
|
||||
|
||||
@@ -86,21 +86,25 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
CordovaHttpHead head = new CordovaHttpHead(urlString, params, headers, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(head);
|
||||
} else if (action.equals("enableSSLPinning")) {
|
||||
try {
|
||||
boolean enable = args.getBoolean(0);
|
||||
this.enableSSLPinning(enable);
|
||||
callbackContext.success();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("There was an error setting up ssl pinning");
|
||||
}
|
||||
} else if (action.equals("acceptAllCerts")) {
|
||||
boolean accept = args.getBoolean(0);
|
||||
} else if (action.equals("setSSLCertMode")) {
|
||||
String mode = args.getString(0);
|
||||
|
||||
CordovaHttp.acceptAllCerts(accept);
|
||||
CordovaHttp.validateDomainName(!accept);
|
||||
callbackContext.success();
|
||||
if (mode.equals("default")) {
|
||||
HttpRequest.setSSLCertMode(HttpRequest.CERT_MODE_DEFAULT);
|
||||
callbackContext.success();
|
||||
} else if (mode.equals("nocheck")) {
|
||||
HttpRequest.setSSLCertMode(HttpRequest.CERT_MODE_TRUSTALL);
|
||||
callbackContext.success();
|
||||
} else if (mode.equals("pinned")) {
|
||||
try {
|
||||
this.loadSSLCerts();
|
||||
HttpRequest.setSSLCertMode(HttpRequest.CERT_MODE_PINNED);
|
||||
callbackContext.success();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("There was an error setting up ssl pinning");
|
||||
}
|
||||
}
|
||||
} else if (action.equals("uploadFile")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
@@ -121,49 +125,33 @@ public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
|
||||
cordova.getThreadPool().execute(download);
|
||||
} else if (action.equals("disableRedirect")) {
|
||||
boolean disable = args.getBoolean(0);
|
||||
CordovaHttp.disableRedirect(disable);
|
||||
callbackContext.success();
|
||||
boolean disable = args.getBoolean(0);
|
||||
CordovaHttp.disableRedirect(disable);
|
||||
callbackContext.success();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void enableSSLPinning(boolean enable) throws GeneralSecurityException, IOException {
|
||||
if (enable) {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
String[] files = assetManager.list("");
|
||||
int index;
|
||||
ArrayList<String> cerFiles = new ArrayList<String>();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
index = files[i].lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
if (files[i].substring(index).equals(".cer")) {
|
||||
cerFiles.add(files[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void loadSSLCerts() throws GeneralSecurityException, IOException {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
String[] files = assetManager.list("www/certificates");
|
||||
ArrayList<String> cerFiles = new ArrayList<String>();
|
||||
|
||||
// scan the www/certificates folder for .cer files as well
|
||||
files = assetManager.list("www/certificates");
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
index = files[i].lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
if (files[i].substring(index).equals(".cer")) {
|
||||
cerFiles.add("www/certificates/" + files[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
int index = files[i].lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
if (files[i].substring(index).equals(".cer")) {
|
||||
cerFiles.add("www/certificates/" + files[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < cerFiles.size(); i++) {
|
||||
InputStream in = cordova.getActivity().getAssets().open(cerFiles.get(i));
|
||||
InputStream caInput = new BufferedInputStream(in);
|
||||
HttpRequest.addCert(caInput);
|
||||
}
|
||||
CordovaHttp.enableSSLPinning(true);
|
||||
} else {
|
||||
CordovaHttp.enableSSLPinning(false);
|
||||
for (int i = 0; i < cerFiles.size(); i++) {
|
||||
InputStream in = cordova.getActivity().getAssets().open(cerFiles.get(i));
|
||||
InputStream caInput = new BufferedInputStream(in);
|
||||
HttpRequest.addCert(caInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
181
src/browser/cordova-http-plugin.js
vendored
Normal file
181
src/browser/cordova-http-plugin.js
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
|
||||
var cordovaProxy = require('cordova/exec/proxy');
|
||||
var helpers = require(pluginId + '.helpers');
|
||||
|
||||
function serializeJsonData(data) {
|
||||
try {
|
||||
return JSON.stringify(data);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function serializePrimitive(key, value) {
|
||||
if (value === null || value === undefined) {
|
||||
return encodeURIComponent(key) + '=';
|
||||
}
|
||||
|
||||
return encodeURIComponent(key) + '=' + encodeURIComponent(value);
|
||||
}
|
||||
|
||||
function serializeArray(key, values) {
|
||||
return values.map(function(value) {
|
||||
return encodeURIComponent(key) + '[]=' + encodeURIComponent(value);
|
||||
}).join('&');
|
||||
}
|
||||
|
||||
function serializeParams(params) {
|
||||
if (params === null) return '';
|
||||
|
||||
return Object.keys(params).map(function(key) {
|
||||
if (helpers.getTypeOf(params[key]) === 'Array') {
|
||||
return serializeArray(key, params[key]);
|
||||
}
|
||||
|
||||
return serializePrimitive(key, params[key]);
|
||||
}).join('&');
|
||||
}
|
||||
|
||||
function deserializeResponseHeaders(headers) {
|
||||
var headerMap = {};
|
||||
var arr = headers.trim().split(/[\r\n]+/);
|
||||
|
||||
arr.forEach(function (line) {
|
||||
var parts = line.split(': ');
|
||||
var header = parts.shift().toLowerCase();
|
||||
var value = parts.join(': ');
|
||||
|
||||
headerMap[header] = value;
|
||||
});
|
||||
|
||||
return headerMap;
|
||||
}
|
||||
|
||||
function createXhrSuccessObject(xhr) {
|
||||
return {
|
||||
url: xhr.responseURL,
|
||||
status: xhr.status,
|
||||
data: helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response,
|
||||
headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
|
||||
};
|
||||
}
|
||||
|
||||
function createXhrFailureObject(xhr) {
|
||||
var obj = {};
|
||||
|
||||
obj.headers = xhr.getAllResponseHeaders();
|
||||
obj.error = helpers.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;
|
||||
if (xhr.status) obj.status = xhr.status;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function setHeaders(xhr, headers) {
|
||||
Object.keys(headers).forEach(function(key) {
|
||||
if (key === 'Cookie') return;
|
||||
|
||||
xhr.setRequestHeader(key, headers[key]);
|
||||
});
|
||||
}
|
||||
|
||||
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 processedData = null;
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open(method, url);
|
||||
|
||||
if (headers.Cookie && headers.Cookie.length > 0) {
|
||||
return failure('advanced-http: custom cookies not supported on browser platform');
|
||||
}
|
||||
|
||||
switch (serializer) {
|
||||
case 'json':
|
||||
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf8');
|
||||
processedData = serializeJsonData(data);
|
||||
|
||||
if (processedData === null) {
|
||||
return failure('advanced-http: failed serializing data');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'utf8':
|
||||
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf8');
|
||||
processedData = data.text;
|
||||
break;
|
||||
|
||||
case 'urlencoded':
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
processedData = serializeParams(data);
|
||||
break;
|
||||
}
|
||||
|
||||
xhr.timeout = timeout * 1000;
|
||||
setHeaders(xhr, headers);
|
||||
|
||||
xhr.onerror = xhr.ontimeout = function () {
|
||||
return failure(createXhrFailureObject(xhr));
|
||||
};
|
||||
|
||||
xhr.onload = function () {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
|
||||
if (xhr.status < 200 || xhr.status > 299) {
|
||||
return failure(createXhrFailureObject(xhr));
|
||||
}
|
||||
|
||||
return success(createXhrSuccessObject(xhr));
|
||||
};
|
||||
|
||||
xhr.send(processedData);
|
||||
}
|
||||
|
||||
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);
|
||||
},
|
||||
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');
|
||||
},
|
||||
enableSSLPinning: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "enableSSLPinning" not supported on browser platform');
|
||||
},
|
||||
acceptAllCerts: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "acceptAllCerts" not supported on browser platform');
|
||||
},
|
||||
disableRedirect: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "disableRedirect" not supported on browser platform');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = browserInterface;
|
||||
cordovaProxy.add('CordovaHttpPlugin', browserInterface);
|
||||
@@ -156,16 +156,9 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
@implementation AFSecurityPolicy
|
||||
|
||||
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
|
||||
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
|
||||
|
||||
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"www/certificates"];
|
||||
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
|
||||
for (NSString *path in paths) {
|
||||
NSData *certificateData = [NSData dataWithContentsOfFile:path];
|
||||
[certificates addObject:certificateData];
|
||||
}
|
||||
|
||||
// also add certs from www/certificates
|
||||
paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"www/certificates"];
|
||||
|
||||
for (NSString *path in paths) {
|
||||
NSData *certificateData = [NSData dataWithContentsOfFile:path];
|
||||
[certificates addObject:certificateData];
|
||||
@@ -284,13 +277,13 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
|
||||
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
|
||||
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
|
||||
|
||||
|
||||
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
|
||||
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
case AFSSLPinningModePublicKey: {
|
||||
@@ -307,7 +300,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
return trustedPublicKeyCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
@interface CordovaHttpPlugin : CDVPlugin
|
||||
|
||||
- (void)enableSSLPinning:(CDVInvokedUrlCommand*)command;
|
||||
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command;
|
||||
- (void)setSSLCertMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)disableRedirect:(CDVInvokedUrlCommand*)command;
|
||||
- (void)post:(CDVInvokedUrlCommand*)command;
|
||||
- (void)get:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#import "TextResponseSerializer.h"
|
||||
#import "TextRequestSerializer.h"
|
||||
#import "AFHTTPSessionManager.h"
|
||||
#import "SDNetworkActivityIndicator.h"
|
||||
|
||||
@interface CordovaHttpPlugin()
|
||||
|
||||
@@ -120,23 +121,31 @@
|
||||
return headerFieldsCopy;
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
|
||||
[manager.requestSerializer setTimeoutInterval:timeout];
|
||||
}
|
||||
- (void)setSSLCertMode:(CDVInvokedUrlCommand*)command {
|
||||
NSString *certMode = [command.arguments objectAtIndex:0];
|
||||
|
||||
- (void)enableSSLPinning:(CDVInvokedUrlCommand*)command {
|
||||
bool enable = [[command.arguments objectAtIndex:0] boolValue];
|
||||
|
||||
if (enable) {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
|
||||
} else {
|
||||
if ([certMode isEqualToString: @"default"]) {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
securityPolicy.allowInvalidCertificates = NO;
|
||||
securityPolicy.validatesDomainName = YES;
|
||||
} else if ([certMode isEqualToString: @"nocheck"]) {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
securityPolicy.allowInvalidCertificates = YES;
|
||||
securityPolicy.validatesDomainName = NO;
|
||||
} else if ([certMode isEqualToString: @"pinned"]) {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
|
||||
securityPolicy.allowInvalidCertificates = NO;
|
||||
securityPolicy.validatesDomainName = YES;
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[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];
|
||||
@@ -147,17 +156,6 @@
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
bool allow = [[command.arguments objectAtIndex:0] boolValue];
|
||||
|
||||
securityPolicy.allowInvalidCertificates = allow;
|
||||
securityPolicy.validatesDomainName = !allow;
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)post:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
@@ -175,6 +173,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager POST:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
@@ -183,15 +182,18 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
@@ -213,6 +215,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager GET:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
@@ -221,15 +224,18 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
@@ -251,6 +257,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager PUT:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
|
||||
@@ -259,15 +266,18 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
@@ -289,6 +299,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager PATCH:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
|
||||
@@ -297,15 +308,18 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
@@ -326,6 +340,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager DELETE:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
|
||||
@@ -334,15 +349,18 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
@@ -361,6 +379,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager HEAD:url parameters:parameters success:^(NSURLSessionTask *task) {
|
||||
@@ -370,15 +389,18 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
@@ -402,6 +424,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
|
||||
@@ -413,6 +436,7 @@
|
||||
[dictionary setObject:@"Could not add file to post body." forKey:@"error"];
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
return;
|
||||
}
|
||||
} progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
@@ -421,15 +445,18 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
@@ -455,6 +482,7 @@
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager GET:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
@@ -495,6 +523,7 @@
|
||||
}
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
return;
|
||||
}
|
||||
NSData *data = (NSData *)responseObject;
|
||||
@@ -504,6 +533,7 @@
|
||||
[dictionary setObject:@"Could not write the data to the given filePath." forKey:@"error"];
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -514,6 +544,7 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
@@ -521,9 +552,11 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
|
||||
20
src/ios/SDNetworkActivityIndicator/LICENSE
Normal file
20
src/ios/SDNetworkActivityIndicator/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
52
src/ios/SDNetworkActivityIndicator/README.md
Normal file
52
src/ios/SDNetworkActivityIndicator/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# SDNetworkActivityIndicator
|
||||
|
||||
Handle showing / hiding of the iOS network activity indicator to allow multiple concurrent threads to show / hide the indicator such that the indicator remains visible until all the requests have completed and requested the indicator to be hidden.
|
||||
|
||||
## Requirements
|
||||
|
||||
* iOS 5.0 or later.
|
||||
* ARC memory management.
|
||||
|
||||
## Installation
|
||||
|
||||
The easiest way to install it is by copying the following files to your project:
|
||||
|
||||
* SDNetworkActivityIndicator.h
|
||||
* SDNetworkActivityIndicator.m
|
||||
|
||||
## Usage
|
||||
|
||||
* When you start a network activity (will show the network activity indicator):
|
||||
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
* When you finish a network activity (will hide the network activity indicator only if the number of calls to `stopActivity` matches the number of calls to `startActivity`):
|
||||
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
|
||||
* To hide the network activity indicator regardless of whether all activities have finished (without having to call `stopActivity` for each `startActivity` called):
|
||||
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopAllActivity];
|
||||
|
||||
|
||||
## License
|
||||
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* This file is part of the SDNetworkActivityIndicator package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SDNetworkActivityIndicator : NSObject
|
||||
|
||||
+ (id)sharedActivityIndicator;
|
||||
- (void)startActivity;
|
||||
- (void)stopActivity;
|
||||
- (void)stopAllActivity;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of the SDNetworkActivityIndicator package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "SDNetworkActivityIndicator.h"
|
||||
|
||||
@interface SDNetworkActivityIndicator()
|
||||
{
|
||||
@private NSUInteger counter;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation SDNetworkActivityIndicator
|
||||
|
||||
+ (instancetype) sharedActivityIndicator
|
||||
{
|
||||
static id _sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startActivity
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
counter++;
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopActivity
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
if (counter > 0 && --counter == 0)
|
||||
{
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopAllActivity
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
counter = 0;
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -83,10 +83,10 @@ describe('Advanced HTTP', function() {
|
||||
}));
|
||||
|
||||
testDefinitions.tests.forEach((definition, index) => {
|
||||
it(definition.description, function() {
|
||||
it(index + ': ' + definition.description, function() {
|
||||
return clickNext()
|
||||
.then(() => validateTestIndex(index))
|
||||
.then(() => validateTestTitle(this.test.title))
|
||||
.then(() => validateTestTitle(definition.description))
|
||||
.then(() => waitToBeFinished(definition.timeout || 10000))
|
||||
.then(() => validateResult(definition))
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
<engine name="android" spec="6.2.3" />
|
||||
<engine name="browser" spec="5.0.0" />
|
||||
<engine name="ios" spec="4.4.0" />
|
||||
<plugin name="cordova-plugin-file" spec="4.3.3" />
|
||||
</widget>
|
||||
|
||||
0
test/app-template/www/certificates/.gitkeep
Normal file
0
test/app-template/www/certificates/.gitkeep
Normal file
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com '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:;">
|
||||
<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">
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
const hooks = {
|
||||
onBeforeEachTest: function(done) {
|
||||
cordova.plugin.http.clearCookies();
|
||||
cordova.plugin.http.acceptAllCerts(false, function() {
|
||||
cordova.plugin.http.enableSSLPinning(false, done, done);
|
||||
}, done);
|
||||
helpers.setDefaultCertMode(done);
|
||||
}
|
||||
};
|
||||
|
||||
const helpers = {
|
||||
acceptAllCerts: function(done) { cordova.plugin.http.acceptAllCerts(true, done, done); },
|
||||
enableSSLPinning: function(done) { cordova.plugin.http.enableSSLPinning(true, done, done); },
|
||||
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')); },
|
||||
@@ -82,7 +81,7 @@ const tests = [
|
||||
},{
|
||||
description: 'should accept bad cert (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.acceptAllCerts,
|
||||
before: helpers.setNoCheckCertMode,
|
||||
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');
|
||||
@@ -91,7 +90,7 @@ const tests = [
|
||||
},{
|
||||
description: 'should accept bad cert (PUT)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PUT is not allowed',
|
||||
before: helpers.acceptAllCerts,
|
||||
before: helpers.setNoCheckCertMode,
|
||||
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');
|
||||
@@ -100,7 +99,7 @@ const tests = [
|
||||
},{
|
||||
description: 'should accept bad cert (POST)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because POST is not allowed',
|
||||
before: helpers.acceptAllCerts,
|
||||
before: helpers.setNoCheckCertMode,
|
||||
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');
|
||||
@@ -109,7 +108,7 @@ const tests = [
|
||||
},{
|
||||
description: 'should accept bad cert (PATCH)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PATCH is not allowed',
|
||||
before: helpers.acceptAllCerts,
|
||||
before: helpers.setNoCheckCertMode,
|
||||
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');
|
||||
@@ -118,7 +117,7 @@ const tests = [
|
||||
},{
|
||||
description: 'should accept bad cert (DELETE)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because DELETE is not allowed',
|
||||
before: helpers.acceptAllCerts,
|
||||
before: helpers.setNoCheckCertMode,
|
||||
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');
|
||||
@@ -127,7 +126,7 @@ const tests = [
|
||||
},{
|
||||
description: 'should fetch data from http://httpbin.org/ (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.acceptAllCerts,
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
@@ -288,14 +287,14 @@ const tests = [
|
||||
.should.be.equal('http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
|
||||
}
|
||||
},{
|
||||
description: 'should reject non-string values in local header object #54',
|
||||
expected: 'rejected: {"status": 0, "error": "advanced-http: header values must be strings" ...',
|
||||
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) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, { myTestHeader: 1 }, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.error.should.be.equal('advanced-http: header values must be strings');
|
||||
result.type.should.be.equal('throwed');
|
||||
result.message.should.be.equal('advanced-http: header values must be strings');
|
||||
}
|
||||
},{
|
||||
description: 'should throw an error while setting non-string value as global header #54',
|
||||
@@ -430,12 +429,24 @@ const tests = [
|
||||
},{
|
||||
description: 'should pin SSL cert correctly (GET)',
|
||||
expected: 'resolved: {"status": 200 ...',
|
||||
before: helpers.enableSSLPinning,
|
||||
before: helpers.setPinnedCertMode,
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('https://httpbin.org', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
}
|
||||
},{
|
||||
description: 'should reject when pinned cert does not match received server cert (GET)',
|
||||
expected: 'rejected: {"status": -1 ...',
|
||||
before: helpers.setPinnedCertMode,
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('https://sha512.badssl.com/', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
|
||||
}
|
||||
},{
|
||||
description: 'should send deeply structured JSON object correctly (POST) #65',
|
||||
@@ -464,6 +475,15 @@ const tests = [
|
||||
result.data.status.should.be.equal(403);
|
||||
result.data.error.should.be.equal('There was an error downloading the file');
|
||||
}
|
||||
},{
|
||||
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) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
JSON.parse(result.data.data).gzipped.should.be.equal(true);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -27,9 +27,7 @@ describe('Advanced HTTP www interface', function() {
|
||||
mock('cordova/exec', noop);
|
||||
mock(`${PLUGIN_ID}.cookie-handler`, {});
|
||||
mock(`${HELPERS_ID}.cookie-handler`, {});
|
||||
mock(`${PLUGIN_ID}.messages`, require('../www/messages'));
|
||||
mock(`${HELPERS_ID}.messages`, require('../www/messages'));
|
||||
mock(`${PLUGIN_ID}.angular-integration`, { registerService: noop });
|
||||
|
||||
loadHttp();
|
||||
});
|
||||
|
||||
@@ -1,238 +1,125 @@
|
||||
/*global angular*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Modified by Andrew Stephan for Sync OnSet
|
||||
* Modified by Sefa Ilkimen
|
||||
*/
|
||||
|
||||
/*
|
||||
* An HTTP Plugin for PhoneGap.
|
||||
* A native HTTP Plugin for Cordova / PhoneGap.
|
||||
*/
|
||||
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
var angularIntegration = require(pluginId +'.angular-integration');
|
||||
var cookieHandler = require(pluginId + '.cookie-handler');
|
||||
var helpers = require(pluginId + '.helpers');
|
||||
var messages = require(pluginId + '.messages');
|
||||
|
||||
var internals = {
|
||||
var globalConfigs = {
|
||||
headers: {},
|
||||
dataSerializer: 'urlencoded',
|
||||
timeoutInSeconds: 60.0,
|
||||
serializer: 'urlencoded',
|
||||
timeout: 60.0,
|
||||
};
|
||||
|
||||
var publicInterface = {
|
||||
getBasicAuthHeader: function (username, password) {
|
||||
return {'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)};
|
||||
},
|
||||
useBasicAuth: function (username, password) {
|
||||
this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password));
|
||||
},
|
||||
getHeaders: function (host) {
|
||||
return internals.headers[host || '*'] || null;
|
||||
},
|
||||
setHeader: function () {
|
||||
// this one is for being backward compatible
|
||||
var host = '*';
|
||||
var header = arguments[0];
|
||||
var value = arguments[1];
|
||||
getBasicAuthHeader: function (username, password) {
|
||||
return {'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)};
|
||||
},
|
||||
useBasicAuth: function (username, password) {
|
||||
this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password));
|
||||
},
|
||||
getHeaders: function (host) {
|
||||
return globalConfigs.headers[host || '*'] || null;
|
||||
},
|
||||
setHeader: function () {
|
||||
// this one is for being backward compatible
|
||||
var host = '*';
|
||||
var header = arguments[0];
|
||||
var value = arguments[1];
|
||||
|
||||
if (arguments.length === 3) {
|
||||
host = arguments[0];
|
||||
header = arguments[1];
|
||||
value = arguments[2];
|
||||
}
|
||||
|
||||
if (header.toLowerCase() === 'cookie') {
|
||||
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
if (helpers.getTypeOf(value) !== 'String') {
|
||||
throw new Error(messages.HEADER_VALUE_MUST_BE_STRING);
|
||||
}
|
||||
|
||||
internals.headers[host] = internals.headers[host] || {};
|
||||
internals.headers[host][header] = value;
|
||||
},
|
||||
getDataSerializer: function () {
|
||||
return internals.dataSerializer;
|
||||
},
|
||||
setDataSerializer: function (serializer) {
|
||||
internals.dataSerializer = helpers.checkSerializer(serializer);
|
||||
},
|
||||
setCookie: function (url, cookie, options) {
|
||||
cookieHandler.setCookie(url, cookie, options);
|
||||
},
|
||||
clearCookies: function () {
|
||||
cookieHandler.clearCookies();
|
||||
},
|
||||
removeCookies: function (url, callback) {
|
||||
cookieHandler.removeCookies(url, callback);
|
||||
},
|
||||
getCookieString: function (url) {
|
||||
return cookieHandler.getCookieString(url);
|
||||
},
|
||||
getRequestTimeout: function () {
|
||||
return internals.timeoutInSeconds;
|
||||
},
|
||||
setRequestTimeout: function (timeout) {
|
||||
internals.timeoutInSeconds = timeout;
|
||||
},
|
||||
enableSSLPinning: function (enable, success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'enableSSLPinning', [enable]);
|
||||
},
|
||||
acceptAllCerts: function (allow, success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'acceptAllCerts', [allow]);
|
||||
},
|
||||
disableRedirect: function (disable, success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [disable]);
|
||||
},
|
||||
validateDomainName: function (validate, success, failure) {
|
||||
failure(messages.DEPRECATED_VDN);
|
||||
},
|
||||
post: function (url, data, headers, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
data = helpers.getProcessedData(data, internals.dataSerializer);
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'post', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]);
|
||||
},
|
||||
get: function (url, params, headers, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
params = params || {};
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'get', [url, params, headers, internals.timeoutInSeconds]);
|
||||
},
|
||||
put: function (url, data, headers, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
data = helpers.getProcessedData(data, internals.dataSerializer);
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'put', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]);
|
||||
},
|
||||
|
||||
patch: function (url, data, headers, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
data = helpers.getProcessedData(data, internals.dataSerializer);
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'patch', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]);
|
||||
},
|
||||
|
||||
delete: function (url, params, headers, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
params = params || {};
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'delete', [url, params, headers, internals.timeoutInSeconds]);
|
||||
},
|
||||
head: function (url, params, headers, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
params = params || {};
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'head', [url, params, headers, internals.timeoutInSeconds]);
|
||||
},
|
||||
uploadFile: function (url, params, headers, filePath, name, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
params = params || {};
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name, internals.timeoutInSeconds]);
|
||||
},
|
||||
downloadFile: function (url, params, headers, filePath, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
params = params || {};
|
||||
headers = helpers.getMergedHeaders(url, headers, internals.headers);
|
||||
|
||||
if (!helpers.checkHeaders(headers)) {
|
||||
return helpers.onInvalidHeader(failure);
|
||||
}
|
||||
|
||||
var onSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, params, headers, filePath, internals.timeoutInSeconds]);
|
||||
if (arguments.length === 3) {
|
||||
host = arguments[0];
|
||||
header = arguments[1];
|
||||
value = arguments[2];
|
||||
}
|
||||
|
||||
helpers.checkForBlacklistedHeaderKey(header);
|
||||
helpers.checkForInvalidHeaderValue(value);
|
||||
|
||||
globalConfigs.headers[host] = globalConfigs.headers[host] || {};
|
||||
globalConfigs.headers[host][header] = value;
|
||||
},
|
||||
getDataSerializer: function () {
|
||||
return globalConfigs.serializer;
|
||||
},
|
||||
setDataSerializer: function (serializer) {
|
||||
globalConfigs.serializer = helpers.checkSerializer(serializer);
|
||||
},
|
||||
setCookie: function (url, cookie, options) {
|
||||
cookieHandler.setCookie(url, cookie, options);
|
||||
},
|
||||
clearCookies: function () {
|
||||
cookieHandler.clearCookies();
|
||||
},
|
||||
removeCookies: function (url, callback) {
|
||||
cookieHandler.removeCookies(url, callback);
|
||||
},
|
||||
getCookieString: function (url) {
|
||||
return cookieHandler.getCookieString(url);
|
||||
},
|
||||
getRequestTimeout: function () {
|
||||
return globalConfigs.timeout;
|
||||
},
|
||||
setRequestTimeout: function (timeout) {
|
||||
globalConfigs.timeout = timeout;
|
||||
},
|
||||
setSSLCertMode: function (mode, success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'setSSLCertMode', [ helpers.checkSSLCertMode(mode) ]);
|
||||
},
|
||||
disableRedirect: function (disable, success, failure) {
|
||||
return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [ !!disable ]);
|
||||
},
|
||||
sendRequest: function (url, options, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
options = helpers.handleMissingOptions(options, globalConfigs);
|
||||
|
||||
var headers = helpers.getMergedHeaders(url, options.headers, globalConfigs.headers);
|
||||
var onSuccess = helpers.injectCookieHandler(url, success);
|
||||
var onFail = helpers.injectCookieHandler(url, failure);
|
||||
|
||||
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 ]);
|
||||
case 'upload':
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [ url, options.params, headers, options.filePath, options.name, options.timeout ]);
|
||||
case 'download':
|
||||
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
|
||||
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [ url, options.params, headers, options.filePath, options.timeout ]);
|
||||
default:
|
||||
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [ url, options.params, headers, options.timeout ]);
|
||||
}
|
||||
},
|
||||
post: function (url, data, headers, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'post', data: data, headers: headers }, success, failure);
|
||||
},
|
||||
get: function (url, params, headers, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure);
|
||||
},
|
||||
put: function (url, data, headers, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'put', data: data, headers: headers }, success, failure);
|
||||
},
|
||||
patch: function (url, data, headers, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'patch', data: data, headers: headers }, success, failure);
|
||||
},
|
||||
delete: function (url, params, headers, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'delete', params: params, headers: headers }, success, failure);
|
||||
},
|
||||
head: function (url, params, headers, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'head', params: params, headers: headers }, success, failure);
|
||||
},
|
||||
uploadFile: function (url, params, headers, filePath, name, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure);
|
||||
},
|
||||
downloadFile: function (url, params, headers, filePath, success, failure) {
|
||||
return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
|
||||
}
|
||||
};
|
||||
|
||||
angularIntegration.registerService(publicInterface);
|
||||
module.exports = publicInterface;
|
||||
|
||||
96
www/angular-integration.js
vendored
96
www/angular-integration.js
vendored
@@ -1,96 +0,0 @@
|
||||
function registerService(http) {
|
||||
if (typeof angular === 'undefined') return;
|
||||
|
||||
angular.module('cordovaHTTP', []).factory('cordovaHTTP', function ($timeout, $q) {
|
||||
function makePromise(fn, args, async) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
var success = function (response) {
|
||||
if (async) {
|
||||
$timeout(function () {
|
||||
deferred.resolve(response);
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(response);
|
||||
}
|
||||
};
|
||||
|
||||
var fail = function (response) {
|
||||
if (async) {
|
||||
$timeout(function () {
|
||||
deferred.reject(response);
|
||||
});
|
||||
} else {
|
||||
deferred.reject(response);
|
||||
}
|
||||
};
|
||||
|
||||
args.push(success);
|
||||
args.push(fail);
|
||||
|
||||
fn.apply(http, args);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var cordovaHTTP = {
|
||||
getBasicAuthHeader: http.getBasicAuthHeader,
|
||||
useBasicAuth: function (username, password) {
|
||||
return http.useBasicAuth(username, password);
|
||||
},
|
||||
setHeader: function (host, header, value) {
|
||||
return http.setHeader(host, header, value);
|
||||
},
|
||||
setDataSerializer: function (serializer) {
|
||||
return http.setDataSerializer(serializer);
|
||||
},
|
||||
clearCookies: function () {
|
||||
return http.clearCookies();
|
||||
},
|
||||
removeCookies: function (url) {
|
||||
return http.removeCookies(url);
|
||||
},
|
||||
setRequestTimeout: function (timeout) {
|
||||
return http.setRequestTimeout(timeout);
|
||||
},
|
||||
enableSSLPinning: function (enable) {
|
||||
return makePromise(http.enableSSLPinning, [enable]);
|
||||
},
|
||||
acceptAllCerts: function (allow) {
|
||||
return makePromise(http.acceptAllCerts, [allow]);
|
||||
},
|
||||
disableRedirect: function(disable) {
|
||||
return makePromise(http.disableRedirect, [disable]);
|
||||
},
|
||||
validateDomainName: function (validate) {
|
||||
return makePromise(http.validateDomainName, [validate]);
|
||||
},
|
||||
post: function (url, data, headers) {
|
||||
return makePromise(http.post, [url, data, headers], true);
|
||||
},
|
||||
get: function (url, params, headers) {
|
||||
return makePromise(http.get, [url, params, headers], true);
|
||||
},
|
||||
put: function (url, data, headers) {
|
||||
return makePromise(http.put, [url, data, headers], true);
|
||||
},
|
||||
delete: function (url, params, headers) {
|
||||
return makePromise(http.delete, [url, params, headers], true);
|
||||
},
|
||||
head: function (url, params, headers) {
|
||||
return makePromise(http.head, [url, params, headers], true);
|
||||
},
|
||||
uploadFile: function (url, params, headers, filePath, name) {
|
||||
return makePromise(http.uploadFile, [url, params, headers, filePath, name], true);
|
||||
},
|
||||
downloadFile: function (url, params, headers, filePath) {
|
||||
return makePromise(http.downloadFile, [url, params, headers, filePath], true);
|
||||
}
|
||||
};
|
||||
return cordovaHTTP;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
registerService: registerService
|
||||
};
|
||||
103
www/helpers.js
103
www/helpers.js
@@ -3,18 +3,22 @@ var cookieHandler = require(pluginId + '.cookie-handler');
|
||||
var messages = require(pluginId + '.messages');
|
||||
|
||||
var validSerializers = [ 'urlencoded', 'json', 'utf8' ];
|
||||
var validCertModes = [ 'default', 'nocheck', 'pinned' ];
|
||||
var validHttpMethods = [ 'get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download' ];
|
||||
|
||||
module.exports = {
|
||||
b64EncodeUnicode: b64EncodeUnicode,
|
||||
getTypeOf: getTypeOf,
|
||||
checkHeaders: checkHeaders,
|
||||
onInvalidHeader: onInvalidHeader,
|
||||
checkSerializer: checkSerializer,
|
||||
checkSSLCertMode: checkSSLCertMode,
|
||||
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
|
||||
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
|
||||
injectCookieHandler: injectCookieHandler,
|
||||
injectFileEntryHandler: injectFileEntryHandler,
|
||||
getMergedHeaders: getMergedHeaders,
|
||||
getProcessedData: getProcessedData,
|
||||
handleMissingCallbacks: handleMissingCallbacks
|
||||
handleMissingCallbacks: handleMissingCallbacks,
|
||||
handleMissingOptions: handleMissingOptions
|
||||
};
|
||||
|
||||
// Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
|
||||
@@ -39,38 +43,78 @@ function mergeHeaders(globalHeaders, localHeaders) {
|
||||
return localHeaders;
|
||||
}
|
||||
|
||||
function checkHeaders(headers) {
|
||||
var keys = Object.keys(headers);
|
||||
var key;
|
||||
function checkForValidStringValue(list, value, onInvalidValueMessage) {
|
||||
if (getTypeOf(value) !== 'String') {
|
||||
throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
|
||||
}
|
||||
|
||||
value = value.trim().toLowerCase();
|
||||
|
||||
if (list.indexOf(value) === -1) {
|
||||
throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function checkKeyValuePairObject(obj, allowedChildren, onInvalidValueMessage) {
|
||||
if (getTypeOf(obj) !== 'Object') {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
key = keys[i];
|
||||
|
||||
if (getTypeOf(headers[key]) !== 'String') {
|
||||
return false;
|
||||
if (allowedChildren.indexOf(getTypeOf(obj[keys[i]])) === -1) {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function onInvalidHeader(handler) {
|
||||
handler({
|
||||
status: 0,
|
||||
error: messages.HEADER_VALUE_MUST_BE_STRING,
|
||||
headers: {}
|
||||
});
|
||||
function checkHttpMethod(method) {
|
||||
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
|
||||
}
|
||||
|
||||
function checkSerializer(serializer) {
|
||||
serializer = serializer || '';
|
||||
serializer = serializer.trim().toLowerCase();
|
||||
return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER);
|
||||
}
|
||||
|
||||
if (validSerializers.indexOf(serializer) > -1) {
|
||||
return serializer;
|
||||
function checkSSLCertMode(mode) {
|
||||
return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
|
||||
}
|
||||
|
||||
function checkForBlacklistedHeaderKey(key) {
|
||||
if (key.toLowerCase() === 'cookie') {
|
||||
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
return serializer[0];
|
||||
return key;
|
||||
}
|
||||
|
||||
function checkForInvalidHeaderValue(value) {
|
||||
if (getTypeOf(value) !== 'String') {
|
||||
throw new Error(messages.INVALID_HEADERS_VALUE);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function checkTimeoutValue(timeout) {
|
||||
if (getTypeOf(timeout) !== 'Number' || timeout < 0) {
|
||||
throw new Error(messages.INVALID_TIMEOUT_VALUE);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
function checkHeadersObject(headers) {
|
||||
return checkKeyValuePairObject(headers, [ 'String' ], messages.INVALID_HEADERS_VALUE);
|
||||
}
|
||||
|
||||
function checkParamsObject(params) {
|
||||
return checkKeyValuePairObject(params, [ 'String', 'Array' ], messages.INVALID_PARAMS_VALUE);
|
||||
}
|
||||
|
||||
function resolveCookieString(headers) {
|
||||
@@ -194,3 +238,18 @@ function handleMissingCallbacks(successFn, failFn) {
|
||||
throw new Error(messages.MANDATORY_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMissingOptions(options, globals) {
|
||||
options = options || {};
|
||||
|
||||
return {
|
||||
method: checkHttpMethod(options.method || validHttpMethods[0]),
|
||||
serializer: checkSerializer(options.serializer || globals.serializer),
|
||||
timeout: checkTimeoutValue(options.timeout || globals.timeout),
|
||||
headers: checkHeadersObject(options.headers || {}),
|
||||
params: checkParamsObject(options.params || {}),
|
||||
data: options.data || null,
|
||||
filePath: options.filePath || '',
|
||||
name: options.name || ''
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
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:',
|
||||
DEPRECATED_VDN: 'advanced-http: "validateDomainName" is no more supported, please see change log for further info',
|
||||
HEADER_VALUE_MUST_BE_STRING: 'advanced-http: header values must be strings',
|
||||
MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
|
||||
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function'
|
||||
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
|
||||
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
|
||||
INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
|
||||
INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
|
||||
INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings',
|
||||
INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
|
||||
INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings'
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user