mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-02-11 00:00:06 +08:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
192059d34e | ||
|
|
7193b636f1 | ||
|
|
662b4352f5 | ||
|
|
e18f9eee51 | ||
|
|
c7eb60e670 | ||
|
|
6a930de82f | ||
|
|
588e4a0e57 | ||
|
|
aded59e3d1 | ||
|
|
9ef582b37f | ||
|
|
33fea67603 | ||
|
|
faffe0e078 | ||
|
|
99c7f5d331 | ||
|
|
b74ad73742 | ||
|
|
7fd7ab223c | ||
|
|
937010bd4e | ||
|
|
7ab4b634ca | ||
|
|
32187a12fe | ||
|
|
a4f121728c | ||
|
|
21d991b04e | ||
|
|
31b1eee355 | ||
|
|
3d1b195831 | ||
|
|
014753e127 | ||
|
|
5ee26bc2cc | ||
|
|
7d7f02b4b3 | ||
|
|
afc9e3e944 | ||
|
|
9af2d1c21a | ||
|
|
9dce2fb964 | ||
|
|
eb946b49ab | ||
|
|
72ca81b515 | ||
|
|
68633f1bb8 | ||
|
|
ee26b78e0d | ||
|
|
d924f98844 | ||
|
|
8fceb4df97 | ||
|
|
7a09fa9460 | ||
|
|
3e5c941fdd | ||
|
|
0f273f401b | ||
|
|
684874184d | ||
|
|
19e1e7206b | ||
|
|
21bec76c11 | ||
|
|
594d03aa41 | ||
|
|
b3276ad2d4 | ||
|
|
3b4a5b7c26 | ||
|
|
867b8ea202 | ||
|
|
cc3e70771c | ||
|
|
9e892119cc | ||
|
|
f93f69e0aa | ||
|
|
4ace394464 | ||
|
|
ae3e821639 | ||
|
|
e17768041c | ||
|
|
34a559bc6e | ||
|
|
2ce1fc407d | ||
|
|
4b73c3762c | ||
|
|
5a9c176bca | ||
|
|
40aa944d0f | ||
|
|
c7feb7aca3 | ||
|
|
680105175b | ||
|
|
07811d0380 | ||
|
|
026e8589c2 | ||
|
|
e1592001af | ||
|
|
f057b126ba | ||
|
|
2b567cdf32 | ||
|
|
d99c7bb154 | ||
|
|
336a56be1c | ||
|
|
8b962cc350 | ||
|
|
13bf4666b0 | ||
|
|
1fc3d6230c | ||
|
|
85346e0381 | ||
|
|
c9f8c8b66a | ||
|
|
9b26a0f031 | ||
|
|
298c031433 | ||
|
|
eab6acf85c | ||
|
|
8cf0d21a7a | ||
|
|
f91727e14a | ||
|
|
7b485507dc | ||
|
|
03b0abb74e | ||
|
|
c3d60c37bf | ||
|
|
54ef8ae169 | ||
|
|
c83940a8f6 | ||
|
|
ccabbf2a29 | ||
|
|
7ba0852698 | ||
|
|
4f3ff9097f | ||
|
|
620ce3f81c | ||
|
|
6e68bf4dfe | ||
|
|
87f0f3600c | ||
|
|
0bf39650e1 | ||
|
|
d444363a8e | ||
|
|
13976bbe43 | ||
|
|
70c8b9bb32 | ||
|
|
4b964e8b7c | ||
|
|
56f8b41f4f | ||
|
|
e1e720fe2d | ||
|
|
a1b6ea94f0 | ||
|
|
d977392a49 | ||
|
|
8d28f4ab80 | ||
|
|
38b3e6ffb1 | ||
|
|
7d984bcf89 | ||
|
|
2cfac2026b | ||
|
|
8bdbf7ca78 | ||
|
|
d5ce3ed76b | ||
|
|
8a83a940cb | ||
|
|
230de36618 | ||
|
|
0af726d8ce | ||
|
|
ba57d3cba1 | ||
|
|
508b1b4f83 | ||
|
|
7992bd0991 | ||
|
|
a331b57ab2 | ||
|
|
ee30160921 | ||
|
|
e8e1c4273f | ||
|
|
752b2cdcb7 | ||
|
|
314314d7f9 | ||
|
|
a8e3637f27 | ||
|
|
56272b9a5d | ||
|
|
8f859db57f | ||
|
|
e673754b13 | ||
|
|
a0f376233c | ||
|
|
fcd142a70b | ||
|
|
807400bc63 | ||
|
|
1fd857f1d9 | ||
|
|
f801d2a283 | ||
|
|
6033ea4b76 | ||
|
|
dadbf97d0c | ||
|
|
f06788d199 | ||
|
|
a195de409d | ||
|
|
0fade8351d | ||
|
|
ebd6ae9793 | ||
|
|
49f219723d | ||
|
|
f1bb4f36d0 | ||
|
|
e2a869bbd2 | ||
|
|
d2f79352ae | ||
|
|
cca1be9d91 | ||
|
|
b6d8763a18 | ||
|
|
c70ce750ed | ||
|
|
54abb2ba37 | ||
|
|
8d433d050f | ||
|
|
bf616fc6e5 | ||
|
|
c31765bbcd | ||
|
|
b5ec508c30 | ||
|
|
6fcf4dc8eb | ||
|
|
f0e1522982 |
33
.github/ISSUE_TEMPLATE/--bug-report.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/--bug-report.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: "\U0001F41BBug report"
|
||||
about: Create a report to help us improve
|
||||
title: "[Bug] [platform] your issue title"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is and what you expected to happen.
|
||||
|
||||
**System info**
|
||||
- affected HTTP plugin version: [e.g. 2.1.1]
|
||||
- affected platform(s) and version(s): [e.g. iOS 12.2]
|
||||
- affected device(s): [e.g. iPhone 8]
|
||||
- cordova version: [e.g. 6.5.0]
|
||||
- cordova platform version(s): [e.g. android 7.0.0, browser 5.0.3]
|
||||
|
||||
**Are you using ionic-native-wrapper?**
|
||||
- ionic-native-wrapper version: [e.g. 5.8.0]
|
||||
- did you check [ionic-native issue tracker](https://github.com/ionic-team/ionic-native/issues) for your problem?
|
||||
|
||||
**Minimum viable code to reproduce**
|
||||
If applicable, add formatted sample coding to help explain your problem.
|
||||
|
||||
e.g.:
|
||||
```js
|
||||
cordova.plugin.http.setDataSerializer('urlencoded');
|
||||
```
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
21
.github/ISSUE_TEMPLATE/--feature-request.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/--feature-request.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: "\U0001F680Feature request"
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. e.g. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen. If applicable, add formatted sample coding to help explain your idea.
|
||||
|
||||
```js
|
||||
// do some fancy stuff here
|
||||
```
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
18
.github/ISSUE_TEMPLATE/--support-question.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE/--support-question.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: "\U0001F914Support question"
|
||||
about: Ask the community
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**You've got questions?**
|
||||
|
||||
We primarily use GitHub as an issue tracker; for usage and support questions, please check out these resources below. Thanks! 😁
|
||||
|
||||
* README.md: https://github.com/silkimen/cordova-plugin-advanced-http/blob/master/README.md
|
||||
* StackOverflow: https://stackoverflow.com/questions/tagged/cordova-plugin-advanced-http using the tag `cordova-plugin-advanced-http`
|
||||
* Wiki: https://github.com/silkimen/cordova-plugin-advanced-http/wiki
|
||||
|
||||
And don't forget: If you get help, help others. Good karma rulez!
|
||||
65
.github/workflows/ci.yml
vendored
Normal file
65
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Cordova HTTP Plugin CI
|
||||
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
nodejs: '10.x'
|
||||
|
||||
jobs:
|
||||
test-www-interface:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Run WWW interface tests
|
||||
run: npm run testjs
|
||||
|
||||
build-ios:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run updatecert
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --ios --emulator
|
||||
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Install JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run updatecert
|
||||
- name: Add workaround for mipsel reference
|
||||
run: sudo mkdir -p $ANDROID_HOME/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --android --device
|
||||
- name: Upload artifact to BrowserStack
|
||||
if: env.BROWSERSTACK_USERNAME != ''
|
||||
run: scripts/upload-browserstack.sh --android
|
||||
- name: Run e2e tests
|
||||
if: env.BROWSERSTACK_USERNAME != ''
|
||||
run: scripts/test-app.sh --android --device
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
node_modules/**
|
||||
test/app-template/www/certificates/*.cer
|
||||
test/e2e-app-template/www/certificates/*.cer
|
||||
test/e2e-app-template/www/certificates/*.pkcs
|
||||
tags
|
||||
.zedstate
|
||||
npm-debug.log
|
||||
|
||||
77
.travis.yml
77
.travis.yml
@@ -1,12 +1,6 @@
|
||||
sudo: false
|
||||
|
||||
language: objective-c
|
||||
os: osx
|
||||
osx_image: xcode9.1
|
||||
|
||||
env:
|
||||
- TARGET_PLATFORM=android
|
||||
- TARGET_PLATFORM=ios
|
||||
notifications:
|
||||
slack:
|
||||
secure: twDT06GAiu0jsKizow7TcghZj70KbuuTrlo02QGmbSxBk2rJfsXSrHAsA3+s/9Q4mudENk6na7fs1aPCxz+u2etUGp+2PaJKVKR5n3jrNNt3SnYeWsBgVo7o7H1aLXatX3a+TdPXh1F5gQ4Ycr93nTYbW/077jsOholwbOHDZE3VcU9dzNPwFaEvhrDbr/ei3tef0ZiM1qxIad74TgwWMKClwai3I7HCVkZOPsyV+ve6cdIJ8Dt47JzFUHSW3SZuoe5Kywxvp0VvMo/QAJw95y3edNafx4EXHwbaN71rpGWSJXIKSZzcSQalZJ9DxGYspIBkWvGsNuQRzG9CzIoNQK10iERlIVC5vKDfKX22gayOQPSDkswJzIduylBUC8zdTPCndXyNEM/Lrj6hg+ksFWN58vYNPgfUeiga7X+LV5HytftsMFW+xx2kbnGeU8doGeX8Q8G7h9OIkHCTTG7R0ldYMIqTm8YJGPkRIv4OReC5ZOhiZD+wSg4KQ0wmMeRi+hyn+I5UPnKEOHAIN8FmLNCZFbgr1wuPFp9xnJIOcumQnQVZ2t6vk6IjIbwhYPWCnf7Sr4BvJxE8eyiLrEaXK0FiPb3My9wK9tLFjj1zdD7e4+SLq+WFMeCxp2eXOGF0Bu+2VK2tGjgWhaudaIpjbRQAAQ5nPa43h16NruEvNWI=
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@@ -15,30 +9,51 @@ cache:
|
||||
addons:
|
||||
sauce_connect: true
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8
|
||||
matrix:
|
||||
include:
|
||||
- name: "iOS Build & Test"
|
||||
language: objective-c
|
||||
sudo: false
|
||||
os: osx
|
||||
osx_image: xcode10.1
|
||||
|
||||
install:
|
||||
- npm install
|
||||
- if [ $TARGET_PLATFORM = "android" ]; then
|
||||
brew update &&
|
||||
brew install gradle &&
|
||||
scripts/setup-android-sdk.sh &&
|
||||
export ANDROID_HOME=$(pwd)/android-sdk-macosx &&
|
||||
export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/23.0.2;
|
||||
fi
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8
|
||||
|
||||
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;
|
||||
install:
|
||||
- npm install
|
||||
|
||||
after_success:
|
||||
script:
|
||||
- npm run testjs &&
|
||||
npm run updatecert &&
|
||||
scripts/build-test-app.sh --ios --emulator &&
|
||||
scripts/upload-saucelabs.sh --ios &&
|
||||
scripts/test-app.sh --ios --emulator;
|
||||
|
||||
deploy:
|
||||
- name: "Android Build & Test"
|
||||
language: android
|
||||
sudo : required
|
||||
|
||||
notifications:
|
||||
slack:
|
||||
secure: lXE+2AgsxZU5G5dI91LkMAIgo8MAWfdM7DB5UOtn5LpuNln+2FmJo1gOI7tkdmLOqpXTGYnpI2VyQN3H4nOF21YhuouzD1Sh8n2wtQg1iTm353kuQpqiVhSBX8ZJ7Be1e1G8OsnxoYOxbs4Zo9qI40EruwkvqLCBHWM5MRGyd4M7EFWwb9Z29VZN0y1Nt5g/c3bT76kdKmF+JCLur2OeEKxAity7sIKgZekSqeIMwEVLSxXnda6Dbjc/cg0MJ0iDArkD7iu6fz/Fcrrxgm/pUxjcgvqze7Gy5i31mjEfspnrglWV1cshMd48BTDKCJ2AMmxH8O3GPSWE2txjIvGRWUve7iViNylvmQCVz3Eyf99+4EuuVGa+5PSodQ/CqODx/65EwtcN3PE1tNz2puKOK8nrOJcFkcbG8KTHKUlQtHCkjitbykUnj/hvhLK5/oWlQYVOLWWrHwdGUh8FI8aFPVGjRjWbHbhdayjEIqxwr1ns+6mYrP1EFNXbaeZxnLNC59XpJl1ifuezqYAk7YEiU5j4rtC7YKgyQ3ueb7anOHTJoTMyDn8mpZXgwuyhoBaeEYytQVgRyMtL6Y5cP98Jn2kv0+vdne3rkk9/JEBTo32HOjvoij6rsqEvXC0LhUDJSNadOVdHht0jjoN6zBH37HIE5/3zysLlPcAcHAS83ow=
|
||||
android:
|
||||
components:
|
||||
- platform-tools
|
||||
- build-tools-28.0.3
|
||||
- android-27
|
||||
- extra-android-support
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - &&
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
script:
|
||||
- npm run testjs &&
|
||||
npm run updatecert &&
|
||||
scripts/build-test-app.sh --android --emulator &&
|
||||
scripts/upload-saucelabs.sh --android &&
|
||||
scripts/test-app.sh --android --emulator;
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"editor.tabSize": 2
|
||||
}
|
||||
88
CHANGELOG.md
88
CHANGELOG.md
@@ -1,5 +1,93 @@
|
||||
# Changelog
|
||||
|
||||
## 2.4.0
|
||||
|
||||
- Feature #291: add support for sending 'raw' requests (thanks to jachstet-sea and chuchuva)
|
||||
- Feature #155: add OPTIONS method
|
||||
- Feature #283: improve error message on timeout on browser platform
|
||||
|
||||
## 2.3.1
|
||||
|
||||
- Fixed #275: getAllCookies() is broken because of a typo (thanks ath0mas)
|
||||
|
||||
## 2.3.0
|
||||
|
||||
- Feature #101: Support "multipart/form-data" requests (thanks SDA SE Open Industry Solutions)
|
||||
|
||||
#### Important information
|
||||
This feature depends on several Web APIs. See https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.
|
||||
|
||||
## 2.2.0
|
||||
|
||||
- Feature #239: add enumeration style object for error codes
|
||||
- Feature #253: add support for response type "json"
|
||||
- Feature #127: add multiple file upload (thanks SDA SE Open Industry Solutions and nilswitschel)
|
||||
|
||||
## 2.1.1
|
||||
|
||||
- Fixed #224: response type "arraybuffer" and "blob" not working on browser platform
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Feature #216: Support for response type `arraybuffer`
|
||||
- Feature #171: Support for response type `blob`
|
||||
- Feature #205: Add preference for configuring OKHTTP version (thanks RougeCiel)
|
||||
|
||||
## 2.0.11
|
||||
|
||||
- Fixed #221: headers not set on Android when request fails due to non-success status code
|
||||
|
||||
## 2.0.10
|
||||
|
||||
- Fixed #218: headers are used as params on browser platform
|
||||
|
||||
## 2.0.9
|
||||
|
||||
- Fixed #204: broken support for cordova-android < 7.0
|
||||
|
||||
- :warning: **Deprecation**: Deprecated "disableRedirect" in favor of "setFollowRedirect"
|
||||
|
||||
## 2.0.8
|
||||
|
||||
- Fixed #198: cookie header is always passed even if there is no cookie
|
||||
- Fixed #201: browser implementation is broken due to broken dependency
|
||||
- Fixed #197: iOS crashes when multiple request are done simultaneously (reverted a8e3637)
|
||||
- Fixed #189: error code mappings are not precise
|
||||
- Fixed #200: compatibility with Java 6 is broken due to string switch on Android
|
||||
|
||||
- :warning: **Deprecation**: Deprecated "setSSLCertMode" in favor of "setServerTrustMode"
|
||||
|
||||
## 2.0.7
|
||||
|
||||
- Fixed #195: URLs are double-encoded on Android
|
||||
|
||||
## 2.0.6
|
||||
|
||||
- Fixed #187: setSSLCertMode with "default" throws an error on Android
|
||||
- Fixed #115: HTTP connections are not kept alive on iOS (thanks MorpheusDe97)
|
||||
|
||||
## 2.0.5
|
||||
|
||||
- Fixed #185: need more detailed SSL error message
|
||||
|
||||
## 2.0.4
|
||||
|
||||
- Fixed #179: sending empty string with utf8 serializer throws an exception
|
||||
|
||||
## 2.0.3
|
||||
|
||||
- Fixed #172: plugin does not respect user installed CA certs on Android
|
||||
|
||||
#### Important information
|
||||
We've changed a default behavior on Android. User installed CA certs are respected now.
|
||||
If you don't want this for your needs, you can switch back to old behavior by setting SSL cert mode to `legacy`.
|
||||
|
||||
## 2.0.2
|
||||
|
||||
- Fixed #142: Plugin affected by REDoS Issue of tough-cookie
|
||||
- Fixed #157: Arguments are double URL-encoded on "downloadFile" (thanks TheZopo)
|
||||
- Fixed #164: Arguments are double URL-encoded on "head" (thanks ath0mas)
|
||||
|
||||
## 2.0.1
|
||||
|
||||
- Fixed #136: Content-Type header non-overwritable on browser platform
|
||||
|
||||
1
LICENSE
1
LICENSE
@@ -1,5 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Sefa Ilkimen
|
||||
Copyright (c) 2017 Mobisys GmbH
|
||||
Copyright (c) 2014 Wymsee, Inc
|
||||
|
||||
|
||||
146
README.md
146
README.md
@@ -1,9 +1,11 @@
|
||||
Cordova Advanced HTTP
|
||||
=====================
|
||||
[](https://badge.fury.io/js/cordova-plugin-advanced-http)
|
||||
[](https://www.npmjs.com/package/cordova-plugin-advanced-http?activeTab=versions)
|
||||
[](https://opensource.org/licenses/mit-license.php)
|
||||
[](https://www.npmjs.com/package/cordova-plugin-advanced-http)
|
||||
[](https://opensource.org/licenses/mit-license.php)
|
||||
[](https://travis-ci.org/silkimen/cordova-plugin-advanced-http)
|
||||
|
||||
[](https://travis-ci.org/silkimen/cordova-plugin-advanced-http)
|
||||
[](https://github.com/silkimen/cordova-plugin-advanced-http/actions)
|
||||
|
||||
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS, Android and [Browser](#browserSupport).
|
||||
@@ -12,9 +14,9 @@ This is a fork of [Wymsee's Cordova-HTTP plugin](https://github.com/wymsee/cordo
|
||||
|
||||
## Advantages over Javascript requests
|
||||
|
||||
- Background threading - all requests are done in a background thread.
|
||||
- Handling of HTTP code 401 - read more at [Issue CB-2415](https://issues.apache.org/jira/browse/CB-2415).
|
||||
- SSL Pinning - read more at [LumberBlog](http://blog.lumberlabs.com/2012/04/why-app-developers-should-care-about.html).
|
||||
- SSL / TLS Pinning
|
||||
- CORS restrictions do not apply
|
||||
- Handling of HTTP code 401 - read more at [Issue CB-2415](https://issues.apache.org/jira/browse/CB-2415)
|
||||
|
||||
## Updates
|
||||
|
||||
@@ -89,13 +91,27 @@ cordova.plugin.http.setDataSerializer('urlencoded');
|
||||
```
|
||||
|
||||
You can choose one of these:
|
||||
* `urlencoded`: send data as url encoded content in body (content type "application/x-www-form-urlencoded")
|
||||
* `json`: send data as JSON encoded content in body (content type "application/json")
|
||||
* `utf8`: send data as plain UTF8 encoded string in body (content type "plain/text")
|
||||
* `urlencoded`: send data as url encoded content in body
|
||||
* default content type "application/x-www-form-urlencoded"
|
||||
* data must be an dictionary style `Object`
|
||||
* `json`: send data as JSON encoded content in body
|
||||
* default content type "application/json"
|
||||
* data must be an `Array` or an dictionary style `Object`
|
||||
* `utf8`: send data as plain UTF8 encoded string in body
|
||||
* default content type "plain/text"
|
||||
* data must be a `String`
|
||||
* `multipart`: send FormData objects as multipart content in body
|
||||
* default content type "multipart/form-data"
|
||||
* data must be an `FormData` instance
|
||||
* `raw`: send data as is, without any processing
|
||||
* default content type "application/octet-stream"
|
||||
* data must be an `Uint8Array` or an `ArrayBuffer`
|
||||
|
||||
You can also override the default content type headers by specifying your own headers (see [setHeader](#setHeader)).
|
||||
This defaults to `urlencoded`. You can also override the default content type headers by specifying your own headers (see [setHeader](#setHeader)).
|
||||
|
||||
__Caution__: `urlencoded` does not support serializing deep structures whereas `json` does.
|
||||
:warning: `urlencoded` does not support serializing deep structures whereas `json` does.
|
||||
|
||||
:warning: `multipart` depends on several Web API standards which need to be supported in your web view. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.
|
||||
|
||||
### setRequestTimeout
|
||||
Set how long to wait for a request to respond, in seconds.
|
||||
@@ -104,6 +120,13 @@ Set how long to wait for a request to respond, in seconds.
|
||||
cordova.plugin.http.setRequestTimeout(5.0);
|
||||
```
|
||||
|
||||
### setFollowRedirect<a name="setFollowRedirect"></a>
|
||||
Configure if it should follow redirects automatically. This defaults to true.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.setFollowRedirect(true);
|
||||
```
|
||||
|
||||
### getCookieString
|
||||
Returns saved cookies (as string) matching given URL.
|
||||
|
||||
@@ -128,59 +151,46 @@ cordova.plugin.http.clearCookies();
|
||||
## Asynchronous Functions
|
||||
These functions all take success and error callbacks as their last 2 arguments.
|
||||
|
||||
### setSSLCertMode<a name="setSSLCertMode"></a>
|
||||
Set SSL Cert handling mode, being one of the following values:
|
||||
### setServerTrustMode<a name="setServerTrustMode"></a>
|
||||
Set server trust mode, being one of the following values:
|
||||
|
||||
* `default`: default SSL cert handling using system's CA certs
|
||||
* `nocheck`: disable SSL cert checking, trusting all certs (meant to be used only for testing purposes)
|
||||
* `pinned`: trust only provided certs
|
||||
* `default`: default SSL trustship and hostname verification handling using system's CA certs
|
||||
* `legacy`: use legacy default behavior (< 2.0.3), excluding user installed CA certs (only for Android)
|
||||
* `nocheck`: disable SSL certificate checking and hostname verification, trusting all certs (meant to be used only for testing purposes)
|
||||
* `pinned`: trust only provided certificates
|
||||
|
||||
To use SSL pinning you must include at least one `.cer` SSL certificate in your app project. You can pin to your server certificate or to one of the issuing CA certificates. Include your certificate in the `www/certificates` folder. All `.cer` files found there will be loaded automatically.
|
||||
|
||||
: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.
|
||||
:warning: Your certificate must be DER encoded! If you only have a PEM encoded certificate read this [stackoverflow answer](http://stackoverflow.com/a/16583429/3182729). You want to convert it to a DER encoded certificate with a .cer extension.
|
||||
|
||||
```js
|
||||
// enable SSL pinning
|
||||
cordova.plugin.http.setSSLCertMode('pinned', function() {
|
||||
cordova.plugin.http.setServerTrustMode('pinned', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
|
||||
// use system's default CA certs
|
||||
cordova.plugin.http.setSSLCertMode('default', function() {
|
||||
cordova.plugin.http.setServerTrustMode('default', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
|
||||
// disable SSL cert checking, only meant for testing purposes, do NOT use in production!
|
||||
cordova.plugin.http.setSSLCertMode('nocheck', function() {
|
||||
cordova.plugin.http.setServerTrustMode('nocheck', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
```
|
||||
|
||||
### enableSSLPinning (obsolete)
|
||||
This function was removed in 2.0.0. Use ["setSSLCertMode"](#setSSLCertMode) to enable SSL pinning (mode "pinned").
|
||||
### disableRedirect (deprecated)
|
||||
This function was deprecated in 2.0.9. Use ["setFollowRedirect"](#setFollowRedirect) instead.
|
||||
|
||||
### acceptAllCerts (obsolete)
|
||||
This function was removed in 2.0.0. Use ["setSSLCertMode"](#setSSLCertMode) to disable checking certs (mode "nocheck").
|
||||
|
||||
### disableRedirect
|
||||
If set to `true`, it won't follow redirects automatically. This defaults to false.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.disableRedirect(true, function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
```
|
||||
|
||||
### validateDomainName (obsolete)
|
||||
This function was removed in v1.6.2. Domain name validation is disabled automatically when you set SSL cert mode to "nocheck".
|
||||
### setSSLCertMode (deprecated)
|
||||
This function was deprecated in 2.0.8. Use ["setServerTrustMode"](#setServerTrustMode) instead.
|
||||
|
||||
### removeCookies
|
||||
Remove all cookies associated with a given URL.
|
||||
@@ -189,20 +199,28 @@ 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.
|
||||
### sendRequest<a name="sendRequest"></a>
|
||||
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. Check the documentation of the respective shorthand function for details on what is returned on success and failure.
|
||||
|
||||
:warning: You need to encode the base URL yourself if it contains special characters like whitespaces. You can use `encodeURI()` for this purpose.
|
||||
|
||||
The options object contains following keys:
|
||||
|
||||
* `method`: HTTP method to be used, defaults to `get`, needs to be one of the following values:
|
||||
* `get`, `post`, `put`, `patch`, `head`, `delete`, `upload`, `download`
|
||||
* `get`, `post`, `put`, `patch`, `head`, `delete`, `options`, `upload`, `download`
|
||||
* `data`: payload to be send to the server (only applicable on `post`, `put` or `patch` methods)
|
||||
* `params`: query params to be appended to the URL (only applicable on `get`, `head`, `delete`, `upload` or `download` methods)
|
||||
* `serializer`: data serializer to be used (only applicable on `post`, `put` or `patch` methods), defaults to global serializer value, see [setDataSerializer](#setDataSerializer) for supported values
|
||||
* `responseType`: expected response type, defaults to `text`, needs to be one of the following values:
|
||||
* `text`: data is returned as decoded string, use this for all kinds of string responses (e.g. XML, HTML, plain text, etc.)
|
||||
* `json` data is treated as JSON and returned as parsed object
|
||||
* `arraybuffer`: data is returned as [ArrayBuffer instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
|
||||
* `blob`: data is returned as [Blob instance](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
|
||||
* `timeout`: timeout value for the request in seconds, defaults to global timeout value
|
||||
* `followRedirect`: enable or disable automatically following redirects
|
||||
* `headers`: headers object (key value pair), will be merged with global values
|
||||
* `filePath`: filePath to be used during upload and download see [uploadFile](#uploadFile) and [downloadFile](#downloadFile) for detailed information
|
||||
* `name`: name to be used during upload see [uploadFile](#uploadFile) for detailed information
|
||||
* `filePath`: file path(s) to be used during upload and download see [uploadFile](#uploadFile) and [downloadFile](#downloadFile) for detailed information
|
||||
* `name`: name(s) to be used during upload see [uploadFile](#uploadFile) for detailed information
|
||||
|
||||
Here's a quick example:
|
||||
|
||||
@@ -228,6 +246,18 @@ cordova.plugin.http.sendRequest('https://google.com/', options, function(respons
|
||||
### post<a name="post"></a>
|
||||
Execute a POST request. Takes a URL, data, and headers.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.post('https://google.com/', {
|
||||
test: 'testString'
|
||||
}, {
|
||||
Authorization: 'OAuth2: token'
|
||||
}, function(response) {
|
||||
console.log(response.status);
|
||||
}, function(response) {
|
||||
console.error(response.error);
|
||||
});
|
||||
```
|
||||
|
||||
#### success
|
||||
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.
|
||||
|
||||
@@ -270,7 +300,7 @@ cordova.plugin.http.post('https://google.com/', {
|
||||
```
|
||||
|
||||
#### failure
|
||||
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.
|
||||
The error function receives a response object with 4 properties: status, error, url, and headers (url and headers being optional). **status** is a HTTP response code or an internal error code. Positive values are HTTP status codes whereas negative values do represent internal error codes. **error** is the error response from the server as a string or an internal error message. **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:
|
||||
|
||||
@@ -285,6 +315,8 @@ Here's a quick example:
|
||||
}
|
||||
```
|
||||
|
||||
:warning: An enumeration style object is exposed as `cordova.plugin.http.ErrorCode`. You can use it to check against internal error codes.
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -311,14 +343,25 @@ Execute a DELETE request. Takes a URL, parameters, and headers. See the [post]
|
||||
### head<a name="head"></a>
|
||||
Execute a HEAD request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
### options<a name="options"></a>
|
||||
Execute a OPTIONS request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
### uploadFile<a name="uploadFile"></a>
|
||||
Uploads 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.
|
||||
Uploads one or more file(s) saved on the device. Takes a URL, parameters, headers, filePath(s), and the name(s) of the parameter to pass the file along as. See the [post](#post) documentation for details on what is returned on success and failure.
|
||||
|
||||
```js
|
||||
// e.g. for single file
|
||||
const filePath = 'file:///somepicture.jpg';
|
||||
const name = 'picture';
|
||||
|
||||
// e.g. for multiple files
|
||||
const filePath = ['file:///somepicture.jpg', 'file:///somedocument.doc'];
|
||||
const name = ['picture', 'document'];
|
||||
|
||||
cordova.plugin.http.uploadFile("https://google.com/", {
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', 'picture', function(response) {
|
||||
}, { Authorization: 'OAuth2: token' }, filePath, name, function(response) {
|
||||
console.log(response.status);
|
||||
}, function(response) {
|
||||
console.error(response.error);
|
||||
@@ -355,6 +398,7 @@ Following features are *not* supported:
|
||||
* Pinning SSL certificate
|
||||
* Disabling SSL certificate check
|
||||
* Disabling transparently following redirects (HTTP codes 3xx)
|
||||
* Circumventing CORS restrictions
|
||||
|
||||
## Libraries
|
||||
|
||||
@@ -366,6 +410,16 @@ This plugin utilizes some awesome open source libraries:
|
||||
|
||||
We made a few modifications to the networking libraries.
|
||||
|
||||
## CI Builds & E2E Testing
|
||||
|
||||
This plugin uses amazing cloud services to maintain quality. CI Builds and E2E testing are powered by:
|
||||
|
||||
* [GitHub Actions](https://github.com/features/actions)
|
||||
* [Travis CI](https://travis-ci.org/)
|
||||
* [BrowserStack](https://www.browserstack.com/)
|
||||
* [Sauce Labs](https://saucelabs.com/)
|
||||
* [httpbin.org](https://httpbin.org/)
|
||||
|
||||
## Contribute & Develop
|
||||
|
||||
We've set up a separate document for our [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
6266
package-lock.json
generated
Normal file
6266
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -1,14 +1,16 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "2.0.1",
|
||||
"version": "2.4.0",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"updatecert": "node ./scripts/update-test-cert.js",
|
||||
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
"buildbrowser": "./scripts/build-test-app.sh --browser",
|
||||
"testandroid": "npm run updatecert && ./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
|
||||
"testios": "npm run updatecert && ./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator",
|
||||
"buildandroid": "./scripts/build-test-app.sh --android --emulator",
|
||||
"buildios": "./scripts/build-test-app.sh --ios --emulator",
|
||||
"testandroid": "npm run updatecert && npm run buildandroid && ./scripts/test-app.sh --android --emulator",
|
||||
"testios": "npm run updatecert && npm run buildios && ./scripts/test-app.sh --ios --emulator",
|
||||
"testapp": "npm run testandroid && npm run testios",
|
||||
"testjs": "mocha ./test/js-mocha-specs.js",
|
||||
"testjs": "mocha ./test/js-specs.js",
|
||||
"test": "npm run testjs && npm run testapp",
|
||||
"release": "npm run test && ./scripts/release.sh"
|
||||
},
|
||||
@@ -48,7 +50,7 @@
|
||||
"pvsaikrishna",
|
||||
"cvillerm",
|
||||
"hideov",
|
||||
"Mobisys"
|
||||
"silkimen"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
@@ -59,11 +61,11 @@
|
||||
"chai": "4.1.2",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"colors": "1.1.2",
|
||||
"cordova": "7.1.0",
|
||||
"cordova": "8.1.2",
|
||||
"mocha": "4.0.0",
|
||||
"mock-require": "2.0.2",
|
||||
"mz": "2.7.0",
|
||||
"umd-tough-cookie": "2.3.2",
|
||||
"umd-tough-cookie": "2.4.3",
|
||||
"wd": "1.4.1",
|
||||
"xml2js": "0.4.19"
|
||||
}
|
||||
|
||||
54
plugin.xml
54
plugin.xml
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.0.1">
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.4.0">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
@@ -8,12 +8,19 @@
|
||||
<engine name="cordova" version=">=4.0.0"/>
|
||||
</engines>
|
||||
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
|
||||
<js-module src="www/lodash.js" name="lodash"/>
|
||||
<js-module src="www/umd-tough-cookie.js" name="tough-cookie"/>
|
||||
<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/dependency-validator.js" name="dependency-validator"/>
|
||||
<js-module src="www/error-codes.js" name="error-codes"/>
|
||||
<js-module src="www/global-configs.js" name="global-configs"/>
|
||||
<js-module src="www/helpers.js" name="helpers"/>
|
||||
<js-module src="www/js-util.js" name="js-util"/>
|
||||
<js-module src="www/local-storage-store.js" name="local-storage-store"/>
|
||||
<js-module src="www/lodash.js" name="lodash"/>
|
||||
<js-module src="www/messages.js" name="messages"/>
|
||||
<js-module src="www/ponyfills.js" name="ponyfills"/>
|
||||
<js-module src="www/public-interface.js" name="public-interface"/>
|
||||
<js-module src="www/umd-tough-cookie.js" name="tough-cookie"/>
|
||||
<js-module src="www/url-util.js" name="url-util"/>
|
||||
<js-module src="www/advanced-http.js" name="http">
|
||||
<clobbers target="cordova.plugin.http"/>
|
||||
</js-module>
|
||||
@@ -24,6 +31,8 @@
|
||||
</feature>
|
||||
</config-file>
|
||||
<header-file src="src/ios/CordovaHttpPlugin.h"/>
|
||||
<header-file src="src/ios/BinaryRequestSerializer.h"/>
|
||||
<header-file src="src/ios/BinaryResponseSerializer.h"/>
|
||||
<header-file src="src/ios/TextResponseSerializer.h"/>
|
||||
<header-file src="src/ios/TextRequestSerializer.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFHTTPSessionManager.h"/>
|
||||
@@ -35,6 +44,8 @@
|
||||
<header-file src="src/ios/AFNetworking/AFURLSessionManager.h"/>
|
||||
<header-file src="src/ios/SDNetworkActivityIndicator/SDNetworkActivityIndicator.h"/>
|
||||
<source-file src="src/ios/CordovaHttpPlugin.m"/>
|
||||
<source-file src="src/ios/BinaryRequestSerializer.m"/>
|
||||
<source-file src="src/ios/BinaryResponseSerializer.m"/>
|
||||
<source-file src="src/ios/TextResponseSerializer.m"/>
|
||||
<source-file src="src/ios/TextRequestSerializer.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFHTTPSessionManager.m"/>
|
||||
@@ -50,26 +61,29 @@
|
||||
<platform name="android">
|
||||
<config-file target="res/xml/config.xml" parent="/*">
|
||||
<feature name="CordovaHttpPlugin">
|
||||
<param name="android-package" value="com.synconset.cordovahttp.CordovaHttpPlugin"/>
|
||||
<param name="android-package" value="com.silkimen.cordovahttp.CordovaHttpPlugin"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
<config-file target="AndroidManifest.xml" parent="/manifest">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</config-file>
|
||||
<source-file src="src/android/com/github/kevinsawicki/http/HttpRequest.java" target-dir="src/com/github/kevinsawicki/http"/>
|
||||
<source-file src="src/android/com/github/kevinsawicki/http/OkConnectionFactory.java" target-dir="src/com/github/kevinsawicki/http"/>
|
||||
<source-file src="src/android/com/github/kevinsawicki/http/TLSSocketFactory.java" target-dir="src/com/github/kevinsawicki/http"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttp.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpDelete.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpDownload.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpGet.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpHead.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPlugin.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPost.java" target-dir="src/com/synconset/cordovahttp"/>
|
||||
<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.10.0"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaClientAuth.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpBase.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/cordovahttp/CordovaServerTrust.java" target-dir="src/com/silkimen/cordovahttp"/>
|
||||
<source-file src="src/android/com/silkimen/http/HttpBodyDecoder.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/HttpRequest.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/JsonUtils.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/KeyChainKeyManager.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/OkConnectionFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSConfiguration.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<preference name="OKHTTP_VERSION" default="3.10.0"/>
|
||||
<framework src="com.squareup.okhttp3:okhttp-urlconnection:$OKHTTP_VERSION"/>
|
||||
</platform>
|
||||
<platform name="browser">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
|
||||
WORKINGCOPY=$ROOT/temp/workingcopy
|
||||
CDV=$ROOT/node_modules/.bin/cordova
|
||||
|
||||
PLATFORM=ios
|
||||
@@ -34,11 +35,13 @@ while :; do
|
||||
shift
|
||||
done
|
||||
|
||||
printf 'Building test app for %s\n' $PLATFORM
|
||||
rm -rf $ROOT/temp
|
||||
mkdir $ROOT/temp
|
||||
cp -r $ROOT/test/app-template/ $ROOT/temp/
|
||||
cp $ROOT/test/app-test-definitions.js $ROOT/temp/www/
|
||||
cp -r $ROOT/test/e2e-app-template/. $ROOT/temp/
|
||||
cp $ROOT/test/e2e-specs.js $ROOT/temp/www/
|
||||
rsync -ax --exclude node_modules --exclude scripts --exclude temp --exclude test $ROOT/. $WORKINGCOPY
|
||||
cd $ROOT/temp
|
||||
$CDV prepare
|
||||
$CDV plugins add $ROOT
|
||||
$CDV build $PLATFORM --$TARGET
|
||||
$CDV plugins add $WORKINGCOPY
|
||||
$CDV build $PLATFORM --$TARGET --buildConfig build.json
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
|
||||
|
||||
pushd $ROOT
|
||||
VERSION=$(node -e "console.log(require('./package.json').version)")
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
curl http://dl.google.com/android/android-sdk_r24.4-macosx.zip -o android-sdk-macosx.zip
|
||||
tar -xvf android-sdk-macosx.zip
|
||||
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter platform-tools
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter build-tools-25.0.0
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter android-25
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-support
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-m2repository
|
||||
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-google-m2repository
|
||||
@@ -1,13 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
|
||||
|
||||
if [ $CI == "true" ] && ([ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]); then
|
||||
echo "Skipping CI tests, because Saucelabs credentials are not set.";
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
printf 'Running e2e tests\n'
|
||||
pushd $ROOT
|
||||
./node_modules/.bin/mocha ./test/app-mocha-specs/test.js "$@"
|
||||
./node_modules/.bin/mocha ./test/e2e-tooling/test.js "$@"
|
||||
popd
|
||||
|
||||
29
scripts/update-e2e-client-cert.js
Normal file
29
scripts/update-e2e-client-cert.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const path = require('path');
|
||||
|
||||
const SOURCE_URL = 'https://badssl.com/certs/badssl.com-client.p12';
|
||||
const TARGET_PATH = path.join(__dirname, '../test/e2e-app-template/www/certificates/badssl-client-cert.pkcs');
|
||||
|
||||
const downloadPkcsContainer = (source, target) => new Promise((resolve, reject) => {
|
||||
const file = fs.createWriteStream(target);
|
||||
|
||||
const req = https.get(source, response => {
|
||||
response.pipe(file)
|
||||
resolve(target);
|
||||
});
|
||||
|
||||
req.on('error', error => {
|
||||
return reject(error)
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
console.log(`Updating client certificate from ${SOURCE_URL}`);
|
||||
|
||||
downloadPkcsContainer(SOURCE_URL, TARGET_PATH)
|
||||
.catch(error => {
|
||||
console.error(`Updating client certificate failed: ${error}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -3,7 +3,7 @@ const https = require('https');
|
||||
const path = require('path');
|
||||
|
||||
const SOURCE_HOST = 'httpbin.org';
|
||||
const TARGET_PATH = path.join(__dirname, '../test/app-template/www/certificates/httpbin.org.cer');
|
||||
const TARGET_PATH = path.join(__dirname, '../test/e2e-app-template/www/certificates/httpbin.org.cer');
|
||||
|
||||
const getCert = hostname => new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
@@ -30,13 +30,11 @@ const getCert = hostname => new Promise((resolve, reject) => {
|
||||
req.end();
|
||||
});
|
||||
|
||||
console.log(`Updating test certificate from ${SOURCE_HOST}`);
|
||||
console.log(`Updating server certificate from ${SOURCE_HOST}`);
|
||||
|
||||
getCert(SOURCE_HOST)
|
||||
.then(cert => {
|
||||
fs.writeFileSync(TARGET_PATH, cert.raw);
|
||||
})
|
||||
.then(cert => fs.writeFileSync(TARGET_PATH, cert.raw))
|
||||
.catch(error => {
|
||||
console.error(`Updating test cert failed: ${error}`);
|
||||
console.error(`Updating server certificate failed: ${error}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
|
||||
|
||||
cd $ROOT
|
||||
npm i
|
||||
|
||||
33
scripts/upload-browserstack.sh
Executable file
33
scripts/upload-browserstack.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
|
||||
TEMP=$ROOT/temp
|
||||
|
||||
if [ -z $BROWSERSTACK_USERNAME ] || [ -z $BROWSERSTACK_ACCESS_KEY ]; then
|
||||
echo "Skipping uploading artifact, because BrowserStack credentials are not set.";
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
if [ $PLATFORM = "android" ]; then
|
||||
curl -u $BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY \
|
||||
-X POST \
|
||||
https://api-cloud.browserstack.com/app-automate/upload \
|
||||
-F "file=@$TEMP/platforms/android/app/build/outputs/apk/debug/app-debug.apk" \
|
||||
-F "data={\"custom_id\": \"HttpTestAppAndroid\"}"
|
||||
else
|
||||
rm -rf $TEMP/HttpDemo.ipa
|
||||
pushd $TEMP/platforms/ios/build/emulator
|
||||
rm -rf ./Payload
|
||||
mkdir -p ./Payload
|
||||
cp -r ./HttpDemo.app ./Payload/HttpDemo.app
|
||||
zip -r $TEMP/HttpDemo.ipa ./Payload
|
||||
popd
|
||||
|
||||
curl -u $BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY \
|
||||
-X POST \
|
||||
https://api-cloud.browserstack.com/app-automate/upload \
|
||||
-F "file=@$TEMP/HttpDemo.ipa" \
|
||||
-F "data={\"custom_id\": \"HttpTestAppIos\"}"
|
||||
fi
|
||||
@@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
|
||||
TEMP=$ROOT/temp
|
||||
|
||||
if [ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]; then
|
||||
@@ -15,7 +15,7 @@ if [ $PLATFORM = "android" ]; then
|
||||
-X POST \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
https://saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/HttpDemo.apk?overwrite=true \
|
||||
--data-binary @$TEMP/platforms/android/build/outputs/apk/android-debug.apk
|
||||
--data-binary @$TEMP/platforms/android/app/build/outputs/apk/debug/app-debug.apk
|
||||
else
|
||||
rm -rf $TEMP/HttpDemo.app.zip
|
||||
pushd $TEMP/platforms/ios/build/emulator
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.github.kevinsawicki.http;
|
||||
|
||||
import okhttp3.OkUrlFactory;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.net.Proxy;
|
||||
|
||||
|
||||
public class OkConnectionFactory implements HttpRequest.ConnectionFactory {
|
||||
|
||||
protected OkHttpClient okHttpClient = new OkHttpClient();
|
||||
|
||||
public HttpURLConnection create(URL url) {
|
||||
OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClient);
|
||||
return (HttpURLConnection) okUrlFactory.open(url);
|
||||
}
|
||||
|
||||
public HttpURLConnection create(URL url, Proxy proxy) {
|
||||
OkHttpClient okHttpClientWithProxy = okHttpClient.newBuilder().proxy(proxy).build();
|
||||
OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClientWithProxy);
|
||||
return (HttpURLConnection) okUrlFactory.open(url);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.github.kevinsawicki.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
public class TLSSocketFactory extends SSLSocketFactory {
|
||||
|
||||
private SSLSocketFactory delegate;
|
||||
|
||||
public TLSSocketFactory(SSLContext context) {
|
||||
delegate = context.getSocketFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return delegate.getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return delegate.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
|
||||
return enableTLSOnSocket(delegate.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
|
||||
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
|
||||
}
|
||||
|
||||
private Socket enableTLSOnSocket(Socket socket) {
|
||||
if(socket != null && (socket instanceof SSLSocket)) {
|
||||
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
113
src/android/com/silkimen/cordovahttp/CordovaClientAuth.java
Normal file
113
src/android/com/silkimen/cordovahttp/CordovaClientAuth.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyChainAliasCallback;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import com.silkimen.http.KeyChainKeyManager;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
class CordovaClientAuth implements Runnable, KeyChainAliasCallback {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private String mode;
|
||||
private String aliasString;
|
||||
private byte[] rawPkcs;
|
||||
private String pkcsPassword;
|
||||
private Activity activity;
|
||||
private Context context;
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
public CordovaClientAuth(final String mode, final String aliasString, final byte[] rawPkcs,
|
||||
final String pkcsPassword, final Activity activity, final Context context, final TLSConfiguration configContainer,
|
||||
final CallbackContext callbackContext) {
|
||||
|
||||
this.mode = mode;
|
||||
this.aliasString = aliasString;
|
||||
this.rawPkcs = rawPkcs;
|
||||
this.pkcsPassword = pkcsPassword;
|
||||
this.activity = activity;
|
||||
this.tlsConfiguration = configContainer;
|
||||
this.context = context;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if ("systemstore".equals(this.mode)) {
|
||||
this.loadFromSystemStore();
|
||||
} else if ("buffer".equals(this.mode)) {
|
||||
this.loadFromBuffer();
|
||||
} else {
|
||||
this.disableClientAuth();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromSystemStore() {
|
||||
if (this.aliasString == null) {
|
||||
KeyChain.choosePrivateKeyAlias(this.activity, this, null, null, null, -1, null);
|
||||
} else {
|
||||
this.alias(this.aliasString);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromBuffer() {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(this.rawPkcs);
|
||||
|
||||
keyStore.load(stream, this.pkcsPassword.toCharArray());
|
||||
keyManagerFactory.init(keyStore, this.pkcsPassword.toCharArray());
|
||||
|
||||
this.tlsConfiguration.setKeyManagers(keyManagerFactory.getKeyManagers());
|
||||
this.callbackContext.success();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Couldn't load given PKCS12 container for authentication", e);
|
||||
this.callbackContext.error("Couldn't load given PKCS12 container for authentication");
|
||||
}
|
||||
}
|
||||
|
||||
private void disableClientAuth() {
|
||||
this.tlsConfiguration.setKeyManagers(null);
|
||||
this.callbackContext.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alias(final String alias) {
|
||||
try {
|
||||
if (alias == null) {
|
||||
throw new Exception("Couldn't get a consent for private key access");
|
||||
}
|
||||
|
||||
PrivateKey key = KeyChain.getPrivateKey(this.context, alias);
|
||||
X509Certificate[] chain = KeyChain.getCertificateChain(this.context, alias);
|
||||
KeyManager keyManager = new KeyChainKeyManager(alias, key, chain);
|
||||
|
||||
this.tlsConfiguration.setKeyManagers(new KeyManager[] { keyManager });
|
||||
|
||||
this.callbackContext.success(alias);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Couldn't load private key and certificate pair with given alias \"" + alias + "\" for authentication",
|
||||
e);
|
||||
this.callbackContext.error(
|
||||
"Couldn't load private key and certificate pair with given alias \"" + alias + "\" for authentication");
|
||||
}
|
||||
}
|
||||
}
|
||||
206
src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
Normal file
206
src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
Normal file
@@ -0,0 +1,206 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import com.silkimen.http.HttpBodyDecoder;
|
||||
import com.silkimen.http.HttpRequest;
|
||||
import com.silkimen.http.HttpRequest.HttpRequestException;
|
||||
import com.silkimen.http.JsonUtils;
|
||||
import com.silkimen.http.OkConnectionFactory;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
abstract class CordovaHttpBase implements Runnable {
|
||||
protected static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
protected String method;
|
||||
protected String url;
|
||||
protected String serializer = "none";
|
||||
protected String responseType;
|
||||
protected Object data;
|
||||
protected JSONObject headers;
|
||||
protected int timeout;
|
||||
protected boolean followRedirects;
|
||||
protected TLSConfiguration tlsConfiguration;
|
||||
protected CallbackContext callbackContext;
|
||||
|
||||
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout,
|
||||
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
this.serializer = serializer;
|
||||
this.data = data;
|
||||
this.headers = headers;
|
||||
this.timeout = timeout;
|
||||
this.followRedirects = followRedirects;
|
||||
this.responseType = responseType;
|
||||
this.tlsConfiguration = tlsConfiguration;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
public CordovaHttpBase(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
|
||||
String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
this.headers = headers;
|
||||
this.timeout = timeout;
|
||||
this.followRedirects = followRedirects;
|
||||
this.responseType = responseType;
|
||||
this.tlsConfiguration = tlsConfiguration;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
CordovaHttpResponse response = new CordovaHttpResponse();
|
||||
|
||||
try {
|
||||
HttpRequest request = this.createRequest();
|
||||
this.prepareRequest(request);
|
||||
this.sendBody(request);
|
||||
this.processResponse(request, response);
|
||||
} catch (HttpRequestException e) {
|
||||
if (e.getCause() instanceof SSLException) {
|
||||
response.setStatus(-2);
|
||||
response.setErrorMessage("TLS connection could not be established: " + e.getMessage());
|
||||
Log.w(TAG, "TLS connection could not be established", e);
|
||||
} else if (e.getCause() instanceof UnknownHostException) {
|
||||
response.setStatus(-3);
|
||||
response.setErrorMessage("Host could not be resolved: " + e.getMessage());
|
||||
Log.w(TAG, "Host could not be resolved", e);
|
||||
} else if (e.getCause() instanceof SocketTimeoutException) {
|
||||
response.setStatus(-4);
|
||||
response.setErrorMessage("Request timed out: " + e.getMessage());
|
||||
Log.w(TAG, "Request timed out", e);
|
||||
} else {
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage("There was an error with the request: " + e.getCause().getMessage());
|
||||
Log.w(TAG, "Generic request error", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage(e.getMessage());
|
||||
Log.e(TAG, "An unexpected error occured", e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (response.hasFailed()) {
|
||||
this.callbackContext.error(response.toJSON());
|
||||
} else {
|
||||
this.callbackContext.success(response.toJSON());
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "An unexpected error occured while creating HTTP response object", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpRequest createRequest() throws JSONException {
|
||||
return new HttpRequest(this.url, this.method);
|
||||
}
|
||||
|
||||
protected void prepareRequest(HttpRequest request) throws JSONException, IOException {
|
||||
request.followRedirects(this.followRedirects);
|
||||
request.readTimeout(this.timeout);
|
||||
request.acceptCharset("UTF-8");
|
||||
request.uncompress(true);
|
||||
HttpRequest.setConnectionFactory(new OkConnectionFactory());
|
||||
|
||||
if (this.tlsConfiguration.getHostnameVerifier() != null) {
|
||||
request.setHostnameVerifier(this.tlsConfiguration.getHostnameVerifier());
|
||||
}
|
||||
|
||||
request.setSSLSocketFactory(this.tlsConfiguration.getTLSSocketFactory());
|
||||
|
||||
// setup content type before applying headers, so user can override it
|
||||
this.setContentType(request);
|
||||
|
||||
request.headers(JsonUtils.getStringMap(this.headers));
|
||||
}
|
||||
|
||||
protected void setContentType(HttpRequest request) {
|
||||
if ("json".equals(this.serializer)) {
|
||||
request.contentType("application/json", "UTF-8");
|
||||
} else if ("utf8".equals(this.serializer)) {
|
||||
request.contentType("text/plain", "UTF-8");
|
||||
} else if ("raw".equals(this.serializer)) {
|
||||
request.contentType("application/octet-stream");
|
||||
} else if ("urlencoded".equals(this.serializer)) {
|
||||
// intentionally left blank, because content type is set in HttpRequest.form()
|
||||
} else if ("multipart".equals(this.serializer)) {
|
||||
request.contentType("multipart/form-data");
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendBody(HttpRequest request) throws Exception {
|
||||
if (this.data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("json".equals(this.serializer)) {
|
||||
request.send(this.data.toString());
|
||||
} else if ("utf8".equals(this.serializer)) {
|
||||
request.send(((JSONObject) this.data).getString("text"));
|
||||
} else if ("raw".equals(this.serializer)) {
|
||||
request.send(Base64.decode((String)this.data, Base64.DEFAULT));
|
||||
} else if ("urlencoded".equals(this.serializer)) {
|
||||
request.form(JsonUtils.getObjectMap((JSONObject) this.data));
|
||||
} else if ("multipart".equals(this.serializer)) {
|
||||
JSONArray buffers = ((JSONObject) this.data).getJSONArray("buffers");
|
||||
JSONArray names = ((JSONObject) this.data).getJSONArray("names");
|
||||
JSONArray fileNames = ((JSONObject) this.data).getJSONArray("fileNames");
|
||||
JSONArray types = ((JSONObject) this.data).getJSONArray("types");
|
||||
|
||||
for (int i = 0; i < buffers.length(); ++i) {
|
||||
byte[] bytes = Base64.decode(buffers.getString(i), Base64.DEFAULT);
|
||||
String name = names.getString(i);
|
||||
|
||||
if (fileNames.isNull(i)) {
|
||||
request.part(name, new String(bytes, "UTF-8"));
|
||||
} else {
|
||||
request.part(name, fileNames.getString(i), types.getString(i), new ByteArrayInputStream(bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void processResponse(HttpRequest request, CordovaHttpResponse response) throws Exception {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
request.receive(outputStream);
|
||||
|
||||
response.setStatus(request.code());
|
||||
response.setUrl(request.url().toString());
|
||||
response.setHeaders(request.headers());
|
||||
|
||||
if (request.code() >= 200 && request.code() < 300) {
|
||||
if ("text".equals(this.responseType)) {
|
||||
String decoded = HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset());
|
||||
response.setBody(decoded);
|
||||
} else {
|
||||
response.setData(outputStream.toByteArray());
|
||||
}
|
||||
} else {
|
||||
response.setErrorMessage(HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.silkimen.http.HttpRequest;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.file.FileUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpDownload extends CordovaHttpBase {
|
||||
private String filePath;
|
||||
|
||||
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout, boolean followRedirects,
|
||||
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
|
||||
super("GET", url, headers, timeout, followRedirects, "text", tlsConfiguration, callbackContext);
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse(HttpRequest request, CordovaHttpResponse response) throws Exception {
|
||||
response.setStatus(request.code());
|
||||
response.setUrl(request.url().toString());
|
||||
response.setHeaders(request.headers());
|
||||
|
||||
if (request.code() >= 200 && request.code() < 300) {
|
||||
File file = new File(new URI(this.filePath));
|
||||
JSONObject fileEntry = FileUtils.getFilePlugin().getEntryForFile(file);
|
||||
|
||||
request.receive(file);
|
||||
response.setFileEntry(fileEntry);
|
||||
} else {
|
||||
response.setErrorMessage("There was an error downloading the file");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpOperation extends CordovaHttpBase {
|
||||
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
|
||||
int timeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CallbackContext callbackContext) {
|
||||
|
||||
super(method, url, serializer, data, headers, timeout, followRedirects, responseType, tlsConfiguration,
|
||||
callbackContext);
|
||||
}
|
||||
|
||||
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
|
||||
String responseType, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
|
||||
|
||||
super(method, url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
|
||||
}
|
||||
}
|
||||
169
src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
Normal file
169
src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.security.KeyStore;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Base64;
|
||||
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
|
||||
this.tlsConfiguration = new TLSConfiguration();
|
||||
|
||||
try {
|
||||
KeyStore store = KeyStore.getInstance("AndroidCAStore");
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
|
||||
store.load(null);
|
||||
tmf.init(store);
|
||||
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while loading system's CA certificates", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext)
|
||||
throws JSONException {
|
||||
|
||||
if (action == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ("get".equals(action)) {
|
||||
return this.executeHttpRequestWithoutData(action, args, callbackContext);
|
||||
} else if ("head".equals(action)) {
|
||||
return this.executeHttpRequestWithoutData(action, args, callbackContext);
|
||||
} else if ("delete".equals(action)) {
|
||||
return this.executeHttpRequestWithoutData(action, args, callbackContext);
|
||||
} else if ("options".equals(action)) {
|
||||
return this.executeHttpRequestWithoutData(action, args, callbackContext);
|
||||
} else if ("post".equals(action)) {
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
} else if ("put".equals(action)) {
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
} else if ("patch".equals(action)) {
|
||||
return this.executeHttpRequestWithData(action, args, callbackContext);
|
||||
} else if ("uploadFiles".equals(action)) {
|
||||
return this.uploadFiles(args, callbackContext);
|
||||
} else if ("downloadFile".equals(action)) {
|
||||
return this.downloadFile(args, callbackContext);
|
||||
} else if ("setServerTrustMode".equals(action)) {
|
||||
return this.setServerTrustMode(args, callbackContext);
|
||||
} else if ("setClientAuthMode".equals(action)) {
|
||||
return this.setClientAuthMode(args, callbackContext);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean executeHttpRequestWithoutData(final String method, final JSONArray args,
|
||||
final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
String url = args.getString(0);
|
||||
JSONObject headers = args.getJSONObject(1);
|
||||
int timeout = args.getInt(2) * 1000;
|
||||
boolean followRedirect = args.getBoolean(3);
|
||||
String responseType = args.getString(4);
|
||||
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(request);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean executeHttpRequestWithData(final String method, final JSONArray args,
|
||||
final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
String url = args.getString(0);
|
||||
Object data = args.get(1);
|
||||
String serializer = args.getString(2);
|
||||
JSONObject headers = args.getJSONObject(3);
|
||||
int timeout = args.getInt(4) * 1000;
|
||||
boolean followRedirect = args.getBoolean(5);
|
||||
String responseType = args.getString(6);
|
||||
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
|
||||
timeout, followRedirect, responseType, this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(request);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean uploadFiles(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String url = args.getString(0);
|
||||
JSONObject headers = args.getJSONObject(1);
|
||||
JSONArray filePaths = args.getJSONArray(2);
|
||||
JSONArray uploadNames = args.getJSONArray(3);
|
||||
int timeout = args.getInt(4) * 1000;
|
||||
boolean followRedirect = args.getBoolean(5);
|
||||
String responseType = args.getString(6);
|
||||
|
||||
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, timeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(upload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean downloadFile(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String url = args.getString(0);
|
||||
JSONObject headers = args.getJSONObject(1);
|
||||
String filePath = args.getString(2);
|
||||
int timeout = args.getInt(3) * 1000;
|
||||
boolean followRedirect = args.getBoolean(4);
|
||||
|
||||
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout, followRedirect,
|
||||
this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(download);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setServerTrustMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
CordovaServerTrust runnable = new CordovaServerTrust(args.getString(0), this.cordova.getActivity(),
|
||||
this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(runnable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setClientAuthMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
byte[] pkcs = args.isNull(2) ? null : Base64.decode(args.getString(2), Base64.DEFAULT);
|
||||
|
||||
CordovaClientAuth runnable = new CordovaClientAuth(args.getString(0), args.isNull(1) ? null : args.getString(1),
|
||||
pkcs, args.getString(3), this.cordova.getActivity(), this.cordova.getActivity().getApplicationContext(),
|
||||
this.tlsConfiguration, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(runnable);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
100
src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java
Normal file
100
src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Base64;
|
||||
|
||||
class CordovaHttpResponse {
|
||||
private int status;
|
||||
private String url;
|
||||
private Map<String, List<String>> headers;
|
||||
private String body;
|
||||
private byte[] rawData;
|
||||
private JSONObject fileEntry;
|
||||
private boolean hasFailed;
|
||||
private boolean isFileOperation;
|
||||
private boolean isRawResponse;
|
||||
private String error;
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, List<String>> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public void setData(byte[] rawData) {
|
||||
this.isRawResponse = true;
|
||||
this.rawData = rawData;
|
||||
}
|
||||
|
||||
public void setFileEntry(JSONObject entry) {
|
||||
this.isFileOperation = true;
|
||||
this.fileEntry = entry;
|
||||
}
|
||||
|
||||
public void setErrorMessage(String message) {
|
||||
this.hasFailed = true;
|
||||
this.error = message;
|
||||
}
|
||||
|
||||
public boolean hasFailed() {
|
||||
return this.hasFailed;
|
||||
}
|
||||
|
||||
public JSONObject toJSON() throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
|
||||
json.put("status", this.status);
|
||||
json.put("url", this.url);
|
||||
|
||||
if (this.headers != null && !this.headers.isEmpty()) {
|
||||
json.put("headers", new JSONObject(getFilteredHeaders()));
|
||||
}
|
||||
|
||||
if (this.hasFailed) {
|
||||
json.put("error", this.error);
|
||||
} else if (this.isFileOperation) {
|
||||
json.put("file", this.fileEntry);
|
||||
} else if (this.isRawResponse) {
|
||||
json.put("data", Base64.encodeToString(this.rawData, Base64.DEFAULT));
|
||||
} else {
|
||||
json.put("data", this.body);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
private Map<String, String> getFilteredHeaders() throws JSONException {
|
||||
Map<String, String> filteredHeaders = new HashMap<String, String>();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
List<String> value = entry.getValue();
|
||||
|
||||
if ((key != null) && (!value.isEmpty())) {
|
||||
filteredHeaders.put(key.toLowerCase(), TextUtils.join(", ", value));
|
||||
}
|
||||
}
|
||||
|
||||
return filteredHeaders;
|
||||
}
|
||||
}
|
||||
92
src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
Normal file
92
src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.silkimen.http.HttpRequest;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpUpload extends CordovaHttpBase {
|
||||
private JSONArray filePaths;
|
||||
private JSONArray uploadNames;
|
||||
private Context applicationContext;
|
||||
|
||||
public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int timeout,
|
||||
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
Context applicationContext, CallbackContext callbackContext) {
|
||||
|
||||
super("POST", url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
|
||||
this.filePaths = filePaths;
|
||||
this.uploadNames = uploadNames;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendBody(HttpRequest request) throws Exception {
|
||||
for (int i = 0; i < this.filePaths.length(); ++i) {
|
||||
String uploadName = this.uploadNames.getString(i);
|
||||
String filePath = this.filePaths.getString(i);
|
||||
|
||||
Uri fileUri = Uri.parse(filePath);
|
||||
|
||||
// File Scheme
|
||||
if (ContentResolver.SCHEME_FILE.equals(fileUri.getScheme())) {
|
||||
File file = new File(new URI(filePath));
|
||||
String fileName = file.getName().trim();
|
||||
String mimeType = this.getMimeTypeFromFileName(fileName);
|
||||
|
||||
request.part(uploadName, fileName, mimeType, file);
|
||||
}
|
||||
|
||||
// Content Scheme
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(fileUri.getScheme())) {
|
||||
InputStream inputStream = this.applicationContext.getContentResolver().openInputStream(fileUri);
|
||||
String fileName = this.getFileNameFromContentScheme(fileUri, this.applicationContext).trim();
|
||||
String mimeType = this.getMimeTypeFromFileName(fileName);
|
||||
|
||||
request.part(uploadName, fileName, mimeType, inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getFileNameFromContentScheme(Uri contentSchemeUri, Context applicationContext) {
|
||||
Cursor returnCursor = applicationContext.getContentResolver().query(contentSchemeUri, null, null, null, null);
|
||||
|
||||
if (returnCursor == null || !returnCursor.moveToFirst()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
String fileName = returnCursor.getString(nameIndex);
|
||||
returnCursor.close();
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private String getMimeTypeFromFileName(String fileName) {
|
||||
if (fileName == null || !fileName.contains(".")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
||||
int extIndex = fileName.lastIndexOf('.') + 1;
|
||||
String extension = fileName.substring(extIndex).toLowerCase();
|
||||
|
||||
return mimeTypeMap.getMimeTypeFromExtension(extension);
|
||||
}
|
||||
}
|
||||
124
src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
Normal file
124
src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
class CordovaServerTrust implements Runnable {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private final TrustManager[] noOpTrustManagers;
|
||||
private final HostnameVerifier noOpVerifier;
|
||||
|
||||
private String mode;
|
||||
private Activity activity;
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
public CordovaServerTrust(final String mode, final Activity activity, final TLSConfiguration configContainer,
|
||||
final CallbackContext callbackContext) {
|
||||
|
||||
this.mode = mode;
|
||||
this.activity = activity;
|
||||
this.tlsConfiguration = configContainer;
|
||||
this.callbackContext = callbackContext;
|
||||
|
||||
this.noOpTrustManagers = new TrustManager[] { new X509TrustManager() {
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
// intentionally left blank
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
// intentionally left blank
|
||||
}
|
||||
} };
|
||||
|
||||
this.noOpVerifier = new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if ("legacy".equals(this.mode)) {
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(null);
|
||||
} else if ("nocheck".equals(this.mode)) {
|
||||
this.tlsConfiguration.setHostnameVerifier(this.noOpVerifier);
|
||||
this.tlsConfiguration.setTrustManagers(this.noOpTrustManagers);
|
||||
} else if ("pinned".equals(this.mode)) {
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromBundle("www/certificates")));
|
||||
} else {
|
||||
this.tlsConfiguration.setHostnameVerifier(null);
|
||||
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while configuring SSL cert mode", e);
|
||||
callbackContext.error("An error occured while configuring SSL cert mode");
|
||||
}
|
||||
}
|
||||
|
||||
private TrustManager[] getTrustManagers(KeyStore store) throws GeneralSecurityException {
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
tmf.init(store);
|
||||
|
||||
return tmf.getTrustManagers();
|
||||
}
|
||||
|
||||
private KeyStore getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
|
||||
AssetManager assetManager = this.activity.getAssets();
|
||||
String[] files = assetManager.list(path);
|
||||
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
|
||||
keyStore.load(null, null);
|
||||
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
int index = files[i].lastIndexOf('.');
|
||||
|
||||
if (index == -1 || !files[i].substring(index).equals(".cer")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
keyStore.setCertificateEntry("CA" + i, cf.generateCertificate(assetManager.open(path + "/" + files[i])));
|
||||
}
|
||||
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private KeyStore getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
|
||||
KeyStore store = KeyStore.getInstance(storeType);
|
||||
store.load(null);
|
||||
|
||||
return store;
|
||||
}
|
||||
}
|
||||
55
src/android/com/silkimen/http/HttpBodyDecoder.java
Normal file
55
src/android/com/silkimen/http/HttpBodyDecoder.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.MalformedInputException;
|
||||
|
||||
public class HttpBodyDecoder {
|
||||
private static final String[] ACCEPTED_CHARSETS = new String[] { "UTF-8", "ISO-8859-1" };
|
||||
|
||||
public static String decodeBody(byte[] body, String charsetName)
|
||||
throws CharacterCodingException, MalformedInputException {
|
||||
|
||||
return decodeBody(ByteBuffer.wrap(body), charsetName);
|
||||
}
|
||||
|
||||
public static String decodeBody(ByteBuffer body, String charsetName)
|
||||
throws CharacterCodingException, MalformedInputException {
|
||||
|
||||
if (charsetName == null) {
|
||||
return tryDecodeByteBuffer(body);
|
||||
} else {
|
||||
return decodeByteBuffer(body, charsetName);
|
||||
}
|
||||
}
|
||||
|
||||
private static String tryDecodeByteBuffer(ByteBuffer buffer)
|
||||
throws CharacterCodingException, MalformedInputException {
|
||||
|
||||
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
|
||||
try {
|
||||
return decodeByteBuffer(buffer, ACCEPTED_CHARSETS[i]);
|
||||
} catch (MalformedInputException e) {
|
||||
continue;
|
||||
} catch (CharacterCodingException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return decodeBody(buffer, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
|
||||
}
|
||||
|
||||
private static String decodeByteBuffer(ByteBuffer buffer, String charsetName)
|
||||
throws CharacterCodingException, MalformedInputException {
|
||||
|
||||
return createCharsetDecoder(charsetName).decode(buffer).toString();
|
||||
}
|
||||
|
||||
private static CharsetDecoder createCharsetDecoder(String charsetName) {
|
||||
return Charset.forName(charsetName).newDecoder().onMalformedInput(CodingErrorAction.REPORT)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPORT);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
58
src/android/com/silkimen/http/JsonUtils.java
Normal file
58
src/android/com/silkimen/http/JsonUtils.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class JsonUtils {
|
||||
public static HashMap<String, String> getStringMap(JSONObject object) throws JSONException {
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
|
||||
if (object == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
Iterator<?> i = object.keys();
|
||||
|
||||
while (i.hasNext()) {
|
||||
String key = (String) i.next();
|
||||
map.put(key, object.getString(key));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static HashMap<String, Object> getObjectMap(JSONObject object) throws JSONException {
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if (object == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
Iterator<?> i = object.keys();
|
||||
|
||||
while (i.hasNext()) {
|
||||
String key = (String) i.next();
|
||||
Object value = object.get(key);
|
||||
|
||||
if (value instanceof JSONArray) {
|
||||
map.put(key, getObjectList((JSONArray) value));
|
||||
} else {
|
||||
map.put(key, object.get(key));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static ArrayList<Object> getObjectList(JSONArray array) throws JSONException {
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
list.add(array.get(i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
57
src/android/com/silkimen/http/KeyChainKeyManager.java
Normal file
57
src/android/com/silkimen/http/KeyChainKeyManager.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import android.content.Context;
|
||||
import android.security.KeyChain;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
|
||||
public class KeyChainKeyManager extends X509ExtendedKeyManager {
|
||||
private final String alias;
|
||||
private final X509Certificate[] chain;
|
||||
private final PrivateKey key;
|
||||
|
||||
public KeyChainKeyManager(String alias, PrivateKey key, X509Certificate[] chain) {
|
||||
this.alias = alias;
|
||||
this.key = key;
|
||||
this.chain = chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
|
||||
return this.alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getCertificateChain(String alias) {
|
||||
return chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey(String alias) {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
|
||||
// not a client SSLSocket callback
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String[] getClientAliases(String keyType, Principal[] issuers) {
|
||||
// not a client SSLSocket callback
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String[] getServerAliases(String keyType, Principal[] issuers) {
|
||||
// not a client SSLSocket callback
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
26
src/android/com/silkimen/http/OkConnectionFactory.java
Normal file
26
src/android/com/silkimen/http/OkConnectionFactory.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.OkUrlFactory;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.net.Proxy;
|
||||
|
||||
public class OkConnectionFactory implements HttpRequest.ConnectionFactory {
|
||||
private final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
public HttpURLConnection create(URL url) {
|
||||
OkUrlFactory urlFactory = new OkUrlFactory(this.client);
|
||||
|
||||
return (HttpURLConnection) urlFactory.open(url);
|
||||
}
|
||||
|
||||
public HttpURLConnection create(URL url, Proxy proxy) {
|
||||
OkHttpClient clientWithProxy = new OkHttpClient.Builder().proxy(proxy).build();
|
||||
OkUrlFactory urlFactory = new OkUrlFactory(clientWithProxy);
|
||||
|
||||
return (HttpURLConnection) urlFactory.open(url);
|
||||
}
|
||||
}
|
||||
63
src/android/com/silkimen/http/TLSConfiguration.java
Normal file
63
src/android/com/silkimen/http/TLSConfiguration.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
|
||||
import com.silkimen.http.TLSSocketFactory;
|
||||
|
||||
public class TLSConfiguration {
|
||||
private TrustManager[] trustManagers;
|
||||
private KeyManager[] keyManagers;
|
||||
private HostnameVerifier hostnameVerifier;
|
||||
|
||||
private SSLSocketFactory socketFactory;
|
||||
|
||||
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
}
|
||||
|
||||
public void setKeyManagers(KeyManager[] keyManagers) {
|
||||
this.keyManagers = keyManagers;
|
||||
this.socketFactory = null;
|
||||
}
|
||||
|
||||
public void setTrustManagers(TrustManager[] trustManagers) {
|
||||
this.trustManagers = trustManagers;
|
||||
this.socketFactory = null;
|
||||
}
|
||||
|
||||
public HostnameVerifier getHostnameVerifier() {
|
||||
return this.hostnameVerifier;
|
||||
}
|
||||
|
||||
public SSLSocketFactory getTLSSocketFactory() throws IOException {
|
||||
if (this.socketFactory != null) {
|
||||
return this.socketFactory;
|
||||
}
|
||||
|
||||
try {
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
|
||||
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 20) {
|
||||
this.socketFactory = new TLSSocketFactory(context);
|
||||
} else {
|
||||
this.socketFactory = context.getSocketFactory();
|
||||
}
|
||||
|
||||
return this.socketFactory;
|
||||
} catch (GeneralSecurityException e) {
|
||||
IOException ioException = new IOException("Security exception occured while configuring TLS context");
|
||||
ioException.initCause(e);
|
||||
throw ioException;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/android/com/silkimen/http/TLSSocketFactory.java
Normal file
63
src/android/com/silkimen/http/TLSSocketFactory.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
public class TLSSocketFactory extends SSLSocketFactory {
|
||||
|
||||
private SSLSocketFactory delegate;
|
||||
|
||||
public TLSSocketFactory(SSLContext context) {
|
||||
delegate = context.getSocketFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return delegate.getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return delegate.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(socket, host, port, autoClose));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
|
||||
return enableTLSOnSocket(delegate.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
|
||||
throws IOException, UnknownHostException {
|
||||
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
|
||||
throws IOException {
|
||||
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
|
||||
}
|
||||
|
||||
private Socket enableTLSOnSocket(Socket socket) {
|
||||
if (socket != null && (socket instanceof SSLSocket)) {
|
||||
((SSLSocket) socket).setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.MalformedInputException;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.Iterator;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
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 disableRedirect = new AtomicBoolean(false);
|
||||
|
||||
private String urlString;
|
||||
private Object params;
|
||||
private String serializerName;
|
||||
private JSONObject headers;
|
||||
private int timeoutInMilliseconds;
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
public CordovaHttp(String urlString, Object params, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
this(urlString, params, "default", headers, timeout, callbackContext);
|
||||
}
|
||||
|
||||
public CordovaHttp(String urlString, Object params, String serializerName, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
this.urlString = urlString;
|
||||
this.params = params;
|
||||
this.serializerName = serializerName;
|
||||
this.headers = headers;
|
||||
this.timeoutInMilliseconds = timeout;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
public static void disableRedirect(boolean disable) {
|
||||
disableRedirect.set(disable);
|
||||
}
|
||||
|
||||
protected String getUrlString() {
|
||||
return this.urlString;
|
||||
}
|
||||
|
||||
protected Object getParamsObject() {
|
||||
return this.params;
|
||||
}
|
||||
|
||||
protected String getSerializerName() {
|
||||
return this.serializerName;
|
||||
}
|
||||
|
||||
protected HashMap<String, Object> getParamsMap() throws JSONException, Exception {
|
||||
if (this.params instanceof JSONObject) {
|
||||
return this.getMapFromJSONObject((JSONObject) this.params);
|
||||
} else {
|
||||
throw new Exception("unsupported params type, needs to be a JSON object");
|
||||
}
|
||||
}
|
||||
|
||||
protected JSONObject getHeadersObject() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
protected HashMap<String, String> getHeadersMap() throws JSONException {
|
||||
return this.getStringMapFromJSONObject(this.headers);
|
||||
}
|
||||
|
||||
protected int getRequestTimeout() {
|
||||
return this.timeoutInMilliseconds;
|
||||
}
|
||||
|
||||
protected CallbackContext getCallbackContext() {
|
||||
return this.callbackContext;
|
||||
}
|
||||
|
||||
protected HttpRequest setupRedirect(HttpRequest request) {
|
||||
if (disableRedirect.get()) {
|
||||
request.followRedirects(false);
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
protected void setupDataSerializer(HttpRequest request) throws JSONException, Exception {
|
||||
if ("json".equals(this.getSerializerName())) {
|
||||
request.contentType(request.CONTENT_TYPE_JSON, request.CHARSET_UTF8);
|
||||
} else if ("utf8".equals(this.getSerializerName())) {
|
||||
request.contentType("text/plain", request.CHARSET_UTF8);
|
||||
}
|
||||
}
|
||||
|
||||
protected void respondWithError(int status, String msg) {
|
||||
try {
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("status", status);
|
||||
response.put("error", msg);
|
||||
this.callbackContext.error(response);
|
||||
} catch (JSONException e) {
|
||||
this.callbackContext.error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected void respondWithError(String msg) {
|
||||
this.respondWithError(-1, msg);
|
||||
}
|
||||
|
||||
protected void addResponseHeaders(HttpRequest request, JSONObject response) throws JSONException {
|
||||
Map<String, List<String>> headers = request.headers();
|
||||
Map<String, String> filteredHeaders = new HashMap<String, String>();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
List<String> value = entry.getValue();
|
||||
|
||||
if ((key != null) && (!value.isEmpty())) {
|
||||
filteredHeaders.put(key.toLowerCase(), TextUtils.join(", ", value));
|
||||
}
|
||||
}
|
||||
|
||||
response.put("headers", new JSONObject(filteredHeaders));
|
||||
}
|
||||
|
||||
protected HashMap<String, String> getStringMapFromJSONObject(JSONObject object) throws JSONException {
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
Iterator<?> i = object.keys();
|
||||
|
||||
while (i.hasNext()) {
|
||||
String key = (String)i.next();
|
||||
map.put(key, object.getString(key));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
protected ArrayList<Object> getListFromJSONArray(JSONArray array) throws JSONException {
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
list.add(array.get(i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
protected HashMap<String, Object> getMapFromJSONObject(JSONObject object) throws JSONException {
|
||||
HashMap<String, Object> map = new HashMap<String, Object>();
|
||||
Iterator<?> i = object.keys();
|
||||
|
||||
while(i.hasNext()) {
|
||||
String key = (String)i.next();
|
||||
Object value = object.get(key);
|
||||
|
||||
if (value instanceof JSONArray) {
|
||||
map.put(key, getListFromJSONArray((JSONArray)value));
|
||||
} else {
|
||||
map.put(key, object.get(key));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
protected void prepareRequest(HttpRequest request) throws HttpRequestException, JSONException {
|
||||
this.setupRedirect(request);
|
||||
|
||||
request.readTimeout(this.getRequestTimeout());
|
||||
request.acceptCharset(ACCEPTED_CHARSETS);
|
||||
request.headers(this.getHeadersMap());
|
||||
request.uncompress(true);
|
||||
}
|
||||
|
||||
protected void prepareRequestBody(HttpRequest request) throws JSONException, Exception {
|
||||
if ("json".equals(this.getSerializerName())) {
|
||||
request.send(this.getParamsObject().toString());
|
||||
} else if ("utf8".equals(this.getSerializerName())) {
|
||||
request.send(this.getParamsMap().get("text").toString());
|
||||
} else {
|
||||
request.form(this.getParamsMap());
|
||||
}
|
||||
}
|
||||
|
||||
private CharsetDecoder createCharsetDecoder(final String charsetName) {
|
||||
return Charset.forName(charsetName).newDecoder()
|
||||
.onMalformedInput(CodingErrorAction.REPORT)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPORT);
|
||||
}
|
||||
|
||||
private String decodeBody(AtomicReference<ByteBuffer> rawOutput, String charsetName)
|
||||
throws CharacterCodingException, MalformedInputException {
|
||||
|
||||
if (charsetName == null) {
|
||||
return tryDecodeByteBuffer(rawOutput);
|
||||
}
|
||||
|
||||
return decodeByteBuffer(rawOutput, charsetName);
|
||||
}
|
||||
|
||||
private String tryDecodeByteBuffer(AtomicReference<ByteBuffer> rawOutput)
|
||||
throws CharacterCodingException, MalformedInputException {
|
||||
|
||||
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
|
||||
try {
|
||||
return decodeByteBuffer(rawOutput, ACCEPTED_CHARSETS[i]);
|
||||
} catch (MalformedInputException e) {
|
||||
continue;
|
||||
} catch (CharacterCodingException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return decodeBody(rawOutput, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
|
||||
}
|
||||
|
||||
private String decodeByteBuffer(AtomicReference<ByteBuffer> rawOutput, String charsetName)
|
||||
throws CharacterCodingException, MalformedInputException {
|
||||
|
||||
return createCharsetDecoder(charsetName).decode(rawOutput.get()).toString();
|
||||
}
|
||||
|
||||
protected void returnResponseObject(HttpRequest request) throws HttpRequestException {
|
||||
try {
|
||||
JSONObject response = new JSONObject();
|
||||
int code = request.code();
|
||||
AtomicReference<ByteBuffer> rawOutputReference = new AtomicReference<ByteBuffer>();
|
||||
|
||||
request.body(rawOutputReference);
|
||||
response.put("status", code);
|
||||
response.put("url", request.url().toString());
|
||||
this.addResponseHeaders(request, response);
|
||||
|
||||
if (code >= 200 && code < 300) {
|
||||
response.put("data", decodeBody(rawOutputReference, request.charset()));
|
||||
this.getCallbackContext().success(response);
|
||||
} else {
|
||||
response.put("error", decodeBody(rawOutputReference, request.charset()));
|
||||
this.getCallbackContext().error(response);
|
||||
}
|
||||
} catch(JSONException e) {
|
||||
this.respondWithError("There was an error generating the response");
|
||||
} catch(MalformedInputException e) {
|
||||
this.respondWithError("Could not decode response data due to malformed data");
|
||||
} catch(CharacterCodingException e) {
|
||||
this.respondWithError("Could not decode response data due to invalid or unknown charset encoding");
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleHttpRequestException(HttpRequestException e) {
|
||||
if (e.getCause() instanceof UnknownHostException) {
|
||||
this.respondWithError(0, "The host could not be resolved");
|
||||
} else if (e.getCause() instanceof SocketTimeoutException) {
|
||||
this.respondWithError(1, "The request timed out");
|
||||
} else if (e.getCause() instanceof SSLHandshakeException) {
|
||||
this.respondWithError("SSL handshake failed");
|
||||
} else {
|
||||
this.respondWithError("There was an error with the request: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
class CordovaHttpDelete extends CordovaHttp implements Runnable {
|
||||
public CordovaHttpDelete(String urlString, Object data, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, data, headers, timeout, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.delete(this.getUrlString(), this.getParamsMap(), false);
|
||||
|
||||
this.prepareRequest(request);
|
||||
this.returnResponseObject(request);
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.file.FileUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpDownload extends CordovaHttp implements Runnable {
|
||||
private String filePath;
|
||||
|
||||
public CordovaHttpDownload(String urlString, Object params, JSONObject headers, String filePath, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, params, headers, timeout, callbackContext);
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.get(this.getUrlString(), this.getParamsMap(), true);
|
||||
|
||||
this.prepareRequest(request);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
int code = request.code();
|
||||
|
||||
response.put("status", code);
|
||||
response.put("url", request.url().toString());
|
||||
this.addResponseHeaders(request, response);
|
||||
|
||||
if (code >= 200 && code < 300) {
|
||||
URI uri = new URI(filePath);
|
||||
File file = new File(uri);
|
||||
request.receive(file);
|
||||
JSONObject fileEntry = FileUtils.getFilePlugin().getEntryForFile(file);
|
||||
response.put("file", fileEntry);
|
||||
this.getCallbackContext().success(response);
|
||||
} else {
|
||||
response.put("error", "There was an error downloading the file");
|
||||
this.getCallbackContext().error(response);
|
||||
}
|
||||
} catch(URISyntaxException e) {
|
||||
this.respondWithError("There was an error with the given filePath");
|
||||
} catch (JSONException e) {
|
||||
this.respondWithError("There was an error generating the response");
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
class CordovaHttpGet extends CordovaHttp implements Runnable {
|
||||
public CordovaHttpGet(String urlString, Object params, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, params, headers, timeout, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.get(this.getUrlString(), this.getParamsMap(), false);
|
||||
|
||||
this.prepareRequest(request);
|
||||
this.returnResponseObject(request);
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
class CordovaHttpHead extends CordovaHttp implements Runnable {
|
||||
public CordovaHttpHead(String urlString, Object params, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, params, headers, timeout, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.head(this.getUrlString(), this.getParamsMap(), true);
|
||||
|
||||
this.prepareRequest(request);
|
||||
this.returnResponseObject(request);
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
class CordovaHttpPatch extends CordovaHttp implements Runnable {
|
||||
public CordovaHttpPatch(String urlString, Object params, String serializerName, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, params, serializerName, headers, timeout, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.patch(this.getUrlString());
|
||||
|
||||
this.setupDataSerializer(request);
|
||||
this.prepareRequest(request);
|
||||
this.prepareRequestBody(request);
|
||||
this.returnResponseObject(request);
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
|
||||
public class CordovaHttpPlugin extends CordovaPlugin {
|
||||
private static final String TAG = "CordovaHTTP";
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("post")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
String serializerName = args.getString(2);
|
||||
JSONObject headers = args.getJSONObject(3);
|
||||
int timeoutInMilliseconds = args.getInt(4) * 1000;
|
||||
CordovaHttpPost post = new CordovaHttpPost(urlString, params, serializerName, headers, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(post);
|
||||
} else if (action.equals("get")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
JSONObject headers = args.getJSONObject(2);
|
||||
int timeoutInMilliseconds = args.getInt(3) * 1000;
|
||||
CordovaHttpGet get = new CordovaHttpGet(urlString, params, headers, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(get);
|
||||
} else if (action.equals("put")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
String serializerName = args.getString(2);
|
||||
JSONObject headers = args.getJSONObject(3);
|
||||
int timeoutInMilliseconds = args.getInt(4) * 1000;
|
||||
CordovaHttpPut put = new CordovaHttpPut(urlString, params, serializerName, headers, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(put);
|
||||
} else if (action.equals("patch")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
String serializerName = args.getString(2);
|
||||
JSONObject headers = args.getJSONObject(3);
|
||||
int timeoutInMilliseconds = args.getInt(4) * 1000;
|
||||
CordovaHttpPatch patch = new CordovaHttpPatch(urlString, params, serializerName, headers, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(patch);
|
||||
}
|
||||
else if (action.equals("delete")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
JSONObject headers = args.getJSONObject(2);
|
||||
int timeoutInMilliseconds = args.getInt(3) * 1000;
|
||||
CordovaHttpDelete delete = new CordovaHttpDelete(urlString, params, headers, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(delete);
|
||||
} else if (action.equals("head")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
JSONObject headers = args.getJSONObject(2);
|
||||
int timeoutInMilliseconds = args.getInt(3) * 1000;
|
||||
CordovaHttpHead head = new CordovaHttpHead(urlString, params, headers, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(head);
|
||||
} else if (action.equals("setSSLCertMode")) {
|
||||
String mode = args.getString(0);
|
||||
|
||||
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);
|
||||
JSONObject headers = args.getJSONObject(2);
|
||||
String filePath = args.getString(3);
|
||||
String name = args.getString(4);
|
||||
int timeoutInMilliseconds = args.getInt(5) * 1000;
|
||||
CordovaHttpUpload upload = new CordovaHttpUpload(urlString, params, headers, filePath, name, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(upload);
|
||||
} else if (action.equals("downloadFile")) {
|
||||
String urlString = args.getString(0);
|
||||
Object params = args.get(1);
|
||||
JSONObject headers = args.getJSONObject(2);
|
||||
String filePath = args.getString(3);
|
||||
int timeoutInMilliseconds = args.getInt(4) * 1000;
|
||||
CordovaHttpDownload download = new CordovaHttpDownload(urlString, params, headers, filePath, timeoutInMilliseconds, callbackContext);
|
||||
|
||||
cordova.getThreadPool().execute(download);
|
||||
} else if (action.equals("disableRedirect")) {
|
||||
boolean disable = args.getBoolean(0);
|
||||
CordovaHttp.disableRedirect(disable);
|
||||
callbackContext.success();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void loadSSLCerts() throws GeneralSecurityException, IOException {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
String[] files = assetManager.list("www/certificates");
|
||||
ArrayList<String> cerFiles = new ArrayList<String>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
class CordovaHttpPost extends CordovaHttp implements Runnable {
|
||||
public CordovaHttpPost(String urlString, Object params, String serializerName, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, params, serializerName, headers, timeout, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.post(this.getUrlString());
|
||||
|
||||
this.setupDataSerializer(request);
|
||||
this.prepareRequest(request);
|
||||
this.prepareRequestBody(request);
|
||||
this.returnResponseObject(request);
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
class CordovaHttpPut extends CordovaHttp implements Runnable {
|
||||
public CordovaHttpPut(String urlString, Object data, String serializerName, JSONObject headers, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, data, serializerName, headers, timeout, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.put(this.getUrlString());
|
||||
|
||||
this.setupDataSerializer(request);
|
||||
this.prepareRequest(request);
|
||||
this.prepareRequestBody(request);
|
||||
this.returnResponseObject(request);
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* A HTTP plugin for Cordova / Phonegap
|
||||
*/
|
||||
package com.synconset.cordovahttp;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.github.kevinsawicki.http.HttpRequest;
|
||||
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
|
||||
|
||||
class CordovaHttpUpload extends CordovaHttp implements Runnable {
|
||||
private String filePath;
|
||||
private String name;
|
||||
|
||||
public CordovaHttpUpload(String urlString, Object params, JSONObject headers, String filePath, String name, int timeout, CallbackContext callbackContext) {
|
||||
super(urlString, params, headers, timeout, callbackContext);
|
||||
this.filePath = filePath;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.post(this.getUrlString());
|
||||
|
||||
this.prepareRequest(request);
|
||||
|
||||
URI uri = new URI(filePath);
|
||||
|
||||
int filenameIndex = filePath.lastIndexOf('/');
|
||||
String filename = filePath.substring(filenameIndex + 1);
|
||||
|
||||
int extIndex = filePath.lastIndexOf('.');
|
||||
String ext = filePath.substring(extIndex + 1);
|
||||
|
||||
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
||||
String mimeType = mimeTypeMap.getMimeTypeFromExtension(ext);
|
||||
|
||||
Set<?> set = (Set<?>)this.getParamsMap().entrySet();
|
||||
Iterator<?> i = set.iterator();
|
||||
|
||||
while (i.hasNext()) {
|
||||
Entry<?, ?> e = (Entry<?, ?>)i.next();
|
||||
String key = (String)e.getKey();
|
||||
Object value = e.getValue();
|
||||
if (value instanceof Number) {
|
||||
request.part(key, (Number)value);
|
||||
} else if (value instanceof String) {
|
||||
request.part(key, (String)value);
|
||||
} else {
|
||||
this.respondWithError("All parameters must be Numbers or Strings");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
request.part(this.name, filename, mimeType, new File(uri));
|
||||
|
||||
this.returnResponseObject(request);
|
||||
} catch (URISyntaxException e) {
|
||||
this.respondWithError("There was an error loading the file");
|
||||
} catch (JSONException e) {
|
||||
this.respondWithError("There was an error generating the response");
|
||||
} catch (HttpRequestException e) {
|
||||
this.handleHttpRequestException(e);
|
||||
} catch (Exception e) {
|
||||
this.respondWithError(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
89
src/browser/cordova-http-plugin.js
vendored
89
src/browser/cordova-http-plugin.js
vendored
@@ -1,7 +1,7 @@
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
|
||||
var cordovaProxy = require('cordova/exec/proxy');
|
||||
var helpers = require(pluginId + '.helpers');
|
||||
var jsUtil = require(pluginId + '.js-util');
|
||||
|
||||
function serializeJsonData(data) {
|
||||
try {
|
||||
@@ -29,7 +29,7 @@ function serializeParams(params) {
|
||||
if (params === null) return '';
|
||||
|
||||
return Object.keys(params).map(function(key) {
|
||||
if (helpers.getTypeOf(params[key]) === 'Array') {
|
||||
if (jsUtil.getTypeOf(params[key]) === 'Array') {
|
||||
return serializeArray(key, params[key]);
|
||||
}
|
||||
|
||||
@@ -52,11 +52,19 @@ function deserializeResponseHeaders(headers) {
|
||||
return headerMap;
|
||||
}
|
||||
|
||||
function getResponseData(xhr) {
|
||||
if (xhr.responseType !== 'text' || jsUtil.getTypeOf(xhr.responseText) !== 'String') {
|
||||
return xhr.response;
|
||||
}
|
||||
|
||||
return xhr.responseText;
|
||||
}
|
||||
|
||||
function createXhrSuccessObject(xhr) {
|
||||
return {
|
||||
url: xhr.responseURL,
|
||||
status: xhr.status,
|
||||
data: helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response,
|
||||
data: getResponseData(xhr),
|
||||
headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
|
||||
};
|
||||
}
|
||||
@@ -65,7 +73,7 @@ function createXhrFailureObject(xhr) {
|
||||
var obj = {};
|
||||
|
||||
obj.headers = xhr.getAllResponseHeaders();
|
||||
obj.error = helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response;
|
||||
obj.error = getResponseData(xhr);
|
||||
obj.error = obj.error || 'advanced-http: please check browser console for error messages';
|
||||
|
||||
if (xhr.responseURL) obj.url = xhr.responseURL;
|
||||
@@ -101,12 +109,23 @@ function setHeaders(xhr, headers) {
|
||||
}
|
||||
|
||||
function sendRequest(method, withData, opts, success, failure) {
|
||||
var data = withData ? opts[1] : null;
|
||||
var params = withData ? null : serializeParams(opts[1]);
|
||||
var serializer = withData ? opts[2] : null;
|
||||
var headers = withData ? opts[3] : opts[2];
|
||||
var timeout = withData ? opts[4] : opts[3];
|
||||
var url = params ? opts[0] + '?' + params : opts[0];
|
||||
var data, serializer, headers, timeout, followRedirect, responseType;
|
||||
var url = opts[0];
|
||||
|
||||
if (withData) {
|
||||
data = opts[1];
|
||||
serializer = opts[2];
|
||||
headers = opts[3];
|
||||
timeout = opts[4];
|
||||
followRedirect = opts[5];
|
||||
responseType = opts[6];
|
||||
} else {
|
||||
headers = opts[1];
|
||||
timeout = opts[2];
|
||||
followRedirect = opts[3];
|
||||
responseType = opts[4];
|
||||
|
||||
}
|
||||
|
||||
var processedData = null;
|
||||
var xhr = new XMLHttpRequest();
|
||||
@@ -117,6 +136,10 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
return failure('advanced-http: custom cookies not supported on browser platform');
|
||||
}
|
||||
|
||||
if (!followRedirect) {
|
||||
return failure('advanced-http: disabling follow redirect not supported on browser platform');
|
||||
}
|
||||
|
||||
switch (serializer) {
|
||||
case 'json':
|
||||
setDefaultContentType(headers, 'application/json; charset=utf8');
|
||||
@@ -137,15 +160,30 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
setDefaultContentType(headers, 'application/x-www-form-urlencoded');
|
||||
processedData = serializeParams(data);
|
||||
break;
|
||||
|
||||
case 'raw':
|
||||
setDefaultContentType(headers, 'application/octet-stream');
|
||||
processedData = data;
|
||||
break;
|
||||
}
|
||||
|
||||
xhr.timeout = timeout * 1000;
|
||||
xhr.responseType = responseType;
|
||||
setHeaders(xhr, headers);
|
||||
|
||||
xhr.onerror = xhr.ontimeout = function () {
|
||||
xhr.onerror = function () {
|
||||
return failure(createXhrFailureObject(xhr));
|
||||
};
|
||||
|
||||
xhr.ontimeout = function () {
|
||||
return failure({
|
||||
status: -4,
|
||||
error: 'Request timed out',
|
||||
url: url,
|
||||
headers: {}
|
||||
});
|
||||
};
|
||||
|
||||
xhr.onload = function () {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
|
||||
@@ -160,35 +198,38 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
}
|
||||
|
||||
var browserInterface = {
|
||||
post: function (success, failure, opts) {
|
||||
return sendRequest('post', true, opts, success, failure);
|
||||
},
|
||||
get: function (success, failure, opts) {
|
||||
return sendRequest('get', false, opts, success, failure);
|
||||
},
|
||||
head: function (success, failure, opts) {
|
||||
return sendRequest('head', false, opts, success, failure);
|
||||
},
|
||||
delete: function (success, failure, opts) {
|
||||
return sendRequest('delete', false, opts, success, failure);
|
||||
},
|
||||
post: function (success, failure, opts) {
|
||||
return sendRequest('post', true, opts, success, failure);
|
||||
},
|
||||
put: function (success, failure, opts) {
|
||||
return sendRequest('put', true, opts, success, failure);
|
||||
},
|
||||
patch: function (success, failure, opts) {
|
||||
return sendRequest('patch', true, opts, success, failure);
|
||||
},
|
||||
delete: function (success, failure, opts) {
|
||||
return sendRequest('delete', false, opts, success, failure);
|
||||
},
|
||||
head: function (success, failure, opts) {
|
||||
return sendRequest('head', false, opts, success, failure);
|
||||
},
|
||||
uploadFile: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "uploadFile" not supported on browser platform');
|
||||
},
|
||||
uploadFiles: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "uploadFiles" not supported on browser platform');
|
||||
},
|
||||
downloadFile: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "downloadFile" not supported on browser platform');
|
||||
},
|
||||
setSSLCertMode: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "setSSLCertMode" not supported on browser platform');
|
||||
setServerTrustMode: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "setServerTrustMode" not supported on browser platform');
|
||||
},
|
||||
disableRedirect: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "disableRedirect" not supported on browser platform');
|
||||
setClientAuthMode: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "setClientAuthMode" not supported on browser platform');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -290,6 +290,64 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a multipart request using given HTTP method.
|
||||
|
||||
@param HTTPMethod The HTTP method used to create the request.
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
|
||||
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)HTTPMethod
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
|
||||
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with given HTTP method.
|
||||
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)HTTPMethod
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
/**
|
||||
Creates and runs an `NSURLSessionDataTask` with a given HTTP method.
|
||||
|
||||
@param HTTPMethod The HTTP method used to create the request.
|
||||
@param URLString The URL string used to create the request URL.
|
||||
@param parameters The parameters to be encoded according to the client request serializer.
|
||||
@param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
|
||||
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
|
||||
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
|
||||
|
||||
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
|
||||
*/
|
||||
- (nullable NSURLSessionDataTask *)downloadTaskWithHTTPMethod:(NSString *)HTTPMethod
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(nullable id)parameters
|
||||
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
|
||||
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
|
||||
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -223,6 +223,42 @@
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
|
||||
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
failure(nil, serializationError);
|
||||
});
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
|
||||
if (error) {
|
||||
if (failure) {
|
||||
failure(task, error);
|
||||
}
|
||||
} else {
|
||||
if (success) {
|
||||
success(task, responseObject);
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
@@ -247,6 +283,71 @@
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
|
||||
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
|
||||
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
|
||||
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
failure(nil, serializationError);
|
||||
});
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
|
||||
if (error) {
|
||||
if (failure) {
|
||||
failure(task, error);
|
||||
}
|
||||
} else {
|
||||
if (success) {
|
||||
success(task, responseObject);
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
|
||||
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
||||
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:method URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
|
||||
|
||||
[dataTask resume];
|
||||
|
||||
return dataTask;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)downloadTaskWithHTTPMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
|
||||
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
|
||||
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
|
||||
{
|
||||
NSURLSessionDataTask *task = [self dataTaskWithHTTPMethod:method URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:downloadProgress success:success failure:failure];
|
||||
|
||||
[task resume];
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
|
||||
URLString:(NSString *)URLString
|
||||
parameters:(id)parameters
|
||||
|
||||
@@ -308,4 +308,11 @@ FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorK
|
||||
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey;
|
||||
|
||||
/**
|
||||
`AFNetworkingOperationFailingURLResponseBodyErrorKey`
|
||||
The corresponding value is an `NSString` containing the decoded error message.
|
||||
*/
|
||||
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyErrorKey;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
NSString * const AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response";
|
||||
NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response";
|
||||
NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data";
|
||||
NSString * const AFNetworkingOperationFailingURLResponseBodyErrorKey = @"com.alamofire.serialization.response.error.body";
|
||||
|
||||
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
|
||||
if (!error) {
|
||||
@@ -525,7 +526,7 @@ static NSLock* imageLock = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
imageLock = [[NSLock alloc] init];
|
||||
});
|
||||
|
||||
|
||||
[imageLock lock];
|
||||
image = [UIImage imageWithData:data];
|
||||
[imageLock unlock];
|
||||
@@ -539,7 +540,7 @@ static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
|
||||
if (image.images) {
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
|
||||
}
|
||||
|
||||
|
||||
@@ -392,11 +392,11 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
- On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled.
|
||||
- On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled.
|
||||
- Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there.
|
||||
|
||||
|
||||
Some Assumptions:
|
||||
- No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
|
||||
- No background task classes override `resume` or `suspend`
|
||||
|
||||
|
||||
The current solution:
|
||||
1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
|
||||
2) Grab a pointer to the original implementation of `af_resume`
|
||||
@@ -415,7 +415,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
#pragma clang diagnostic pop
|
||||
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
|
||||
Class currentClass = [localDataTask class];
|
||||
|
||||
|
||||
while (class_getInstanceMethod(currentClass, @selector(resume))) {
|
||||
Class superClass = [currentClass superclass];
|
||||
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
|
||||
@@ -426,7 +426,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
}
|
||||
currentClass = [currentClass superclass];
|
||||
}
|
||||
|
||||
|
||||
[localDataTask cancel];
|
||||
[session finishTasksAndInvalidate];
|
||||
}
|
||||
@@ -454,7 +454,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
|
||||
NSURLSessionTaskState state = [self state];
|
||||
[self af_resume];
|
||||
|
||||
|
||||
if (state != NSURLSessionTaskStateRunning) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
|
||||
}
|
||||
@@ -464,7 +464,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
|
||||
NSURLSessionTaskState state = [self state];
|
||||
[self af_suspend];
|
||||
|
||||
|
||||
if (state != NSURLSessionTaskStateSuspended) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
|
||||
}
|
||||
@@ -978,7 +978,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
@@ -1025,7 +1025,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
|
||||
8
src/ios/BinaryRequestSerializer.h
Normal file
8
src/ios/BinaryRequestSerializer.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFURLRequestSerialization.h"
|
||||
|
||||
@interface BinaryRequestSerializer : AFHTTPRequestSerializer
|
||||
|
||||
+ (instancetype)serializer;
|
||||
|
||||
@end
|
||||
53
src/ios/BinaryRequestSerializer.m
Normal file
53
src/ios/BinaryRequestSerializer.m
Normal file
@@ -0,0 +1,53 @@
|
||||
#import "BinaryRequestSerializer.h"
|
||||
|
||||
@implementation BinaryRequestSerializer
|
||||
|
||||
+ (instancetype)serializer
|
||||
{
|
||||
BinaryRequestSerializer *serializer = [[self alloc] init];
|
||||
return serializer;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLRequestSerialization
|
||||
|
||||
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
|
||||
withParameters:(id)parameters
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(request);
|
||||
|
||||
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
|
||||
return [super requestBySerializingRequest:request withParameters:parameters error:error];
|
||||
}
|
||||
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
|
||||
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
|
||||
if (![request valueForHTTPHeaderField:field]) {
|
||||
[mutableRequest setValue:value forHTTPHeaderField:field];
|
||||
}
|
||||
}];
|
||||
|
||||
if (parameters) {
|
||||
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
|
||||
[mutableRequest setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
|
||||
}
|
||||
|
||||
[mutableRequest setHTTPBody: parameters];
|
||||
}
|
||||
|
||||
return mutableRequest;
|
||||
}
|
||||
|
||||
#pragma mark - NSSecureCoding
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
self = [super initWithCoder:decoder];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
8
src/ios/BinaryResponseSerializer.h
Normal file
8
src/ios/BinaryResponseSerializer.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFURLResponseSerialization.h"
|
||||
|
||||
@interface BinaryResponseSerializer : AFHTTPResponseSerializer
|
||||
|
||||
+ (instancetype)serializer;
|
||||
|
||||
@end
|
||||
126
src/ios/BinaryResponseSerializer.m
Normal file
126
src/ios/BinaryResponseSerializer.m
Normal file
@@ -0,0 +1,126 @@
|
||||
#import "BinaryResponseSerializer.h"
|
||||
|
||||
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
|
||||
if (!error) {
|
||||
return underlyingError;
|
||||
}
|
||||
|
||||
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
|
||||
return error;
|
||||
}
|
||||
|
||||
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
|
||||
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
|
||||
|
||||
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
|
||||
}
|
||||
|
||||
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
|
||||
if ([error.domain isEqualToString:domain] && error.code == code) {
|
||||
return YES;
|
||||
} else if (error.userInfo[NSUnderlyingErrorKey]) {
|
||||
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@implementation BinaryResponseSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
BinaryResponseSerializer *serializer = [[self alloc] init];
|
||||
return serializer;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.acceptableContentTypes = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)decodeResponseData:(NSData*)rawResponseData withEncoding:(CFStringEncoding)cfEncoding {
|
||||
NSStringEncoding nsEncoding;
|
||||
NSString* decoded = nil;
|
||||
|
||||
if (cfEncoding != kCFStringEncodingInvalidId) {
|
||||
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
}
|
||||
|
||||
NSStringEncoding supportedEncodings[6] = {
|
||||
NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding,
|
||||
NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(supportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
|
||||
if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == supportedEncodings[i]) {
|
||||
decoded = [[NSString alloc] initWithData:rawResponseData encoding:supportedEncodings[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
- (CFStringEncoding) getEncoding:(NSURLResponse *)response {
|
||||
CFStringEncoding encoding = kCFStringEncodingInvalidId;
|
||||
|
||||
if (response.textEncodingName) {
|
||||
encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
}
|
||||
|
||||
return encoding;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
|
||||
data:(NSData *)data
|
||||
error:(NSError * __autoreleasing *)error
|
||||
{
|
||||
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
|
||||
NSMutableDictionary *mutableUserInfo = [@{
|
||||
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
|
||||
NSURLErrorFailingURLErrorKey: [response URL],
|
||||
AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
} mutableCopy];
|
||||
|
||||
if (data) {
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
|
||||
// trying to decode error message in body
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
|
||||
}
|
||||
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
|
||||
- (id)responseObjectForResponse:(NSURLResponse *)response
|
||||
data:(NSData *)data
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return [data base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -4,14 +4,15 @@
|
||||
|
||||
@interface CordovaHttpPlugin : CDVPlugin
|
||||
|
||||
- (void)setSSLCertMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)disableRedirect:(CDVInvokedUrlCommand*)command;
|
||||
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)post:(CDVInvokedUrlCommand*)command;
|
||||
- (void)get:(CDVInvokedUrlCommand*)command;
|
||||
- (void)put:(CDVInvokedUrlCommand*)command;
|
||||
- (void)patch:(CDVInvokedUrlCommand*)command;
|
||||
- (void)get:(CDVInvokedUrlCommand*)command;
|
||||
- (void)delete:(CDVInvokedUrlCommand*)command;
|
||||
- (void)uploadFile:(CDVInvokedUrlCommand*)command;
|
||||
- (void)head:(CDVInvokedUrlCommand*)command;
|
||||
- (void)options:(CDVInvokedUrlCommand*)command;
|
||||
- (void)uploadFiles:(CDVInvokedUrlCommand*)command;
|
||||
- (void)downloadFile:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#import "CordovaHttpPlugin.h"
|
||||
#import "CDVFile.h"
|
||||
#import "BinaryRequestSerializer.h"
|
||||
#import "BinaryResponseSerializer.h"
|
||||
#import "TextResponseSerializer.h"
|
||||
#import "TextRequestSerializer.h"
|
||||
#import "AFHTTPSessionManager.h"
|
||||
@@ -13,18 +15,16 @@
|
||||
- (NSNumber*)getStatusCode:(NSError*) error;
|
||||
- (NSMutableDictionary*)copyHeaderFields:(NSDictionary*)headerFields;
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager;
|
||||
- (void)setRedirect:(AFHTTPSessionManager*)manager;
|
||||
- (void)setRedirect:(bool)redirect forManager:(AFHTTPSessionManager*)manager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CordovaHttpPlugin {
|
||||
AFSecurityPolicy *securityPolicy;
|
||||
bool redirect;
|
||||
}
|
||||
|
||||
- (void)pluginInitialize {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
redirect = true;
|
||||
}
|
||||
|
||||
- (void)setRequestSerializer:(NSString*)serializerName forManager:(AFHTTPSessionManager*)manager {
|
||||
@@ -32,6 +32,8 @@
|
||||
manager.requestSerializer = [AFJSONRequestSerializer serializer];
|
||||
} else if ([serializerName isEqualToString:@"utf8"]) {
|
||||
manager.requestSerializer = [TextRequestSerializer serializer];
|
||||
} else if ([serializerName isEqualToString:@"raw"]) {
|
||||
manager.requestSerializer = [BinaryRequestSerializer serializer];
|
||||
} else {
|
||||
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
|
||||
}
|
||||
@@ -43,9 +45,11 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setRedirect:(AFHTTPSessionManager*)manager {
|
||||
[manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest * _Nonnull(NSURLSession * _Nonnull session, NSURLSessionTask * _Nonnull task, NSURLResponse * _Nonnull response, NSURLRequest * _Nonnull request) {
|
||||
if (redirect) {
|
||||
- (void)setRedirect:(bool)followRedirect forManager:(AFHTTPSessionManager*)manager {
|
||||
[manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest * _Nonnull(NSURLSession * _Nonnull session,
|
||||
NSURLSessionTask * _Nonnull task, NSURLResponse * _Nonnull response, NSURLRequest * _Nonnull request) {
|
||||
|
||||
if (followRedirect) {
|
||||
return request;
|
||||
} else {
|
||||
return nil;
|
||||
@@ -53,6 +57,19 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
|
||||
[manager.requestSerializer setTimeoutInterval:timeout];
|
||||
}
|
||||
|
||||
- (void)setResponseSerializer:(NSString*)responseType forManager:(AFHTTPSessionManager*)manager {
|
||||
if ([responseType isEqualToString: @"text"]) {
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
} else {
|
||||
manager.responseSerializer = [BinaryResponseSerializer serializer];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data {
|
||||
if (response != nil) {
|
||||
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
|
||||
@@ -70,8 +87,8 @@
|
||||
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
|
||||
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
|
||||
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
|
||||
if (error.userInfo[AFNetworkingOperationFailingURLResponseBodyKey]) {
|
||||
[dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyKey] forKey:@"error"];
|
||||
if (error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey]) {
|
||||
[dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] forKey:@"error"];
|
||||
}
|
||||
} else {
|
||||
[dictionary setObject:[self getStatusCode:error] forKey:@"status"];
|
||||
@@ -94,16 +111,23 @@
|
||||
switch ([error code]) {
|
||||
case -1001:
|
||||
// timeout
|
||||
return [NSNumber numberWithInt:1];
|
||||
return [NSNumber numberWithInt:-4];
|
||||
case -1002:
|
||||
// unsupported URL
|
||||
return [NSNumber numberWithInt:2];
|
||||
return [NSNumber numberWithInt:-5];
|
||||
case -1003:
|
||||
// server not found
|
||||
return [NSNumber numberWithInt:0];
|
||||
return [NSNumber numberWithInt:-3];
|
||||
case -1009:
|
||||
// no connection
|
||||
return [NSNumber numberWithInt:3];
|
||||
return [NSNumber numberWithInt:-6];
|
||||
case -1200: // secure connection failed
|
||||
case -1201: // certificate has bad date
|
||||
case -1202: // certificate untrusted
|
||||
case -1203: // certificate has unknown root
|
||||
case -1204: // certificate is not yet valid
|
||||
// configuring SSL failed
|
||||
return [NSNumber numberWithInt:-2];
|
||||
default:
|
||||
return [NSNumber numberWithInt:-1];
|
||||
}
|
||||
@@ -121,10 +145,146 @@
|
||||
return headerFieldsCopy;
|
||||
}
|
||||
|
||||
- (void)setSSLCertMode:(CDVInvokedUrlCommand*)command {
|
||||
- (void)executeRequestWithoutData:(CDVInvokedUrlCommand*)command withMethod:(NSString*) method {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:1];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:4];
|
||||
|
||||
[self setRequestSerializer: @"default" forManager: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
|
||||
// no 'body' for HEAD request, omitting 'data'
|
||||
if ([method isEqualToString:@"HEAD"]) {
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
|
||||
} else {
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
}
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
};
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(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];
|
||||
};
|
||||
|
||||
[manager downloadTaskWithHTTPMethod:method URLString:url parameters:nil progress:nil success:onSuccess failure:onFailure];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)executeRequestWithData:(CDVInvokedUrlCommand*)command withMethod:(NSString*)method {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *data = [command.arguments objectAtIndex:1];
|
||||
NSString *serializerName = [command.arguments objectAtIndex:2];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:3];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:6];
|
||||
|
||||
[self setRequestSerializer: serializerName forManager: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
void (^constructBody)(id<AFMultipartFormData>) = ^(id<AFMultipartFormData> formData) {
|
||||
NSArray *buffers = [data mutableArrayValueForKey:@"buffers"];
|
||||
NSArray *fileNames = [data mutableArrayValueForKey:@"fileNames"];
|
||||
NSArray *names = [data mutableArrayValueForKey:@"names"];
|
||||
NSArray *types = [data mutableArrayValueForKey:@"types"];
|
||||
|
||||
NSError *error;
|
||||
|
||||
for (int i = 0; i < [buffers count]; ++i) {
|
||||
NSData *decodedBuffer = [[NSData alloc] initWithBase64EncodedString:[buffers objectAtIndex:i] options:0];
|
||||
NSString *fileName = [fileNames objectAtIndex:i];
|
||||
NSString *partName = [names objectAtIndex:i];
|
||||
NSString *partType = [types objectAtIndex:i];
|
||||
|
||||
if (![fileName isEqual:[NSNull null]]) {
|
||||
[formData appendPartWithFileData:decodedBuffer name:partName fileName:fileName mimeType:partType];
|
||||
} else {
|
||||
[formData appendPartWithFormData:decodedBuffer name:[names objectAtIndex:i]];
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:[NSNumber numberWithInt:400] forKey:@"status"];
|
||||
[dictionary setObject:@"Could not add part to multipart request body." forKey:@"error"];
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
};
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(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];
|
||||
};
|
||||
|
||||
if ([serializerName isEqualToString:@"multipart"]) {
|
||||
[manager uploadTaskWithHTTPMethod:method URLString:url parameters:nil constructingBodyWithBlock:constructBody progress:nil success:onSuccess failure:onFailure];
|
||||
} else {
|
||||
[manager uploadTaskWithHTTPMethod:method URLString:url parameters:data progress:nil success:onSuccess failure:onFailure];
|
||||
}
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command {
|
||||
NSString *certMode = [command.arguments objectAtIndex:0];
|
||||
|
||||
if ([certMode isEqualToString: @"default"]) {
|
||||
if ([certMode isEqualToString: @"default"] || [certMode isEqualToString: @"legacy"]) {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
securityPolicy.allowInvalidCertificates = NO;
|
||||
securityPolicy.validatesDomainName = YES;
|
||||
@@ -142,294 +302,63 @@
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
|
||||
[manager.requestSerializer setTimeoutInterval:timeout];
|
||||
}
|
||||
|
||||
- (void)disableRedirect:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
bool disable = [[command.arguments objectAtIndex:0] boolValue];
|
||||
|
||||
redirect = !disable;
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)post:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSString *serializerName = [command.arguments objectAtIndex:2];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:3];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
|
||||
[self setRequestSerializer: serializerName forManager: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager POST:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)get:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:2];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
|
||||
|
||||
[self setRequestSerializer: @"default" forManager: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager GET:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
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];
|
||||
}
|
||||
[self executeRequestWithData: command withMethod:@"POST"];
|
||||
}
|
||||
|
||||
- (void)put:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSString *serializerName = [command.arguments objectAtIndex:2];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:3];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
|
||||
[self setRequestSerializer: serializerName forManager: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager PUT:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
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];
|
||||
}
|
||||
[self executeRequestWithData: command withMethod:@"PUT"];
|
||||
}
|
||||
|
||||
- (void)patch:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
[self executeRequestWithData: command withMethod:@"PATCH"];
|
||||
}
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSString *serializerName = [command.arguments objectAtIndex:2];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:3];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
|
||||
[self setRequestSerializer: serializerName forManager: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager PATCH:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
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];
|
||||
}
|
||||
- (void)get:(CDVInvokedUrlCommand*)command {
|
||||
[self executeRequestWithoutData: command withMethod:@"GET"];
|
||||
}
|
||||
|
||||
- (void)delete:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:2];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
|
||||
[self setRequestSerializer: @"default" forManager: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager DELETE:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
|
||||
|
||||
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];
|
||||
}
|
||||
[self executeRequestWithoutData: command withMethod:@"DELETE"];
|
||||
}
|
||||
|
||||
- (void)head:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:2];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager HEAD:url parameters:parameters success:^(NSURLSessionTask *task) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
// no 'body' for HEAD request, omitting 'data'
|
||||
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
|
||||
|
||||
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];
|
||||
}
|
||||
[self executeRequestWithoutData: command withMethod:@"HEAD"];
|
||||
}
|
||||
|
||||
- (void)uploadFile:(CDVInvokedUrlCommand*)command {
|
||||
- (void)options:(CDVInvokedUrlCommand*)command {
|
||||
[self executeRequestWithoutData: command withMethod:@"OPTIONS"];
|
||||
}
|
||||
|
||||
- (void)uploadFiles:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:2];
|
||||
NSString *filePath = [command.arguments objectAtIndex: 3];
|
||||
NSString *name = [command.arguments objectAtIndex: 4];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:5] doubleValue];
|
||||
|
||||
NSURL *fileURL = [NSURL URLWithString: filePath];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:1];
|
||||
NSArray *filePaths = [command.arguments objectAtIndex: 2];
|
||||
NSArray *names = [command.arguments objectAtIndex: 3];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:6];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
|
||||
[manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
|
||||
NSError *error;
|
||||
[formData appendPartWithFileURL:fileURL name:name error:&error];
|
||||
for (int i = 0; i < [filePaths count]; i++) {
|
||||
NSString *filePath = (NSString *) [filePaths objectAtIndex:i];
|
||||
NSString *uploadName = (NSString *) [names objectAtIndex:i];
|
||||
NSURL *fileURL = [NSURL URLWithString: filePath];
|
||||
[formData appendPartWithFileURL:fileURL name:uploadName error:&error];
|
||||
}
|
||||
if (error) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
|
||||
@@ -461,31 +390,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)downloadFile:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *parameters = [command.arguments objectAtIndex:1];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:2];
|
||||
NSString *filePath = [command.arguments objectAtIndex: 3];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:1];
|
||||
NSString *filePath = [command.arguments objectAtIndex: 2];
|
||||
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
|
||||
if ([filePath hasPrefix:@"file://"]) {
|
||||
filePath = [filePath substringFromIndex:7];
|
||||
}
|
||||
|
||||
CordovaHttpPlugin* __weak weakSelf = self;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
@try {
|
||||
[manager GET:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
[manager GET:url parameters:nil progress: nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
|
||||
@@ -5,6 +5,4 @@
|
||||
|
||||
+ (instancetype)serializer;
|
||||
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyKey;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#import "TextResponseSerializer.h"
|
||||
|
||||
NSString * const AFNetworkingOperationFailingURLResponseBodyKey = @"com.alamofire.serialization.response.error.body";
|
||||
NSStringEncoding const SupportedEncodings[6] = { NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding, NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding };
|
||||
|
||||
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
|
||||
if (!error) {
|
||||
return underlyingError;
|
||||
@@ -55,9 +52,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
|
||||
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
}
|
||||
|
||||
for (int i = 0; i < sizeof(SupportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
|
||||
if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == SupportedEncodings[i]) {
|
||||
decoded = [[NSString alloc] initWithData:rawResponseData encoding:SupportedEncodings[i]];
|
||||
NSStringEncoding supportedEncodings[6] = {
|
||||
NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding,
|
||||
NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(supportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
|
||||
if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == supportedEncodings[i]) {
|
||||
decoded = [[NSString alloc] initWithData:rawResponseData encoding:supportedEncodings[i]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +96,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
|
||||
NSURLErrorFailingURLErrorKey:[response URL],
|
||||
AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
AFNetworkingOperationFailingURLResponseDataErrorKey: data,
|
||||
AFNetworkingOperationFailingURLResponseBodyKey: @"Could not decode response data due to invalid or unknown charset encoding",
|
||||
AFNetworkingOperationFailingURLResponseBodyErrorKey: @"Could not decode response data due to invalid or unknown charset encoding",
|
||||
} mutableCopy];
|
||||
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
@@ -108,7 +110,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
|
||||
|
||||
if (data) {
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyKey] = *decoded;
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] = *decoded;
|
||||
}
|
||||
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
const path = require('path');
|
||||
|
||||
if (process.env.SAUCE_USERNAME) {
|
||||
exports.iosTestApp = 'sauce-storage:HttpDemo.app.zip';
|
||||
exports.androidTestApp = 'sauce-storage:HttpDemo.apk';
|
||||
} else {
|
||||
// these paths are relative to working directory
|
||||
exports.iosTestApp = path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app');
|
||||
exports.androidTestApp = path.resolve('temp/platforms/android/build/outputs/apk/android-debug.apk');
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
const local = {
|
||||
iosDevice: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
deviceName: 'iPhone 6',
|
||||
autoWebview: true,
|
||||
app: undefined // will be set later
|
||||
},
|
||||
iosEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '11.0',
|
||||
deviceName: 'iPhone Simulator',
|
||||
autoWebview: true,
|
||||
app: undefined // will be set later
|
||||
},
|
||||
androidEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
platformName: 'Android',
|
||||
platformVersion: '5.1',
|
||||
deviceName: 'Android Emulator',
|
||||
autoWebview: true,
|
||||
app: undefined // will be set later
|
||||
}
|
||||
};
|
||||
|
||||
const sauce = {
|
||||
iosDevice: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
deviceName: 'iPhone 6',
|
||||
autoWebview: true,
|
||||
app: undefined // will be set later
|
||||
},
|
||||
iosEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
deviceName: 'iPhone Simulator',
|
||||
autoWebview: true,
|
||||
app: undefined // will be set later
|
||||
},
|
||||
androidEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.7.1',
|
||||
platformName: 'Android',
|
||||
platformVersion: '5.1',
|
||||
deviceName: 'Android Emulator',
|
||||
autoWebview: true,
|
||||
app: undefined // will be set later
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.SAUCE_USERNAME) {
|
||||
module.exports = sauce;
|
||||
} else {
|
||||
module.exports = local;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
const local = {
|
||||
host: 'localhost',
|
||||
port: 4723
|
||||
};
|
||||
|
||||
const sauce = {
|
||||
host: 'ondemand.saucelabs.com',
|
||||
port: 80,
|
||||
auth: process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY
|
||||
};
|
||||
|
||||
if (process.env.SAUCE_USERNAME) {
|
||||
module.exports = sauce;
|
||||
} else {
|
||||
module.exports = local;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
const wd = require("wd");
|
||||
|
||||
require('colors');
|
||||
|
||||
const chai = require('chai');
|
||||
const chaiAsPromised = require('chai-as-promised');
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
const should = chai.should();
|
||||
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
|
||||
|
||||
exports.should = should;
|
||||
@@ -1,94 +0,0 @@
|
||||
require('./helpers/setup');
|
||||
|
||||
const wd = require('wd');
|
||||
const apps = require('./helpers/apps');
|
||||
const caps = Object.assign({}, require('./helpers/caps'));
|
||||
const serverConfig = require('./helpers/server');
|
||||
const testDefinitions = require('../app-test-definitions');
|
||||
const pkgjson = require('../../package.json');
|
||||
|
||||
describe('Advanced HTTP', function() {
|
||||
const isDevice = process.argv.includes('--device');
|
||||
const isAndroid = process.argv.includes('--android');
|
||||
const targetInfo = { isDevice, isAndroid };
|
||||
|
||||
let driver = null;
|
||||
let allPassed = true;
|
||||
|
||||
this.timeout(900000);
|
||||
|
||||
const getCaps = appName => {
|
||||
const desiredOs = isAndroid ? 'android' : 'ios';
|
||||
const desiredCaps = caps[desiredOs + (isDevice ? 'Device' : 'Emulator')];
|
||||
const desiredApp = apps[desiredOs + appName];
|
||||
|
||||
desiredCaps.name = pkgjson.name + ` (${desiredOs})`;
|
||||
desiredCaps.app = desiredApp;
|
||||
|
||||
return desiredCaps;
|
||||
};
|
||||
|
||||
const validateTestIndex = number => driver
|
||||
.elementById('descriptionLbl')
|
||||
.text()
|
||||
.then(text => parseInt(text.match(/(\d+):/)[1], 10))
|
||||
.should.eventually.become(number, 'Test index is not matching!');
|
||||
|
||||
const validateTestTitle = testTitle => driver
|
||||
.elementById('descriptionLbl')
|
||||
.text()
|
||||
.then(text => text.match(/\d+:\ (.*)/)[1])
|
||||
.should.eventually.become(testTitle, 'Test description is not matching!');
|
||||
|
||||
const waitToBeFinished = timeout => new Promise((resolve, reject) => {
|
||||
const timeoutTimestamp = Date.now() + timeout;
|
||||
const checkIfFinished = () => driver
|
||||
.elementById('statusInput')
|
||||
.getValue()
|
||||
.then(value => {
|
||||
if (value === 'finished') {
|
||||
resolve();
|
||||
} else if (Date.now() > timeoutTimestamp) {
|
||||
reject('Test function timed out!');
|
||||
} else {
|
||||
setTimeout(checkIfFinished, 500);
|
||||
}
|
||||
});
|
||||
|
||||
checkIfFinished();
|
||||
});
|
||||
|
||||
const validateResult = testDefinition => driver
|
||||
.safeExecute('app.lastResult')
|
||||
.then(result => testDefinition.validationFunc(driver, result, targetInfo));
|
||||
|
||||
const clickNext = () => driver
|
||||
.elementById('nextBtn')
|
||||
.click()
|
||||
.sleep(1000);
|
||||
|
||||
before(() => {
|
||||
driver = wd.promiseChainRemote(serverConfig);
|
||||
require('./helpers/logging').configure(driver);
|
||||
|
||||
return driver.init(getCaps('TestApp'));
|
||||
});
|
||||
|
||||
after(() => driver
|
||||
.quit()
|
||||
.finally(function () {
|
||||
if (process.env.SAUCE_USERNAME) {
|
||||
return driver.sauceJobStatus(allPassed);
|
||||
}
|
||||
}));
|
||||
|
||||
testDefinitions.tests.forEach((definition, index) => {
|
||||
it(index + ': ' + definition.description, function() {
|
||||
return clickNext()
|
||||
.then(() => validateTestIndex(index))
|
||||
.then(() => validateTestTitle(definition.description))
|
||||
.then(() => waitToBeFinished(definition.timeout || 10000))
|
||||
.then(() => validateResult(definition))
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,107 +0,0 @@
|
||||
const app = {
|
||||
testIndex: -1,
|
||||
|
||||
lastResult: null,
|
||||
|
||||
initialize: function() {
|
||||
document.getElementById('nextBtn').addEventListener('click', app.onNextBtnClick);
|
||||
},
|
||||
|
||||
printResult: function(prefix, content) {
|
||||
const text = prefix + ': ' + JSON.stringify(content);
|
||||
|
||||
document.getElementById('resultTextarea').value += text;
|
||||
},
|
||||
|
||||
reject: function(content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - rejected', content);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'rejected',
|
||||
data: content
|
||||
};
|
||||
},
|
||||
|
||||
resolve: function(content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - resolved', content);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'resolved',
|
||||
data: content
|
||||
};
|
||||
},
|
||||
|
||||
throw: function(error) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - throwed', error.message);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'throwed',
|
||||
message: error.message
|
||||
};
|
||||
},
|
||||
|
||||
getResult: function(cb) {
|
||||
cb(app.lastResult);
|
||||
},
|
||||
|
||||
runTest: function(index) {
|
||||
const testDefinition = tests[index];
|
||||
const titleText = app.testIndex + ': ' + testDefinition.description;
|
||||
const expectedText = 'expected - ' + testDefinition.expected;
|
||||
|
||||
document.getElementById('statusInput').value = 'running';
|
||||
document.getElementById('expectedTextarea').value = expectedText;
|
||||
document.getElementById('resultTextarea').value = '';
|
||||
document.getElementById('descriptionLbl').innerText = titleText;
|
||||
|
||||
try {
|
||||
testDefinition.func(app.resolve, app.reject);
|
||||
} catch (error) {
|
||||
app.throw(error);
|
||||
}
|
||||
},
|
||||
|
||||
onBeforeTest: function(testIndex, cb) {
|
||||
app.lastResult = null;
|
||||
|
||||
if (hooks && hooks.onBeforeEachTest) {
|
||||
return hooks.onBeforeEachTest(function() {
|
||||
const testDefinition = tests[testIndex];
|
||||
|
||||
if (testDefinition.before) {
|
||||
testDefinition.before(cb);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
},
|
||||
|
||||
onFinishedAllTests: function() {
|
||||
const titleText = 'No more tests';
|
||||
const expectedText = 'You have run all available tests.';
|
||||
|
||||
document.getElementById('expectedTextarea').value = expectedText;
|
||||
document.getElementById('resultTextarea').value = '';
|
||||
document.getElementById('descriptionLbl').innerText = titleText;
|
||||
},
|
||||
|
||||
onNextBtnClick: function() {
|
||||
app.testIndex += 1;
|
||||
|
||||
if (app.testIndex < tests.length) {
|
||||
app.onBeforeTest(app.testIndex, function() {
|
||||
app.runTest(app.testIndex);
|
||||
});
|
||||
} else {
|
||||
app.onFinishedAllTests();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
||||
@@ -1,492 +0,0 @@
|
||||
const hooks = {
|
||||
onBeforeEachTest: function(done) {
|
||||
cordova.plugin.http.clearCookies();
|
||||
helpers.setDefaultCertMode(done);
|
||||
}
|
||||
};
|
||||
|
||||
const helpers = {
|
||||
setDefaultCertMode: function(done) { cordova.plugin.http.setSSLCertMode('default', done, done); },
|
||||
setNoCheckCertMode: function(done) { cordova.plugin.http.setSSLCertMode('nocheck', done, done); },
|
||||
setPinnedCertMode: function(done) { cordova.plugin.http.setSSLCertMode('pinned', done, done); },
|
||||
setJsonSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('json')); },
|
||||
setUtf8StringSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('utf8')); },
|
||||
setUrlEncodedSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('urlencoded')); },
|
||||
getWithXhr: function(done, url) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.addEventListener('load', function() {
|
||||
done(this.responseText);
|
||||
});
|
||||
|
||||
xhr.open('GET', url);
|
||||
xhr.send();
|
||||
},
|
||||
writeToFile: function (done, fileName, content) {
|
||||
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function (directoryEntry) {
|
||||
directoryEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
|
||||
fileEntry.createWriter(function (fileWriter) {
|
||||
var blob = new Blob([ content ], { type: 'text/plain' });
|
||||
|
||||
fileWriter.onwriteend = done;
|
||||
fileWriter.onerror = done;
|
||||
fileWriter.write(blob);
|
||||
}, done);
|
||||
}, done);
|
||||
}, done);
|
||||
}
|
||||
};
|
||||
|
||||
const tests = [
|
||||
{
|
||||
description: 'should reject self signed cert (GET)',
|
||||
expected: 'rejected: {"status":-1,"error":"cancelled"}',
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
|
||||
}
|
||||
},{
|
||||
description: 'should reject self signed cert (PUT)',
|
||||
expected: 'rejected: {"status":-1,"error":"cancelled"}',
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
|
||||
}
|
||||
},{
|
||||
description: 'should reject self signed cert (POST)',
|
||||
expected: 'rejected: {"status":-1,"error":"cancelled"}',
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
|
||||
}
|
||||
},{
|
||||
description: 'should reject self signed cert (PATCH)',
|
||||
expected: 'rejected: {"status":-1,"error":"cancelled"}',
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
|
||||
}
|
||||
},{
|
||||
description: 'should reject self signed cert (DELETE)',
|
||||
expected: 'rejected: {"status":-1,"error":"cancelled"}',
|
||||
func: function(resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
|
||||
}
|
||||
},{
|
||||
description: 'should accept bad cert (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.should.include({ status: 200 });
|
||||
}
|
||||
},{
|
||||
description: 'should accept bad cert (PUT)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PUT is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},{
|
||||
description: 'should accept bad cert (POST)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because POST is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},{
|
||||
description: 'should accept bad cert (PATCH)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PATCH is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},{
|
||||
description: 'should accept bad cert (DELETE)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because DELETE is not allowed',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},{
|
||||
description: 'should fetch data from http://httpbin.org/ (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setNoCheckCertMode,
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.should.include({ status: 200 });
|
||||
}
|
||||
},{
|
||||
description: 'should send JSON object correctly (POST)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
},{
|
||||
description: 'should send JSON object correctly (PUT)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
},{
|
||||
description: 'should send JSON object correctly (PATCH)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
},{
|
||||
description: 'should send JSON array correctly (POST) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', [ 1, 2, 3 ], {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
|
||||
}
|
||||
},{
|
||||
description: 'should send JSON array correctly (PUT) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', [ 1, 2, 3 ], {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
|
||||
}
|
||||
},{
|
||||
description: 'should send JSON array correctly (PATCH) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', [ 1, 2, 3 ], {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
|
||||
}
|
||||
},{
|
||||
description: 'should send url encoded data correctly (POST) #41',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},{
|
||||
description: 'should send url encoded data correctly (PUT)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},{
|
||||
description: 'should send url encoded data correctly (PATCH)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},{
|
||||
description: 'should resolve correct URL after redirect (GET) #33',
|
||||
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.url.should.be.equal('http://httpbin.org/anything');
|
||||
}
|
||||
},{
|
||||
description: 'should download a file from given URL to given path in local filesystem',
|
||||
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
|
||||
func: function(resolve, reject) {
|
||||
var sourceUrl = 'http://httpbin.org/xml';
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function(entry) {
|
||||
helpers.getWithXhr(function(content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
fullPath: entry.fullPath,
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('test.xml');
|
||||
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
|
||||
}
|
||||
},{
|
||||
description: 'should upload a file from given path in local filesystem to given URL #27',
|
||||
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
|
||||
func: function(resolve, reject) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
var sourcePath = cordova.file.cacheDirectory + fileName;
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
|
||||
helpers.writeToFile(function() {
|
||||
cordova.plugin.http.uploadFile(targetUrl, {}, {}, sourcePath, fileName, resolve, reject);
|
||||
}, fileName, fileContent);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.files[fileName]
|
||||
.should.be.equal(fileContent);
|
||||
}
|
||||
},{
|
||||
description: 'should encode HTTP array params correctly (GET) #45',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3\\"}\" ...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', { myArray: [ 'val1', 'val2', 'val3' ], myString: 'testString' }, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.url
|
||||
.should.be.equal('http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
|
||||
}
|
||||
},{
|
||||
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('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',
|
||||
expected: 'throwed: "advanced-http: header values must be strings"',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.setHeader('myTestHeader', 2);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('throwed');
|
||||
result.message.should.be.equal('advanced-http: header values must be strings');
|
||||
}
|
||||
},{
|
||||
description: 'should accept content-type "application/xml" #58',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/xml', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
}
|
||||
},{
|
||||
description: 'should send programmatically set cookies correctly (GET)',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.headers
|
||||
.Cookie
|
||||
.should.be.equal('myCookie=myValue; mySecondCookie=mySecondValue');
|
||||
}
|
||||
},{
|
||||
description: 'should not send any cookies after running "clearCookies" (GET) #59',
|
||||
expected: 'resolved: {"status": 200, "data": "{\"headers\": {\"Cookie\": \"\"...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
cordova.plugin.http.clearCookies();
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.headers
|
||||
.Cookie
|
||||
.should.be.equal('');
|
||||
}
|
||||
},{
|
||||
description: 'should send programmatically set cookies correctly (DOWNLOAD) #57',
|
||||
expected: 'resolved: {"content":{"cookies":{"myCookie":"myValue ...',
|
||||
func: function(resolve, reject) {
|
||||
var sourceUrl = 'http://httpbin.org/cookies';
|
||||
var targetPath = cordova.file.cacheDirectory + 'cookies.json';
|
||||
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function(entry) {
|
||||
helpers.getWithXhr(function(content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
fullPath: entry.fullPath,
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('cookies.json');
|
||||
result.data.content.should.be.a('string');
|
||||
|
||||
var cookies = JSON.parse(result.data.content).cookies;
|
||||
|
||||
cookies.myCookie.should.be.equal('myValue');
|
||||
cookies.mySecondCookie.should.be.equal('mySecondValue');
|
||||
}
|
||||
},{
|
||||
description: 'should send UTF-8 encoded raw string correctly (POST) #34',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"this is a test string\\"...',
|
||||
before: helpers.setUtf8StringSerializer,
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.post('http://httpbin.org/anything', 'this is a test string', {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).data.should.be.equal('this is a test string');
|
||||
}
|
||||
},{
|
||||
description: 'should encode spaces in query string (params object) correctly (GET) #71',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"args\\": \\"query param\\": \\"and value with spaces\\"...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', { 'query param': 'and value with spaces' }, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).args['query param'].should.be.equal('and value with spaces');
|
||||
}
|
||||
},{
|
||||
description: 'should decode latin1 (iso-8859-1) encoded body correctly (GET) #72',
|
||||
expected: 'resolved: {"status": 200, "data": "<!DOCTYPE HTML PUBLIC \\"-//W3C//DTD HTML 4.01 Transitional//EN\\"> ...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('http://www.columbia.edu/kermit/latin1.html', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.include('[¡] 161 10/01 241 A1 INVERTED EXCLAMATION MARK\n[¢] 162 10/02 242 A2 CENT SIGN');
|
||||
}
|
||||
},{
|
||||
description: 'should return empty body string correctly (GET)',
|
||||
expected: 'resolved: {"status": 200, "data": "" ...',
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/stream/0', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.equal('');
|
||||
}
|
||||
},{
|
||||
description: 'should pin SSL cert correctly (GET)',
|
||||
expected: 'resolved: {"status": 200 ...',
|
||||
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',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"{\\\\"outerObj\\\\":{\\\\"innerStr\\\\":\\\\"testString\\\\",\\\\"innerArr\\\\":[1,2,3]}}\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] }}, {}, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] }});
|
||||
}
|
||||
},{
|
||||
description: 'should override header "content-type" correctly (POST) #78',
|
||||
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Type\\": \\"text/plain\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', {}, { 'Content-Type': 'text/plain' }, resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).headers['Content-Type'].should.be.equal('text/plain');
|
||||
}
|
||||
},{
|
||||
description: 'should handle error during file download correctly (DOWNLOAD) #83',
|
||||
expected: 'rejected: {"status": 403, "error": "There was an error downloading the file" ...',
|
||||
func: function(resolve, reject) { cordova.plugin.http.downloadFile('http://httpbin.org/status/403', {}, {}, cordova.file.tempDirectory + 'testfile.txt', resolve, reject); },
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(403);
|
||||
result.data.error.should.be.equal('There was an error downloading the file');
|
||||
}
|
||||
},{
|
||||
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);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { tests: tests, hooks: hooks };
|
||||
}
|
||||
14
test/e2e-app-template/build.json
Normal file
14
test/e2e-app-template/build.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"ios": {
|
||||
"debug": {
|
||||
"buildFlag": [
|
||||
"-UseModernBuildSystem=0"
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"buildFlag": [
|
||||
"-UseModernBuildSystem=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,9 @@
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
<engine name="android" spec="6.2.3" />
|
||||
<engine name="android" spec="7.1.0" />
|
||||
<engine name="browser" spec="5.0.0" />
|
||||
<engine name="ios" spec="4.4.0" />
|
||||
<plugin name="cordova-plugin-file" spec="4.3.3" />
|
||||
<engine name="ios" spec="5.0.1" />
|
||||
<plugin name="cordova-plugin-file" spec="6.0.1" />
|
||||
<preference name="AndroidPersistentFileLocation" value="Internal" />
|
||||
</widget>
|
||||
@@ -12,8 +12,9 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cordova": "7.0.1",
|
||||
"cordova-android": "6.2.3",
|
||||
"cordova-ios": "4.4.0"
|
||||
"cordova-android": "7.1.0",
|
||||
"cordova-browser": "5.0.0",
|
||||
"cordova-ios": "5.0.1"
|
||||
},
|
||||
"cordova": {
|
||||
"platforms": [
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com https://self-signed.badssl.com http://httpbin.org http://www.columbia.edu 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com https://self-signed.badssl.com http://httpbin.org http://www.columbia.edu 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content: blob:;">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
|
||||
@@ -15,7 +15,7 @@
|
||||
<button id="nextBtn">Run next test</button>
|
||||
|
||||
<script type="text/javascript" src="cordova.js"></script>
|
||||
<script type="text/javascript" src="app-test-definitions.js"></script>
|
||||
<script type="text/javascript" src="e2e-specs.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
158
test/e2e-app-template/www/index.js
Normal file
158
test/e2e-app-template/www/index.js
Normal file
@@ -0,0 +1,158 @@
|
||||
const app = {
|
||||
testIndex: -1,
|
||||
|
||||
lastResult: null,
|
||||
|
||||
testsFlaggedToRun: [],
|
||||
|
||||
initialize: function () {
|
||||
document.getElementById('nextBtn').addEventListener('click', app.onNextBtnClick);
|
||||
|
||||
var onlyFlaggedTests = [];
|
||||
var enabledTests = [];
|
||||
|
||||
tests.forEach(function (test) {
|
||||
if (test.only) {
|
||||
onlyFlaggedTests.push(test);
|
||||
}
|
||||
|
||||
if (!test.disabled) {
|
||||
enabledTests.push(test);
|
||||
}
|
||||
});
|
||||
|
||||
app.testsFlaggedToRun = onlyFlaggedTests.length ? onlyFlaggedTests : enabledTests;
|
||||
},
|
||||
|
||||
printResult: function (prefix, content) {
|
||||
const text = prefix + ': ' + JSON.stringify(content);
|
||||
|
||||
document.getElementById('resultTextarea').value += text;
|
||||
},
|
||||
|
||||
reject: function (content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - rejected', content);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'rejected',
|
||||
data: content
|
||||
};
|
||||
},
|
||||
|
||||
resolve: function (content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - resolved', content);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'resolved',
|
||||
data: content
|
||||
};
|
||||
},
|
||||
|
||||
throw: function (error) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - throwed', error.message);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'throwed',
|
||||
message: error.message
|
||||
};
|
||||
},
|
||||
|
||||
getResult: function (cb) {
|
||||
cb(app.lastResult);
|
||||
},
|
||||
|
||||
runTest: function (tests, index) {
|
||||
const testDefinition = tests[index];
|
||||
const titleText = index + ': ' + testDefinition.description;
|
||||
const expectedText = 'expected - ' + testDefinition.expected;
|
||||
|
||||
document.getElementById('statusInput').value = 'running';
|
||||
document.getElementById('expectedTextarea').value = expectedText;
|
||||
document.getElementById('resultTextarea').value = '';
|
||||
document.getElementById('descriptionLbl').innerText = titleText;
|
||||
|
||||
const onSuccessFactory = function (cbChain) {
|
||||
return function () {
|
||||
cbChain.shift()(cbChain);
|
||||
}
|
||||
};
|
||||
|
||||
const onFailFactory = function (prefix) {
|
||||
return function (errorMessage) {
|
||||
app.reject(prefix + ': ' + errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
const onThrowedHandler = function (prefix, error) {
|
||||
app.throw(new Error(prefix + ': ' + error.message));
|
||||
};
|
||||
|
||||
const execBeforeEachTest = function (cbChain) {
|
||||
const prefix = 'in before each hook';
|
||||
|
||||
try {
|
||||
if (!hooks || !hooks.onBeforeEachTest) {
|
||||
return onSuccessFactory(cbChain)();
|
||||
}
|
||||
|
||||
hooks.onBeforeEachTest(
|
||||
onSuccessFactory(cbChain),
|
||||
onFailFactory(prefix)
|
||||
);
|
||||
} catch (error) {
|
||||
onThrowedHandler(prefix, error);
|
||||
}
|
||||
};
|
||||
|
||||
const execBeforeTest = function (cbChain) {
|
||||
const prefix = 'in before hook';
|
||||
|
||||
try {
|
||||
if (!testDefinition.before) {
|
||||
return onSuccessFactory(cbChain)();
|
||||
}
|
||||
|
||||
testDefinition.before(
|
||||
onSuccessFactory(cbChain),
|
||||
onFailFactory(prefix)
|
||||
);
|
||||
} catch (error) {
|
||||
onThrowedHandler(prefix, error);
|
||||
}
|
||||
};
|
||||
|
||||
const execTest = function () {
|
||||
try {
|
||||
testDefinition.func(app.resolve, app.reject);
|
||||
} catch (error) {
|
||||
app.throw(error);
|
||||
}
|
||||
};
|
||||
|
||||
onSuccessFactory([execBeforeEachTest, execBeforeTest, execTest])();
|
||||
},
|
||||
|
||||
onFinishedAllTests: function () {
|
||||
const titleText = 'No more tests';
|
||||
const expectedText = 'You have run all available tests.';
|
||||
|
||||
document.getElementById('expectedTextarea').value = expectedText;
|
||||
document.getElementById('resultTextarea').value = '';
|
||||
document.getElementById('descriptionLbl').innerText = titleText;
|
||||
},
|
||||
|
||||
onNextBtnClick: function () {
|
||||
app.testIndex += 1;
|
||||
|
||||
if (app.testIndex < app.testsFlaggedToRun.length) {
|
||||
app.runTest(app.testsFlaggedToRun, app.testIndex);
|
||||
} else {
|
||||
app.onFinishedAllTests();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
||||
BIN
test/e2e-app-template/www/res/cordova_logo.png
Normal file
BIN
test/e2e-app-template/www/res/cordova_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
909
test/e2e-specs.js
Normal file
909
test/e2e-specs.js
Normal file
@@ -0,0 +1,909 @@
|
||||
const hooks = {
|
||||
onBeforeEachTest: function (resolve, reject) {
|
||||
cordova.plugin.http.clearCookies();
|
||||
|
||||
helpers.enableFollowingRedirect(function() {
|
||||
// server trust mode is not supported on brpwser platform
|
||||
if (cordova.platformId === 'browser') {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
helpers.setDefaultServerTrustMode(function () {
|
||||
// @TODO: not ready yet
|
||||
// helpers.setNoneClientAuthMode(resolve, reject);
|
||||
resolve();
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const helpers = {
|
||||
setDefaultServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('default', resolve, reject); },
|
||||
setNoCheckServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('nocheck', resolve, reject); },
|
||||
setPinnedServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('pinned', resolve, reject); },
|
||||
setNoneClientAuthMode: function (resolve, reject) { cordova.plugin.http.setClientAuthMode('none', resolve, reject); },
|
||||
setBufferClientAuthMode: function (resolve, reject) {
|
||||
helpers.getWithXhr(function(pkcs) {
|
||||
cordova.plugin.http.setClientAuthMode('buffer', {
|
||||
rawPkcs: pkcs,
|
||||
pkcsPassword: 'badssl.com'
|
||||
}, resolve, reject);
|
||||
}, './certificates/badssl-client-cert.pkcs', 'arraybuffer');
|
||||
},
|
||||
setJsonSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('json')); },
|
||||
setUtf8StringSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('utf8')); },
|
||||
setUrlEncodedSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('urlencoded')); },
|
||||
setMultipartSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('multipart')); },
|
||||
setRawSerializer: function(resolve) { resolve(cordova.plugin.http.setDataSerializer('raw')); },
|
||||
disableFollowingRedirect: function (resolve) { resolve(cordova.plugin.http.setFollowRedirect(false)); },
|
||||
enableFollowingRedirect: function(resolve) { resolve(cordova.plugin.http.setFollowRedirect(true)); },
|
||||
getWithXhr: function (done, url, type) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.addEventListener('load', function () {
|
||||
if (!type || type === 'text') {
|
||||
done(this.responseText);
|
||||
} else {
|
||||
done(this.response);
|
||||
}
|
||||
});
|
||||
|
||||
xhr.responseType = type;
|
||||
xhr.open('GET', url);
|
||||
xhr.send();
|
||||
},
|
||||
writeToFile: function (done, fileName, content) {
|
||||
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function (directoryEntry) {
|
||||
directoryEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
|
||||
fileEntry.createWriter(function (fileWriter) {
|
||||
var blob = new Blob([content], { type: 'text/plain' });
|
||||
|
||||
fileWriter.onwriteend = done;
|
||||
fileWriter.onerror = done;
|
||||
fileWriter.write(blob);
|
||||
}, done);
|
||||
}, done);
|
||||
}, done);
|
||||
},
|
||||
// adopted from: https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
|
||||
hashArrayBuffer: function (buffer) {
|
||||
var hash = 0;
|
||||
var byteArray = new Uint8Array(buffer);
|
||||
|
||||
for (var i = 0; i < byteArray.length; i++) {
|
||||
hash = ((hash << 5) - hash) + byteArray[i];
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
|
||||
return hash;
|
||||
},
|
||||
checkResult: function (result, expected) {
|
||||
if (result.type === 'throwed' && expected !== 'throwed') {
|
||||
throw new Error('Expected function not to throw: ' + result.message);
|
||||
}
|
||||
|
||||
result.type.should.be.equal(expected);
|
||||
}
|
||||
};
|
||||
|
||||
const messageFactory = {
|
||||
sslTrustAnchor: function () { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
|
||||
invalidCertificate: function (domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
|
||||
}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
description: 'should reject self signed cert (GET)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should reject self signed cert (PUT)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should reject self signed cert (POST)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should reject self signed cert (PATCH)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should reject self signed cert (DELETE)',
|
||||
expected: 'rejected: {"status":-2, ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result, targetInfo) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should accept bad cert (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.should.include({ status: 200 });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should accept bad cert (PUT)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PUT is not allowed',
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should accept bad cert (POST)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because POST is not allowed',
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should accept bad cert (PATCH)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because PATCH is not allowed',
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should accept bad cert (DELETE)',
|
||||
expected: 'rejected: {"status":405, ... // will be rejected because DELETE is not allowed',
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.should.include({ status: 405 });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should fetch data from http://httpbin.org/ (GET)',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setNoCheckServerTrustMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.should.include({ status: 200 });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON object correctly (POST)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON object correctly (PUT)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON object correctly (PATCH)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON array correctly (POST) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON array correctly (PUT) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send JSON array correctly (PATCH) #26',
|
||||
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send url encoded data correctly (POST) #41',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send url encoded data correctly (PUT)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send url encoded data correctly (PATCH)',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
|
||||
before: helpers.setUrlEncodedSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should resolve correct URL after redirect (GET) #33',
|
||||
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.url.should.be.equal('http://httpbin.org/anything');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not follow 302 redirect when following redirects is disabled',
|
||||
expected: 'rejected: {"status": 302, ...',
|
||||
before: function(resolve, reject) { cordova.plugin.http.disableRedirect(true, resolve, reject)},
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(302);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should download a file from given URL to given path in local filesystem',
|
||||
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
|
||||
func: function (resolve, reject) {
|
||||
var sourceUrl = 'http://httpbin.org/xml';
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
fullPath: entry.fullPath,
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('test.xml');
|
||||
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should upload a file from given path in local filesystem to given URL #27',
|
||||
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
|
||||
func: function (resolve, reject) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
var sourcePath = cordova.file.cacheDirectory + fileName;
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
|
||||
helpers.writeToFile(function () {
|
||||
cordova.plugin.http.uploadFile(targetUrl, {}, {}, sourcePath, fileName, resolve, reject);
|
||||
}, fileName, fileContent);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.files[fileName]
|
||||
.should.be.equal(fileContent);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should upload multiple files from given paths in local filesystem to given URL #127',
|
||||
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
|
||||
func: function (resolve, reject) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileName2 = 'test-file2.txt';
|
||||
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
var fileContent2 = 'I am the second dummy file. I am used for testing purposes!';
|
||||
|
||||
var sourcePath = cordova.file.cacheDirectory + fileName;
|
||||
var sourcePath2 = cordova.file.cacheDirectory + fileName2;
|
||||
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
|
||||
helpers.writeToFile(function () {
|
||||
helpers.writeToFile(function() {
|
||||
cordova.plugin.http.uploadFile(targetUrl, {}, {}, [sourcePath, sourcePath2], [fileName, fileName2], resolve, reject);
|
||||
}, fileName2, fileContent2);
|
||||
}, fileName, fileContent);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
var fileName = 'test-file.txt';
|
||||
var fileName2 = 'test-file2.txt';
|
||||
|
||||
var fileContent = 'I am a dummy file. I am used for testing purposes!';
|
||||
var fileContent2 = 'I am the second dummy file. I am used for testing purposes!';
|
||||
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
var parsed = JSON.parse(result.data.data);
|
||||
|
||||
parsed.files[fileName].should.be.equal(fileContent);
|
||||
parsed.files[fileName2].should.be.equal(fileContent2);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should encode HTTP array params correctly (GET) #45',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3\\"}\" ...',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', { myArray: ['val1', 'val2', 'val3'], myString: 'testString' }, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.url
|
||||
.should.include('httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
|
||||
}
|
||||
},
|
||||
{
|
||||
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('throwed');
|
||||
result.message.should.be.equal(require('../www/messages').TYPE_MISMATCH_HEADERS);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should throw an error while setting non-string value as global header #54',
|
||||
expected: 'throwed: "advanced-http: header values must be strings"',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.setHeader('myTestHeader', 2);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('throwed');
|
||||
result.message.should.be.equal(require('../www/messages').INVALID_HEADER_VALUE);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should accept content-type "application/xml" #58',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/xml', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send programmatically set cookies correctly (GET)',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.headers
|
||||
.Cookie
|
||||
.should.be.equal('myCookie=myValue; mySecondCookie=mySecondValue');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not send any cookies after running "clearCookies" (GET) #59',
|
||||
expected: 'resolved: {"status": 200, "data": "{\"headers\": {\"Cookie\": \"\"...',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
cordova.plugin.http.clearCookies();
|
||||
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.a('string');
|
||||
|
||||
JSON
|
||||
.parse(result.data.data)
|
||||
.headers
|
||||
.should.not.have.property('Cookie');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send programmatically set cookies correctly (DOWNLOAD) #57',
|
||||
expected: 'resolved: {"content":{"cookies":{"myCookie":"myValue ...',
|
||||
func: function (resolve, reject) {
|
||||
var sourceUrl = 'http://httpbin.org/cookies';
|
||||
var targetPath = cordova.file.cacheDirectory + 'cookies.json';
|
||||
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
fullPath: entry.fullPath,
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('cookies.json');
|
||||
result.data.content.should.be.a('string');
|
||||
|
||||
var cookies = JSON.parse(result.data.content).cookies;
|
||||
|
||||
cookies.myCookie.should.be.equal('myValue');
|
||||
cookies.mySecondCookie.should.be.equal('mySecondValue');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send UTF-8 encoded raw string correctly (POST) #34',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"this is a test string\\"...',
|
||||
before: helpers.setUtf8StringSerializer,
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.post('http://httpbin.org/anything', 'this is a test string', {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).data.should.be.equal('this is a test string');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should encode spaces in query string (params object) correctly (GET) #71',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"args\\": \\"query param\\": \\"and value with spaces\\"...',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/get', { 'query param': 'and value with spaces' }, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).args['query param'].should.be.equal('and value with spaces');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should decode latin1 (iso-8859-1) encoded body correctly (GET) #72',
|
||||
expected: 'resolved: {"status": 200, "data": "<!DOCTYPE HTML PUBLIC \\"-//W3C//DTD HTML 4.01 Transitional//EN\\"> ...',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://www.columbia.edu/kermit/latin1.html', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.include('[¡] 161 10/01 241 A1 INVERTED EXCLAMATION MARK\n[¢] 162 10/02 242 A2 CENT SIGN');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return empty body string correctly (GET)',
|
||||
expected: 'resolved: {"status": 200, "data": "" ...',
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('http://httpbin.org/stream/0', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.be.equal('');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should pin SSL cert correctly (GET)',
|
||||
expected: 'resolved: {"status": 200 ...',
|
||||
before: helpers.setPinnedServerTrustMode,
|
||||
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": -2 ...',
|
||||
before: helpers.setPinnedServerTrustMode,
|
||||
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: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('sha512.badssl.com') });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send deeply structured JSON object correctly (POST) #65',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"{\\\\"outerObj\\\\":{\\\\"innerStr\\\\":\\\\"testString\\\\",\\\\"innerArr\\\\":[1,2,3]}}\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] } }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.should.eql({ outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] } });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should override header "content-type" correctly (POST) #78',
|
||||
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Type\\": \\"text/plain\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', {}, { 'Content-Type': 'text/plain' }, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).headers['Content-Type'].should.be.equal('text/plain');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should handle error during file download correctly (DOWNLOAD) #83',
|
||||
expected: 'rejected: {"status": 403, "error": "There was an error downloading the file" ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.downloadFile('http://httpbin.org/status/403', {}, {}, cordova.file.tempDirectory + 'testfile.txt', resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(403);
|
||||
result.data.error.should.be.equal('There was an error downloading the file');
|
||||
}
|
||||
},
|
||||
{
|
||||
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);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send empty string correctly',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"\\" ...',
|
||||
before: helpers.setUtf8StringSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', '', {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).data.should.be.equal('');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'shouldn\'t escape forward slashes #184',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"/\\" ...',
|
||||
before: helpers.setJsonSerializer,
|
||||
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { testString: '/' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).json.testString.should.be.equal('/');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not double encode spaces in url path #195',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"https://httpbin.org/anything/containing spaces in url\\" ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything/containing%20spaces%20in%20url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything/containing spaces in url');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should encode spaces in url query correctly',
|
||||
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"https://httpbin.org/anything?query key=very long query value with spaces\\" ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything', { 'query key': 'very long query value with spaces' }, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything?query key=very long query value with spaces');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should download a file from given HTTPS URL to given path in local filesystem #197',
|
||||
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
|
||||
func: function (resolve, reject) {
|
||||
var sourceUrl = 'https://httpbin.org/xml';
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
fullPath: entry.fullPath,
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.name.should.be.equal('test.xml');
|
||||
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return header object when request failed due to non-success response from server #221',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/status/418', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.headers.should.be.an('object');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return status code when request failed due to non-success response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/status/418', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(418);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return url string when request failed due to non-success response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/status/418', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.url.should.be.equal('https://httpbin.org/status/418');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'shouldn\'t return header object when request failed before receiving response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://not_existing_url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
should.equal(result.data.headers, undefined);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should return status code when request failed before receiving response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://not_existing_url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.a('number');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'shouldn\'t return url string when request failed before receiving response from server',
|
||||
expected: 'rejected:',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://not_existing_url', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
should.equal(result.data.url, undefined);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should fetch binary correctly when response type is "arraybuffer"',
|
||||
expected: 'resolved: {"isArrayBuffer:true,"hash":-1032603775,"byteLength":35588}',
|
||||
func: function (resolve, reject) {
|
||||
var url = 'https://httpbin.org/image/jpeg';
|
||||
var options = { method: 'get', responseType: 'arraybuffer' };
|
||||
var success = function (response) {
|
||||
resolve({
|
||||
isArrayBuffer: response.data.constructor === ArrayBuffer,
|
||||
hash: helpers.hashArrayBuffer(response.data),
|
||||
byteLength: response.data.byteLength
|
||||
});
|
||||
};
|
||||
cordova.plugin.http.sendRequest(url, options, success, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.isArrayBuffer.should.be.equal(true);
|
||||
result.data.hash.should.be.equal(-1032603775);
|
||||
result.data.byteLength.should.be.equal(35588);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should fetch binary correctly when response type is "blob"',
|
||||
expected: 'resolved: {"isBlob":true,byteLength":35588}',
|
||||
func: function (resolve, reject) {
|
||||
var url = 'https://httpbin.org/image/jpeg';
|
||||
var options = { method: 'get', responseType: 'blob' };
|
||||
var success = function (response) {
|
||||
resolve({
|
||||
isBlob: response.data.constructor === Blob,
|
||||
type: response.data.type,
|
||||
byteLength: response.data.size
|
||||
});
|
||||
};
|
||||
cordova.plugin.http.sendRequest(url, options, success, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.isBlob.should.be.equal(true);
|
||||
result.data.type.should.be.equal('image/jpeg');
|
||||
result.data.byteLength.should.be.equal(35588);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should decode error body even if response type is "arraybuffer"',
|
||||
expected: 'rejected: {"status": 418, ...',
|
||||
func: function (resolve, reject) {
|
||||
var url = 'https://httpbin.org/status/418';
|
||||
var options = { method: 'get', responseType: 'arraybuffer' };
|
||||
cordova.plugin.http.sendRequest(url, options, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(418);
|
||||
result.data.error.should.be.equal("\n -=[ teapot ]=-\n\n _...._\n .' _ _ `.\n | .\"` ^ `\". _,\n \\_;`\"---\"`|//\n | ;/\n \\_ _/\n `\"\"\"`\n");
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should serialize FormData instance correctly when it contains string value',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
before: helpers.setMultipartSerializer,
|
||||
func: function (resolve, reject) {
|
||||
var ponyfills = cordova.plugin.http.ponyfills;
|
||||
var formData = new ponyfills.FormData();
|
||||
formData.append('myString', 'This is a test!');
|
||||
|
||||
var url = 'https://httpbin.org/anything';
|
||||
var options = { method: 'post', data: formData };
|
||||
cordova.plugin.http.sendRequest(url, options, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
JSON.parse(result.data.data).form.should.be.eql({ myString: 'This is a test!' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should serialize FormData instance correctly when it contains blob value',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
before: helpers.setMultipartSerializer,
|
||||
func: function (resolve, reject) {
|
||||
var ponyfills = cordova.plugin.http.ponyfills;
|
||||
helpers.getWithXhr(function(blob) {
|
||||
var formData = new ponyfills.FormData();
|
||||
formData.append('CordovaLogo', blob);
|
||||
|
||||
var url = 'https://httpbin.org/anything';
|
||||
var options = { method: 'post', data: formData };
|
||||
cordova.plugin.http.sendRequest(url, options, resolve, reject);
|
||||
}, './res/cordova_logo.png', 'blob');
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
|
||||
// httpbin.org encodes posted binaries in base64 and echoes them back
|
||||
// therefore we need to check for base64 string with mime type prefix
|
||||
const fs = require('fs');
|
||||
const rawLogo = fs.readFileSync('./test/e2e-app-template/www/res/cordova_logo.png');
|
||||
const b64Logo = rawLogo.toString('base64');
|
||||
JSON.parse(result.data.data).files.CordovaLogo.should.be.equal('data:image/png;base64,' + b64Logo);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should send raw byte array correctly (POST) #291',
|
||||
expected: 'resolved: {"status":200,"data:application/octet-stream;base64,iVBORw0KGgoAAAANSUhEUg ...',
|
||||
before: helpers.setRawSerializer,
|
||||
func: function (resolve, reject) {
|
||||
helpers.getWithXhr(function(buffer) {
|
||||
cordova.plugin.http.post('http://httpbin.org/anything', buffer, {}, resolve, reject);
|
||||
}, './res/cordova_logo.png', 'arraybuffer');
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
|
||||
// httpbin.org encodes posted binaries in base64 and echoes them back
|
||||
// therefore we need to check for base64 string with mime type prefix
|
||||
const fs = require('fs');
|
||||
const rawLogo = fs.readFileSync('./test/e2e-app-template/www/res/cordova_logo.png');
|
||||
const b64Logo = rawLogo.toString('base64');
|
||||
const parsed = JSON.parse(result.data.data);
|
||||
|
||||
parsed.headers['Content-Type'].should.be.equal('application/octet-stream');
|
||||
parsed.data.should.be.equal('data:application/octet-stream;base64,' + b64Logo);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should perform an OPTIONS request correctly #155',
|
||||
expected: 'resolved: {"status":200,"headers":{"allow":"GET, PUT, DELETE, HEAD, PATCH, TRACE, POST, OPTIONS" ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.options('http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
|
||||
result.data.headers.should.be.an('object');
|
||||
result.data.headers.allow.should.include('GET');
|
||||
result.data.headers.allow.should.include('PUT');
|
||||
result.data.headers.allow.should.include('DELETE');
|
||||
result.data.headers.allow.should.include('HEAD');
|
||||
result.data.headers.allow.should.include('PATCH');
|
||||
result.data.headers.allow.should.include('TRACE');
|
||||
result.data.headers.allow.should.include('POST');
|
||||
result.data.headers.allow.should.include('OPTIONS');
|
||||
|
||||
result.data.headers['access-control-allow-origin'].should.be.equal('*');
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: not ready yet
|
||||
// {
|
||||
// description: 'should authenticate correctly when client cert auth is configured with a PKCS12 container',
|
||||
// expected: 'resolved: {"status": 200, ...',
|
||||
// before: helpers.setBufferClientAuthMode,
|
||||
// func: function (resolve, reject) { cordova.plugin.http.get('https://client.badssl.com/', {}, {}, resolve, reject); },
|
||||
// validationFunc: function (driver, result) {
|
||||
// result.type.should.be.equal('resolved');
|
||||
// result.data.data.should.include('TLS handshake');
|
||||
// }
|
||||
// }
|
||||
];
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = { tests: tests, hooks: hooks };
|
||||
}
|
||||
89
test/e2e-tooling/caps.js
Normal file
89
test/e2e-tooling/caps.js
Normal file
@@ -0,0 +1,89 @@
|
||||
module.exports = { getCaps };
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const configs = {
|
||||
// testing on local machine
|
||||
localIosDevice: {
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
automationName: 'XCUITest',
|
||||
deviceName: 'iPhone 8',
|
||||
autoWebview: true,
|
||||
app: path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app')
|
||||
},
|
||||
localIosEmulator: {
|
||||
platformName: 'iOS',
|
||||
platformVersion: '13.2',
|
||||
automationName: 'XCUITest',
|
||||
deviceName: 'iPhone 8',
|
||||
autoWebview: true,
|
||||
app: path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app')
|
||||
},
|
||||
localAndroidEmulator: {
|
||||
platformName: 'Android',
|
||||
platformVersion: '5',
|
||||
deviceName: 'Android Emulator',
|
||||
autoWebview: true,
|
||||
fullReset: true,
|
||||
app: path.resolve('temp/platforms/android/app/build/outputs/apk/debug/app-debug.apk')
|
||||
},
|
||||
|
||||
// testing on SauceLabs
|
||||
saucelabsIosDevice: {
|
||||
browserName: '',
|
||||
'appium-version': '1.9.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
deviceName: 'iPhone 6',
|
||||
autoWebview: true,
|
||||
app: 'sauce-storage:HttpDemo.app.zip'
|
||||
},
|
||||
saucelabsIosEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.9.1',
|
||||
platformName: 'iOS',
|
||||
platformVersion: '10.3',
|
||||
deviceName: 'iPhone Simulator',
|
||||
autoWebview: true,
|
||||
app: 'sauce-storage:HttpDemo.app.zip'
|
||||
},
|
||||
saucelabsAndroidEmulator: {
|
||||
browserName: '',
|
||||
'appium-version': '1.9.1',
|
||||
platformName: 'Android',
|
||||
platformVersion: '5.1',
|
||||
deviceName: 'Android Emulator',
|
||||
autoWebview: true,
|
||||
app: 'sauce-storage:HttpDemo.apk'
|
||||
},
|
||||
|
||||
// testing on BrowserStack
|
||||
browserstackIosDevice: {
|
||||
device: 'iPhone 7',
|
||||
os_version: '10',
|
||||
project: 'HTTP Test App',
|
||||
autoWebview: true,
|
||||
app: 'HttpTestAppAndroid'
|
||||
},
|
||||
browserstackAndroidDevice: {
|
||||
device: 'Google Nexus 9',
|
||||
os_version: '5.1',
|
||||
project: 'HTTP Test App',
|
||||
autoWebview: true,
|
||||
app: 'HttpTestAppAndroid'
|
||||
}
|
||||
};
|
||||
|
||||
function getCaps(environment, os, runtime) {
|
||||
const key = environment.toLowerCase() + capitalize(os) + capitalize(runtime);
|
||||
const caps = configs[key];
|
||||
|
||||
caps.name = `cordova-plugin-advanced-http (${os})`;
|
||||
|
||||
return caps;
|
||||
};
|
||||
|
||||
function capitalize(text) {
|
||||
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
exports.configure = driver => {
|
||||
module.exports = { setupLogging };
|
||||
|
||||
function setupLogging(driver) {
|
||||
require('colors');
|
||||
|
||||
driver.on('status', info => {
|
||||
console.log(info.cyan);
|
||||
});
|
||||
@@ -10,4 +14,4 @@ exports.configure = driver => {
|
||||
driver.on('http', (meth, path, data) => {
|
||||
console.log(' > ' + meth.magenta, path, (data || '').grey);
|
||||
});
|
||||
};
|
||||
}
|
||||
22
test/e2e-tooling/server.js
Normal file
22
test/e2e-tooling/server.js
Normal file
@@ -0,0 +1,22 @@
|
||||
module.exports = { getServer };
|
||||
|
||||
const configs = {
|
||||
local: {
|
||||
host: 'localhost',
|
||||
port: 4723
|
||||
},
|
||||
saucelabs: {
|
||||
host: 'ondemand.saucelabs.com',
|
||||
port: 80,
|
||||
auth: process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY
|
||||
},
|
||||
browserstack: {
|
||||
host: 'hub-cloud.browserstack.com',
|
||||
port: 80,
|
||||
auth: process.env.BROWSERSTACK_USERNAME + ":" + process.env.BROWSERSTACK_ACCESS_KEY
|
||||
}
|
||||
}
|
||||
|
||||
function getServer(environment) {
|
||||
return configs[environment.toLowerCase()];
|
||||
}
|
||||
125
test/e2e-tooling/test.js
Normal file
125
test/e2e-tooling/test.js
Normal file
@@ -0,0 +1,125 @@
|
||||
const wd = require('wd');
|
||||
const chai = require('chai');
|
||||
const chaiAsPromised = require('chai-as-promised');
|
||||
const logging = require('./logging');
|
||||
const capsConfig = require('./caps');
|
||||
const serverConfig = require('./server');
|
||||
const testDefinitions = require('../e2e-specs');
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
|
||||
global.should = chai.should();
|
||||
|
||||
describe('Advanced HTTP e2e test suite', function () {
|
||||
const isSauceLabs = !!process.env.SAUCE_USERNAME;
|
||||
const isBrowserStack = !!process.env.BROWSERSTACK_USERNAME;
|
||||
const isVerbose = process.argv.includes('--verbose');
|
||||
const isDevice = process.argv.includes('--device');
|
||||
const isAndroid = process.argv.includes('--android');
|
||||
|
||||
const targetInfo = { isSauceLabs, isBrowserStack, isDevice, isAndroid };
|
||||
const environment = isSauceLabs ? 'saucelabs' : isBrowserStack ? 'browserstack' : 'local';
|
||||
|
||||
let driver;
|
||||
let allPassed = true;
|
||||
|
||||
this.timeout(15000);
|
||||
this.slow(4000);
|
||||
|
||||
before(async function () {
|
||||
// connecting to saucelabs can take some time
|
||||
this.timeout(300000);
|
||||
|
||||
driver = await wd.promiseChainRemote(serverConfig.getServer(environment));
|
||||
|
||||
if (isVerbose) {
|
||||
logging.setupLogging(driver);
|
||||
}
|
||||
|
||||
await driver.init(
|
||||
capsConfig.getCaps(
|
||||
environment,
|
||||
isAndroid ? 'android' : 'ios',
|
||||
isDevice ? 'device' : 'emulator'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await driver.quit().finally(
|
||||
() => isSauceLabs && driver.sauceJobStatus(allPassed)
|
||||
);
|
||||
});
|
||||
|
||||
const defineTestForMocha = (test, index) => {
|
||||
it(index + ': ' + test.description, async () => {
|
||||
await clickNext(driver);
|
||||
await validateTestIndex(driver, index);
|
||||
await validateTestTitle(driver, test.description);
|
||||
await waitToBeFinished(driver, test.timeout || 10000);
|
||||
await validateResult(driver, test.validationFunc, targetInfo);
|
||||
});
|
||||
};
|
||||
|
||||
const onlyFlaggedTests = [];
|
||||
const enabledTests = [];
|
||||
|
||||
testDefinitions.tests.forEach(test => {
|
||||
if (test.only) {
|
||||
onlyFlaggedTests.push(test);
|
||||
}
|
||||
|
||||
if (!test.disabled) {
|
||||
enabledTests.push(test);
|
||||
}
|
||||
});
|
||||
|
||||
if (onlyFlaggedTests.length) {
|
||||
onlyFlaggedTests.forEach(defineTestForMocha);
|
||||
} else {
|
||||
enabledTests.forEach(defineTestForMocha);
|
||||
}
|
||||
});
|
||||
|
||||
async function clickNext(driver) {
|
||||
await driver.elementById('nextBtn').click().sleep(1000);
|
||||
}
|
||||
|
||||
async function validateTestIndex(driver, testIndex) {
|
||||
const description = await driver.elementById('descriptionLbl').text();
|
||||
const index = parseInt(description.match(/(\d+):/)[1], 10);
|
||||
|
||||
index.should.be.equal(testIndex, 'Test index is not matching!');
|
||||
}
|
||||
|
||||
async function validateTestTitle(driver, testTitle) {
|
||||
const description = await driver.elementById('descriptionLbl').text();
|
||||
const title = description.match(/\d+:\ (.*)/)[1];
|
||||
|
||||
title.should.be.equal(testTitle, 'Test description is not matching!');
|
||||
}
|
||||
|
||||
async function waitToBeFinished(driver, timeout) {
|
||||
const timeoutTimestamp = Date.now() + timeout;
|
||||
|
||||
while (true) {
|
||||
if (await driver.elementById('statusInput').getValue() === 'finished') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Date.now() > timeoutTimestamp) {
|
||||
throw new Error('Test function timed out!');
|
||||
}
|
||||
|
||||
await sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
async function validateResult(driver, validationFunc, targetInfo) {
|
||||
const result = await driver.safeExecute('app.lastResult');
|
||||
validationFunc(driver, result, targetInfo);
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
const chai = require('chai');
|
||||
const mock = require('mock-require');
|
||||
const path = require('path');
|
||||
|
||||
const should = chai.should();
|
||||
|
||||
const HELPERS_ID = path.resolve(__dirname, '..', 'www', 'helpers');
|
||||
const PLUGIN_ID = path.resolve(__dirname, '..', 'www', 'advanced-http');
|
||||
|
||||
describe('Advanced HTTP www interface', function() {
|
||||
let http = {};
|
||||
let helpers = {};
|
||||
|
||||
const noop = () => { /* intentionally doing nothing */ };
|
||||
|
||||
const loadHttp = () => {
|
||||
mock(`${PLUGIN_ID}.helpers`, mock.reRequire('../www/helpers'));
|
||||
http = mock.reRequire('../www/advanced-http');
|
||||
};
|
||||
|
||||
this.timeout(900000);
|
||||
|
||||
beforeEach(() => {
|
||||
// mocked btoa function (base 64 encoding strings)
|
||||
global.btoa = decoded => new Buffer(decoded).toString('base64');
|
||||
|
||||
mock('cordova/exec', noop);
|
||||
mock(`${PLUGIN_ID}.cookie-handler`, {});
|
||||
mock(`${HELPERS_ID}.cookie-handler`, {});
|
||||
mock(`${HELPERS_ID}.messages`, require('../www/messages'));
|
||||
|
||||
loadHttp();
|
||||
});
|
||||
|
||||
it('sets global headers correctly with two args (old interface)', () => {
|
||||
http.setHeader('myKey', 'myValue');
|
||||
http.getHeaders('*').myKey.should.equal('myValue');
|
||||
});
|
||||
|
||||
it('sets global headers correctly with three args (new interface) #24', () => {
|
||||
http.setHeader('*', 'myKey', 'myValue');
|
||||
http.getHeaders('*').myKey.should.equal('myValue');
|
||||
});
|
||||
|
||||
it('sets host headers correctly #24', () => {
|
||||
http.setHeader('www.google.de', 'myKey', 'myValue');
|
||||
http.getHeaders('www.google.de').myKey.should.equal('myValue');
|
||||
});
|
||||
|
||||
it('resolves global headers correctly #24', () => {
|
||||
mock(`${HELPERS_ID}.cookie-handler`, {
|
||||
getCookieString: () => 'fakeCookieString'
|
||||
});
|
||||
|
||||
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[2];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
});
|
||||
|
||||
loadHttp();
|
||||
|
||||
http.setHeader('*', 'myKey', 'myValue');
|
||||
http.get('url', {}, {}, noop, noop);
|
||||
});
|
||||
|
||||
it('resolves host headers correctly (set without port number) #37', () => {
|
||||
mock(`${HELPERS_ID}.cookie-handler`, {
|
||||
getCookieString: () => 'fakeCookieString'
|
||||
});
|
||||
|
||||
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[2];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
});
|
||||
|
||||
loadHttp();
|
||||
|
||||
http.setHeader('www.google.de', 'myKey', 'myValue');
|
||||
http.get('https://www.google.de/?gws_rd=ssl', {}, {}, noop, noop);
|
||||
});
|
||||
|
||||
it('resolves host headers correctly (set with port number) #37', () => {
|
||||
mock(`${HELPERS_ID}.cookie-handler`, {
|
||||
getCookieString: () => 'fakeCookieString'
|
||||
});
|
||||
|
||||
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[2];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
});
|
||||
|
||||
loadHttp();
|
||||
|
||||
http.setHeader('www.google.de:8080', 'myKey', 'myValue');
|
||||
http.get('https://www.google.de:8080/?gws_rd=ssl', {}, {}, noop, noop);
|
||||
});
|
||||
|
||||
it('resolves request headers correctly', () => {
|
||||
mock(`${HELPERS_ID}.cookie-handler`, {
|
||||
getCookieString: () => 'fakeCookieString'
|
||||
});
|
||||
|
||||
mock('cordova/exec', (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[2];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
});
|
||||
|
||||
loadHttp();
|
||||
|
||||
http.get('https://www.google.de/?gws_rd=ssl', {}, { myKey: 'myValue' }, noop, noop);
|
||||
});
|
||||
|
||||
it('sets basic authentication header correctly #36', () => {
|
||||
http.useBasicAuth('name', 'pass');
|
||||
http.getHeaders('*').Authorization.should.equal('Basic bmFtZTpwYXNz');
|
||||
});
|
||||
|
||||
it('throws an Error when you try to add a cookie by using "setHeader" #46', () => {
|
||||
(function() { http.setHeader('*', 'cookie', 'value') }).should.throw();
|
||||
});
|
||||
});
|
||||
755
test/js-specs.js
Normal file
755
test/js-specs.js
Normal file
@@ -0,0 +1,755 @@
|
||||
const chai = require('chai');
|
||||
const mock = require('mock-require');
|
||||
const util = require('util');
|
||||
const should = chai.should();
|
||||
|
||||
const BlobMock = require('./mocks/Blob.mock');
|
||||
const ConsoleMock = require('./mocks/Console.mock');
|
||||
const FileMock = require('./mocks/File.mock');
|
||||
const FileReaderMock = require('./mocks/FileReader.mock');
|
||||
const FormDataMock = require('./mocks/FormData.mock');
|
||||
|
||||
describe('Advanced HTTP public interface', function () {
|
||||
const messages = require('../www/messages');
|
||||
|
||||
let http = {};
|
||||
|
||||
const noop = () => { /* intentionally doing nothing */ };
|
||||
|
||||
const getDependenciesBlueprint = () => {
|
||||
const globalConfigs = require('../www/global-configs');
|
||||
const jsUtil = require('../www/js-util');
|
||||
const ToughCookie = require('../www/umd-tough-cookie');
|
||||
const lodash = require('../www/lodash');
|
||||
const errorCodes = require('../www/error-codes');
|
||||
const WebStorageCookieStore = require('../www/local-storage-store')(ToughCookie, lodash);
|
||||
const cookieHandler = require('../www/cookie-handler')(null, ToughCookie, WebStorageCookieStore);
|
||||
const helpers = require('../www/helpers')(null, jsUtil, cookieHandler, messages, errorCodes);
|
||||
const urlUtil = require('../www/url-util')(jsUtil);
|
||||
|
||||
return { exec: noop, cookieHandler, urlUtil: urlUtil, helpers, globalConfigs, errorCodes };
|
||||
};
|
||||
|
||||
const loadHttp = (deps) => {
|
||||
http = require('../www/public-interface')(deps.exec, deps.cookieHandler, deps.urlUtil, deps.helpers, deps.globalConfigs, deps.errorCodes);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// mocked btoa function (base 64 encoding strings)
|
||||
global.btoa = decoded => Buffer.from(decoded).toString('base64');
|
||||
loadHttp(getDependenciesBlueprint());
|
||||
});
|
||||
|
||||
it('sets global headers correctly with two args (old interface)', () => {
|
||||
http.setHeader('myKey', 'myValue');
|
||||
http.getHeaders('*').myKey.should.equal('myValue');
|
||||
});
|
||||
|
||||
it('sets global headers correctly with three args (new interface) #24', () => {
|
||||
http.setHeader('*', 'myKey', 'myValue');
|
||||
http.getHeaders('*').myKey.should.equal('myValue');
|
||||
});
|
||||
|
||||
it('sets host headers correctly #24', () => {
|
||||
http.setHeader('www.google.de', 'myKey', 'myValue');
|
||||
http.getHeaders('www.google.de').myKey.should.equal('myValue');
|
||||
});
|
||||
|
||||
it('resolves global headers correctly #24', () => {
|
||||
const deps = getDependenciesBlueprint();
|
||||
|
||||
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
|
||||
|
||||
deps.exec = (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[1];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
};
|
||||
|
||||
loadHttp(deps);
|
||||
|
||||
http.setHeader('*', 'myKey', 'myValue');
|
||||
http.get('url', {}, {}, noop, noop);
|
||||
});
|
||||
|
||||
it('resolves host headers correctly (set without port number) #37', () => {
|
||||
const deps = getDependenciesBlueprint();
|
||||
|
||||
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
|
||||
|
||||
deps.exec = (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[1];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
};
|
||||
|
||||
loadHttp(deps);
|
||||
|
||||
http.setHeader('www.google.de', 'myKey', 'myValue');
|
||||
http.get('https://www.google.de/?gws_rd=ssl', {}, {}, noop, noop);
|
||||
});
|
||||
|
||||
it('resolves host headers correctly (set with port number) #37', () => {
|
||||
const deps = getDependenciesBlueprint();
|
||||
|
||||
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
|
||||
|
||||
deps.exec = (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[1];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
};
|
||||
|
||||
loadHttp(deps);
|
||||
|
||||
http.setHeader('www.google.de:8080', 'myKey', 'myValue');
|
||||
http.get('https://www.google.de:8080/?gws_rd=ssl', {}, {}, noop, noop);
|
||||
});
|
||||
|
||||
it('resolves request headers correctly', () => {
|
||||
const deps = getDependenciesBlueprint();
|
||||
|
||||
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
|
||||
|
||||
deps.exec = (onSuccess, onFail, namespace, method, params) => {
|
||||
const headers = params[1];
|
||||
headers.should.eql({
|
||||
Cookie: 'fakeCookieString',
|
||||
myKey: 'myValue'
|
||||
});
|
||||
};
|
||||
|
||||
loadHttp(deps);
|
||||
|
||||
http.get('https://www.google.de/?gws_rd=ssl', {}, { myKey: 'myValue' }, noop, noop);
|
||||
});
|
||||
|
||||
it('sets basic authentication header correctly #36', () => {
|
||||
http.useBasicAuth('name', 'pass');
|
||||
http.getHeaders('*').Authorization.should.equal('Basic bmFtZTpwYXNz');
|
||||
});
|
||||
|
||||
it('throws an Error when you try to add a cookie by using "setHeader" #46', () => {
|
||||
(() => { http.setHeader('*', 'cookie', 'value'); }).should.throw(messages.ADDING_COOKIES_NOT_SUPPORTED);
|
||||
});
|
||||
|
||||
it('configures global timeout value correctly with given valid value', () => {
|
||||
http.setRequestTimeout(10);
|
||||
http.getRequestTimeout().should.equal(10);
|
||||
});
|
||||
|
||||
it('throws an Error when you try to configure global timeout with a string', () => {
|
||||
(() => { http.setRequestTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
|
||||
});
|
||||
|
||||
it('sets global option for following redirects correctly', () => {
|
||||
http.setFollowRedirect(false);
|
||||
http.getFollowRedirect().should.equal(false);
|
||||
});
|
||||
|
||||
it('throws an Error when you try to configure global option for following redirects with a string', () => {
|
||||
(() => { http.setFollowRedirect('myString'); }).should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
|
||||
});
|
||||
|
||||
it('exposes an enumeration style object with mappings for the error codes', () => {
|
||||
Object.keys(http.ErrorCode).forEach(key => http.ErrorCode[key].should.be.a('number'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('URL util', function () {
|
||||
const jsUtil = require('../www/js-util');
|
||||
const util = require('../www/url-util')(jsUtil);
|
||||
|
||||
it('parses URL with protocol, hostname and path correctly', () => {
|
||||
util.parseUrl('http://ilkimen.net/test').should.include({
|
||||
protocol: 'http:',
|
||||
host: 'ilkimen.net',
|
||||
hostname: 'ilkimen.net',
|
||||
pathname: '/test',
|
||||
port: '',
|
||||
search: '',
|
||||
hash: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('parses URL with protocol, hostname, port and path correctly', () => {
|
||||
util.parseUrl('http://ilkimen.net:8080/test').should.include({
|
||||
protocol: 'http:',
|
||||
host: 'ilkimen.net:8080',
|
||||
hostname: 'ilkimen.net',
|
||||
pathname: '/test',
|
||||
port: '8080',
|
||||
search: '',
|
||||
hash: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('parses URL with protocol, hostname, port, path and query string correctly', () => {
|
||||
util.parseUrl('http://ilkimen.net:8080/test?param=value').should.include({
|
||||
protocol: 'http:',
|
||||
host: 'ilkimen.net:8080',
|
||||
hostname: 'ilkimen.net',
|
||||
pathname: '/test',
|
||||
port: '8080',
|
||||
search: '?param=value',
|
||||
hash: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('parses URL with protocol, hostname, port, path, query string and hash param correctly', () => {
|
||||
util.parseUrl('http://ilkimen.net:8080/test?param=value#myHash').should.include({
|
||||
protocol: 'http:',
|
||||
host: 'ilkimen.net:8080',
|
||||
hostname: 'ilkimen.net',
|
||||
pathname: '/test',
|
||||
port: '8080',
|
||||
search: '?param=value',
|
||||
hash: '#myHash'
|
||||
});
|
||||
});
|
||||
|
||||
it('serializes query params correctly', () => {
|
||||
util.serializeQueryParams({
|
||||
strParam1: 'value with spaces',
|
||||
strParam2: 'value with special character äöü%',
|
||||
boolParam: true,
|
||||
numberParam: 1,
|
||||
nullParam: null,
|
||||
}, false).should.equal('strParam1=value with spaces&strParam2=value with special character äöü%&boolParam=true&numberParam=1&nullParam=null');
|
||||
});
|
||||
|
||||
it('serializes query params correctly with URL encoding enabled', () => {
|
||||
util.serializeQueryParams({
|
||||
'param 1': 'value with spaces',
|
||||
'param 2': 'value with special character äöü%&'
|
||||
}, true).should.equal('param%201=value%20with%20spaces¶m%202=value%20with%20special%20character%20%C3%A4%C3%B6%C3%BC%25%26');
|
||||
});
|
||||
|
||||
it('serializes array of query params correctly', () => {
|
||||
util.serializeQueryParams({
|
||||
myArray: ['val1', 'val2', 'val3'],
|
||||
myString: 'testString'
|
||||
}, false).should.equal('myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
|
||||
});
|
||||
|
||||
it('serializes deeply structured array of query params correctly', () => {
|
||||
util.serializeQueryParams({
|
||||
myArray: [['val1.1', 'val1.2', 'val1.3'], 'val2', 'val3'],
|
||||
myString: 'testString'
|
||||
}, false).should.equal('myArray[][]=val1.1&myArray[][]=val1.2&myArray[][]=val1.3&myArray[]=val2&myArray[]=val3&myString=testString');
|
||||
});
|
||||
|
||||
it('serializes deeply structured object of query params correctly', () => {
|
||||
util.serializeQueryParams({
|
||||
myObject: { obj1: { 'param1.1': 'val1.1', 'param1.2': 'val1.2' }, param2: 'val2' }
|
||||
}, false).should.equal('myObject[obj1][param1.1]=val1.1&myObject[obj1][param1.2]=val1.2&myObject[param2]=val2');
|
||||
});
|
||||
|
||||
it('appends query params string correctly to given URL without query parameters', () => {
|
||||
util.appendQueryParamsString('http://ilkimen.net/', 'param1=value1')
|
||||
.should.equal('http://ilkimen.net/?param1=value1');
|
||||
});
|
||||
|
||||
it('appends query params string correctly to given URL with existing query parameters', () => {
|
||||
util.appendQueryParamsString('http://ilkimen.net/?myParam=myValue', 'param1=value1')
|
||||
.should.equal('http://ilkimen.net/?myParam=myValue¶m1=value1');
|
||||
});
|
||||
|
||||
it('appends query params string correctly to given URL with existing query parameters and hash value', () => {
|
||||
util.appendQueryParamsString('http://ilkimen.net/?myParam=myValue#myHash', 'param1=value1')
|
||||
.should.equal('http://ilkimen.net/?myParam=myValue¶m1=value1#myHash');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Common helpers', function () {
|
||||
describe('mergeHeaders(globalHeaders, localHeaders)', function () {
|
||||
const init = require('../www/helpers');
|
||||
init.debug = true;
|
||||
|
||||
const helpers = init(null, null, null, null, null, null);
|
||||
|
||||
it('merges empty header sets correctly', () => {
|
||||
helpers.mergeHeaders({}, {}).should.eql({});
|
||||
});
|
||||
|
||||
it('merges simple header sets without collision correctly', () => {
|
||||
helpers.mergeHeaders({ a: 1 }, { b: 2 }).should.eql({ a: 1, b: 2 });
|
||||
});
|
||||
|
||||
it('merges header sets with collision correctly', () => {
|
||||
helpers.mergeHeaders({ a: 1 }, { a: 2 }).should.eql({ a: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCookieHeader(url)', function () {
|
||||
it('resolves cookie header correctly when no cookie is set #198', () => {
|
||||
const helpers = require('../www/helpers')(null, null, { getCookieString: () => '' }, null);
|
||||
|
||||
helpers.getCookieHeader('http://ilkimen.net').should.eql({});
|
||||
});
|
||||
|
||||
it('resolves cookie header correctly when a cookie is set', () => {
|
||||
const helpers = require('../www/helpers')(null, null, { getCookieString: () => 'cookie=value' }, null);
|
||||
|
||||
helpers.getCookieHeader('http://ilkimen.net').should.eql({ Cookie: 'cookie=value' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkClientAuthOptions()', function () {
|
||||
const jsUtil = require('../www/js-util');
|
||||
const messages = require('../www/messages');
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages);
|
||||
|
||||
it('returns options object with empty values when mode is "none" and no options are given', () => {
|
||||
helpers.checkClientAuthOptions('none').should.eql({
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('returns options object with empty values when mode is "none" and random options are given', () => {
|
||||
helpers.checkClientAuthOptions('none', {
|
||||
alias: 'myAlias',
|
||||
pkcsPath: 'myPath'
|
||||
}).should.eql({
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an error when mode is "systemstore" and alias is not a string or undefined', () => {
|
||||
(() => helpers.checkClientAuthOptions('systemstore', { alias: 1 }))
|
||||
.should.throw(messages.INVALID_CLIENT_AUTH_ALIAS);
|
||||
|
||||
(() => helpers.checkClientAuthOptions('systemstore', { alias: undefined }))
|
||||
.should.not.throw();
|
||||
});
|
||||
|
||||
it('returns an object with null alias when mode is "systemstore" and no options object is given', () => {
|
||||
helpers.checkClientAuthOptions('systemstore').should.eql({
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an error when mode is "buffer" and rawPkcs is not an array buffer', () => {
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
rawPkcs: undefined,
|
||||
pkcsPassword: 'password'
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
|
||||
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
pkcsPath: 1,
|
||||
pkcsPassword: 'password'
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
|
||||
});
|
||||
|
||||
it('throws an error when mode is "buffer" and pkcsPassword is not a string', () => {
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
rawPkcs: new ArrayBuffer(),
|
||||
pkcsPassword: undefined
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
|
||||
|
||||
(() => helpers.checkClientAuthOptions('buffer', {
|
||||
rawPkcs: new ArrayBuffer(),
|
||||
pkcsPassword: 1
|
||||
})).should.throw(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleMissingOptions()', function () {
|
||||
const jsUtil = require('../www/js-util');
|
||||
const messages = require('../www/messages');
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages);
|
||||
const mockGlobals = {
|
||||
headers: {},
|
||||
serializer: 'urlencoded',
|
||||
followRedirect: true,
|
||||
timeout: 60.0,
|
||||
}
|
||||
|
||||
it('adds missing "followRedirect" option correctly', () => {
|
||||
helpers.handleMissingOptions({}, mockGlobals).should.include({ followRedirect: true });
|
||||
});
|
||||
|
||||
it('throws an error when "followRedirect" option is not a boolean', () => {
|
||||
(() => helpers.handleMissingOptions({ followRedirect: 1 }, mockGlobals))
|
||||
.should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('injectRawResponseHandler()', function () {
|
||||
const jsUtil = require('../www/js-util');
|
||||
const messages = require('../www/messages');
|
||||
const errorCodes = require('../www/error-codes');
|
||||
|
||||
const fakeBase64 = { toArrayBuffer: () => 'fakeArrayBuffer' };
|
||||
|
||||
global.Blob = function (array, meta) {
|
||||
this.isFakeBlob = true;
|
||||
this.array = array;
|
||||
this.meta = meta;
|
||||
};
|
||||
|
||||
it('does not change response data if it is an ArrayBuffer', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, null, errorCodes);
|
||||
const buffer = new ArrayBuffer(5);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'arraybuffer',
|
||||
response => response.data.should.be.equal(buffer)
|
||||
);
|
||||
|
||||
handler({ data: buffer });
|
||||
});
|
||||
|
||||
it('does not change response data if it is a Blob', () => {
|
||||
const fakeJsUtil = { getTypeOf: () => 'Blob' };
|
||||
const helpers = require('../www/helpers')(null, fakeJsUtil, null, messages, null, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'blob',
|
||||
response => response.data.should.be.equal('fakeData')
|
||||
);
|
||||
|
||||
handler({ data: 'fakeData' });
|
||||
});
|
||||
|
||||
it('does not change response data if response type is "text"', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, null, errorCodes);
|
||||
const example = 'exampleText';
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'text',
|
||||
response => response.data.should.be.equal(example)
|
||||
);
|
||||
|
||||
handler({ data: example });
|
||||
});
|
||||
|
||||
it('handles response type "json" correctly', () => {
|
||||
const fakeData = { myString: 'bla', myNumber: 10 };
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, null, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'json',
|
||||
response => response.data.should.be.eql(fakeData)
|
||||
);
|
||||
|
||||
handler({ data: JSON.stringify(fakeData) });
|
||||
});
|
||||
|
||||
it('handles response type "arraybuffer" correctly', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, fakeBase64, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'arraybuffer',
|
||||
response => response.data.should.be.equal('fakeArrayBuffer')
|
||||
);
|
||||
|
||||
handler({ data: 'myString' });
|
||||
});
|
||||
|
||||
it('handles response type "blob" correctly', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, fakeBase64, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'blob',
|
||||
(response) => {
|
||||
response.data.isFakeBlob.should.be.equal(true);
|
||||
response.data.array.should.be.eql(['fakeArrayBuffer']);
|
||||
response.data.meta.type.should.be.equal('fakeType');
|
||||
}
|
||||
);
|
||||
|
||||
handler({ data: 'myString', headers: { 'content-type': 'fakeType'} });
|
||||
});
|
||||
|
||||
it('calls failure callback when post-processing fails', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, fakeBase64, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'json',
|
||||
null,
|
||||
(response) => {
|
||||
response.status.should.be.equal(errorCodes.POST_PROCESSING_FAILED);
|
||||
response.error.should.include('Unexpected token N in JSON at position 0');
|
||||
}
|
||||
);
|
||||
|
||||
handler({ data: 'NotValidJson' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkUploadFileOptions()', function () {
|
||||
const jsUtil = require('../www/js-util');
|
||||
const messages = require('../www/messages');
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, null, null);
|
||||
|
||||
it('checks valid file options correctly', () => {
|
||||
const opts = {
|
||||
filePaths: ['file://path/to/file.png'],
|
||||
names: ['ScreenCapture']
|
||||
};
|
||||
|
||||
// string values
|
||||
helpers.checkUploadFileOptions(opts.filePaths[0], opts.names[0]).should.be.eql(opts);
|
||||
// string array values
|
||||
helpers.checkUploadFileOptions(opts.filePaths, opts.names).should.be.eql(opts);
|
||||
});
|
||||
|
||||
it('throws an error when file options are missing', () => {
|
||||
(() => helpers.checkUploadFileOptions(undefined, ['ScreenCapture'])).should.throw(messages.FILE_PATHS_TYPE_MISMATCH);
|
||||
(() => helpers.checkUploadFileOptions(['file://path/to/file.png'], undefined)).should.throw(messages.NAMES_TYPE_MISMATCH);
|
||||
});
|
||||
|
||||
it('throws an error when file options contains empty arrays', () => {
|
||||
(() => helpers.checkUploadFileOptions([], ['ScreenCapture'])).should.throw(messages.EMPTY_FILE_PATHS);
|
||||
(() => helpers.checkUploadFileOptions(['file://path/to/file.png'], [])).should.throw(messages.EMPTY_NAMES);
|
||||
});
|
||||
|
||||
it('throws an error when file options contains invalid values', () => {
|
||||
(() => helpers.checkUploadFileOptions([1], ['ScreenCapture'])).should.throw(messages.FILE_PATHS_TYPE_MISMATCH);
|
||||
(() => helpers.checkUploadFileOptions(['file://path/to/file.png'], [1])).should.throw(messages.NAMES_TYPE_MISMATCH);
|
||||
});
|
||||
});
|
||||
|
||||
describe('processData()', function () {
|
||||
const mockWindow = {
|
||||
Blob: BlobMock,
|
||||
File: FileMock,
|
||||
FileReader: FileReaderMock,
|
||||
FormData: FormDataMock,
|
||||
TextEncoder: util.TextEncoder,
|
||||
}
|
||||
|
||||
const base64 = { fromArrayBuffer: ab => Buffer.from(ab).toString('base64') };
|
||||
const jsUtil = require('../www/js-util');
|
||||
const messages = require('../www/messages');
|
||||
const dependencyValidator = require('../www/dependency-validator')(mockWindow, null, messages);
|
||||
const helpers = require('../www/helpers')(mockWindow, jsUtil, null, messages, base64, null, dependencyValidator, {});
|
||||
|
||||
const testString = 'Test String öäüß 👍😉';
|
||||
const testStringBase64 = Buffer.from(testString).toString('base64');
|
||||
|
||||
it('throws an error when given data does not match allowed data types', () => {
|
||||
(() => helpers.processData('myString', 'urlencoded')).should.throw(messages.TYPE_MISMATCH_DATA);
|
||||
(() => helpers.processData('myString', 'json')).should.throw(messages.TYPE_MISMATCH_DATA);
|
||||
(() => helpers.processData({}, 'utf8')).should.throw(messages.TYPE_MISMATCH_DATA);
|
||||
});
|
||||
|
||||
it('throws an error when given data does not match allowed instance types', () => {
|
||||
(() => helpers.processData('myString', 'multipart')).should.throw(messages.INSTANCE_TYPE_MISMATCH_DATA);
|
||||
});
|
||||
|
||||
it('processes data correctly when serializer "utf8" is configured', (cb) => {
|
||||
helpers.processData('myString', 'utf8', (data) => {
|
||||
data.should.be.eql({text: 'myString'});
|
||||
cb();
|
||||
})
|
||||
});
|
||||
|
||||
it('processes data correctly when serializer "multipart" is configured and form data contains string value', (cb) => {
|
||||
const formData = new FormDataMock();
|
||||
formData.append('myString', testString);
|
||||
|
||||
helpers.processData(formData, 'multipart', (data) => {
|
||||
data.buffers.length.should.be.equal(1);
|
||||
data.names.length.should.be.equal(1);
|
||||
data.fileNames.length.should.be.equal(1);
|
||||
data.types.length.should.be.equal(1);
|
||||
|
||||
data.buffers[0].should.be.eql(testStringBase64);
|
||||
data.names[0].should.be.equal('myString');
|
||||
should.equal(data.fileNames[0], null);
|
||||
data.types[0].should.be.equal('text/plain');
|
||||
|
||||
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
it('processes data correctly when serializer "multipart" is configured and form data contains file value', (cb) => {
|
||||
const formData = new FormDataMock();
|
||||
formData.append('myFile', new BlobMock([testString], { type: 'application/octet-stream' }));
|
||||
|
||||
helpers.processData(formData, 'multipart', (data) => {
|
||||
data.buffers.length.should.be.equal(1);
|
||||
data.names.length.should.be.equal(1);
|
||||
data.fileNames.length.should.be.equal(1);
|
||||
data.types.length.should.be.equal(1);
|
||||
|
||||
data.buffers[0].should.be.eql(testStringBase64);
|
||||
data.names[0].should.be.equal('myFile');
|
||||
data.fileNames[0].should.be.equal('blob');
|
||||
data.types[0].should.be.equal('application/octet-stream');
|
||||
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
it('processes data correctly when serializer "raw" is configured', (cb) => {
|
||||
const byteArray = new Uint8Array([1,2,3]);
|
||||
helpers.processData(byteArray, 'raw', (data) => {
|
||||
data.should.be.a('ArrayBuffer');
|
||||
data.should.be.equal(byteArray.buffer);
|
||||
cb();
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dependency Validator', function () {
|
||||
const messages = require('../www/messages');
|
||||
|
||||
describe('logWarnings()', function () {
|
||||
it('logs a warning message if FormData API is not supported', function () {
|
||||
const console = new ConsoleMock();
|
||||
|
||||
require('../www/dependency-validator')({}, console, messages).logWarnings();
|
||||
|
||||
console.messageList.length.should.be.equal(1);
|
||||
console.messageList[0].type.should.be.equal('warn');
|
||||
console.messageList[0].message.should.be.eql([messages.MISSING_FORMDATA_API]);
|
||||
});
|
||||
|
||||
it('logs a warning message if FormData.entries() API is not supported', function () {
|
||||
const console = new ConsoleMock();
|
||||
|
||||
require('../www/dependency-validator')({ FormData: {} }, console, messages).logWarnings();
|
||||
|
||||
console.messageList.length.should.be.equal(1);
|
||||
console.messageList[0].type.should.be.equal('warn');
|
||||
console.messageList[0].message.should.be.eql([messages.MISSING_FORMDATA_ENTRIES_API]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkBlobApi()', function () {
|
||||
it('throws an error if Blob API is not supported', function () {
|
||||
const console = new ConsoleMock();
|
||||
const validator = require('../www/dependency-validator')({}, console, messages);
|
||||
|
||||
(() => validator.checkBlobApi()).should.throw(messages.MISSING_BLOB_API);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkFileReaderApi()', function () {
|
||||
it('throws an error if FileReader API is not supported', function () {
|
||||
const console = new ConsoleMock();
|
||||
const validator = require('../www/dependency-validator')({}, console, messages);
|
||||
|
||||
(() => validator.checkFileReaderApi()).should.throw(messages.MISSING_FILE_READER_API);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkFormDataInstance()', function () {
|
||||
it('throws an error if FormData.entries() is not supported on given instance', function () {
|
||||
const console = new ConsoleMock();
|
||||
const validator = require('../www/dependency-validator')({ FormData: {}}, console, messages);
|
||||
|
||||
(() => validator.checkFormDataInstance({})).should.throw(messages.MISSING_FORMDATA_ENTRIES_API);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkTextEncoderApi()', function () {
|
||||
it('throws an error if TextEncoder API is not supported', function () {
|
||||
const console = new ConsoleMock();
|
||||
const validator = require('../www/dependency-validator')({}, console, messages);
|
||||
|
||||
(() => validator.checkTextEncoderApi()).should.throw(messages.MISSING_TEXT_ENCODER_API);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ponyfills', function () {
|
||||
const mockWindow = {
|
||||
Blob: BlobMock,
|
||||
File: FileMock,
|
||||
};
|
||||
|
||||
const init = require('../www/ponyfills');
|
||||
init.debug = true;
|
||||
const ponyfills = init(mockWindow);
|
||||
|
||||
describe('Iterator', function () {
|
||||
it('exposes interface correctly', () => {
|
||||
const iterator = new ponyfills.Iterator([]);
|
||||
iterator.next.should.be.a('function');
|
||||
});
|
||||
|
||||
describe('next()', function () {
|
||||
it('returns iteration object correctly when list is empty', () => {
|
||||
const iterator = new ponyfills.Iterator([]);
|
||||
iterator.next().should.be.eql({ done: true, value: undefined });
|
||||
});
|
||||
|
||||
it('returns iteration object correctly when end posititon of list is not reached yet', () => {
|
||||
const iterator = new ponyfills.Iterator([['first', 'this is the first item']]);
|
||||
iterator.next().should.be.eql({ done: false, value: ['first', 'this is the first item'] });
|
||||
});
|
||||
|
||||
it('returns iteration object correctly when end posititon of list is already reached', () => {
|
||||
const iterator = new ponyfills.Iterator([['first', 'this is the first item']]);
|
||||
iterator.next();
|
||||
iterator.next().should.be.eql({ done: true, value: undefined });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('FormData', function () {
|
||||
it('exposes interface correctly', () => {
|
||||
const formData = new ponyfills.FormData();
|
||||
|
||||
formData.append.should.be.a('function');
|
||||
formData.entries.should.be.a('function');
|
||||
});
|
||||
|
||||
describe('append()', function () {
|
||||
it('appends string value correctly', () => {
|
||||
const formData = new ponyfills.FormData();
|
||||
|
||||
formData.append('test', 'myTestString');
|
||||
formData.__items[0].should.be.eql(['test', 'myTestString']);
|
||||
});
|
||||
|
||||
it('appends numeric value correctly', () => {
|
||||
const formData = new ponyfills.FormData();
|
||||
|
||||
formData.append('test', 10);
|
||||
formData.__items[0].should.be.eql(['test', '10']);
|
||||
formData.__items[0][1].should.be.a('string');
|
||||
});
|
||||
|
||||
it('appends Blob value correctly', () => {
|
||||
const formData = new ponyfills.FormData();
|
||||
const blob = new BlobMock(['another test'], { type: 'text/plain' });
|
||||
|
||||
formData.append('myBlob', blob, 'myFileName.txt');
|
||||
formData.__items[0].should.be.eql(['myBlob', blob]);
|
||||
formData.__items[0][1].name.should.be.equal('myFileName.txt');
|
||||
formData.__items[0][1].lastModifiedDate.should.be.a('Date');
|
||||
});
|
||||
|
||||
it('appends File value correctly', () => {
|
||||
const formData = new ponyfills.FormData();
|
||||
const blob = new BlobMock(['another test'], { type: 'text/plain' });
|
||||
const file = new FileMock(blob, 'myFileName.txt');
|
||||
|
||||
formData.append('myFile', file, 'myOverriddenFileName.txt');
|
||||
formData.__items[0].should.be.eql(['myFile', file]);
|
||||
formData.__items[0][1].name.should.be.equal('myFileName.txt');
|
||||
formData.__items[0][1].lastModifiedDate.should.be.eql(file.lastModifiedDate);
|
||||
});
|
||||
});
|
||||
|
||||
describe('entries()', function () {
|
||||
it('returns an iterator correctly', () => {
|
||||
const formData = new ponyfills.FormData();
|
||||
|
||||
formData.entries().should.be.an.instanceof(ponyfills.Iterator);
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
35
test/mocks/Blob.mock.js
Normal file
35
test/mocks/Blob.mock.js
Normal file
@@ -0,0 +1,35 @@
|
||||
module.exports = class BlobMock {
|
||||
constructor(blobParts, options) {
|
||||
if (blobParts instanceof BlobMock) {
|
||||
this._buffer = blobParts._buffer;
|
||||
} else {
|
||||
this._buffer = new Uint8Array(Buffer.concat(blobParts.map(part => Buffer.from(part, 'utf8')))).buffer;
|
||||
}
|
||||
|
||||
this._type = options.type || '';
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this._buffer.length;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
arrayBuffer() {
|
||||
throw new Error('Not implemented in BlobMock.');
|
||||
}
|
||||
|
||||
slice() {
|
||||
throw new Error('Not implemented in BlobMock.');
|
||||
}
|
||||
|
||||
stream() {
|
||||
throw new Error('Not implemented in BlobMock.');
|
||||
}
|
||||
|
||||
text() {
|
||||
throw new Error('Not implemented in BlobMock.');
|
||||
}
|
||||
}
|
||||
11
test/mocks/Console.mock.js
Normal file
11
test/mocks/Console.mock.js
Normal file
@@ -0,0 +1,11 @@
|
||||
module.exports = class ConsoleMock {
|
||||
constructor() {
|
||||
this.messageList = [];
|
||||
}
|
||||
|
||||
debug(...message) { this.messageList.push({ type: 'debug', message }); }
|
||||
error(...message) { this.messageList.push({ type: 'error', message }); }
|
||||
log(...message) { this.messageList.push({ type: 'log', message }); }
|
||||
info(...message) { this.messageList.push({ type: 'info', message }); }
|
||||
warn(...message) { this.messageList.push({ type: 'warn', message }); }
|
||||
}
|
||||
17
test/mocks/File.mock.js
Normal file
17
test/mocks/File.mock.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const BlobMock = require('./Blob.mock');
|
||||
|
||||
module.exports = class FileMock extends BlobMock {
|
||||
constructor(blob, fileName) {
|
||||
super(blob, { type: blob.type });
|
||||
this._fileName = fileName || '';
|
||||
this.__lastModifiedDate = new Date();
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._fileName;
|
||||
}
|
||||
|
||||
get lastModifiedDate() {
|
||||
return this.__lastModifiedDate;
|
||||
}
|
||||
}
|
||||
39
test/mocks/FileReader.mock.js
Normal file
39
test/mocks/FileReader.mock.js
Normal file
@@ -0,0 +1,39 @@
|
||||
module.exports = class FileReaderMock {
|
||||
constructor() {
|
||||
this.EMPTY = 0;
|
||||
this.LOADING = 1;
|
||||
this.DONE = 2;
|
||||
|
||||
this.error = null;
|
||||
this.onabort = () => {};
|
||||
this.onerror = () => {};
|
||||
this.onload = () => {};
|
||||
this.onloadend = () => {};
|
||||
this.onloadstart = () => {};
|
||||
this.onprogress = () => {};
|
||||
this.readyState = this.EMPTY;
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
readAsArrayBuffer(file) {
|
||||
this.readyState = this.LOADING;
|
||||
this.onloadstart();
|
||||
this.onprogress();
|
||||
this.result = file._buffer;
|
||||
this.readyState = this.DONE;
|
||||
this.onloadend();
|
||||
this.onload();
|
||||
}
|
||||
|
||||
readAsBinaryString() {
|
||||
throw new Error('Not implemented in FileReaderMock.');
|
||||
}
|
||||
|
||||
readAsDataUrl() {
|
||||
throw new Error('Not implemented in FileReaderMock.');
|
||||
}
|
||||
|
||||
readAsText() {
|
||||
throw new Error('Not implemented in FileReaderMock.');
|
||||
}
|
||||
}
|
||||
52
test/mocks/FormData.mock.js
Normal file
52
test/mocks/FormData.mock.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const BlobMock = require('./Blob.mock');
|
||||
const FileMock = require('./File.mock');
|
||||
|
||||
module.exports = class FormDataMock {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
|
||||
append(name, value, filename) {
|
||||
if (value instanceof BlobMock) {
|
||||
this.map.set(name, new FileMock(value, filename))
|
||||
} else {
|
||||
this.map.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
delete() {
|
||||
throw new Error('Not implemented in FormDataMock.');
|
||||
}
|
||||
|
||||
entries() {
|
||||
return this.map.entries();
|
||||
}
|
||||
|
||||
forEach(cb) {
|
||||
return this.map.forEach(cb);
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.map.get(key);
|
||||
}
|
||||
|
||||
getAll() {
|
||||
throw new Error('Not implemented in FormDataMock.');
|
||||
}
|
||||
|
||||
has(key) {
|
||||
return this.map.has(key);
|
||||
}
|
||||
|
||||
keys() {
|
||||
return this.map.keys();
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
return this.map.set(key, value);
|
||||
}
|
||||
|
||||
values() {
|
||||
return this.map.values();
|
||||
}
|
||||
};
|
||||
@@ -5,121 +5,21 @@
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
var cookieHandler = require(pluginId + '.cookie-handler');
|
||||
var helpers = require(pluginId + '.helpers');
|
||||
var base64 = require('cordova/base64');
|
||||
var messages = require(pluginId + '.messages');
|
||||
var errorCodes = require(pluginId + '.error-codes');
|
||||
var globalConfigs = require(pluginId + '.global-configs');
|
||||
var jsUtil = require(pluginId + '.js-util');
|
||||
var ToughCookie = require(pluginId + '.tough-cookie');
|
||||
var lodash = require(pluginId + '.lodash');
|
||||
var WebStorageCookieStore = require(pluginId + '.local-storage-store')(ToughCookie, lodash);
|
||||
var cookieHandler = require(pluginId + '.cookie-handler')(window.localStorage, ToughCookie, WebStorageCookieStore);
|
||||
var dependencyValidator = require(pluginId + '.dependency-validator')(window, window.console, messages);
|
||||
var ponyfills = require(pluginId + '.ponyfills')(window);
|
||||
var helpers = require(pluginId + '.helpers')(window, jsUtil, cookieHandler, messages, base64, errorCodes, dependencyValidator, ponyfills);
|
||||
var urlUtil = require(pluginId + '.url-util')(jsUtil);
|
||||
var publicInterface = require(pluginId + '.public-interface')(exec, cookieHandler, urlUtil, helpers, globalConfigs, errorCodes, ponyfills);
|
||||
|
||||
var globalConfigs = {
|
||||
headers: {},
|
||||
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 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];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
dependencyValidator.logWarnings();
|
||||
|
||||
module.exports = publicInterface;
|
||||
|
||||
@@ -1,73 +1,70 @@
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
var ToughCookie = require(pluginId + '.tough-cookie');
|
||||
var WebStorageCookieStore = require(pluginId + '.local-storage-store');
|
||||
module.exports = function init(storage, ToughCookie, WebStorageCookieStore) {
|
||||
var storeKey = '__advancedHttpCookieStore__';
|
||||
|
||||
var storage = window.localStorage;
|
||||
var storeKey = '__advancedHttpCookieStore__';
|
||||
var store = new WebStorageCookieStore(storage, storeKey);
|
||||
var cookieJar = new ToughCookie.CookieJar(store);
|
||||
|
||||
var store = new WebStorageCookieStore(storage, storeKey);
|
||||
var cookieJar = new ToughCookie.CookieJar(store);
|
||||
|
||||
module.exports = {
|
||||
return {
|
||||
setCookieFromString: setCookieFromString,
|
||||
setCookie: setCookie,
|
||||
getCookieString: getCookieString,
|
||||
clearCookies: clearCookies,
|
||||
removeCookies: removeCookies
|
||||
}
|
||||
};
|
||||
|
||||
function splitCookieString(cookieStr) {
|
||||
function splitCookieString(cookieStr) {
|
||||
var cookieParts = cookieStr.split(',');
|
||||
var splitCookies = [];
|
||||
var processedCookie = null;
|
||||
|
||||
for (var i = 0; i < cookieParts.length; ++i) {
|
||||
if (cookieParts[i].substr(-11, 8).toLowerCase() === 'expires=') {
|
||||
processedCookie = cookieParts[i] + ',' + cookieParts[i + 1];
|
||||
i++;
|
||||
} else {
|
||||
processedCookie = cookieParts[i];
|
||||
}
|
||||
if (cookieParts[i].substr(-11, 8).toLowerCase() === 'expires=') {
|
||||
processedCookie = cookieParts[i] + ',' + cookieParts[i + 1];
|
||||
i++;
|
||||
} else {
|
||||
processedCookie = cookieParts[i];
|
||||
}
|
||||
|
||||
processedCookie = processedCookie.trim();
|
||||
splitCookies.push(processedCookie);
|
||||
processedCookie = processedCookie.trim();
|
||||
splitCookies.push(processedCookie);
|
||||
}
|
||||
|
||||
return splitCookies;
|
||||
}
|
||||
}
|
||||
|
||||
function setCookieFromString(url, cookieStr) {
|
||||
function setCookieFromString(url, cookieStr) {
|
||||
if (!cookieStr) return;
|
||||
|
||||
var cookies = splitCookieString(cookieStr);
|
||||
|
||||
for (var i = 0; i < cookies.length; ++i) {
|
||||
cookieJar.setCookieSync(cookies[i], url, { ignoreError: true });
|
||||
cookieJar.setCookieSync(cookies[i], url, { ignoreError: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setCookie(url, cookie, options) {
|
||||
options = options || {};
|
||||
options.ignoreError = false;
|
||||
cookieJar.setCookieSync(cookie, url, options);
|
||||
}
|
||||
function setCookie(url, cookie, options) {
|
||||
options = options || {};
|
||||
options.ignoreError = false;
|
||||
cookieJar.setCookieSync(cookie, url, options);
|
||||
}
|
||||
|
||||
function getCookieString(url) {
|
||||
function getCookieString(url) {
|
||||
return cookieJar.getCookieStringSync(url);
|
||||
}
|
||||
}
|
||||
|
||||
function clearCookies() {
|
||||
function clearCookies() {
|
||||
window.localStorage.removeItem(storeKey);
|
||||
}
|
||||
}
|
||||
|
||||
function removeCookies(url, cb) {
|
||||
cookieJar.getCookies(url, function(error, cookies) {
|
||||
if (!cookies || cookies.length === 0) {
|
||||
function removeCookies(url, cb) {
|
||||
cookieJar.getCookies(url, function (error, cookies) {
|
||||
if (!cookies || cookies.length === 0) {
|
||||
return cb(null, []);
|
||||
}
|
||||
}
|
||||
|
||||
var domain = cookies[0].domain;
|
||||
var domain = cookies[0].domain;
|
||||
|
||||
cookieJar.store.removeCookies(domain, null, cb);
|
||||
cookieJar.store.removeCookies(domain, null, cb);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
43
www/dependency-validator.js
Normal file
43
www/dependency-validator.js
Normal file
@@ -0,0 +1,43 @@
|
||||
module.exports = function init(global, console, messages) {
|
||||
var interface = {
|
||||
checkBlobApi: checkBlobApi,
|
||||
checkFileReaderApi: checkFileReaderApi,
|
||||
checkFormDataInstance: checkFormDataInstance,
|
||||
checkTextEncoderApi: checkTextEncoderApi,
|
||||
logWarnings: logWarnings,
|
||||
};
|
||||
|
||||
return interface;
|
||||
|
||||
function logWarnings() {
|
||||
if (!global.FormData) {
|
||||
console.warn(messages.MISSING_FORMDATA_API);
|
||||
} else if (!global.FormData.prototype || !global.FormData.prototype.entries) {
|
||||
console.warn(messages.MISSING_FORMDATA_ENTRIES_API);
|
||||
}
|
||||
}
|
||||
|
||||
function checkBlobApi() {
|
||||
if (!global.Blob || !global.Blob.prototype) {
|
||||
throw new Error(messages.MISSING_BLOB_API);
|
||||
}
|
||||
}
|
||||
|
||||
function checkFileReaderApi() {
|
||||
if (!global.FileReader || !global.FileReader.prototype) {
|
||||
throw new Error(messages.MISSING_FILE_READER_API);
|
||||
}
|
||||
}
|
||||
|
||||
function checkFormDataInstance(instance) {
|
||||
if (!instance || !instance.entries) {
|
||||
throw new Error(messages.MISSING_FORMDATA_ENTRIES_API);
|
||||
}
|
||||
}
|
||||
|
||||
function checkTextEncoderApi() {
|
||||
if (!global.TextEncoder || !global.TextEncoder.prototype) {
|
||||
throw new Error(messages.MISSING_TEXT_ENCODER_API);
|
||||
}
|
||||
}
|
||||
};
|
||||
9
www/error-codes.js
Normal file
9
www/error-codes.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
GENERIC: -1,
|
||||
SSL_EXCEPTION: -2,
|
||||
SERVER_NOT_FOUND: -3,
|
||||
TIMEOUT: -4,
|
||||
UNSUPPORTED_URL: -5,
|
||||
NOT_CONNECTED: -6,
|
||||
POST_PROCESSING_FAILED: -7,
|
||||
};
|
||||
8
www/global-configs.js
Normal file
8
www/global-configs.js
Normal file
@@ -0,0 +1,8 @@
|
||||
var globalConfigs = {
|
||||
headers: {},
|
||||
serializer: 'urlencoded',
|
||||
followRedirect: true,
|
||||
timeout: 60.0,
|
||||
};
|
||||
|
||||
module.exports = globalConfigs;
|
||||
664
www/helpers.js
664
www/helpers.js
@@ -1,255 +1,493 @@
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
var cookieHandler = require(pluginId + '.cookie-handler');
|
||||
var messages = require(pluginId + '.messages');
|
||||
module.exports = function init(global, jsUtil, cookieHandler, messages, base64, errorCodes, dependencyValidator, ponyfills) {
|
||||
var validSerializers = ['urlencoded', 'json', 'utf8', 'raw', 'multipart'];
|
||||
var validCertModes = ['default', 'nocheck', 'pinned', 'legacy'];
|
||||
var validClientAuthModes = ['none', 'systemstore', 'buffer'];
|
||||
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'options', 'upload', 'download'];
|
||||
var validResponseTypes = ['text', 'json', 'arraybuffer', 'blob'];
|
||||
|
||||
var validSerializers = [ 'urlencoded', 'json', 'utf8' ];
|
||||
var validCertModes = [ 'default', 'nocheck', 'pinned' ];
|
||||
var validHttpMethods = [ 'get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download' ];
|
||||
var interface = {
|
||||
b64EncodeUnicode: b64EncodeUnicode,
|
||||
checkClientAuthMode: checkClientAuthMode,
|
||||
checkClientAuthOptions: checkClientAuthOptions,
|
||||
checkDownloadFilePath: checkDownloadFilePath,
|
||||
checkFollowRedirectValue: checkFollowRedirectValue,
|
||||
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
|
||||
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
|
||||
checkSerializer: checkSerializer,
|
||||
checkSSLCertMode: checkSSLCertMode,
|
||||
checkTimeoutValue: checkTimeoutValue,
|
||||
checkUploadFileOptions: checkUploadFileOptions,
|
||||
getMergedHeaders: getMergedHeaders,
|
||||
processData: processData,
|
||||
handleMissingCallbacks: handleMissingCallbacks,
|
||||
handleMissingOptions: handleMissingOptions,
|
||||
injectCookieHandler: injectCookieHandler,
|
||||
injectFileEntryHandler: injectFileEntryHandler,
|
||||
injectRawResponseHandler: injectRawResponseHandler,
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
b64EncodeUnicode: b64EncodeUnicode,
|
||||
getTypeOf: getTypeOf,
|
||||
checkSerializer: checkSerializer,
|
||||
checkSSLCertMode: checkSSLCertMode,
|
||||
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
|
||||
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
|
||||
injectCookieHandler: injectCookieHandler,
|
||||
injectFileEntryHandler: injectFileEntryHandler,
|
||||
getMergedHeaders: getMergedHeaders,
|
||||
getProcessedData: getProcessedData,
|
||||
handleMissingCallbacks: handleMissingCallbacks,
|
||||
handleMissingOptions: handleMissingOptions
|
||||
};
|
||||
// expose all functions for testing purposes
|
||||
if (init.debug) {
|
||||
interface.mergeHeaders = mergeHeaders;
|
||||
interface.checkForValidStringValue = checkForValidStringValue;
|
||||
interface.checkKeyValuePairObject = checkKeyValuePairObject;
|
||||
interface.checkHttpMethod = checkHttpMethod;
|
||||
interface.checkResponseType = checkResponseType;
|
||||
interface.checkHeadersObject = checkHeadersObject;
|
||||
interface.checkParamsObject = checkParamsObject;
|
||||
interface.resolveCookieString = resolveCookieString;
|
||||
interface.createFileEntry = createFileEntry;
|
||||
interface.getCookieHeader = getCookieHeader;
|
||||
interface.getMatchingHostHeaders = getMatchingHostHeaders;
|
||||
interface.getAllowedDataTypes = getAllowedDataTypes;
|
||||
}
|
||||
|
||||
// Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
|
||||
function b64EncodeUnicode(str) {
|
||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
||||
return String.fromCharCode('0x' + p1);
|
||||
}));
|
||||
}
|
||||
return interface;
|
||||
|
||||
function mergeHeaders(globalHeaders, localHeaders) {
|
||||
var globalKeys = Object.keys(globalHeaders);
|
||||
var key;
|
||||
// Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
|
||||
function b64EncodeUnicode(str) {
|
||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
||||
return String.fromCharCode('0x' + p1);
|
||||
}));
|
||||
}
|
||||
|
||||
for (var i = 0; i < globalKeys.length; i++) {
|
||||
key = globalKeys[i];
|
||||
function mergeHeaders(globalHeaders, localHeaders) {
|
||||
var globalKeys = Object.keys(globalHeaders);
|
||||
var key;
|
||||
|
||||
if (!localHeaders.hasOwnProperty(key)) {
|
||||
localHeaders[key] = globalHeaders[key];
|
||||
for (var i = 0; i < globalKeys.length; i++) {
|
||||
key = globalKeys[i];
|
||||
|
||||
if (!localHeaders.hasOwnProperty(key)) {
|
||||
localHeaders[key] = globalHeaders[key];
|
||||
}
|
||||
}
|
||||
|
||||
return localHeaders;
|
||||
}
|
||||
|
||||
return localHeaders;
|
||||
}
|
||||
function checkForValidStringValue(list, value, onInvalidValueMessage) {
|
||||
if (jsUtil.getTypeOf(value) !== 'String') {
|
||||
throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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++) {
|
||||
if (allowedChildren.indexOf(getTypeOf(obj[keys[i]])) === -1) {
|
||||
function checkKeyValuePairObject(obj, allowedChildren, onInvalidValueMessage) {
|
||||
if (jsUtil.getTypeOf(obj) !== 'Object') {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (allowedChildren.indexOf(jsUtil.getTypeOf(obj[keys[i]])) === -1) {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
function checkArray(array, allowedDataTypes, onInvalidValueMessage) {
|
||||
if (jsUtil.getTypeOf(array) !== 'Array') {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
|
||||
function checkHttpMethod(method) {
|
||||
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
|
||||
}
|
||||
for (var i = 0; i < array.length; ++i) {
|
||||
if (allowedDataTypes.indexOf(jsUtil.getTypeOf(array[i])) === -1) {
|
||||
throw new Error(onInvalidValueMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function checkSerializer(serializer) {
|
||||
return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_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 array;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
function checkForInvalidHeaderValue(value) {
|
||||
if (getTypeOf(value) !== 'String') {
|
||||
throw new Error(messages.INVALID_HEADERS_VALUE);
|
||||
function checkHttpMethod(method) {
|
||||
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function checkTimeoutValue(timeout) {
|
||||
if (getTypeOf(timeout) !== 'Number' || timeout < 0) {
|
||||
throw new Error(messages.INVALID_TIMEOUT_VALUE);
|
||||
function checkResponseType(type) {
|
||||
return checkForValidStringValue(validResponseTypes, type, messages.INVALID_RESPONSE_TYPE);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
function checkSerializer(serializer) {
|
||||
return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER);
|
||||
}
|
||||
|
||||
function checkHeadersObject(headers) {
|
||||
return checkKeyValuePairObject(headers, [ 'String' ], messages.INVALID_HEADERS_VALUE);
|
||||
}
|
||||
function checkSSLCertMode(mode) {
|
||||
return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
|
||||
}
|
||||
|
||||
function checkParamsObject(params) {
|
||||
return checkKeyValuePairObject(params, [ 'String', 'Array' ], messages.INVALID_PARAMS_VALUE);
|
||||
}
|
||||
function checkClientAuthMode(mode) {
|
||||
return checkForValidStringValue(validClientAuthModes, mode, messages.INVALID_CLIENT_AUTH_MODE);
|
||||
}
|
||||
|
||||
function resolveCookieString(headers) {
|
||||
var keys = Object.keys(headers || {});
|
||||
function checkClientAuthOptions(mode, options) {
|
||||
options = options || {};
|
||||
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
if (keys[i].match(/^set-cookie$/i)) {
|
||||
return headers[keys[i]];
|
||||
// none
|
||||
if (mode === validClientAuthModes[0]) {
|
||||
return {
|
||||
alias: null,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
};
|
||||
}
|
||||
|
||||
if (jsUtil.getTypeOf(options) !== 'Object') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_OPTIONS);
|
||||
}
|
||||
|
||||
// systemstore
|
||||
if (mode === validClientAuthModes[1]) {
|
||||
if (jsUtil.getTypeOf(options.alias) !== 'String'
|
||||
&& jsUtil.getTypeOf(options.alias) !== 'Undefined') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_ALIAS);
|
||||
}
|
||||
|
||||
return {
|
||||
alias: jsUtil.getTypeOf(options.alias) === 'Undefined' ? null : options.alias,
|
||||
rawPkcs: null,
|
||||
pkcsPassword: ''
|
||||
};
|
||||
}
|
||||
|
||||
// buffer
|
||||
if (mode === validClientAuthModes[2]) {
|
||||
if (jsUtil.getTypeOf(options.rawPkcs) !== 'ArrayBuffer') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
|
||||
}
|
||||
|
||||
if (jsUtil.getTypeOf(options.pkcsPassword) !== 'String') {
|
||||
throw new Error(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
|
||||
}
|
||||
|
||||
return {
|
||||
alias: null,
|
||||
rawPkcs: options.rawPkcs,
|
||||
pkcsPassword: options.pkcsPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
function checkForBlacklistedHeaderKey(key) {
|
||||
if (key.toLowerCase() === 'cookie') {
|
||||
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
function createFileEntry(rawEntry) {
|
||||
var entry = new (require('cordova-plugin-file.FileEntry'))();
|
||||
|
||||
entry.isDirectory = rawEntry.isDirectory;
|
||||
entry.isFile = rawEntry.isFile;
|
||||
entry.name = rawEntry.name;
|
||||
entry.fullPath = rawEntry.fullPath;
|
||||
entry.filesystem = new FileSystem(rawEntry.filesystemName || (rawEntry.filesystem == window.PERSISTENT ? 'persistent' : 'temporary'));
|
||||
entry.nativeURL = rawEntry.nativeURL;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
function injectCookieHandler(url, cb) {
|
||||
return function(response) {
|
||||
cookieHandler.setCookieFromString(url, resolveCookieString(response.headers));
|
||||
cb(response);
|
||||
}
|
||||
}
|
||||
|
||||
function injectFileEntryHandler(cb) {
|
||||
return function(response) {
|
||||
cb(createFileEntry(response.file));
|
||||
}
|
||||
}
|
||||
|
||||
function getCookieHeader(url) {
|
||||
return { Cookie: cookieHandler.getCookieString(url) };
|
||||
}
|
||||
|
||||
function getMatchingHostHeaders(url, headersList) {
|
||||
var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
|
||||
var domain = matches && matches[1];
|
||||
|
||||
return headersList[domain] || null;
|
||||
}
|
||||
|
||||
function getMergedHeaders(url, requestHeaders, predefinedHeaders) {
|
||||
var globalHeaders = predefinedHeaders['*'] || {};
|
||||
var hostHeaders = getMatchingHostHeaders(url, predefinedHeaders) || {};
|
||||
var mergedHeaders = mergeHeaders(globalHeaders, hostHeaders);
|
||||
|
||||
mergedHeaders = mergeHeaders(mergedHeaders, requestHeaders);
|
||||
mergedHeaders = mergeHeaders(mergedHeaders, getCookieHeader(url));
|
||||
|
||||
return mergedHeaders;
|
||||
}
|
||||
|
||||
// typeof is not working reliably in JS
|
||||
function getTypeOf(object) {
|
||||
switch (Object.prototype.toString.call(object)) {
|
||||
case '[object Array]':
|
||||
return 'Array';
|
||||
case '[object Boolean]':
|
||||
return 'Boolean';
|
||||
case '[object Function]':
|
||||
return 'Function';
|
||||
case '[object Null]':
|
||||
return 'Null';
|
||||
case '[object Number]':
|
||||
return 'Number';
|
||||
case '[object Object]':
|
||||
return 'Object';
|
||||
case '[object String]':
|
||||
return 'String';
|
||||
case '[object Undefined]':
|
||||
return 'Undefined';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
function getAllowedDataTypes(dataSerializer) {
|
||||
switch (dataSerializer) {
|
||||
case 'utf8':
|
||||
return ['String'];
|
||||
case 'urlencoded':
|
||||
return ['Object'];
|
||||
default:
|
||||
return ['Array', 'Object'];
|
||||
}
|
||||
}
|
||||
|
||||
function getProcessedData(data, dataSerializer) {
|
||||
data = data || {};
|
||||
|
||||
var currentDataType = getTypeOf(data);
|
||||
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
|
||||
|
||||
if (allowedDataTypes.indexOf(currentDataType) === -1) {
|
||||
throw new Error(messages.DATA_TYPE_MISMATCH + ' ' + allowedDataTypes.join(', '));
|
||||
return key;
|
||||
}
|
||||
|
||||
if (dataSerializer === 'utf8') {
|
||||
data = { text: data };
|
||||
function checkForInvalidHeaderValue(value) {
|
||||
if (jsUtil.getTypeOf(value) !== 'String') {
|
||||
throw new Error(messages.INVALID_HEADER_VALUE);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
function checkTimeoutValue(timeout) {
|
||||
if (jsUtil.getTypeOf(timeout) !== 'Number' || timeout < 0) {
|
||||
throw new Error(messages.INVALID_TIMEOUT_VALUE);
|
||||
}
|
||||
|
||||
function handleMissingCallbacks(successFn, failFn) {
|
||||
if (getTypeOf(successFn) !== 'Function') {
|
||||
throw new Error(messages.MANDATORY_SUCCESS);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
if (getTypeOf(failFn) !== 'Function') {
|
||||
throw new Error(messages.MANDATORY_FAIL);
|
||||
function checkFollowRedirectValue(follow) {
|
||||
if (jsUtil.getTypeOf(follow) !== 'Boolean') {
|
||||
throw new Error(messages.INVALID_FOLLOW_REDIRECT_VALUE);
|
||||
}
|
||||
|
||||
return follow;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMissingOptions(options, globals) {
|
||||
options = options || {};
|
||||
function checkHeadersObject(headers) {
|
||||
return checkKeyValuePairObject(headers, ['String'], messages.TYPE_MISMATCH_HEADERS);
|
||||
}
|
||||
|
||||
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 || ''
|
||||
};
|
||||
}
|
||||
function checkParamsObject(params) {
|
||||
return checkKeyValuePairObject(params, ['String', 'Array'], messages.TYPE_MISMATCH_PARAMS);
|
||||
}
|
||||
|
||||
function checkDownloadFilePath(filePath) {
|
||||
if (!filePath || jsUtil.getTypeOf(filePath) !== 'String') {
|
||||
throw new Error(messages.INVALID_DOWNLOAD_FILE_PATH);
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
function checkUploadFileOptions(filePaths, names) {
|
||||
if (jsUtil.getTypeOf(filePaths) === 'String') {
|
||||
filePaths = [filePaths];
|
||||
}
|
||||
|
||||
if (jsUtil.getTypeOf(names) === 'String') {
|
||||
names = [names];
|
||||
}
|
||||
|
||||
var opts = {
|
||||
filePaths: checkArray(filePaths, ['String'], messages.TYPE_MISMATCH_FILE_PATHS),
|
||||
names: checkArray(names, ['String'], messages.TYPE_MISMATCH_NAMES)
|
||||
};
|
||||
|
||||
if (!opts.filePaths.length) {
|
||||
throw new Error(messages.EMPTY_FILE_PATHS);
|
||||
}
|
||||
|
||||
if (!opts.names.length) {
|
||||
throw new Error(messages.EMPTY_NAMES);
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
function resolveCookieString(headers) {
|
||||
var keys = Object.keys(headers || {});
|
||||
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
if (keys[i].match(/^set-cookie$/i)) {
|
||||
return headers[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function createFileEntry(rawEntry) {
|
||||
var entry = new (require('cordova-plugin-file.FileEntry'))();
|
||||
|
||||
entry.isDirectory = rawEntry.isDirectory;
|
||||
entry.isFile = rawEntry.isFile;
|
||||
entry.name = rawEntry.name;
|
||||
entry.fullPath = rawEntry.fullPath;
|
||||
entry.filesystem = new FileSystem(rawEntry.filesystemName || (rawEntry.filesystem == global.PERSISTENT ? 'persistent' : 'temporary'));
|
||||
entry.nativeURL = rawEntry.nativeURL;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
function injectCookieHandler(url, cb) {
|
||||
return function (response) {
|
||||
cookieHandler.setCookieFromString(url, resolveCookieString(response.headers));
|
||||
cb(response);
|
||||
}
|
||||
}
|
||||
|
||||
function injectRawResponseHandler(responseType, success, failure) {
|
||||
return function (response) {
|
||||
var dataType = jsUtil.getTypeOf(response.data);
|
||||
|
||||
// don't need post-processing if it's already binary type (on browser platform)
|
||||
if (dataType === 'ArrayBuffer' || dataType === 'Blob') {
|
||||
return success(response);
|
||||
}
|
||||
|
||||
try {
|
||||
// json
|
||||
if (responseType === validResponseTypes[1]) {
|
||||
response.data = JSON.parse(response.data);
|
||||
}
|
||||
|
||||
// arraybuffer
|
||||
else if (responseType === validResponseTypes[2]) {
|
||||
response.data = base64.toArrayBuffer(response.data);
|
||||
}
|
||||
|
||||
// blob
|
||||
else if (responseType === validResponseTypes[3]) {
|
||||
var buffer = base64.toArrayBuffer(response.data);
|
||||
var type = response.headers['content-type'] || '';
|
||||
var blob = new Blob([ buffer ], { type: type });
|
||||
response.data = blob;
|
||||
}
|
||||
|
||||
success(response);
|
||||
} catch (error) {
|
||||
failure({
|
||||
status: errorCodes.POST_PROCESSING_FAILED,
|
||||
error: messages.POST_PROCESSING_FAILED + ' ' + error.message,
|
||||
url: response.url,
|
||||
headers: response.headers
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function injectFileEntryHandler(cb) {
|
||||
return function (response) {
|
||||
cb(createFileEntry(response.file));
|
||||
}
|
||||
}
|
||||
|
||||
function getCookieHeader(url) {
|
||||
var cookieString = cookieHandler.getCookieString(url);
|
||||
|
||||
if (cookieString.length) {
|
||||
return { Cookie: cookieHandler.getCookieString(url) };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function getMatchingHostHeaders(url, headersList) {
|
||||
var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
|
||||
var domain = matches && matches[1];
|
||||
|
||||
return headersList[domain] || null;
|
||||
}
|
||||
|
||||
function getMergedHeaders(url, requestHeaders, predefinedHeaders) {
|
||||
var globalHeaders = predefinedHeaders['*'] || {};
|
||||
var hostHeaders = getMatchingHostHeaders(url, predefinedHeaders) || {};
|
||||
var mergedHeaders = mergeHeaders(globalHeaders, hostHeaders);
|
||||
|
||||
mergedHeaders = mergeHeaders(mergedHeaders, requestHeaders);
|
||||
mergedHeaders = mergeHeaders(mergedHeaders, getCookieHeader(url));
|
||||
|
||||
return mergedHeaders;
|
||||
}
|
||||
|
||||
function getAllowedDataTypes(dataSerializer) {
|
||||
switch (dataSerializer) {
|
||||
case 'utf8':
|
||||
return ['String'];
|
||||
case 'urlencoded':
|
||||
return ['Object'];
|
||||
case 'json':
|
||||
return ['Array', 'Object'];
|
||||
case 'raw':
|
||||
return ['Uint8Array', 'ArrayBuffer'];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function getAllowedInstanceTypes(dataSerializer) {
|
||||
return dataSerializer === 'multipart' ? ['FormData'] : null;
|
||||
}
|
||||
|
||||
function processData(data, dataSerializer, cb) {
|
||||
var currentDataType = jsUtil.getTypeOf(data);
|
||||
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
|
||||
var allowedInstanceTypes = getAllowedInstanceTypes(dataSerializer);
|
||||
|
||||
if (allowedInstanceTypes) {
|
||||
var isCorrectInstanceType = false;
|
||||
|
||||
allowedInstanceTypes.forEach(function(type) {
|
||||
if ((global[type] && data instanceof global[type]) || (ponyfills[type] && data instanceof ponyfills[type])) {
|
||||
isCorrectInstanceType = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!isCorrectInstanceType) {
|
||||
throw new Error(messages.INSTANCE_TYPE_MISMATCH_DATA + ' ' + allowedInstanceTypes.join(', '));
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowedInstanceTypes && allowedDataTypes.indexOf(currentDataType) === -1) {
|
||||
throw new Error(messages.TYPE_MISMATCH_DATA + ' ' + allowedDataTypes.join(', '));
|
||||
}
|
||||
|
||||
switch (dataSerializer) {
|
||||
case 'utf8':
|
||||
return cb({ text: data });
|
||||
case 'raw':
|
||||
return cb(currentDataType === 'Uint8Array' ? data.buffer : data);
|
||||
case 'multipart':
|
||||
return processFormData(data, cb);
|
||||
default:
|
||||
return cb(data);
|
||||
}
|
||||
}
|
||||
|
||||
function processFormData(data, cb) {
|
||||
dependencyValidator.checkBlobApi();
|
||||
dependencyValidator.checkFileReaderApi();
|
||||
dependencyValidator.checkTextEncoderApi();
|
||||
dependencyValidator.checkFormDataInstance(data);
|
||||
|
||||
var textEncoder = new global.TextEncoder('utf8');
|
||||
var iterator = data.entries();
|
||||
|
||||
var result = {
|
||||
buffers: [],
|
||||
names: [],
|
||||
fileNames: [],
|
||||
types: []
|
||||
};
|
||||
|
||||
processFormDataIterator(iterator, textEncoder, result, cb);
|
||||
}
|
||||
|
||||
function processFormDataIterator(iterator, textEncoder, result, onFinished) {
|
||||
var entry = iterator.next();
|
||||
|
||||
if (entry.done) {
|
||||
return onFinished(result);
|
||||
}
|
||||
|
||||
if (entry.value[1] instanceof global.Blob || entry.value[1] instanceof global.File) {
|
||||
var reader = new global.FileReader();
|
||||
|
||||
reader.onload = function() {
|
||||
result.buffers.push(base64.fromArrayBuffer(reader.result));
|
||||
result.names.push(entry.value[0]);
|
||||
result.fileNames.push(entry.value[1].name || 'blob');
|
||||
result.types.push(entry.value[1].type || '');
|
||||
processFormDataIterator(iterator, textEncoder, result, onFinished);
|
||||
};
|
||||
|
||||
return reader.readAsArrayBuffer(entry.value[1]);
|
||||
}
|
||||
|
||||
if (jsUtil.getTypeOf(entry.value[1]) === 'String') {
|
||||
result.buffers.push(base64.fromArrayBuffer(textEncoder.encode(entry.value[1]).buffer));
|
||||
result.names.push(entry.value[0]);
|
||||
result.fileNames.push(null);
|
||||
result.types.push('text/plain');
|
||||
|
||||
return processFormDataIterator(iterator, textEncoder, result, onFinished)
|
||||
}
|
||||
|
||||
// skip items which are not supported
|
||||
processFormDataIterator(iterator, textEncoder, result, onFinished);
|
||||
}
|
||||
|
||||
function handleMissingCallbacks(successFn, failFn) {
|
||||
if (jsUtil.getTypeOf(successFn) !== 'Function') {
|
||||
throw new Error(messages.MANDATORY_SUCCESS);
|
||||
}
|
||||
|
||||
if (jsUtil.getTypeOf(failFn) !== 'Function') {
|
||||
throw new Error(messages.MANDATORY_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMissingOptions(options, globals) {
|
||||
options = options || {};
|
||||
|
||||
return {
|
||||
data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data,
|
||||
filePath: options.filePath,
|
||||
followRedirect: checkFollowRedirectValue(options.followRedirect || globals.followRedirect),
|
||||
headers: checkHeadersObject(options.headers || {}),
|
||||
method: checkHttpMethod(options.method || validHttpMethods[0]),
|
||||
name: options.name,
|
||||
params: checkParamsObject(options.params || {}),
|
||||
responseType: checkResponseType(options.responseType || validResponseTypes[0]),
|
||||
serializer: checkSerializer(options.serializer || globals.serializer),
|
||||
timeout: checkTimeoutValue(options.timeout || globals.timeout),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
32
www/js-util.js
Normal file
32
www/js-util.js
Normal file
@@ -0,0 +1,32 @@
|
||||
module.exports = {
|
||||
// typeof is not working reliably in JS
|
||||
getTypeOf: function (object) {
|
||||
switch (Object.prototype.toString.call(object)) {
|
||||
case '[object Array]':
|
||||
return 'Array';
|
||||
case '[object Blob]':
|
||||
return 'Blob';
|
||||
case '[object Uint8Array]':
|
||||
return 'Uint8Array';
|
||||
case '[object ArrayBuffer]':
|
||||
return 'ArrayBuffer';
|
||||
case '[object Boolean]':
|
||||
return 'Boolean';
|
||||
case '[object Function]':
|
||||
return 'Function';
|
||||
case '[object Null]':
|
||||
return 'Null';
|
||||
case '[object Number]':
|
||||
return 'Number';
|
||||
case '[object Object]':
|
||||
return 'Object';
|
||||
case '[object String]':
|
||||
return 'String';
|
||||
case '[object Undefined]':
|
||||
return 'Undefined';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,30 +30,27 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
|
||||
var ToughCookie = require(pluginId + '.tough-cookie');
|
||||
var _ = require(pluginId + '.lodash');
|
||||
|
||||
function WebStorageCookieStore(storage, storeKey) {
|
||||
module.exports = function init(ToughCookie, _) {
|
||||
function WebStorageCookieStore(storage, storeKey) {
|
||||
ToughCookie.Store.call(this);
|
||||
this._storage = storage || window.localStorage;
|
||||
this._storage = storage;
|
||||
this._storeKey = storeKey || '__cookieStore__';
|
||||
this.synchronous = true;
|
||||
}
|
||||
}
|
||||
|
||||
WebStorageCookieStore.prototype = Object.create(ToughCookie.Store);
|
||||
WebStorageCookieStore.prototype = Object.create(ToughCookie.Store);
|
||||
|
||||
WebStorageCookieStore.prototype.findCookie = function(domain, path, key, callback) {
|
||||
WebStorageCookieStore.prototype.findCookie = function (domain, path, key, callback) {
|
||||
var store = this._readStore();
|
||||
var cookie = _.get(store, [domain, path, key], null);
|
||||
|
||||
callback(null, ToughCookie.Cookie.fromJSON(cookie));
|
||||
};
|
||||
};
|
||||
|
||||
WebStorageCookieStore.prototype.findCookies = function(domain, path, callback) {
|
||||
WebStorageCookieStore.prototype.findCookies = function (domain, path, callback) {
|
||||
if (!domain) {
|
||||
callback(null, []);
|
||||
return;
|
||||
callback(null, []);
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
@@ -61,123 +58,124 @@ WebStorageCookieStore.prototype.findCookies = function(domain, path, callback) {
|
||||
var store = this._readStore();
|
||||
var domains = ToughCookie.permuteDomain(domain) || [domain];
|
||||
|
||||
domains.forEach(function(domain) {
|
||||
if (!store[domain]) {
|
||||
return;
|
||||
}
|
||||
domains.forEach(function (domain) {
|
||||
if (!store[domain]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var matchingPaths = Object.keys(store[domain]);
|
||||
var matchingPaths = Object.keys(store[domain]);
|
||||
|
||||
if (path != null) {
|
||||
matchingPaths = matchingPaths.filter(function(cookiePath) {
|
||||
return that._isOnPath(cookiePath, path);
|
||||
});
|
||||
}
|
||||
|
||||
matchingPaths.forEach(function(path) {
|
||||
Array.prototype.push.apply(cookies, _.values(store[domain][path]));
|
||||
if (path != null) {
|
||||
matchingPaths = matchingPaths.filter(function (cookiePath) {
|
||||
return that._isOnPath(cookiePath, path);
|
||||
});
|
||||
}
|
||||
|
||||
matchingPaths.forEach(function (path) {
|
||||
Array.prototype.push.apply(cookies, _.values(store[domain][path]));
|
||||
});
|
||||
});
|
||||
|
||||
cookies = cookies.map(function(cookie) {
|
||||
return ToughCookie.Cookie.fromJSON(cookie);
|
||||
cookies = cookies.map(function (cookie) {
|
||||
return ToughCookie.Cookie.fromJSON(cookie);
|
||||
});
|
||||
|
||||
callback(null, cookies);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether `cookiePath` is on the given `urlPath`
|
||||
*/
|
||||
WebStorageCookieStore.prototype._isOnPath = function(cookiePath, urlPath) {
|
||||
/**
|
||||
* Returns whether `cookiePath` is on the given `urlPath`
|
||||
*/
|
||||
WebStorageCookieStore.prototype._isOnPath = function (cookiePath, urlPath) {
|
||||
if (!cookiePath) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cookiePath === urlPath) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (urlPath.indexOf(cookiePath) !== 0) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cookiePath[cookiePath.length - 1] !== '/' && urlPath[cookiePath.length] !== '/') {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
WebStorageCookieStore.prototype.putCookie = function(cookie, callback) {
|
||||
var store = this._readStore();
|
||||
WebStorageCookieStore.prototype.putCookie = function (cookie, callback) {
|
||||
var store = this._readStore();
|
||||
|
||||
_.set(store, [cookie.domain, cookie.path, cookie.key], cookie);
|
||||
this._writeStore(store);
|
||||
callback(null);
|
||||
};
|
||||
_.set(store, [cookie.domain, cookie.path, cookie.key], cookie);
|
||||
this._writeStore(store);
|
||||
callback(null);
|
||||
};
|
||||
|
||||
WebStorageCookieStore.prototype.updateCookie = function(oldCookie, newCookie, callback) {
|
||||
WebStorageCookieStore.prototype.updateCookie = function (oldCookie, newCookie, callback) {
|
||||
this.putCookie(newCookie, callback);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
WebStorageCookieStore.prototype.removeCookie = function(domain, path, key, callback) {
|
||||
WebStorageCookieStore.prototype.removeCookie = function (domain, path, key, callback) {
|
||||
var store = this._readStore();
|
||||
|
||||
_.unset(store, [domain, path, key]);
|
||||
this._writeStore(store);
|
||||
callback(null);
|
||||
};
|
||||
};
|
||||
|
||||
WebStorageCookieStore.prototype.removeCookies = function(domain, path, callback) {
|
||||
WebStorageCookieStore.prototype.removeCookies = function (domain, path, callback) {
|
||||
var store = this._readStore();
|
||||
|
||||
if (path == null) {
|
||||
_.unset(store, [domain]);
|
||||
_.unset(store, [domain]);
|
||||
} else {
|
||||
_.unset(store, [domain, path]);
|
||||
_.unset(store, [domain, path]);
|
||||
}
|
||||
|
||||
this._writeStore(store);
|
||||
callback(null);
|
||||
};
|
||||
};
|
||||
|
||||
WebStorageCookieStore.prototype.getAllCookies = function(callback) {
|
||||
WebStorageCookieStore.prototype.getAllCookies = function (callback) {
|
||||
var cookies = [];
|
||||
var store = this._readStore();
|
||||
|
||||
Object.keys(store).forEach(function(domain) {
|
||||
Object.keys(store[domain]).forEach(function(path) {
|
||||
Array.protype.push.apply(cookies, _.values(store[domain][path]));
|
||||
});
|
||||
Object.keys(store).forEach(function (domain) {
|
||||
Object.keys(store[domain]).forEach(function (path) {
|
||||
Array.prototype.push.apply(cookies, _.values(store[domain][path]));
|
||||
});
|
||||
});
|
||||
|
||||
cookies = cookies.map(function(cookie) {
|
||||
return ToughCookie.Cookie.fromJSON(cookie);
|
||||
cookies = cookies.map(function (cookie) {
|
||||
return ToughCookie.Cookie.fromJSON(cookie);
|
||||
});
|
||||
|
||||
cookies.sort(function(c1, c2) {
|
||||
return (c1.creationIndex || 0) - (c2.creationIndex || 0);
|
||||
cookies.sort(function (c1, c2) {
|
||||
return (c1.creationIndex || 0) - (c2.creationIndex || 0);
|
||||
});
|
||||
|
||||
callback(null, cookies);
|
||||
};
|
||||
};
|
||||
|
||||
WebStorageCookieStore.prototype._readStore = function() {
|
||||
WebStorageCookieStore.prototype._readStore = function () {
|
||||
var json = this._storage.getItem(this._storeKey);
|
||||
|
||||
if (json !== null) {
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (e) { }
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
};
|
||||
|
||||
WebStorageCookieStore.prototype._writeStore = function(store) {
|
||||
WebStorageCookieStore.prototype._writeStore = function (store) {
|
||||
this._storage.setItem(this._storeKey, JSON.stringify(store));
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = WebStorageCookieStore;
|
||||
return WebStorageCookieStore;
|
||||
};
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
module.exports = {
|
||||
ADDING_COOKIES_NOT_SUPPORTED: 'advanced-http: "setHeader" does not support adding cookies, please use "setCookie" function instead',
|
||||
DATA_TYPE_MISMATCH: 'advanced-http: "data" argument supports only following data types:',
|
||||
MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
|
||||
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
|
||||
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
|
||||
EMPTY_FILE_PATHS: 'advanced-http: "filePaths" option array must not be empty, <filePaths: string[]>',
|
||||
EMPTY_NAMES: 'advanced-http: "names" option array must not be empty, <names: string[]>',
|
||||
INSTANCE_TYPE_MISMATCH_DATA: 'advanced-http: "data" option is configured to support only following instance types:',
|
||||
INVALID_CLIENT_AUTH_ALIAS: 'advanced-http: invalid client certificate alias, needs to be a string or undefined, <alias: string | undefined>',
|
||||
INVALID_CLIENT_AUTH_MODE: 'advanced-http: invalid client certificate authentication mode, supported modes are:',
|
||||
INVALID_CLIENT_AUTH_OPTIONS: 'advanced-http: invalid client certificate authentication options, needs to be an dictionary style object',
|
||||
INVALID_CLIENT_AUTH_PKCS_PASSWORD: 'advanced-http: invalid PKCS12 container password, needs to be a string, <pkcsPassword: string>',
|
||||
INVALID_CLIENT_AUTH_RAW_PKCS: 'advanced-http: invalid PKCS12 container, needs to be an array buffer, <rawPkcs: ArrayBuffer>',
|
||||
INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
|
||||
INVALID_DOWNLOAD_FILE_PATH: 'advanced-http: invalid "filePath" value, needs to be a string, <filePath: string>',
|
||||
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value, <followRedirect: boolean>',
|
||||
INVALID_HEADER_VALUE: 'advanced-http: invalid header value, needs to be a string, <header: string>',
|
||||
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
|
||||
INVALID_RESPONSE_TYPE: 'advanced-http: invalid response type, supported types are:',
|
||||
INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
|
||||
INVALID_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'
|
||||
INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value, <timeout: number>',
|
||||
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
|
||||
MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
|
||||
MISSING_BLOB_API: 'advanced-http: Blob API is not supported in this webview. If you want to use "multipart/form-data" requests, you need to load a polyfill library before loading this plugin. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.',
|
||||
MISSING_FILE_READER_API: 'advanced-http: FileReader API is not supported in this webview. If you want to use "multipart/form-data" requests, you need to load a polyfill library before loading this plugin. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.',
|
||||
MISSING_FORMDATA_API: 'advanced-http: FormData API is not supported in this webview. If you want to use "multipart/form-data" requests, you need to load a polyfill library before loading this plugin. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.',
|
||||
MISSING_FORMDATA_ENTRIES_API: 'advanced-http: Given instance of FormData does not implement FormData API specification correctly, FormData.entries() is missing. If you want to use "multipart/form-data" requests, you can use an included ponyfill. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.',
|
||||
MISSING_TEXT_ENCODER_API: 'advanced-http: TextEncoder API is not supported in this webview. If you want to use "multipart/form-data" requests, you need to load a polyfill library before loading this plugin. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.',
|
||||
POST_PROCESSING_FAILED: 'advanced-http: an error occured during post processing response:',
|
||||
TYPE_MISMATCH_DATA: 'advanced-http: "data" option is configured to support only following data types:',
|
||||
TYPE_MISMATCH_FILE_PATHS: 'advanced-http: "filePaths" option needs to be an string array, <filePaths: string[]>',
|
||||
TYPE_MISMATCH_HEADERS: 'advanced-http: "headers" option needs to be an dictionary style object with string values, <headers: {[key: string]: string}>',
|
||||
TYPE_MISMATCH_NAMES: 'advanced-http: "names" option needs to be an string array, <names: string[]>',
|
||||
TYPE_MISMATCH_PARAMS: 'advanced-http: "params" option needs to be an dictionary style object, <params: {[key: string]: string | string[]}>',
|
||||
};
|
||||
|
||||
47
www/ponyfills.js
Normal file
47
www/ponyfills.js
Normal file
@@ -0,0 +1,47 @@
|
||||
module.exports = function init(global) {
|
||||
var interface = { FormData: FormData };
|
||||
|
||||
// expose all constructor functions for testing purposes
|
||||
if (init.debug) {
|
||||
interface.Iterator = Iterator;
|
||||
}
|
||||
|
||||
function FormData() {
|
||||
this.__items = [];
|
||||
}
|
||||
|
||||
FormData.prototype.append = function(name, value, filename) {
|
||||
if (global.File && value instanceof global.File) {
|
||||
// nothing to do
|
||||
} else if (global.Blob && value instanceof global.Blob) {
|
||||
// mimic File instance by adding missing properties
|
||||
value.lastModifiedDate = new Date();
|
||||
value.name = filename || '';
|
||||
} else {
|
||||
value = value.toString ? value.toString() : value;
|
||||
}
|
||||
|
||||
this.__items.push([ name, value ]);
|
||||
};
|
||||
|
||||
FormData.prototype.entries = function() {
|
||||
return new Iterator(this.__items);
|
||||
};
|
||||
|
||||
function Iterator(items) {
|
||||
this.__items = items;
|
||||
this.__position = -1;
|
||||
}
|
||||
|
||||
Iterator.prototype.next = function() {
|
||||
this.__position += 1;
|
||||
|
||||
if (this.__position < this.__items.length) {
|
||||
return { done: false, value: this.__items[this.__position] };
|
||||
}
|
||||
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
|
||||
return interface;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user