mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-01-31 00:00:03 +08:00
Compare commits
360 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f5b0f5b49 | ||
|
|
af5e06902e | ||
|
|
73f37d6a35 | ||
|
|
3678f5f6e1 | ||
|
|
41861aa2ec | ||
|
|
91573159e3 | ||
|
|
b4ff7735de | ||
|
|
60c042164a | ||
|
|
342022d3a7 | ||
|
|
dbe28b284f | ||
|
|
fe1a0fdbf7 | ||
|
|
32a250fbd5 | ||
|
|
cb84860bb1 | ||
|
|
7f5345eafd | ||
|
|
54cbb840e2 | ||
|
|
866034ea18 | ||
|
|
6615f1bf53 | ||
|
|
3ee42a41e4 | ||
|
|
8acefd2791 | ||
|
|
f6adf1e920 | ||
|
|
0c26ada847 | ||
|
|
3d20ab6b8c | ||
|
|
6fc8a7e06d | ||
|
|
00dc85cdac | ||
|
|
c86874a01a | ||
|
|
86fc3892a4 | ||
|
|
469ace48b5 | ||
|
|
b1ad996585 | ||
|
|
1218a57e5f | ||
|
|
320ed35022 | ||
|
|
845f90d43f | ||
|
|
f6c3ea7f66 | ||
|
|
d52eb08982 | ||
|
|
57c87db5a1 | ||
|
|
8ea1f3aed6 | ||
|
|
e41684cc79 | ||
|
|
d393d3ca78 | ||
|
|
479c644b1d | ||
|
|
bfa446c7fd | ||
|
|
c92f07775b | ||
|
|
9870dde50a | ||
|
|
ed08534cf3 | ||
|
|
ea15617fae | ||
|
|
87c915f7c0 | ||
|
|
1f4ccd37b4 | ||
|
|
343c536a3b | ||
|
|
38da25bb75 | ||
|
|
42175e27ad | ||
|
|
43b65fa887 | ||
|
|
48445786b6 | ||
|
|
08ec3203e9 | ||
|
|
9684c03685 | ||
|
|
8f3cfb11d0 | ||
| d48512c8df | |||
| ee2d727c2b | |||
| 0ae3756a74 | |||
|
|
935e7d6ba3 | ||
|
|
5b8f20e1c4 | ||
|
|
b3b97306f4 | ||
|
|
6a9a22f81e | ||
|
|
b20faa9c3c | ||
|
|
d66b4b7645 | ||
|
|
fb77849d10 | ||
|
|
e2919e51e1 | ||
|
|
ad1d500eae | ||
|
|
36d7e1813c | ||
|
|
1bafe6cdd9 | ||
|
|
1dd5ee59c8 | ||
|
|
6f68aab736 | ||
|
|
c2a2ebc31b | ||
|
|
c9bff478d8 | ||
|
|
9ff32f1892 | ||
|
|
16ac763a2a | ||
|
|
80315156ad | ||
|
|
5499f2a8b8 | ||
|
|
9d870e7748 | ||
|
|
b6a8acd25b | ||
|
|
c2a0317b8f | ||
|
|
0ccf64e289 | ||
|
|
986ddeefd4 | ||
|
|
67c2c733f5 | ||
|
|
d077d02062 | ||
|
|
060794354d | ||
|
|
4ebd259c7e | ||
|
|
bbd4cf01ab | ||
|
|
7eb3395aa4 | ||
|
|
05abdfcd91 | ||
|
|
bda4eedfb9 | ||
|
|
9d6005af29 | ||
|
|
94126b9021 | ||
|
|
007d5c609f | ||
|
|
badf6dcdc2 | ||
|
|
c081060a9e | ||
|
|
2ce130133c | ||
|
|
cfcc572ca0 | ||
|
|
7c1836e87f | ||
|
|
87ddbbe3b1 | ||
|
|
d7688b485d | ||
|
|
81ba667e37 | ||
|
|
5ad1967b46 | ||
|
|
90ed474b29 | ||
|
|
1947906c4c | ||
|
|
b25b07a2a7 | ||
|
|
a01625ecfb | ||
|
|
b03ae7d6d1 | ||
|
|
fdaea418be | ||
|
|
d84df0fb00 | ||
|
|
baedd60329 | ||
|
|
4687bad20d | ||
|
|
80b22d4202 | ||
|
|
89ac260ef6 | ||
|
|
6a266218fb | ||
|
|
6050830423 | ||
|
|
95c9eb85d0 | ||
|
|
7e3ff25195 | ||
|
|
8a6b9e583e | ||
|
|
b32dfc7143 | ||
|
|
a72bf758c6 | ||
|
|
45f9cbfaa4 | ||
|
|
f8f52e1e97 | ||
|
|
9b995724f4 | ||
|
|
25a0a9a7ae | ||
|
|
e44def06a5 | ||
|
|
3d288951bf | ||
|
|
f39063cec9 | ||
|
|
1bcb8016ff | ||
|
|
6339b9b83d | ||
|
|
389534d661 | ||
|
|
c10722eca0 | ||
|
|
6a60058fc6 | ||
|
|
fcedfae1ac | ||
|
|
aca165b900 | ||
|
|
09c2b383ff | ||
|
|
6918a2ed15 | ||
|
|
5b827d500d | ||
|
|
1c27b62148 | ||
|
|
f33a911e7e | ||
|
|
097faad07a | ||
|
|
62c400c6db | ||
|
|
f823d24438 | ||
|
|
64a7148444 | ||
|
|
f6736d9150 | ||
|
|
bc90ae85fb | ||
|
|
389e860125 | ||
|
|
2367d264c1 | ||
|
|
269d5d4c8a | ||
|
|
b6ee4de379 | ||
|
|
5f327dc82a | ||
|
|
1639efe8d0 | ||
|
|
9bb0c58e35 | ||
|
|
57562a0dcf | ||
|
|
ad4079625e | ||
|
|
98d3d38e07 | ||
|
|
c748406090 | ||
|
|
dc6cf4d45b | ||
|
|
7661e02598 | ||
|
|
20ec4bee31 | ||
|
|
4eed89e530 | ||
|
|
f43b2f9f5c | ||
|
|
7b4d37acd9 | ||
|
|
ca5306cb47 | ||
|
|
c0c1af5ee6 | ||
|
|
42e629e34d | ||
|
|
3bec8dde5f | ||
|
|
8a3bc17810 | ||
|
|
1eb83478dc | ||
|
|
1f0df54111 | ||
|
|
ef09e466ec | ||
|
|
88de0550f4 | ||
|
|
7f680b07ec | ||
|
|
a259e7cf8d | ||
|
|
1e3c9e6645 | ||
|
|
e0cf458179 | ||
|
|
4f4e7ffa33 | ||
|
|
65e4a4d4dc | ||
|
|
fed2b03cc6 | ||
|
|
29e0b385de | ||
|
|
bfc6ba2008 | ||
|
|
060aa088f5 | ||
|
|
e775057389 | ||
|
|
81874c6bc8 | ||
|
|
658bbd8b28 | ||
|
|
a1e4be37d4 | ||
|
|
5be52d78d1 | ||
|
|
0a23e29403 | ||
|
|
6009ae82f7 | ||
|
|
0bfb81c57f | ||
|
|
6797d2c3e0 | ||
|
|
f8f4bdc9df | ||
|
|
d84e3995d2 | ||
|
|
f5597dd176 | ||
|
|
b25b7db4be | ||
|
|
92be8f8e96 | ||
|
|
9ffdc5d2eb | ||
|
|
23a98ae491 | ||
|
|
ab9dad8f46 | ||
|
|
69344b5357 | ||
|
|
c88d1b6cc7 | ||
|
|
b6f369b868 | ||
|
|
39fa17e4ed | ||
|
|
5a19c6ad06 | ||
|
|
192059d34e | ||
|
|
7193b636f1 | ||
|
|
662b4352f5 | ||
|
|
e18f9eee51 | ||
|
|
c7eb60e670 | ||
|
|
6a930de82f | ||
|
|
588e4a0e57 | ||
|
|
78db1dc516 | ||
|
|
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 | ||
|
|
c387e52ea3 | ||
|
|
f87afa2217 | ||
|
|
516aa6b61d | ||
|
|
a6bf9041a5 | ||
|
|
c41fc11888 | ||
|
|
91515d30bd | ||
|
|
c8638ff204 | ||
|
|
0acf2e2574 | ||
|
|
f4674028b5 | ||
|
|
5919d90698 | ||
|
|
27e959800a | ||
|
|
96f45d7274 | ||
|
|
3dbd07c9df | ||
|
|
cb597c0d30 | ||
|
|
60189a68b3 | ||
|
|
8e4bfdbc70 | ||
|
|
2ae4c7cf39 | ||
|
|
32fdf49d31 | ||
|
|
db0f233737 |
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
temp
|
||||
platforms
|
||||
**/*.min.js
|
||||
www/umd-tough-cookie.js
|
||||
www/lodash.js
|
||||
25
.eslintrc.json
Normal file
25
.eslintrc.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es2020": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "script"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"test/**/*.js",
|
||||
"www/**/*.js"
|
||||
],
|
||||
"env": {
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: silkimen
|
||||
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!
|
||||
166
.github/workflows/ci.yml
vendored
Normal file
166
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
name: Cordova HTTP Plugin CI
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ci_reason:
|
||||
description: "Optional note for manual runs"
|
||||
required: false
|
||||
|
||||
env:
|
||||
node_version: "22.x"
|
||||
java_distribution: "zulu"
|
||||
java_version: "17"
|
||||
gradle_version: "7.6.1"
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Run ESLint
|
||||
run: npm run lint
|
||||
|
||||
test-www-interface:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Run WWW interface tests
|
||||
run: npm run test:js
|
||||
|
||||
build-ios:
|
||||
runs-on: macOS-latest
|
||||
outputs:
|
||||
run-tests: ${{ steps.should-run-tests.outputs.run_tests }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- id: should-run-tests
|
||||
# need to find a solution for signing iOS App so we can build for device target instead simulator
|
||||
# for now we skip iOS tests on BrowserStack
|
||||
run: echo "run_tests=false" >> "$GITHUB_OUTPUT"
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run update:cert
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --ios --emulator
|
||||
- name: Upload artifact to BrowserStack
|
||||
if: steps.should-run-tests.outputs.run_tests == 'true'
|
||||
run: scripts/upload-browserstack.sh --ios
|
||||
|
||||
test-ios:
|
||||
needs: build-ios
|
||||
if: needs.build-ios.outputs.run-tests == 'true'
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Run e2e tests (iOS)
|
||||
run: scripts/test-app.sh --ios --device
|
||||
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
run-tests: ${{ steps.should-run-tests.outputs.run_tests }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- id: should-run-tests
|
||||
run: |
|
||||
if [ -n "${BROWSERSTACK_USERNAME}" ]; then
|
||||
echo "run_tests=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "run_tests=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Install JDK
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: ${{ env.java_distribution }}
|
||||
java-version: ${{ env.java_version }}
|
||||
java-package: jdk
|
||||
- name: Ensure Android build-tools 33.0.2
|
||||
run: yes | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "build-tools;33.0.2"
|
||||
- name: Install Gradle
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl -sSL "https://services.gradle.org/distributions/gradle-${{ env.gradle_version }}-bin.zip" -o gradle.zip
|
||||
unzip -q gradle.zip -d "$HOME/gradle"
|
||||
echo "$HOME/gradle/gradle-${{ env.gradle_version }}/bin" >> "$GITHUB_PATH"
|
||||
rm gradle.zip
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run update:cert
|
||||
- name: Shim missing DX binaries (https://stackoverflow.com/a/68430992)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
BUILD_TOOLS_PATH="$(ls -d $ANDROID_HOME/build-tools/*/ | sort -V | tail -n 1)"
|
||||
D8_BIN="${BUILD_TOOLS_PATH}d8"
|
||||
DX_BIN="${BUILD_TOOLS_PATH}dx"
|
||||
D8_JAR="${BUILD_TOOLS_PATH}lib/d8.jar"
|
||||
DX_JAR="${BUILD_TOOLS_PATH}lib/dx.jar"
|
||||
if [ -f "$D8_BIN" ] && [ ! -e "$DX_BIN" ]; then
|
||||
ln -s "$D8_BIN" "$DX_BIN"
|
||||
fi
|
||||
if [ -f "$D8_JAR" ] && [ ! -e "$DX_JAR" ]; then
|
||||
ln -s "$D8_JAR" "$DX_JAR"
|
||||
fi
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --android --device
|
||||
- name: Upload artifact to BrowserStack
|
||||
if: steps.should-run-tests.outputs.run_tests == 'true'
|
||||
run: scripts/upload-browserstack.sh --android
|
||||
|
||||
test-android:
|
||||
needs: build-android
|
||||
if: needs.build-android.outputs.run-tests == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Run e2e tests (Android)
|
||||
run: scripts/test-app.sh --android --device
|
||||
103
.github/workflows/codeql-analysis.yml
vendored
Normal file
103
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
branches: ["master"]
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: actions
|
||||
build-mode: none
|
||||
- language: java-kotlin
|
||||
build-mode: none # This mode only analyzes Java. Set this to 'autobuild' or 'manual' to analyze Kotlin too.
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||
# or others). This is typically only required for manual builds.
|
||||
# - name: Setup runtime (example)
|
||||
# uses: actions/setup-example@v1
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- name: Run manual build steps
|
||||
if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,7 +1,11 @@
|
||||
node_modules/**
|
||||
test/e2e-app-template/www/certificates/*.cer
|
||||
test/e2e-app-template/www/certificates/*.pkcs
|
||||
tags
|
||||
.zedstate
|
||||
npm-debug.log
|
||||
/temp
|
||||
/android-sdk-macosx.zip
|
||||
/android-sdk-macosx/**
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
43
.travis.yml
43
.travis.yml
@@ -1,43 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: objective-c
|
||||
os: osx
|
||||
osx_image: xcode9.1
|
||||
|
||||
env:
|
||||
- TARGET_PLATFORM=android
|
||||
- TARGET_PLATFORM=ios
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
addons:
|
||||
sauce_connect: true
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8
|
||||
|
||||
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
|
||||
|
||||
script:
|
||||
- npm run testjs
|
||||
- travis_wait scripts/build-test-app.sh --$TARGET_PLATFORM --emulator &&
|
||||
scripts/upload-artifact.sh --$TARGET_PLATFORM &&
|
||||
scripts/test-app.sh --$TARGET_PLATFORM --emulator;
|
||||
|
||||
after_success:
|
||||
|
||||
deploy:
|
||||
|
||||
notifications:
|
||||
slack:
|
||||
secure: lXE+2AgsxZU5G5dI91LkMAIgo8MAWfdM7DB5UOtn5LpuNln+2FmJo1gOI7tkdmLOqpXTGYnpI2VyQN3H4nOF21YhuouzD1Sh8n2wtQg1iTm353kuQpqiVhSBX8ZJ7Be1e1G8OsnxoYOxbs4Zo9qI40EruwkvqLCBHWM5MRGyd4M7EFWwb9Z29VZN0y1Nt5g/c3bT76kdKmF+JCLur2OeEKxAity7sIKgZekSqeIMwEVLSxXnda6Dbjc/cg0MJ0iDArkD7iu6fz/Fcrrxgm/pUxjcgvqze7Gy5i31mjEfspnrglWV1cshMd48BTDKCJ2AMmxH8O3GPSWE2txjIvGRWUve7iViNylvmQCVz3Eyf99+4EuuVGa+5PSodQ/CqODx/65EwtcN3PE1tNz2puKOK8nrOJcFkcbG8KTHKUlQtHCkjitbykUnj/hvhLK5/oWlQYVOLWWrHwdGUh8FI8aFPVGjRjWbHbhdayjEIqxwr1ns+6mYrP1EFNXbaeZxnLNC59XpJl1ifuezqYAk7YEiU5j4rtC7YKgyQ3ueb7anOHTJoTMyDn8mpZXgwuyhoBaeEYytQVgRyMtL6Y5cP98Jn2kv0+vdne3rkk9/JEBTo32HOjvoij6rsqEvXC0LhUDJSNadOVdHht0jjoN6zBH37HIE5/3zysLlPcAcHAS83ow=
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"editor.tabSize": 2
|
||||
}
|
||||
165
CHANGELOG.md
165
CHANGELOG.md
@@ -1,5 +1,170 @@
|
||||
# Changelog
|
||||
|
||||
# 3.3.1
|
||||
|
||||
- Fixed #427: missing connection check on Android (thanks moshe5745)
|
||||
- Fixed #459: namespace collision on iOS when another plugin is also using AFNetworking (thanks zafirskthelifehacker)
|
||||
- Fixed #429: intermediate CA certs are not respected on iOS when using client certs (thanks pavrda)
|
||||
- Fixed #444: errors are not handled when invalid header values are applied on browser (thanks @MobisysGmbH)
|
||||
- Fixed #441: sensible data can be cached in cache.db on iOS (thanks dtarnawsky)
|
||||
|
||||
# 3.3.0
|
||||
|
||||
- Feature #451: expose response object on `downloadFile()` (thanks to @MobisysGmbH)
|
||||
|
||||
# 3.2.2
|
||||
|
||||
- Fixed #438: requests not working correctly on browser platform because request options are not processed correctly
|
||||
|
||||
## 3.2.1
|
||||
|
||||
- Fixed #425: plugin crashes on Android SDK levels < 24
|
||||
- Fixed #418: deprecated AFNetworking method causes app crash (thanks meiram-tr)
|
||||
- Fixed #404: wrong timeout implementation (thanks YouYue123)
|
||||
|
||||
## 3.2.0
|
||||
|
||||
- Feature #420: implement blacklist feature to disable SSL/TLS versions on Android (thanks to @MobisysGmbH)
|
||||
|
||||
## 3.1.1
|
||||
|
||||
- Fixed #372: malformed empty multipart request on Android
|
||||
- Fixed #399: memory leakage leads to app crashes on iOS (thanks avargaskun)
|
||||
|
||||
## 3.1.0
|
||||
|
||||
- Feature #272: add support for aborting requests (thanks russaa)
|
||||
|
||||
## 3.0.1
|
||||
|
||||
- Fixed #359: memory leakage leads to app crashes on Android
|
||||
- Fixed #355: responseType "json" not working with valid JSON response on browser (thanks millerg6711)
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- Feature #158: support removing headers which were previously set via "setHeader"
|
||||
|
||||
- Fixed #345: empty file names are not handled correctly (thanks ikosta)
|
||||
|
||||
- :warning: **Breaking Change**: Dropped support for Android < 5.1
|
||||
- :warning: **Breaking Change**: Removed "disableRedirect", use "setFollowRedirect" instead
|
||||
- :warning: **Breaking Change**: Removed "setSSLCertMode", use "setServerTrustMode" instead
|
||||
|
||||
## 2.5.1
|
||||
|
||||
- Fixed #334: empty JSON response triggers error even though request is successful (thanks antikalk)
|
||||
- Fixed #248: clearCookies() does not work on iOS
|
||||
|
||||
## 2.5.0
|
||||
|
||||
- Feature #56: add support for X.509 client certificate based authentication
|
||||
|
||||
## 2.4.1
|
||||
|
||||
- Fixed #296: multipart requests are not serialized on browser platform
|
||||
- Fixed #301: data is not decoded correctly when responseType is "json" (thanks antikalk)
|
||||
- Fixed #300: FormData object containing null or undefined value is not serialized correctly
|
||||
|
||||
## 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
|
||||
|
||||
## 2.0.0
|
||||
|
||||
- Feature #103: implement HTTP SSL cert modes
|
||||
|
||||
- :warning: **Breaking Change**: Removed AngularJS (v1) integration service
|
||||
- :warning: **Breaking Change**: Removed "enableSSLPinning" and "acceptAllCerts", use "setSSLCertMode" instead
|
||||
- :warning: **Breaking Change**: Certificates must be placed in "www/certificates" folder
|
||||
|
||||
## 1.11.1
|
||||
|
||||
- Fixed #92: headers not deserialized on platform "browser"
|
||||
|
||||
@@ -73,10 +73,5 @@ If we suggest changes or the [CI build fails](#cibuild), then:
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
### <a name="cibuild"></a> Pull Request Feedback
|
||||
You can always check the results of the latest CI builds on
|
||||
[Travis CI](https://travis-ci.org/silkimen/cordova-plugin-advanced-http/).
|
||||
You can always check the results of the latest CI builds on [Github Actions](https://github.com/silkimen/cordova-plugin-advanced-http/actions).
|
||||
You can use this information to inspect failing tests in your PR.
|
||||
|
||||
## Attribution
|
||||
This document is adapted from
|
||||
[AngularJS' Contribution Guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
|
||||
|
||||
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
|
||||
|
||||
|
||||
312
README.md
312
README.md
@@ -1,10 +1,9 @@
|
||||
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://github.com/silkimen/cordova-plugin-advanced-http/actions)
|
||||
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS, Android and [Browser](#browserSupport).
|
||||
|
||||
@@ -12,9 +11,10 @@ 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
|
||||
- X.509 client certificate based authentication
|
||||
- Handling of HTTP code 401 - read more at [Issue CB-2415](https://issues.apache.org/jira/browse/CB-2415)
|
||||
|
||||
## Updates
|
||||
|
||||
@@ -31,6 +31,19 @@ phonegap plugin add cordova-plugin-advanced-http
|
||||
cordova plugin add cordova-plugin-advanced-http
|
||||
```
|
||||
|
||||
### Plugin Preferences
|
||||
|
||||
`AndroidBlacklistSecureSocketProtocols`: define a blacklist of secure socket protocols for Android. This preference allows you to disable protocols which are considered unsafe. You need to provide a comma-separated list of protocols ([check Android SSLSocket#protocols docu for protocol names](https://developer.android.com/reference/javax/net/ssl/SSLSocket#protocols)).
|
||||
|
||||
e.g. blacklist `SSLv3` and `TLSv1`:
|
||||
```xml
|
||||
<preference name="AndroidBlacklistSecureSocketProtocols" value="SSLv3,TLSv1" />
|
||||
```
|
||||
|
||||
## Currently known issues
|
||||
|
||||
- [abort](#abort)ing sent requests is not working reliably
|
||||
|
||||
## Usage
|
||||
|
||||
### Plain Cordova
|
||||
@@ -41,19 +54,6 @@ This plugin registers a global object located at `cordova.plugin.http`.
|
||||
|
||||
Check the [Ionic docs](https://ionicframework.com/docs/native/http/) for how to use this plugin with Ionic-native.
|
||||
|
||||
### With AngularJS (Deprecated)
|
||||
|
||||
:warning: *This feature is deprecated and will be removed anytime soon.* :warning:
|
||||
|
||||
This plugin creates a cordovaHTTP service inside of a cordovaHTTP module. You must load the module when you create your app's module.
|
||||
|
||||
```js
|
||||
var app = angular.module('myApp', ['ngRoute', 'ngAnimate', 'cordovaHTTP']);
|
||||
```
|
||||
|
||||
You can then inject the cordovaHTTP service into your controllers. The functions can then be used identically to the examples shown below except that instead of accepting success and failure callback functions, each function returns a promise. For more information on promises in AngularJS read the [AngularJS docs](http://docs.angularjs.org/api/ng/service/$q). For more info on promises in general check out this article on [html5rocks](http://www.html5rocks.com/en/tutorials/es6/promises/). Make sure that you load cordova.js or phonegap.js after AngularJS is loaded.
|
||||
|
||||
|
||||
## Synchronous Functions
|
||||
|
||||
### getBasicAuthHeader
|
||||
@@ -71,7 +71,7 @@ cordova.plugin.http.useBasicAuth('user', 'password');
|
||||
```
|
||||
|
||||
### setHeader<a name="setHeader"></a>
|
||||
Set a header for all future requests to a specified host. Takes a hostname, a header and a value (must be a string value).
|
||||
Set a header for all future requests to a specified host. Takes a hostname, a header and a value (must be a string value or null).
|
||||
|
||||
```js
|
||||
cordova.plugin.http.setHeader('Hostname', 'Header', 'Value');
|
||||
@@ -102,21 +102,56 @@ 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.
|
||||
|
||||
For Android, this will set both [connectTimeout](https://developer.android.com/reference/java/net/URLConnection#getConnectTimeout()) and [readTimeout](https://developer.android.com/reference/java/net/URLConnection#setReadTimeout(int)).
|
||||
For iOS, this will set [timeout interval](https://developer.apple.com/documentation/foundation/nsmutableurlrequest/1414063-timeoutinterval).
|
||||
For browser platform, this will set [timeout](https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest/timeout).
|
||||
```js
|
||||
cordova.plugin.http.setRequestTimeout(5.0);
|
||||
```
|
||||
|
||||
### setConnectTimeout (Android Only)
|
||||
Set connect timeout for Android
|
||||
```js
|
||||
cordova.plugin.http.setRequestTimeout(5.0);
|
||||
```
|
||||
|
||||
### setReadTimeout (Android Only)
|
||||
Set read timeout for Android
|
||||
```js
|
||||
cordova.plugin.http.setReadTimeout(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.
|
||||
|
||||
@@ -125,7 +160,7 @@ cordova.plugin.http.getCookieString(url);
|
||||
```
|
||||
|
||||
### setCookie
|
||||
Add a custom cookie. Takes a URL, a cookie string and an options object. See [ToughCookie documentation](https://github.com/salesforce/tough-cookie#setcookiecookieorstring-currenturl-options-cberrcookie) for allowed options.
|
||||
Add a custom cookie. Takes a URL, a cookie string and an options object. See [ToughCookie documentation](https://github.com/salesforce/tough-cookie#setcookiecookieorstring-currenturl-options-cberrcookie) for allowed options. Cookie will persist until removed with [removeCookies](#removecookies) or [clearCookies](#clearcookies).
|
||||
|
||||
```js
|
||||
cordova.plugin.http.setCookie(url, cookie, options);
|
||||
@@ -141,46 +176,64 @@ cordova.plugin.http.clearCookies();
|
||||
## Asynchronous Functions
|
||||
These functions all take success and error callbacks as their last 2 arguments.
|
||||
|
||||
### enableSSLPinning
|
||||
Enable or disable SSL pinning. This defaults to false.
|
||||
### setServerTrustMode<a name="setServerTrustMode"></a>
|
||||
Set server trust mode, being one of the following values:
|
||||
|
||||
To use SSL pinning you must include at least one .cer SSL certificate in your app project. You can pin to your server certificate or to one of the issuing CA certificates. For ios include your certificate in the root level of your bundle (just add the .cer file to your project/target at the root level). For android include your certificate in your project's platforms/android/assets folder. In both cases all .cer files found will be loaded automatically. If you only have a .pem certificate see this [stackoverflow answer](http://stackoverflow.com/a/16583429/3182729). You want to convert it to a DER encoded certificate with a .cer extension.
|
||||
* `default`: default SSL 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
|
||||
|
||||
As an alternative, you can store your .cer files in the www/certificates folder.
|
||||
To use SSL pinning you must include at least one `.cer` SSL certificate in your app project. You can pin to your server certificate or to one of the issuing CA certificates. Include your certificate in the `www/certificates` folder. All `.cer` files found there will be loaded automatically.
|
||||
|
||||
:warning: Your certificate must be DER encoded! If you only have a PEM 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
|
||||
cordova.plugin.http.enableSSLPinning(true, function() {
|
||||
// enable SSL pinning
|
||||
cordova.plugin.http.setServerTrustMode('pinned', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
|
||||
// use system's default CA certs
|
||||
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.setServerTrustMode('nocheck', function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
```
|
||||
|
||||
### acceptAllCerts
|
||||
Accept all SSL certificates. Or disable accepting all certificates. This defaults to false.
|
||||
### setClientAuthMode<a name="setClientAuthMode"></a>
|
||||
Configure X.509 client certificate authentication. Takes mode and options. `mode` being one of following values:
|
||||
|
||||
* `none`: disable client certificate authentication
|
||||
* `systemstore` (only on Android): use client certificate installed in the Android system store; user will be presented with a list of all installed certificates
|
||||
* `buffer`: use given client certificate; you will need to provide an options object:
|
||||
* `rawPkcs`: ArrayBuffer containing raw PKCS12 container with client certificate and private key
|
||||
* `pkcsPassword`: password of the PKCS container
|
||||
|
||||
```js
|
||||
cordova.plugin.http.acceptAllCerts(true, function() {
|
||||
console.log('success!');
|
||||
}, function() {
|
||||
console.log('error :(');
|
||||
});
|
||||
// enable client auth using PKCS12 container given in ArrayBuffer `myPkcs12ArrayBuffer`
|
||||
cordova.plugin.http.setClientAuthMode('buffer', {
|
||||
rawPkcs: myPkcs12ArrayBuffer,
|
||||
pkcsPassword: 'mySecretPassword'
|
||||
}, success, fail);
|
||||
|
||||
// enable client auth using certificate in system store (only on Android)
|
||||
cordova.plugin.http.setClientAuthMode('systemstore', {}, success, fail);
|
||||
|
||||
// disable client auth
|
||||
cordova.plugin.http.setClientAuthMode('none', {}, success, fail);
|
||||
```
|
||||
|
||||
### 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
|
||||
This function was removed in v1.6.2. Domain name validation is disabled automatically when you enable "acceptAllCerts".
|
||||
|
||||
### removeCookies
|
||||
Remove all cookies associated with a given URL.
|
||||
|
||||
@@ -188,20 +241,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, returns `undefined` when response body is empty
|
||||
* `arraybuffer`: data is returned as [ArrayBuffer instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), returns `null` when response body is empty
|
||||
* `blob`: data is returned as [Blob instance](https://developer.mozilla.org/en-US/docs/Web/API/Blob), returns `null` when response body is empty
|
||||
* `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:
|
||||
|
||||
@@ -227,8 +288,20 @@ 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 3 properties: status, data, and headers. **status** is the HTTP response code as numeric value. **data** is the response from the server as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
|
||||
The success function receives a response object with 4 properties: status, data, url, and headers. **status** is the HTTP response code as numeric value. **data** is the response from the server as a string. **url** is the final URL obtained after any redirects as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
|
||||
|
||||
Here's a quick example:
|
||||
|
||||
@@ -236,6 +309,7 @@ Here's a quick example:
|
||||
{
|
||||
status: 200,
|
||||
data: '{"id": 12, "message": "test"}',
|
||||
url: 'http://example.net/rest'
|
||||
headers: {
|
||||
'content-length': '247'
|
||||
}
|
||||
@@ -268,7 +342,7 @@ cordova.plugin.http.post('https://google.com/', {
|
||||
```
|
||||
|
||||
#### failure
|
||||
The error function receives a response object with 3 properties: status, error and headers. **status** is the HTTP response code as numeric value. **error** is the error response from the server as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
|
||||
The error function receives a response object with 4 properties: status, error, url, and headers (url and headers being optional). **status** is 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:
|
||||
|
||||
@@ -276,18 +350,21 @@ Here's a quick example:
|
||||
{
|
||||
status: 403,
|
||||
error: 'Permission denied',
|
||||
url: 'http://example.net/noperm'
|
||||
headers: {
|
||||
'content-length': '247'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
: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.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.get('https://google.com/', {
|
||||
id: 12,
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, function(response) {
|
||||
console.log(response.status);
|
||||
@@ -308,14 +385,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,
|
||||
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);
|
||||
@@ -323,18 +411,72 @@ cordova.plugin.http.uploadFile("https://google.com/", {
|
||||
```
|
||||
|
||||
### downloadFile<a name="downloadFile"></a>
|
||||
Downloads a file and saves it to the device. Takes a URL, parameters, headers, and a filePath. See [post](#post) documentation for details on what is returned on failure. On success this function returns a cordova [FileEntry object](http://cordova.apache.org/docs/en/3.3.0/cordova_file_file.md.html#FileEntry).
|
||||
Downloads a file and saves it to the device. Takes a URL, parameters, headers, and a filePath. See [post](#post) documentation for details on what is returned on failure. On success this function returns a cordova [FileEntry object](http://cordova.apache.org/docs/en/3.3.0/cordova_file_file.md.html#FileEntry) as first and the response object as second parameter.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
id: 12,
|
||||
cordova.plugin.http.downloadFile(
|
||||
"https://google.com/",
|
||||
{ id: '12', message: 'test' },
|
||||
{ Authorization: 'OAuth2: token' },
|
||||
'file:///somepicture.jpg',
|
||||
// success callback
|
||||
function(entry, response) {
|
||||
// prints the filename
|
||||
console.log(entry.name);
|
||||
|
||||
// prints the filePath
|
||||
console.log(entry.fullPath);
|
||||
|
||||
// prints all header key/value pairs
|
||||
Object.keys(response.headers).forEach(function (key) {
|
||||
console.log(key, response.headers[key]);
|
||||
});
|
||||
},
|
||||
// error callback
|
||||
function(response) {
|
||||
console.error(response.error);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### abort<a name="abort"></a>
|
||||
Abort a HTTP request. Takes the `requestId` which is returned by [sendRequest](#sendRequest) and its shorthand functions ([post](#post), [get](#get), [put](#put), [patch](#patch), [delete](#delete), [head](#head), [uploadFile](#uploadFile) and [downloadFile](#downloadFile)).
|
||||
|
||||
If the request already has finished, the request will finish normally and the abort call result will be `{ aborted: false }`.
|
||||
|
||||
If the request is still in progress, the request's `failure` callback will be invoked with response `{ status: -8 }`, and the abort call result `{ aborted: true }`.
|
||||
|
||||
:warning: Not supported for Android < 6 (API level < 23). For Android 5.1 and below, calling `abort(reqestId)` will have no effect, i.e. the requests will finish as if the request was not cancelled.
|
||||
|
||||
```js
|
||||
// start a request and get its requestId
|
||||
var requestId = cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry) {
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry, response) {
|
||||
// prints the filename
|
||||
console.log(entry.name);
|
||||
|
||||
// prints the filePath
|
||||
console.log(entry.fullPath);
|
||||
|
||||
// prints the status code
|
||||
console.log(response.status);
|
||||
}, function(response) {
|
||||
// if request was actually aborted, failure callback with status -8 will be invoked
|
||||
if(response.status === -8){
|
||||
console.log('download aborted');
|
||||
} else {
|
||||
console.error(response.error);
|
||||
}
|
||||
});
|
||||
|
||||
//...
|
||||
|
||||
// abort request
|
||||
cordova.plugin.http.abort(requestId, function(result) {
|
||||
// prints if request was aborted: true | false
|
||||
console.log(result.aborted);
|
||||
}, function(response) {
|
||||
console.error(response.error);
|
||||
});
|
||||
@@ -352,6 +494,7 @@ Following features are *not* supported:
|
||||
* Pinning SSL certificate
|
||||
* Disabling SSL certificate check
|
||||
* Disabling transparently following redirects (HTTP codes 3xx)
|
||||
* Circumventing CORS restrictions
|
||||
|
||||
## Libraries
|
||||
|
||||
@@ -363,6 +506,37 @@ 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)
|
||||
* [BrowserStack](https://www.browserstack.com/)
|
||||
* [Sauce Labs](https://saucelabs.com/)
|
||||
* [httpbin.org](https://httpbin.org/)
|
||||
* [go-httpbin](https://httpbingo.org/)
|
||||
|
||||
### Local Testing
|
||||
|
||||
First, install current package with `npm install` to fetch dev dependencies.
|
||||
|
||||
Then, to execute Javascript tests:
|
||||
```shell
|
||||
npm run test:js
|
||||
```
|
||||
|
||||
And, to execute E2E tests:
|
||||
- setup local Android sdk and emulators, or Xcode and simulators for iOS
|
||||
- launch emulator or simulator
|
||||
- install [Appium](http://appium.io/) (see [Getting Started](https://github.com/appium/appium/blob/HEAD/docs/en/about-appium/getting-started.md))
|
||||
- start `appium`
|
||||
- run
|
||||
- updating client and server certificates, building test app, and running e2e tests
|
||||
```shell
|
||||
npm run test:android
|
||||
npm run test:ios
|
||||
```
|
||||
|
||||
## Contribute & Develop
|
||||
|
||||
We've set up a separate document for our [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
12732
package-lock.json
generated
Normal file
12732
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@@ -1,14 +1,18 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "1.11.1",
|
||||
"version": "3.3.1",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"buildbrowser": "./scripts/build-test-app.sh --browser",
|
||||
"testandroid": "./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
|
||||
"testios": "./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator",
|
||||
"testapp": "npm run testandroid && npm run testios",
|
||||
"testjs": "mocha ./test/js-mocha-specs.js",
|
||||
"test": "npm run testjs && npm run testapp",
|
||||
"update:cert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
"build:browser": "./scripts/build-test-app.sh --browser",
|
||||
"build:android": "./scripts/build-test-app.sh --android --emulator",
|
||||
"build:ios": "./scripts/build-test-app.sh --ios --emulator",
|
||||
"lint": "eslint . --ext .js",
|
||||
"test:android": "npm run update:cert && npm run build:android && ./scripts/test-app.sh --android --emulator",
|
||||
"test:ios": "npm run update:cert && npm run build:ios && ./scripts/test-app.sh --ios --emulator",
|
||||
"test:app": "npm run test:android && npm run test:ios",
|
||||
"test:js": "mocha ./test/js-specs.js",
|
||||
"test": "npm run test:js && npm run test:app",
|
||||
"release": "npm run test && ./scripts/release.sh"
|
||||
},
|
||||
"cordova": {
|
||||
@@ -47,7 +51,7 @@
|
||||
"pvsaikrishna",
|
||||
"cvillerm",
|
||||
"hideov",
|
||||
"Mobisys"
|
||||
"silkimen"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
@@ -55,15 +59,13 @@
|
||||
},
|
||||
"homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme",
|
||||
"devDependencies": {
|
||||
"chai": "4.1.2",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"colors": "1.1.2",
|
||||
"cordova": "7.1.0",
|
||||
"mocha": "4.0.0",
|
||||
"mock-require": "2.0.2",
|
||||
"mz": "2.7.0",
|
||||
"umd-tough-cookie": "2.3.2",
|
||||
"wd": "1.4.1",
|
||||
"xml2js": "0.4.19"
|
||||
"chai": "4.3.6",
|
||||
"colors": "1.4.0",
|
||||
"cordova": "11.0.0",
|
||||
"eslint": "^8.57.0",
|
||||
"mocha": "9.2.2",
|
||||
"umd-tough-cookie": "3.0.0",
|
||||
"wd": "1.14.0",
|
||||
"xml2js": "0.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
83
plugin.xml
83
plugin.xml
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="1.11.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="3.3.1">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
@@ -8,13 +8,20 @@
|
||||
<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"/>
|
||||
<preference name="AndroidBlacklistSecureSocketProtocols" default="SSLv3,TLSv1"/>
|
||||
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
|
||||
<js-module src="www/angular-integration.js" name="angular-integration"/>
|
||||
<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>
|
||||
@@ -25,50 +32,58 @@
|
||||
</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"/>
|
||||
<header-file src="src/ios/AFNetworking/AFNetworking.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFNetworkReachabilityManager.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFSecurityPolicy.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFURLRequestSerialization.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFURLResponseSerialization.h"/>
|
||||
<header-file src="src/ios/AFNetworking/AFURLSessionManager.h"/>
|
||||
<header-file src="src/ios/SM_AFNetworking/SM_AFHTTPSessionManager.h"/>
|
||||
<header-file src="src/ios/SM_AFNetworking/SM_AFNetworking.h"/>
|
||||
<header-file src="src/ios/SM_AFNetworking/SM_AFNetworkReachabilityManager.h"/>
|
||||
<header-file src="src/ios/SM_AFNetworking/SM_AFSecurityPolicy.h"/>
|
||||
<header-file src="src/ios/SM_AFNetworking/SM_AFURLRequestSerialization.h"/>
|
||||
<header-file src="src/ios/SM_AFNetworking/SM_AFURLResponseSerialization.h"/>
|
||||
<header-file src="src/ios/SM_AFNetworking/SM_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"/>
|
||||
<source-file src="src/ios/AFNetworking/AFNetworkReachabilityManager.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFSecurityPolicy.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFURLRequestSerialization.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFURLResponseSerialization.m"/>
|
||||
<source-file src="src/ios/AFNetworking/AFURLSessionManager.m"/>
|
||||
<source-file src="src/ios/SM_AFNetworking/SM_AFHTTPSessionManager.m"/>
|
||||
<source-file src="src/ios/SM_AFNetworking/SM_AFNetworkReachabilityManager.m"/>
|
||||
<source-file src="src/ios/SM_AFNetworking/SM_AFSecurityPolicy.m"/>
|
||||
<source-file src="src/ios/SM_AFNetworking/SM_AFURLRequestSerialization.m"/>
|
||||
<source-file src="src/ios/SM_AFNetworking/SM_AFURLResponseSerialization.m"/>
|
||||
<source-file src="src/ios/SM_AFNetworking/SM_AFURLSessionManager.m"/>
|
||||
<source-file src="src/ios/SDNetworkActivityIndicator/SDNetworkActivityIndicator.m"/>
|
||||
<framework src="Security.framework"/>
|
||||
<framework src="SystemConfiguration.framework"/>
|
||||
</platform>
|
||||
<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"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
</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/CordovaObservableCallbackContext.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/TLSConfiguration.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
</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.";
|
||||
if [ $CI == "true" ] && ([ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]) && ([ -z $BROWSERSTACK_USERNAME ] || [ -z $BROWSERSTACK_ACCESS_KEY ]); then
|
||||
echo "Skipping CI tests, because Saucelabs and BrowserStack 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 --reporter ./test/e2e-tooling/reporter.js ./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);
|
||||
});
|
||||
40
scripts/update-e2e-server-cert.js
Normal file
40
scripts/update-e2e-server-cert.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const path = require('path');
|
||||
|
||||
const SOURCE_HOST = 'httpbin.org';
|
||||
const TARGET_PATH = path.join(__dirname, '../test/e2e-app-template/www/certificates/httpbin.org.cer');
|
||||
|
||||
const getCert = hostname => new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname,
|
||||
agent: false,
|
||||
rejectUnauthorized: false,
|
||||
ciphers: 'ALL'
|
||||
};
|
||||
|
||||
const req = https.get(options, response => {
|
||||
const certificate = response.socket.getPeerCertificate();
|
||||
|
||||
if (certificate === null) {
|
||||
return reject({ message: 'The website did not provide a certificate' });
|
||||
}
|
||||
|
||||
resolve(certificate);
|
||||
});
|
||||
|
||||
req.on('error', error => {
|
||||
return reject(error)
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
console.log(`Updating server certificate from ${SOURCE_HOST}`);
|
||||
|
||||
getCert(SOURCE_HOST)
|
||||
.then(cert => fs.writeFileSync(TARGET_PATH, cert.raw))
|
||||
.catch(error => {
|
||||
console.error(`Updating server certificate failed: ${error}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
const args = process.argv.slice(2);
|
||||
const fs = require('mz/fs');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const xml2js = require('xml2js');
|
||||
const xmlPath = path.join(__dirname, '..', 'plugin.xml');
|
||||
@@ -22,10 +22,12 @@ const stringify = obj => {
|
||||
return builder.buildObject(obj);
|
||||
};
|
||||
|
||||
fs.readFile(xmlPath, 'utf-8')
|
||||
.then(xml => parse(xml))
|
||||
.then(parsed => {
|
||||
parsed.plugin.$.version = args[0];
|
||||
const update = async (version) => {
|
||||
const xml = fs.readFileSync(xmlPath, 'utf-8');
|
||||
const parsed = await parse(xml);
|
||||
|
||||
return fs.writeFile(xmlPath, stringify(parsed));
|
||||
});
|
||||
parsed.plugin.$.version = version;
|
||||
fs.writeFileSync(xmlPath, stringify(parsed));
|
||||
};
|
||||
|
||||
return update(args[0]);
|
||||
|
||||
@@ -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
|
||||
|
||||
40
scripts/upload-browserstack.sh
Executable file
40
scripts/upload-browserstack.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/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"
|
||||
IOS_BUILD_DIR="$TEMP/platforms/ios/build/Debug-iphonesimulator"
|
||||
APP_PATH="$IOS_BUILD_DIR/HttpDemo.app"
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
echo "Unable to locate $APP_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PAYLOAD_DIR="$TEMP/Payload"
|
||||
rm -rf "$PAYLOAD_DIR"
|
||||
mkdir -p "$PAYLOAD_DIR"
|
||||
cp -R "$APP_PATH" "$PAYLOAD_DIR/HttpDemo.app"
|
||||
(cd "$TEMP" && zip -qr HttpDemo.ipa Payload)
|
||||
rm -rf "$PAYLOAD_DIR"
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
236
src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
Normal file
236
src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
Normal file
@@ -0,0 +1,236 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
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.TLSConfiguration;
|
||||
|
||||
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 connectTimeout;
|
||||
protected int readTimeout;
|
||||
protected boolean followRedirects;
|
||||
protected TLSConfiguration tlsConfiguration;
|
||||
protected CordovaObservableCallbackContext callbackContext;
|
||||
|
||||
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int connectTimeout,
|
||||
int readTimeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
this.serializer = serializer;
|
||||
this.data = data;
|
||||
this.headers = headers;
|
||||
this.connectTimeout = connectTimeout;
|
||||
this.readTimeout = readTimeout;
|
||||
this.followRedirects = followRedirects;
|
||||
this.responseType = responseType;
|
||||
this.tlsConfiguration = tlsConfiguration;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
public CordovaHttpBase(String method, String url, JSONObject headers, int connectTimeout, int readTimeout, boolean followRedirects,
|
||||
String responseType, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
this.headers = headers;
|
||||
this.connectTimeout = connectTimeout;
|
||||
this.readTimeout = readTimeout;
|
||||
this.followRedirects = followRedirects;
|
||||
this.responseType = responseType;
|
||||
this.tlsConfiguration = tlsConfiguration;
|
||||
this.callbackContext = callbackContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
CordovaHttpResponse response = new CordovaHttpResponse();
|
||||
HttpRequest request = null;
|
||||
|
||||
try {
|
||||
request = this.createRequest();
|
||||
this.prepareRequest(request);
|
||||
this.sendBody(request);
|
||||
this.processResponse(request, response);
|
||||
request.disconnect();
|
||||
} catch (HttpRequestException e) {
|
||||
Throwable cause = e.getCause();
|
||||
String message = cause.getMessage();
|
||||
|
||||
if (cause 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 (cause 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 (cause instanceof SocketTimeoutException) {
|
||||
response.setStatus(-4);
|
||||
response.setErrorMessage("Request timed out: " + e.getMessage());
|
||||
Log.w(TAG, "Request timed out", e);
|
||||
} else if (cause instanceof InterruptedIOException && "thread interrupted".equals(message.toLowerCase())) {
|
||||
this.setAborted(request, response);
|
||||
} else {
|
||||
response.setStatus(-1);
|
||||
response.setErrorMessage("There was an error with the request: " + message);
|
||||
Log.w(TAG, "Generic request error", e);
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
this.setAborted(request, response);
|
||||
} 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.connectTimeout(this.connectTimeout);
|
||||
request.readTimeout(this.readTimeout);
|
||||
request.uncompress(true);
|
||||
|
||||
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)) {
|
||||
// intentionally left blank, because content type is set in HttpRequest.part()
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// prevent sending malformed empty multipart requests (#372)
|
||||
if (buffers.length() == 0) {
|
||||
request.contentType("multipart/form-data; boundary=00content0boundary00");
|
||||
request.send("\r\n--00content0boundary00--\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) || "json".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()));
|
||||
}
|
||||
}
|
||||
|
||||
protected void setAborted(HttpRequest request, CordovaHttpResponse response) {
|
||||
response.setStatus(-8);
|
||||
response.setErrorMessage("Request was aborted");
|
||||
|
||||
if (request != null) {
|
||||
try {
|
||||
request.disconnect();
|
||||
} catch(Exception any){
|
||||
Log.w(TAG, "Failed to close aborted request", any);
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, "Request was aborted");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
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.file.FileUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpDownload extends CordovaHttpBase {
|
||||
private String filePath;
|
||||
|
||||
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int connectTimeout, int readTimeout,
|
||||
boolean followRedirects, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super("GET", url, headers, connectTimeout, readTimeout, 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,24 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
class CordovaHttpOperation extends CordovaHttpBase {
|
||||
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
|
||||
int connectTimeout, int readTimeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super(method, url, serializer, data, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration,
|
||||
callbackContext);
|
||||
}
|
||||
|
||||
public CordovaHttpOperation(String method, String url, JSONObject headers, int connectTimeout, int readTimeout, boolean followRedirects,
|
||||
String responseType, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super(method, url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
|
||||
}
|
||||
}
|
||||
273
src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
Normal file
273
src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
Normal file
@@ -0,0 +1,273 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.util.HashMap;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
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.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
import android.util.Base64;
|
||||
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
|
||||
private static final String TAG = "Cordova-Plugin-HTTP";
|
||||
|
||||
private TLSConfiguration tlsConfiguration;
|
||||
|
||||
private HashMap<Integer, Future<?>> reqMap;
|
||||
private final Object reqMapLock = new Object();
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
|
||||
this.tlsConfiguration = new TLSConfiguration();
|
||||
|
||||
this.reqMap = new HashMap<Integer, Future<?>>();
|
||||
|
||||
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());
|
||||
|
||||
if (this.preferences.contains("androidblacklistsecuresocketprotocols")) {
|
||||
this.tlsConfiguration.setBlacklistedProtocols(
|
||||
this.preferences.getString("androidblacklistsecuresocketprotocols", "").split(",")
|
||||
);
|
||||
}
|
||||
|
||||
} 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 ("setServerTrustMode".equals(action)) {
|
||||
return this.setServerTrustMode(args, callbackContext);
|
||||
} else if ("setClientAuthMode".equals(action)) {
|
||||
return this.setClientAuthMode(args, callbackContext);
|
||||
} else if ("abort".equals(action)) {
|
||||
return this.abort(args, callbackContext);
|
||||
}
|
||||
|
||||
if (!isNetworkAvailable()) {
|
||||
CordovaHttpResponse response = new CordovaHttpResponse();
|
||||
response.setStatus(-6);
|
||||
response.setErrorMessage("No network connection available");
|
||||
callbackContext.error(response.toJSON());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 {
|
||||
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 connectTimeout = args.getInt(2) * 1000;
|
||||
int readTimeout = args.getInt(3) * 1000;
|
||||
boolean followRedirect = args.getBoolean(4);
|
||||
String responseType = args.getString(5);
|
||||
Integer reqId = args.getInt(6);
|
||||
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, connectTimeout, readTimeout,
|
||||
followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, 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 connectTimeout = args.getInt(4) * 1000;
|
||||
int readTimeout = args.getInt(5) * 1000;
|
||||
boolean followRedirect = args.getBoolean(6);
|
||||
String responseType = args.getString(7);
|
||||
Integer reqId = args.getInt(8);
|
||||
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
|
||||
connectTimeout, readTimeout, followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, 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 connectTimeout = args.getInt(4) * 1000;
|
||||
int readTimeout = args.getInt(5) * 1000;
|
||||
boolean followRedirect = args.getBoolean(6);
|
||||
String responseType = args.getString(7);
|
||||
Integer reqId = args.getInt(8);
|
||||
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, connectTimeout, readTimeout, followRedirect,
|
||||
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, 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 connectTimeout = args.getInt(3) * 1000;
|
||||
int readTimeout = args.getInt(4) * 1000;
|
||||
boolean followRedirect = args.getBoolean(5);
|
||||
Integer reqId = args.getInt(6);
|
||||
|
||||
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
|
||||
|
||||
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, connectTimeout, readTimeout,
|
||||
followRedirect, this.tlsConfiguration, observableCallbackContext);
|
||||
|
||||
startRequest(reqId, observableCallbackContext, download);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startRequest(Integer reqId, CordovaObservableCallbackContext observableCallbackContext, CordovaHttpBase request) {
|
||||
synchronized (reqMapLock) {
|
||||
observableCallbackContext.setObserver(this);
|
||||
Future<?> task = cordova.getThreadPool().submit(request);
|
||||
this.addReq(reqId, task, observableCallbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private boolean abort(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
int reqId = args.getInt(0);
|
||||
boolean result = false;
|
||||
// NOTE no synchronized (reqMapLock), since even if the req was already removed from reqMap,
|
||||
// the worst that would happen calling task.cancel(true) is a result of false
|
||||
// (i.e. same result as locking & not finding the req in reqMap)
|
||||
Future<?> task = this.reqMap.get(reqId);
|
||||
|
||||
if (task != null && !task.isDone()) {
|
||||
result = task.cancel(true);
|
||||
}
|
||||
|
||||
callbackContext.success(new JSONObject().put("aborted", result));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addReq(final Integer reqId, final Future<?> task, final CordovaObservableCallbackContext observableCallbackContext) {
|
||||
synchronized (reqMapLock) {
|
||||
if (!task.isDone()){
|
||||
this.reqMap.put(reqId, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeReq(final Integer reqId) {
|
||||
synchronized (reqMapLock) {
|
||||
this.reqMap.remove(reqId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
synchronized (reqMapLock) {
|
||||
CordovaObservableCallbackContext c = (CordovaObservableCallbackContext) arg;
|
||||
if (c.getCallbackContext().isFinished()) {
|
||||
removeReq(c.getRequestId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNetworkAvailable() {
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) cordova.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
|
||||
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
91
src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
Normal file
91
src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
Normal file
@@ -0,0 +1,91 @@
|
||||
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.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 connectTimeout, int readTimeout,
|
||||
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
|
||||
Context applicationContext, CordovaObservableCallbackContext callbackContext) {
|
||||
|
||||
super("POST", url, headers, connectTimeout, readTimeout, 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.silkimen.cordovahttp;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Observer;
|
||||
|
||||
public class CordovaObservableCallbackContext {
|
||||
|
||||
private CallbackContext callbackContext;
|
||||
private Integer requestId;
|
||||
private Observer observer;
|
||||
|
||||
public CordovaObservableCallbackContext(CallbackContext callbackContext, Integer requestId) {
|
||||
this.callbackContext = callbackContext;
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
public void success(JSONObject message) {
|
||||
this.callbackContext.success(message);
|
||||
this.notifyObserver();
|
||||
}
|
||||
|
||||
public void error(JSONObject message) {
|
||||
this.callbackContext.error(message);
|
||||
this.notifyObserver();
|
||||
}
|
||||
|
||||
public Integer getRequestId() {
|
||||
return this.requestId;
|
||||
}
|
||||
|
||||
public CallbackContext getCallbackContext() {
|
||||
return callbackContext;
|
||||
}
|
||||
|
||||
public Observer getObserver() {
|
||||
return observer;
|
||||
}
|
||||
|
||||
protected void notifyObserver() {
|
||||
if(this.observer != null){
|
||||
this.observer.update(null, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an observer that is notified, when {@link #success(JSONObject)}
|
||||
* or {@link #error(JSONObject)} are called.
|
||||
*
|
||||
* NOTE the observer is notified with
|
||||
* <pre>observer.update(null, cordovaObservableCallbackContext)</pre>
|
||||
* @param observer
|
||||
*/
|
||||
public void setObserver(Observer observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
}
|
||||
132
src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
Normal file
132
src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
Normal file
@@ -0,0 +1,132 @@
|
||||
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(getWebAssetDir() + "/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 String getWebAssetDir() {
|
||||
return isRunningOnCapacitor()? "public" : "www";
|
||||
}
|
||||
|
||||
private boolean isRunningOnCapacitor() {
|
||||
return this.activity.getClass().getSuperclass().getName().contains("com.getcapacitor");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
64
src/android/com/silkimen/http/TLSConfiguration.java
Normal file
64
src/android/com/silkimen/http/TLSConfiguration.java
Normal file
@@ -0,0 +1,64 @@
|
||||
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 = null;
|
||||
private KeyManager[] keyManagers = null;
|
||||
private HostnameVerifier hostnameVerifier = null;
|
||||
private String[] blacklistedProtocols = {};
|
||||
|
||||
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 void setBlacklistedProtocols(String[] protocols) {
|
||||
this.blacklistedProtocols = protocols;
|
||||
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());
|
||||
this.socketFactory = new TLSSocketFactory(context, this.blacklistedProtocols);
|
||||
|
||||
return this.socketFactory;
|
||||
} catch (GeneralSecurityException e) {
|
||||
IOException ioException = new IOException("Security exception occured while configuring TLS context");
|
||||
ioException.initCause(e);
|
||||
throw ioException;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/android/com/silkimen/http/TLSSocketFactory.java
Normal file
84
src/android/com/silkimen/http/TLSSocketFactory.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
public class TLSSocketFactory extends SSLSocketFactory {
|
||||
|
||||
private SSLSocketFactory delegate;
|
||||
private List<String> blacklistedProtocols;
|
||||
|
||||
public TLSSocketFactory(SSLContext context, String[] blacklistedProtocols) {
|
||||
this.delegate = context.getSocketFactory();
|
||||
this.blacklistedProtocols = new ArrayList();
|
||||
|
||||
for (int i = 0; i < blacklistedProtocols.length; ++i) {
|
||||
this.blacklistedProtocols.add(blacklistedProtocols[i].trim());
|
||||
}
|
||||
}
|
||||
|
||||
@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)) {
|
||||
return socket;
|
||||
}
|
||||
|
||||
String[] supported = ((SSLSocket) socket).getSupportedProtocols();
|
||||
List<String> filtered = new ArrayList();
|
||||
|
||||
for (int i = 0; i < supported.length; ++i) {
|
||||
if (!this.blacklistedProtocols.contains(supported[i])) {
|
||||
filtered.add(supported[i]);
|
||||
}
|
||||
}
|
||||
|
||||
((SSLSocket) socket).setEnabledProtocols(filtered.toArray(new String[0]));
|
||||
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
@@ -1,319 +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 sslPinning = new AtomicBoolean(false);
|
||||
private static AtomicBoolean acceptAllCerts = new AtomicBoolean(false);
|
||||
private static AtomicBoolean validateDomainName = new AtomicBoolean(true);
|
||||
private static AtomicBoolean disableRedirect = new AtomicBoolean(false);
|
||||
|
||||
private String urlString;
|
||||
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 enableSSLPinning(boolean enable) {
|
||||
sslPinning.set(enable);
|
||||
if (enable) {
|
||||
acceptAllCerts.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void acceptAllCerts(boolean accept) {
|
||||
acceptAllCerts.set(accept);
|
||||
if (accept) {
|
||||
sslPinning.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateDomainName(boolean accept) {
|
||||
validateDomainName.set(accept);
|
||||
}
|
||||
|
||||
public static void disableRedirect(boolean disable) {
|
||||
disableRedirect.set(disable);
|
||||
}
|
||||
|
||||
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 setupSecurity(HttpRequest request) {
|
||||
if (acceptAllCerts.get()) {
|
||||
request.trustAllCerts();
|
||||
}
|
||||
if (!validateDomainName.get()) {
|
||||
request.trustAllHosts();
|
||||
}
|
||||
if (sslPinning.get()) {
|
||||
request.pinToCerts();
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
protected HttpRequest setupRedirect(HttpRequest request) {
|
||||
if (disableRedirect.get()) {
|
||||
request.followRedirects(false);
|
||||
}
|
||||
|
||||
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);
|
||||
this.setupSecurity(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,169 +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("enableSSLPinning")) {
|
||||
try {
|
||||
boolean enable = args.getBoolean(0);
|
||||
this.enableSSLPinning(enable);
|
||||
callbackContext.success();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("There was an error setting up ssl pinning");
|
||||
}
|
||||
} else if (action.equals("acceptAllCerts")) {
|
||||
boolean accept = args.getBoolean(0);
|
||||
|
||||
CordovaHttp.acceptAllCerts(accept);
|
||||
CordovaHttp.validateDomainName(!accept);
|
||||
callbackContext.success();
|
||||
} 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 enableSSLPinning(boolean enable) throws GeneralSecurityException, IOException {
|
||||
if (enable) {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
String[] files = assetManager.list("");
|
||||
int index;
|
||||
ArrayList<String> cerFiles = new ArrayList<String>();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
index = files[i].lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
if (files[i].substring(index).equals(".cer")) {
|
||||
cerFiles.add(files[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// scan the www/certificates folder for .cer files as well
|
||||
files = assetManager.list("www/certificates");
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
index = files[i].lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
if (files[i].substring(index).equals(".cer")) {
|
||||
cerFiles.add("www/certificates/" + files[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < cerFiles.size(); i++) {
|
||||
InputStream in = cordova.getActivity().getAssets().open(cerFiles.get(i));
|
||||
InputStream caInput = new BufferedInputStream(in);
|
||||
HttpRequest.addCert(caInput);
|
||||
}
|
||||
CordovaHttp.enableSSLPinning(true);
|
||||
} else {
|
||||
CordovaHttp.enableSSLPinning(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
253
src/browser/cordova-http-plugin.js
vendored
253
src/browser/cordova-http-plugin.js
vendored
@@ -1,7 +1,9 @@
|
||||
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');
|
||||
|
||||
var reqMap = {};
|
||||
|
||||
function serializeJsonData(data) {
|
||||
try {
|
||||
@@ -20,7 +22,7 @@ function serializePrimitive(key, value) {
|
||||
}
|
||||
|
||||
function serializeArray(key, values) {
|
||||
return values.map(function(value) {
|
||||
return values.map(function (value) {
|
||||
return encodeURIComponent(key) + '[]=' + encodeURIComponent(value);
|
||||
}).join('&');
|
||||
}
|
||||
@@ -28,8 +30,8 @@ function serializeArray(key, values) {
|
||||
function serializeParams(params) {
|
||||
if (params === null) return '';
|
||||
|
||||
return Object.keys(params).map(function(key) {
|
||||
if (helpers.getTypeOf(params[key]) === 'Array') {
|
||||
return Object.keys(params).map(function (key) {
|
||||
if (jsUtil.getTypeOf(params[key]) === 'Array') {
|
||||
return serializeArray(key, params[key]);
|
||||
}
|
||||
|
||||
@@ -37,6 +39,39 @@ function serializeParams(params) {
|
||||
}).join('&');
|
||||
}
|
||||
|
||||
function decodeB64(dataString) {
|
||||
var binaryString = atob(dataString);
|
||||
var bytes = new Uint8Array(binaryString.length);
|
||||
|
||||
for (var i = 0; i < binaryString.length; ++i) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
function processMultipartData(data) {
|
||||
if (!data) return null;
|
||||
|
||||
var fd = new FormData();
|
||||
|
||||
for (var i = 0; i < data.buffers.length; ++i) {
|
||||
var buffer = data.buffers[i];
|
||||
var name = data.names[i];
|
||||
var fileName = data.fileNames[i];
|
||||
var type = data.types[i];
|
||||
|
||||
if (fileName) {
|
||||
fd.append(name, new Blob([decodeB64(buffer)], { type: type }), fileName);
|
||||
} else {
|
||||
// we assume it's plain text if no filename was given
|
||||
fd.append(name, atob(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
function deserializeResponseHeaders(headers) {
|
||||
var headerMap = {};
|
||||
var arr = headers.trim().split(/[\r\n]+/);
|
||||
@@ -52,11 +87,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 +108,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;
|
||||
@@ -74,106 +117,226 @@ function createXhrFailureObject(xhr) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function injectRequestIdHandler(reqId, cb) {
|
||||
return function (response) {
|
||||
delete reqMap[reqId];
|
||||
cb(response);
|
||||
}
|
||||
}
|
||||
|
||||
function getHeaderValue(headers, headerName) {
|
||||
let result = null;
|
||||
|
||||
Object.keys(headers).forEach(function (key) {
|
||||
if (key.toLowerCase() === headerName.toLowerCase()) {
|
||||
result = headers[key];
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function setDefaultContentType(headers, contentType) {
|
||||
if (getHeaderValue(headers, 'Content-Type') === null) {
|
||||
headers['Content-Type'] = contentType;
|
||||
}
|
||||
}
|
||||
|
||||
function setHeaders(xhr, headers) {
|
||||
Object.keys(headers).forEach(function(key) {
|
||||
if (key === 'Cookie') return;
|
||||
Object.keys(headers).forEach(function (key) {
|
||||
if (key.toLowerCase() === 'cookie') return;
|
||||
|
||||
xhr.setRequestHeader(key, headers[key]);
|
||||
});
|
||||
}
|
||||
|
||||
function sendRequest(method, withData, opts, success, failure) {
|
||||
var data = withData ? opts[1] : null;
|
||||
var params = withData ? null : serializeParams(opts[1]);
|
||||
var serializer = withData ? opts[2] : null;
|
||||
var headers = withData ? opts[3] : opts[2];
|
||||
var timeout = withData ? opts[4] : opts[3];
|
||||
var url = params ? opts[0] + '?' + params : opts[0];
|
||||
var data, serializer, headers, readTimeout, followRedirect, responseType, reqId;
|
||||
var url = opts[0];
|
||||
|
||||
if (withData) {
|
||||
data = opts[1];
|
||||
serializer = opts[2];
|
||||
headers = opts[3];
|
||||
// connect timeout not applied
|
||||
// connectTimeout = opts[4];
|
||||
readTimeout = opts[5];
|
||||
followRedirect = opts[6];
|
||||
responseType = opts[7];
|
||||
reqId = opts[8];
|
||||
} else {
|
||||
headers = opts[1];
|
||||
// connect timeout not applied
|
||||
// connectTimeout = opts[2];
|
||||
readTimeout = opts[3];
|
||||
followRedirect = opts[4];
|
||||
responseType = opts[5];
|
||||
reqId = opts[6];
|
||||
}
|
||||
|
||||
var onSuccess = injectRequestIdHandler(reqId, success);
|
||||
var onFail = injectRequestIdHandler(reqId, failure);
|
||||
|
||||
var processedData = null;
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
reqMap[reqId] = xhr;
|
||||
|
||||
xhr.open(method, url);
|
||||
|
||||
if (headers.Cookie && headers.Cookie.length > 0) {
|
||||
return failure('advanced-http: custom cookies not supported on browser platform');
|
||||
return onFail('advanced-http: custom cookies not supported on browser platform');
|
||||
}
|
||||
|
||||
if (!followRedirect) {
|
||||
return onFail('advanced-http: disabling follow redirect not supported on browser platform');
|
||||
}
|
||||
|
||||
switch (serializer) {
|
||||
case 'json':
|
||||
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf8');
|
||||
case 'json': {
|
||||
setDefaultContentType(headers, 'application/json; charset=utf8');
|
||||
processedData = serializeJsonData(data);
|
||||
|
||||
if (processedData === null) {
|
||||
return failure('advanced-http: failed serializing data');
|
||||
return onFail('advanced-http: failed serializing data');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'utf8':
|
||||
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf8');
|
||||
case 'utf8': {
|
||||
setDefaultContentType(headers, 'text/plain; charset=utf8');
|
||||
processedData = data.text;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'urlencoded':
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
case 'urlencoded': {
|
||||
setDefaultContentType(headers, 'application/x-www-form-urlencoded');
|
||||
processedData = serializeParams(data);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'multipart': {
|
||||
const contentType = getHeaderValue(headers, 'Content-Type');
|
||||
|
||||
// intentionally don't set a default content type
|
||||
// it's set by the browser together with the content disposition string
|
||||
if (contentType) {
|
||||
headers['Content-Type'] = contentType;
|
||||
}
|
||||
|
||||
processedData = processMultipartData(data);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'raw': {
|
||||
setDefaultContentType(headers, 'application/octet-stream');
|
||||
processedData = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
xhr.timeout = timeout * 1000;
|
||||
setHeaders(xhr, headers);
|
||||
// requesting text instead of JSON because it's parsed in the response handler
|
||||
xhr.responseType = responseType === 'json' ? 'text' : responseType;
|
||||
|
||||
xhr.onerror = xhr.ontimeout = function () {
|
||||
return failure(createXhrFailureObject(xhr));
|
||||
// we can't set connect timeout and read timeout separately on browser platform
|
||||
xhr.timeout = readTimeout * 1000;
|
||||
|
||||
try {
|
||||
setHeaders(xhr, headers);
|
||||
} catch(error) {
|
||||
return onFail({
|
||||
status: -1,
|
||||
error: error,
|
||||
url: url,
|
||||
headers: headers
|
||||
});
|
||||
}
|
||||
|
||||
xhr.onerror = function () {
|
||||
return onFail(createXhrFailureObject(xhr));
|
||||
};
|
||||
|
||||
xhr.onabort = function () {
|
||||
return onFail({
|
||||
status: -8,
|
||||
error: 'Request was aborted',
|
||||
url: url,
|
||||
headers: {}
|
||||
});
|
||||
};
|
||||
|
||||
xhr.ontimeout = function () {
|
||||
return onFail({
|
||||
status: -4,
|
||||
error: 'Request timed out',
|
||||
url: url,
|
||||
headers: {}
|
||||
});
|
||||
};
|
||||
|
||||
xhr.onload = function () {
|
||||
if (xhr.readyState !== xhr.DONE) return;
|
||||
|
||||
if (xhr.status < 200 || xhr.status > 299) {
|
||||
return failure(createXhrFailureObject(xhr));
|
||||
return onFail(createXhrFailureObject(xhr));
|
||||
}
|
||||
|
||||
return success(createXhrSuccessObject(xhr));
|
||||
return onSuccess(createXhrSuccessObject(xhr));
|
||||
};
|
||||
|
||||
xhr.send(processedData);
|
||||
}
|
||||
|
||||
function abort(opts, success) {
|
||||
var reqId = opts[0];
|
||||
var result = false;
|
||||
|
||||
var xhr = reqMap[reqId];
|
||||
if(xhr && xhr.readyState !== xhr.DONE){
|
||||
xhr.abort();
|
||||
result = true;
|
||||
}
|
||||
|
||||
success({aborted: result});
|
||||
}
|
||||
|
||||
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);
|
||||
abort: function (success, failure, opts) {
|
||||
return abort(opts, success, failure);
|
||||
},
|
||||
head: function (success, failure, opts) {
|
||||
return sendRequest('head', false, opts, success, failure);
|
||||
},
|
||||
uploadFile: function (success, failure, opts) {
|
||||
uploadFile: function (success, failure) {
|
||||
return failure('advanced-http: function "uploadFile" not supported on browser platform');
|
||||
},
|
||||
downloadFile: function (success, failure, opts) {
|
||||
uploadFiles: function (success, failure) {
|
||||
return failure('advanced-http: function "uploadFiles" not supported on browser platform');
|
||||
},
|
||||
downloadFile: function (success, failure) {
|
||||
return failure('advanced-http: function "downloadFile" not supported on browser platform');
|
||||
},
|
||||
enableSSLPinning: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "enableSSLPinning" not supported on browser platform');
|
||||
setServerTrustMode: function (success, failure) {
|
||||
return failure('advanced-http: function "setServerTrustMode" not supported on browser platform');
|
||||
},
|
||||
acceptAllCerts: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "acceptAllCerts" not supported on browser platform');
|
||||
},
|
||||
disableRedirect: function (success, failure, opts) {
|
||||
return failure('advanced-http: function "disableRedirect" not supported on browser platform');
|
||||
setClientAuthMode: function (success, failure) {
|
||||
return failure('advanced-http: function "setClientAuthMode" not supported on browser platform');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
8
src/ios/BinaryRequestSerializer.h
Normal file
8
src/ios/BinaryRequestSerializer.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SM_AFURLRequestSerialization.h"
|
||||
|
||||
@interface BinaryRequestSerializer : SM_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 - SM_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 "SM_AFURLResponseSerialization.h"
|
||||
|
||||
@interface BinaryResponseSerializer : SM_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)", @"SM_AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
|
||||
NSURLErrorFailingURLErrorKey: [response URL],
|
||||
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
} mutableCopy];
|
||||
|
||||
if (data) {
|
||||
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
|
||||
// trying to decode error message in body
|
||||
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
|
||||
}
|
||||
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - SM_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, SM_AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return [data base64EncodedStringWithOptions:0];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -4,15 +4,17 @@
|
||||
|
||||
@interface CordovaHttpPlugin : CDVPlugin
|
||||
|
||||
- (void)enableSSLPinning:(CDVInvokedUrlCommand*)command;
|
||||
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command;
|
||||
- (void)disableRedirect:(CDVInvokedUrlCommand*)command;
|
||||
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)setClientAuthMode:(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;
|
||||
- (void)abort:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,50 +1,95 @@
|
||||
#import "CordovaHttpPlugin.h"
|
||||
#import "CDVFile.h"
|
||||
#import "BinaryRequestSerializer.h"
|
||||
#import "BinaryResponseSerializer.h"
|
||||
#import "TextResponseSerializer.h"
|
||||
#import "TextRequestSerializer.h"
|
||||
#import "AFHTTPSessionManager.h"
|
||||
#import "SM_AFHTTPSessionManager.h"
|
||||
#import "SDNetworkActivityIndicator.h"
|
||||
|
||||
@interface CordovaHttpPlugin()
|
||||
|
||||
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(AFHTTPSessionManager*)manager;
|
||||
- (void)addRequest:(NSNumber*)reqId forTask:(NSURLSessionDataTask*)task;
|
||||
- (void)removeRequest:(NSNumber*)reqId;
|
||||
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(SM_AFHTTPSessionManager*)manager;
|
||||
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data;
|
||||
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error;
|
||||
- (NSNumber*)getStatusCode:(NSError*) error;
|
||||
- (NSMutableDictionary*)copyHeaderFields:(NSDictionary*)headerFields;
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager;
|
||||
- (void)setRedirect:(AFHTTPSessionManager*)manager;
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(SM_AFHTTPSessionManager*)manager;
|
||||
- (void)setRedirect:(bool)redirect forManager:(SM_AFHTTPSessionManager*)manager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CordovaHttpPlugin {
|
||||
AFSecurityPolicy *securityPolicy;
|
||||
bool redirect;
|
||||
SM_AFSecurityPolicy *securityPolicy;
|
||||
NSURLCredential *x509Credential;
|
||||
NSMutableDictionary *reqDict;
|
||||
}
|
||||
|
||||
- (void)pluginInitialize {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
redirect = true;
|
||||
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
reqDict = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
- (void)setRequestSerializer:(NSString*)serializerName forManager:(AFHTTPSessionManager*)manager {
|
||||
- (void)addRequest:(NSNumber*)reqId forTask:(NSURLSessionDataTask*)task {
|
||||
[reqDict setObject:task forKey:reqId];
|
||||
}
|
||||
|
||||
- (void)removeRequest:(NSNumber*)reqId {
|
||||
[reqDict removeObjectForKey:reqId];
|
||||
}
|
||||
|
||||
- (void)setRequestSerializer:(NSString*)serializerName forManager:(SM_AFHTTPSessionManager*)manager {
|
||||
if ([serializerName isEqualToString:@"json"]) {
|
||||
manager.requestSerializer = [AFJSONRequestSerializer serializer];
|
||||
manager.requestSerializer = [SM_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];
|
||||
manager.requestSerializer = [SM_AFHTTPRequestSerializer serializer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(AFHTTPSessionManager*)manager {
|
||||
- (void)setupAuthChallengeBlock:(SM_AFHTTPSessionManager*)manager {
|
||||
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(
|
||||
NSURLSession * _Nonnull session,
|
||||
NSURLAuthenticationChallenge * _Nonnull challenge,
|
||||
NSURLCredential * _Nullable __autoreleasing * _Nullable credential
|
||||
) {
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodServerTrust]) {
|
||||
*credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
|
||||
if (![self->securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
|
||||
return NSURLSessionAuthChallengeRejectProtectionSpace;
|
||||
}
|
||||
|
||||
if (credential) {
|
||||
return NSURLSessionAuthChallengeUseCredential;
|
||||
}
|
||||
}
|
||||
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodClientCertificate] && self->x509Credential) {
|
||||
*credential = self->x509Credential;
|
||||
return NSURLSessionAuthChallengeUseCredential;
|
||||
}
|
||||
|
||||
return NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(SM_AFHTTPSessionManager*)manager {
|
||||
[headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
[manager.requestSerializer setValue:obj forHTTPHeaderField:key];
|
||||
}];
|
||||
}
|
||||
|
||||
- (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:(SM_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;
|
||||
@@ -52,6 +97,19 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(SM_AFHTTPSessionManager*)manager {
|
||||
[manager.requestSerializer setTimeoutInterval:timeout];
|
||||
}
|
||||
|
||||
- (void)setResponseSerializer:(NSString*)responseType forManager:(SM_AFHTTPSessionManager*)manager {
|
||||
if ([responseType isEqualToString: @"text"] || [responseType isEqualToString: @"json"]) {
|
||||
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"];
|
||||
@@ -65,14 +123,21 @@
|
||||
}
|
||||
|
||||
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error {
|
||||
bool aborted = error.code == NSURLErrorCancelled;
|
||||
if(aborted){
|
||||
[dictionary setObject:[NSNumber numberWithInt:-8] forKey:@"status"];
|
||||
[dictionary setObject:@"Request was aborted" forKey:@"error"];
|
||||
}
|
||||
if (response != nil) {
|
||||
[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(!aborted){
|
||||
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
|
||||
if (error.userInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey]) {
|
||||
[dictionary setObject:error.userInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey] forKey:@"error"];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if(!aborted) {
|
||||
[dictionary setObject:[self getStatusCode:error] forKey:@"status"];
|
||||
[dictionary setObject:[error localizedDescription] forKey:@"error"];
|
||||
}
|
||||
@@ -93,16 +158,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];
|
||||
}
|
||||
@@ -120,344 +192,362 @@
|
||||
return headerFieldsCopy;
|
||||
}
|
||||
|
||||
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
|
||||
[manager.requestSerializer setTimeoutInterval:timeout];
|
||||
- (void)executeRequestWithoutData:(CDVInvokedUrlCommand*)command withMethod:(NSString*) method {
|
||||
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:1];
|
||||
NSTimeInterval connectTimeout = [[command.arguments objectAtIndex:2] doubleValue];
|
||||
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:5];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:6];
|
||||
|
||||
[self setRequestSerializer: @"default" forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:readTimeout 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) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
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];
|
||||
[manager invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
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 invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
NSURLSessionDataTask *task = [manager downloadTaskWithHTTPMethod:method URLString:url parameters:nil progress:nil success:onSuccess failure:onFailure];
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enableSSLPinning:(CDVInvokedUrlCommand*)command {
|
||||
bool enable = [[command.arguments objectAtIndex:0] boolValue];
|
||||
- (void)executeRequestWithData:(CDVInvokedUrlCommand*)command withMethod:(NSString*)method {
|
||||
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
|
||||
|
||||
if (enable) {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
|
||||
} else {
|
||||
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
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 connectTimeout = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:5] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:6] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:7];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:8];
|
||||
|
||||
[self setRequestSerializer: serializerName forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:readTimeout 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) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
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) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
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];
|
||||
[manager invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
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 invalidateSessionCancelingTasks:YES];
|
||||
};
|
||||
|
||||
NSURLSessionDataTask *task;
|
||||
if ([serializerName isEqualToString:@"multipart"]) {
|
||||
task = [manager uploadTaskWithHTTPMethod:method URLString:url parameters:nil constructingBodyWithBlock:constructBody progress:nil success:onSuccess failure:onFailure];
|
||||
} else {
|
||||
task = [manager uploadTaskWithHTTPMethod:method URLString:url parameters:data progress:nil success:onSuccess failure:onFailure];
|
||||
}
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@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"] || [certMode isEqualToString: @"legacy"]) {
|
||||
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
securityPolicy.allowInvalidCertificates = NO;
|
||||
securityPolicy.validatesDomainName = YES;
|
||||
} else if ([certMode isEqualToString: @"nocheck"]) {
|
||||
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
|
||||
securityPolicy.allowInvalidCertificates = YES;
|
||||
securityPolicy.validatesDomainName = NO;
|
||||
} else if ([certMode isEqualToString: @"pinned"]) {
|
||||
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
|
||||
securityPolicy.allowInvalidCertificates = NO;
|
||||
securityPolicy.validatesDomainName = YES;
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)disableRedirect:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
bool disable = [[command.arguments objectAtIndex:0] boolValue];
|
||||
- (void)setClientAuthMode:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult;
|
||||
NSString *mode = [command.arguments objectAtIndex:0];
|
||||
|
||||
redirect = !disable;
|
||||
if ([mode isEqualToString:@"none"]) {
|
||||
x509Credential = nil;
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
if ([mode isEqualToString:@"systemstore"]) {
|
||||
NSString *alias = [command.arguments objectAtIndex:1];
|
||||
|
||||
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult = nil;
|
||||
bool allow = [[command.arguments objectAtIndex:0] boolValue];
|
||||
// TODO
|
||||
|
||||
securityPolicy.allowInvalidCertificates = allow;
|
||||
securityPolicy.validatesDomainName = !allow;
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"mode 'systemstore' is not supported on iOS"];
|
||||
}
|
||||
|
||||
if ([mode isEqualToString:@"buffer"]) {
|
||||
CFDataRef container = (__bridge CFDataRef) [command.arguments objectAtIndex:2];
|
||||
CFStringRef password = (__bridge CFStringRef) [command.arguments objectAtIndex:3];
|
||||
|
||||
const void *keys[] = { kSecImportExportPassphrase };
|
||||
const void *values[] = { password };
|
||||
|
||||
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
|
||||
CFArrayRef items;
|
||||
OSStatus securityError = SecPKCS12Import(container, options, &items);
|
||||
CFRelease(options);
|
||||
|
||||
if (securityError != noErr) {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
|
||||
} else {
|
||||
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
|
||||
SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
|
||||
SecTrustRef trust = (SecTrustRef)CFDictionaryGetValue(identityDict, kSecImportItemTrust);
|
||||
|
||||
int count = (int)SecTrustGetCertificateCount(trust);
|
||||
NSMutableArray* trustCertificates = nil;
|
||||
if (count > 1) {
|
||||
trustCertificates = [NSMutableArray arrayWithCapacity:SecTrustGetCertificateCount(trust)];
|
||||
for (int i=1;i<count; ++i) {
|
||||
[trustCertificates addObject:(id)SecTrustGetCertificateAtIndex(trust, i)];
|
||||
}
|
||||
}
|
||||
|
||||
self->x509Credential = [NSURLCredential credentialWithIdentity:identity certificates: trustCertificates persistence:NSURLCredentialPersistenceForSession];
|
||||
CFRelease(items);
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
@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];
|
||||
} 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];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[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];
|
||||
|
||||
@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];
|
||||
} 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];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[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];
|
||||
|
||||
@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];
|
||||
} 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];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[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];
|
||||
|
||||
@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];
|
||||
} 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];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[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];
|
||||
|
||||
@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];
|
||||
} 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];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[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];
|
||||
|
||||
@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];
|
||||
} 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];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
[self executeRequestWithoutData: command withMethod:@"HEAD"];
|
||||
}
|
||||
|
||||
- (void)uploadFile:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
- (void)options:(CDVInvokedUrlCommand*)command {
|
||||
[self executeRequestWithoutData: command withMethod:@"OPTIONS"];
|
||||
}
|
||||
|
||||
- (void)uploadFiles:(CDVInvokedUrlCommand*)command {
|
||||
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
|
||||
|
||||
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 connectTimeout = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:5] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:6] boolValue];
|
||||
NSString *responseType = [command.arguments objectAtIndex:7];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:8];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
[self setTimeout:readTimeout forManager: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) {
|
||||
NSURLSessionDataTask *task = [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) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
|
||||
[dictionary setObject:@"Could not add file to post body." forKey:@"error"];
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
return;
|
||||
}
|
||||
} progress:nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
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) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
|
||||
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];
|
||||
}];
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)downloadFile:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
|
||||
manager.responseSerializer = [SM_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 connectTimeout = [[command.arguments objectAtIndex:3] doubleValue];
|
||||
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:4] doubleValue];
|
||||
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:6];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
[self setTimeout:readTimeout forManager: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) {
|
||||
NSURLSessionDataTask *task = [manager GET:url parameters:nil progress: nil success:^(NSURLSessionTask *task, id responseObject) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
/*
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
@@ -495,6 +585,7 @@
|
||||
}
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
return;
|
||||
}
|
||||
NSData *data = (NSData *)responseObject;
|
||||
@@ -504,6 +595,7 @@
|
||||
[dictionary setObject:@"Could not write the data to the given filePath." forKey:@"error"];
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -514,18 +606,51 @@
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
} failure:^(NSURLSessionTask *task, NSError *error) {
|
||||
[weakSelf removeRequest:reqId];
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
|
||||
[dictionary setObject:@"There was an error downloading the file" forKey:@"error"];
|
||||
|
||||
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
}];
|
||||
[self addRequest:reqId forTask:task];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
[self handleException:exception withCommand:command];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)abort:(CDVInvokedUrlCommand*)command {
|
||||
|
||||
NSNumber *reqId = [command.arguments objectAtIndex:0];
|
||||
|
||||
CDVPluginResult *pluginResult;
|
||||
bool removed = false;
|
||||
NSURLSessionDataTask *task = [reqDict objectForKey:reqId];
|
||||
if(task){
|
||||
@try{
|
||||
[task cancel];
|
||||
removed = true;
|
||||
} @catch (NSException *exception) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setValue:exception.userInfo forKey:@"error"];
|
||||
[dictionary setObject:[NSNumber numberWithInt:-1] forKey:@"status"];
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
|
||||
}
|
||||
}
|
||||
|
||||
if(!pluginResult){
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
[dictionary setObject:[NSNumber numberWithBool:removed] forKey:@"aborted"];
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
20
src/ios/SDNetworkActivityIndicator/LICENSE
Normal file
20
src/ios/SDNetworkActivityIndicator/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
52
src/ios/SDNetworkActivityIndicator/README.md
Normal file
52
src/ios/SDNetworkActivityIndicator/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# SDNetworkActivityIndicator
|
||||
|
||||
Handle showing / hiding of the iOS network activity indicator to allow multiple concurrent threads to show / hide the indicator such that the indicator remains visible until all the requests have completed and requested the indicator to be hidden.
|
||||
|
||||
## Requirements
|
||||
|
||||
* iOS 5.0 or later.
|
||||
* ARC memory management.
|
||||
|
||||
## Installation
|
||||
|
||||
The easiest way to install it is by copying the following files to your project:
|
||||
|
||||
* SDNetworkActivityIndicator.h
|
||||
* SDNetworkActivityIndicator.m
|
||||
|
||||
## Usage
|
||||
|
||||
* When you start a network activity (will show the network activity indicator):
|
||||
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
|
||||
|
||||
* When you finish a network activity (will hide the network activity indicator only if the number of calls to `stopActivity` matches the number of calls to `startActivity`):
|
||||
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
|
||||
|
||||
* To hide the network activity indicator regardless of whether all activities have finished (without having to call `stopActivity` for each `startActivity` called):
|
||||
|
||||
[[SDNetworkActivityIndicator sharedActivityIndicator] stopAllActivity];
|
||||
|
||||
|
||||
## License
|
||||
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* This file is part of the SDNetworkActivityIndicator package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SDNetworkActivityIndicator : NSObject
|
||||
|
||||
+ (id)sharedActivityIndicator;
|
||||
- (void)startActivity;
|
||||
- (void)stopActivity;
|
||||
- (void)stopAllActivity;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of the SDNetworkActivityIndicator package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "SDNetworkActivityIndicator.h"
|
||||
|
||||
@interface SDNetworkActivityIndicator()
|
||||
{
|
||||
@private NSUInteger counter;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation SDNetworkActivityIndicator
|
||||
|
||||
+ (instancetype) sharedActivityIndicator
|
||||
{
|
||||
static id _sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_sharedInstance = [[self alloc] init];
|
||||
});
|
||||
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startActivity
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
counter++;
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopActivity
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
if (counter > 0 && --counter == 0)
|
||||
{
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopAllActivity
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
counter = 0;
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFHTTPSessionManager.h
|
||||
// SM_AFHTTPSessionManager.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -31,14 +31,14 @@
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#import "AFURLSessionManager.h"
|
||||
#import "SM_AFURLSessionManager.h"
|
||||
|
||||
/**
|
||||
`AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths.
|
||||
`SM_AFHTTPSessionManager` is a subclass of `SM_AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths.
|
||||
|
||||
## Subclassing Notes
|
||||
|
||||
Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application.
|
||||
Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass `SM_AFHTTPSessionManager`, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application.
|
||||
|
||||
For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect.
|
||||
|
||||
@@ -48,9 +48,9 @@
|
||||
|
||||
## Serialization
|
||||
|
||||
Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to `<AFURLRequestSerialization>`.
|
||||
Requests created by an HTTP client will contain default headers and encode parameters according to the `requestSerializer` property, which is an object conforming to `<SM_AFURLRequestSerialization>`.
|
||||
|
||||
Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `<AFURLResponseSerialization>`
|
||||
Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `<SM_AFURLResponseSerialization>`
|
||||
|
||||
## URL Construction Using Relative Paths
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>
|
||||
@interface SM_AFHTTPSessionManager : SM_AFURLSessionManager <NSSecureCoding, NSCopying>
|
||||
|
||||
/**
|
||||
The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods.
|
||||
@@ -81,30 +81,30 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
|
||||
|
||||
/**
|
||||
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
|
||||
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `SM_AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
|
||||
|
||||
@warning `requestSerializer` must not be `nil`.
|
||||
*/
|
||||
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
|
||||
@property (nonatomic, strong) SM_AFHTTPRequestSerializer <SM_AFURLRequestSerialization> * requestSerializer;
|
||||
|
||||
/**
|
||||
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
|
||||
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `SM_AFJSONResponseSerializer`.
|
||||
|
||||
@warning `responseSerializer` must not be `nil`.
|
||||
*/
|
||||
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
|
||||
@property (nonatomic, strong) SM_AFHTTPResponseSerializer <SM_AFURLResponseSerialization> * responseSerializer;
|
||||
|
||||
///---------------------
|
||||
/// @name Initialization
|
||||
///---------------------
|
||||
|
||||
/**
|
||||
Creates and returns an `AFHTTPSessionManager` object.
|
||||
Creates and returns an `SM_AFHTTPSessionManager` object.
|
||||
*/
|
||||
+ (instancetype)manager;
|
||||
|
||||
/**
|
||||
Initializes an `AFHTTPSessionManager` object with the specified base URL.
|
||||
Initializes an `SM_AFHTTPSessionManager` object with the specified base URL.
|
||||
|
||||
@param url The base URL for the HTTP client.
|
||||
|
||||
@@ -113,7 +113,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
|
||||
|
||||
/**
|
||||
Initializes an `AFHTTPSessionManager` object with the specified base URL.
|
||||
Initializes an `SM_AFHTTPSessionManager` object with the specified base URL.
|
||||
|
||||
This is the designated initializer.
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFHTTPSessionManager.m
|
||||
// SM_AFHTTPSessionManager.m
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -19,10 +19,10 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "AFHTTPSessionManager.h"
|
||||
#import "SM_AFHTTPSessionManager.h"
|
||||
|
||||
#import "AFURLRequestSerialization.h"
|
||||
#import "AFURLResponseSerialization.h"
|
||||
#import "SM_AFURLRequestSerialization.h"
|
||||
#import "SM_AFURLResponseSerialization.h"
|
||||
|
||||
#import <Availability.h>
|
||||
#import <TargetConditionals.h>
|
||||
@@ -40,11 +40,11 @@
|
||||
#import <WatchKit/WatchKit.h>
|
||||
#endif
|
||||
|
||||
@interface AFHTTPSessionManager ()
|
||||
@interface SM_AFHTTPSessionManager ()
|
||||
@property (readwrite, nonatomic, strong) NSURL *baseURL;
|
||||
@end
|
||||
|
||||
@implementation AFHTTPSessionManager
|
||||
@implementation SM_AFHTTPSessionManager
|
||||
@dynamic responseSerializer;
|
||||
|
||||
+ (instancetype)manager {
|
||||
@@ -78,21 +78,21 @@
|
||||
|
||||
self.baseURL = url;
|
||||
|
||||
self.requestSerializer = [AFHTTPRequestSerializer serializer];
|
||||
self.responseSerializer = [AFJSONResponseSerializer serializer];
|
||||
self.requestSerializer = [SM_AFHTTPRequestSerializer serializer];
|
||||
self.responseSerializer = [SM_AFJSONResponseSerializer serializer];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setRequestSerializer:(AFHTTPRequestSerializer <AFURLRequestSerialization> *)requestSerializer {
|
||||
- (void)setRequestSerializer:(SM_AFHTTPRequestSerializer <SM_AFURLRequestSerialization> *)requestSerializer {
|
||||
NSParameterAssert(requestSerializer);
|
||||
|
||||
_requestSerializer = requestSerializer;
|
||||
}
|
||||
|
||||
- (void)setResponseSerializer:(AFHTTPResponseSerializer <AFURLResponseSerialization> *)responseSerializer {
|
||||
- (void)setResponseSerializer:(SM_AFHTTPResponseSerializer <SM_AFURLResponseSerialization> *)responseSerializer {
|
||||
NSParameterAssert(responseSerializer);
|
||||
|
||||
[super setResponseSerializer:responseSerializer];
|
||||
@@ -184,6 +184,8 @@
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
@@ -223,6 +225,44 @@
|
||||
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];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
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 +287,73 @@
|
||||
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];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
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
|
||||
@@ -257,6 +364,8 @@
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
@@ -317,9 +426,9 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))];
|
||||
self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))];
|
||||
AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))];
|
||||
self.requestSerializer = [decoder decodeObjectOfClass:[SM_AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))];
|
||||
self.responseSerializer = [decoder decodeObjectOfClass:[SM_AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))];
|
||||
SM_AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[SM_AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))];
|
||||
if (decodedPolicy) {
|
||||
self.securityPolicy = decodedPolicy;
|
||||
}
|
||||
@@ -344,7 +453,7 @@
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration];
|
||||
SM_AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration];
|
||||
|
||||
HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone];
|
||||
HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone];
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFNetworkReachabilityManager.h
|
||||
// SM_AFNetworkReachabilityManager.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -34,15 +34,15 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
`AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
|
||||
`SM_AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
|
||||
|
||||
Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability.
|
||||
|
||||
See Apple's Reachability Sample Code ( https://developer.apple.com/library/ios/samplecode/reachability/ )
|
||||
|
||||
@warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined.
|
||||
@warning Instances of `SM_AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined.
|
||||
*/
|
||||
@interface AFNetworkReachabilityManager : NSObject
|
||||
@interface SM_AFNetworkReachabilityManager : NSObject
|
||||
|
||||
/**
|
||||
The current network reachability status.
|
||||
@@ -150,7 +150,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
## Network Reachability
|
||||
|
||||
The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses.
|
||||
The following constants are provided by `SM_AFNetworkReachabilityManager` as possible network reachability statuses.
|
||||
|
||||
enum {
|
||||
AFNetworkReachabilityStatusUnknown,
|
||||
@@ -175,8 +175,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification.
|
||||
|
||||
`AFNetworkingReachabilityNotificationStatusItem`
|
||||
A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification.
|
||||
`SM_AFNetworkingReachabilityNotificationStatusItem`
|
||||
A key in the userInfo dictionary in a `SM_AFNetworkingReachabilityDidChangeNotification` notification.
|
||||
The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status.
|
||||
*/
|
||||
|
||||
@@ -186,12 +186,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Posted when network reachability changes.
|
||||
This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability.
|
||||
This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `SM_AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability.
|
||||
|
||||
@warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingReachabilityDidChangeNotification;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingReachabilityNotificationStatusItem;
|
||||
|
||||
///--------------------
|
||||
/// @name Functions
|
||||
@@ -200,7 +200,7 @@ FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusIte
|
||||
/**
|
||||
Returns a localized string representation of an `AFNetworkReachabilityStatus` value.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
|
||||
FOUNDATION_EXPORT NSString * SM_AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFNetworkReachabilityManager.m
|
||||
// SM_AFNetworkReachabilityManager.m
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -19,7 +19,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "AFNetworkReachabilityManager.h"
|
||||
#import "SM_AFNetworkReachabilityManager.h"
|
||||
#if !TARGET_OS_WATCH
|
||||
|
||||
#import <netinet/in.h>
|
||||
@@ -28,22 +28,22 @@
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
|
||||
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
|
||||
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
|
||||
NSString * const SM_AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
|
||||
NSString * const SM_AFNetworkingReachabilityNotificationStatusItem = @"SM_AFNetworkingReachabilityNotificationStatusItem";
|
||||
|
||||
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
|
||||
|
||||
NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
|
||||
NSString * SM_AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
|
||||
switch (status) {
|
||||
case AFNetworkReachabilityStatusNotReachable:
|
||||
return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
|
||||
return NSLocalizedStringFromTable(@"Not Reachable", @"SM_AFNetworking", nil);
|
||||
case AFNetworkReachabilityStatusReachableViaWWAN:
|
||||
return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
|
||||
return NSLocalizedStringFromTable(@"Reachable via WWAN", @"SM_AFNetworking", nil);
|
||||
case AFNetworkReachabilityStatusReachableViaWiFi:
|
||||
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
|
||||
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"SM_AFNetworking", nil);
|
||||
case AFNetworkReachabilityStatusUnknown:
|
||||
default:
|
||||
return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
|
||||
return NSLocalizedStringFromTable(@"Unknown", @"SM_AFNetworking", nil);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFN
|
||||
block(status);
|
||||
}
|
||||
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
|
||||
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
|
||||
NSDictionary *userInfo = @{ SM_AFNetworkingReachabilityNotificationStatusItem: @(status) };
|
||||
[notificationCenter postNotificationName:SM_AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,16 +105,16 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
|
||||
}
|
||||
}
|
||||
|
||||
@interface AFNetworkReachabilityManager ()
|
||||
@interface SM_AFNetworkReachabilityManager ()
|
||||
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
|
||||
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
|
||||
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
|
||||
@end
|
||||
|
||||
@implementation AFNetworkReachabilityManager
|
||||
@implementation SM_AFNetworkReachabilityManager
|
||||
|
||||
+ (instancetype)sharedManager {
|
||||
static AFNetworkReachabilityManager *_sharedManager = nil;
|
||||
static SM_AFNetworkReachabilityManager *_sharedManager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_sharedManager = [self manager];
|
||||
@@ -126,7 +126,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
|
||||
+ (instancetype)managerForDomain:(NSString *)domain {
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
|
||||
|
||||
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
|
||||
SM_AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
|
||||
|
||||
CFRelease(reachability);
|
||||
|
||||
@@ -135,7 +135,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
|
||||
|
||||
+ (instancetype)managerForAddress:(const void *)address {
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
|
||||
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
|
||||
SM_AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
|
||||
|
||||
CFRelease(reachability);
|
||||
|
||||
@@ -240,7 +240,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
|
||||
#pragma mark -
|
||||
|
||||
- (NSString *)localizedNetworkReachabilityStatusString {
|
||||
return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
|
||||
return SM_AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -1,6 +1,6 @@
|
||||
// AFNetworking.h
|
||||
// SM_AFNetworking.h
|
||||
//
|
||||
// Copyright (c) 2013 AFNetworking (http://afnetworking.com/)
|
||||
// Copyright (c) 2013 SM_AFNetworking (http://afnetworking.com/)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,15 +27,15 @@
|
||||
#ifndef _AFNETWORKING_
|
||||
#define _AFNETWORKING_
|
||||
|
||||
#import "AFURLRequestSerialization.h"
|
||||
#import "AFURLResponseSerialization.h"
|
||||
#import "AFSecurityPolicy.h"
|
||||
#import "SM_AFURLRequestSerialization.h"
|
||||
#import "SM_AFURLResponseSerialization.h"
|
||||
#import "SM_AFSecurityPolicy.h"
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
#import "AFNetworkReachabilityManager.h"
|
||||
#import "SM_AFNetworkReachabilityManager.h"
|
||||
#endif
|
||||
|
||||
#import "AFURLSessionManager.h"
|
||||
#import "AFHTTPSessionManager.h"
|
||||
#import "SM_AFURLSessionManager.h"
|
||||
#import "SM_AFHTTPSessionManager.h"
|
||||
|
||||
#endif /* _AFNETWORKING_ */
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFSecurityPolicy.h
|
||||
// SM_AFSecurityPolicy.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -29,14 +29,14 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
|
||||
};
|
||||
|
||||
/**
|
||||
`AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections.
|
||||
`SM_AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections.
|
||||
|
||||
Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled.
|
||||
*/
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AFSecurityPolicy : NSObject <NSSecureCoding, NSCopying>
|
||||
@interface SM_AFSecurityPolicy : NSObject <NSSecureCoding, NSCopying>
|
||||
|
||||
/**
|
||||
The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`.
|
||||
@@ -46,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
The certificates used to evaluate server trust according to the SSL pinning mode.
|
||||
|
||||
By default, this property is set to any (`.cer`) certificates included in the target compiling AFNetworking. Note that if you are using AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`.
|
||||
By default, this property is set to any (`.cer`) certificates included in the target compiling SM_AFNetworking. Note that if you are using SM_AFNetworking as embedded framework, no certificates will be pinned by default. Use `certificatesInBundle` to load certificates from your target, and then create a new policy by calling `policyWithPinningMode:withPinnedCertificates`.
|
||||
|
||||
Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches.
|
||||
*/
|
||||
@@ -67,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
///-----------------------------------------
|
||||
|
||||
/**
|
||||
Returns any certificates included in the bundle. If you are using AFNetworking as an embedded framework, you must use this method to find the certificates you have included in your app bundle, and use them when creating your security policy by calling `policyWithPinningMode:withPinnedCertificates`.
|
||||
Returns any certificates included in the bundle. If you are using SM_AFNetworking as an embedded framework, you must use this method to find the certificates you have included in your app bundle, and use them when creating your security policy by calling `policyWithPinningMode:withPinnedCertificates`.
|
||||
|
||||
@return The certificates included in the given bundle.
|
||||
*/
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFSecurityPolicy.m
|
||||
// SM_AFSecurityPolicy.m
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -19,7 +19,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "AFSecurityPolicy.h"
|
||||
#import "SM_AFSecurityPolicy.h"
|
||||
|
||||
#import <AssertMacros.h>
|
||||
|
||||
@@ -148,24 +148,27 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFSecurityPolicy()
|
||||
@interface SM_AFSecurityPolicy()
|
||||
@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
|
||||
@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;
|
||||
@end
|
||||
|
||||
@implementation AFSecurityPolicy
|
||||
@implementation SM_AFSecurityPolicy
|
||||
|
||||
+(BOOL) isRunningOnCapacitor {
|
||||
return NSClassFromString(@"CAPPlugin") != nil;
|
||||
}
|
||||
|
||||
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
|
||||
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
|
||||
|
||||
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
|
||||
for (NSString *path in paths) {
|
||||
NSData *certificateData = [NSData dataWithContentsOfFile:path];
|
||||
[certificates addObject:certificateData];
|
||||
NSString* assetDir = @"www";
|
||||
if([self isRunningOnCapacitor]) {
|
||||
// we are running on capacitor and its assets dir is 'public'
|
||||
assetDir = @"public";
|
||||
}
|
||||
|
||||
// also add certs from www/certificates
|
||||
paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"www/certificates"];
|
||||
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory: [NSString stringWithFormat:@"%@/certificates", assetDir]];
|
||||
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
|
||||
|
||||
for (NSString *path in paths) {
|
||||
NSData *certificateData = [NSData dataWithContentsOfFile:path];
|
||||
[certificates addObject:certificateData];
|
||||
@@ -178,7 +181,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
static NSSet *_defaultPinnedCertificates = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
|
||||
NSBundle *bundle = [self isRunningOnCapacitor] ? [NSBundle mainBundle] : [NSBundle bundleForClass:[self class]];
|
||||
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
|
||||
});
|
||||
|
||||
@@ -186,7 +189,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
}
|
||||
|
||||
+ (instancetype)defaultPolicy {
|
||||
AFSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
SM_AFSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
|
||||
|
||||
return securityPolicy;
|
||||
@@ -197,7 +200,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
}
|
||||
|
||||
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
|
||||
AFSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
SM_AFSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
securityPolicy.SSLPinningMode = pinningMode;
|
||||
|
||||
[securityPolicy setPinnedCertificates:pinnedCertificates];
|
||||
@@ -284,13 +287,13 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
|
||||
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
|
||||
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
|
||||
|
||||
|
||||
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
|
||||
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
case AFSSLPinningModePublicKey: {
|
||||
@@ -307,7 +310,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
return trustedPublicKeyCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -348,7 +351,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
|
||||
securityPolicy.SSLPinningMode = self.SSLPinningMode;
|
||||
securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
|
||||
securityPolicy.validatesDomainName = self.validatesDomainName;
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFURLRequestSerialization.h
|
||||
// SM_AFURLRequestSerialization.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -44,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@return The percent-escaped string.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
|
||||
FOUNDATION_EXPORT NSString * SM_AFPercentEscapedStringFromString(NSString *string);
|
||||
|
||||
/**
|
||||
A helper method to generate encoded url query parameters for appending to the end of a URL.
|
||||
@@ -53,14 +53,14 @@ FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
|
||||
|
||||
@return A url encoded query string
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * AFQueryStringFromParameters(NSDictionary *parameters);
|
||||
FOUNDATION_EXPORT NSString * SM_AFQueryStringFromParameters(NSDictionary *parameters);
|
||||
|
||||
/**
|
||||
The `AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary.
|
||||
The `SM_AFURLRequestSerialization` protocol is adopted by an object that encodes parameters for a specified HTTP requests. Request serializers may encode parameters as query strings, HTTP bodies, setting the appropriate HTTP header fields as necessary.
|
||||
|
||||
For example, a JSON request serializer may set the HTTP body of the request to a JSON representation, and set the `Content-Type` HTTP header field value to `application/json`.
|
||||
*/
|
||||
@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
|
||||
@protocol SM_AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
|
||||
|
||||
/**
|
||||
Returns a request with the specified parameters encoded into a copy of the original request.
|
||||
@@ -89,11 +89,11 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
|
||||
@protocol AFMultipartFormData;
|
||||
|
||||
/**
|
||||
`AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
|
||||
`SM_AFHTTPRequestSerializer` conforms to the `SM_AFURLRequestSerialization` & `SM_AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
|
||||
|
||||
Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior.
|
||||
Any request or response serializer dealing with HTTP is encouraged to subclass `SM_AFHTTPRequestSerializer` in order to ensure consistent default behavior.
|
||||
*/
|
||||
@interface AFHTTPRequestSerializer : NSObject <AFURLRequestSerialization>
|
||||
@interface SM_AFHTTPRequestSerializer : NSObject <SM_AFURLRequestSerialization>
|
||||
|
||||
/**
|
||||
The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default.
|
||||
@@ -265,7 +265,7 @@ forHTTPHeaderField:(NSString *)field;
|
||||
@param fileURL The file URL to write multipart form contents to.
|
||||
@param handler A handler block to execute.
|
||||
|
||||
@discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request.
|
||||
@discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `SM_AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request.
|
||||
|
||||
@see https://github.com/AFNetworking/AFNetworking/issues/1398
|
||||
*/
|
||||
@@ -278,7 +278,7 @@ forHTTPHeaderField:(NSString *)field;
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`.
|
||||
The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `SM_AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`.
|
||||
*/
|
||||
@protocol AFMultipartFormData
|
||||
|
||||
@@ -365,7 +365,7 @@ forHTTPHeaderField:(NSString *)field;
|
||||
/**
|
||||
Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream.
|
||||
|
||||
When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth.
|
||||
When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`SM_kAFUploadStream3GSuggestedPacketSize` and `SM_kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth.
|
||||
|
||||
@param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb.
|
||||
@param delay Duration of delay each time a packet is read. By default, no delay is set.
|
||||
@@ -378,9 +378,9 @@ forHTTPHeaderField:(NSString *)field;
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
`AFJSONRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`.
|
||||
`SM_AFJSONRequestSerializer` is a subclass of `SM_AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`.
|
||||
*/
|
||||
@interface AFJSONRequestSerializer : AFHTTPRequestSerializer
|
||||
@interface SM_AFJSONRequestSerializer : SM_AFHTTPRequestSerializer
|
||||
|
||||
/**
|
||||
Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default.
|
||||
@@ -399,9 +399,9 @@ forHTTPHeaderField:(NSString *)field;
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
`AFPropertyListRequestSerializer` is a subclass of `AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`.
|
||||
`SM_AFPropertyListRequestSerializer` is a subclass of `SM_AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`.
|
||||
*/
|
||||
@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer
|
||||
@interface SM_AFPropertyListRequestSerializer : SM_AFHTTPRequestSerializer
|
||||
|
||||
/**
|
||||
The property list format. Possible values are described in "NSPropertyListFormat".
|
||||
@@ -437,28 +437,28 @@ forHTTPHeaderField:(NSString *)field;
|
||||
|
||||
The following error domain is predefined.
|
||||
|
||||
- `NSString * const AFURLRequestSerializationErrorDomain`
|
||||
- `NSString * const SM_AFURLRequestSerializationErrorDomain`
|
||||
|
||||
### Constants
|
||||
|
||||
`AFURLRequestSerializationErrorDomain`
|
||||
AFURLRequestSerializer errors. Error codes for `AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
|
||||
`SM_AFURLRequestSerializationErrorDomain`
|
||||
AFURLRequestSerializer errors. Error codes for `SM_AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFURLRequestSerializationErrorDomain;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFURLRequestSerializationErrorDomain;
|
||||
|
||||
/**
|
||||
## User info dictionary keys
|
||||
|
||||
These keys may exist in the user info dictionary, in addition to those defined for NSError.
|
||||
|
||||
- `NSString * const AFNetworkingOperationFailingURLRequestErrorKey`
|
||||
- `NSString * const SM_AFNetworkingOperationFailingURLRequestErrorKey`
|
||||
|
||||
### Constants
|
||||
|
||||
`AFNetworkingOperationFailingURLRequestErrorKey`
|
||||
The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFURLRequestSerializationErrorDomain`.
|
||||
`SM_AFNetworkingOperationFailingURLRequestErrorKey`
|
||||
The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `SM_AFURLRequestSerializationErrorDomain`.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLRequestErrorKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingOperationFailingURLRequestErrorKey;
|
||||
|
||||
/**
|
||||
## Throttling Bandwidth for HTTP Request Input Streams
|
||||
@@ -467,13 +467,13 @@ FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLRequestErrorKe
|
||||
|
||||
### Constants
|
||||
|
||||
`kAFUploadStream3GSuggestedPacketSize`
|
||||
`SM_kAFUploadStream3GSuggestedPacketSize`
|
||||
Maximum packet size, in number of bytes. Equal to 16kb.
|
||||
|
||||
`kAFUploadStream3GSuggestedDelay`
|
||||
`SM_kAFUploadStream3GSuggestedDelay`
|
||||
Duration of delay each time a packet is read. Equal to 0.2 seconds.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSUInteger const kAFUploadStream3GSuggestedPacketSize;
|
||||
FOUNDATION_EXPORT NSTimeInterval const kAFUploadStream3GSuggestedDelay;
|
||||
FOUNDATION_EXPORT NSUInteger const SM_kAFUploadStream3GSuggestedPacketSize;
|
||||
FOUNDATION_EXPORT NSTimeInterval const SM_kAFUploadStream3GSuggestedDelay;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFURLRequestSerialization.m
|
||||
// SM_AFURLRequestSerialization.m
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -19,7 +19,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "AFURLRequestSerialization.h"
|
||||
#import "SM_AFURLRequestSerialization.h"
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
@@ -27,8 +27,8 @@
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
NSString * const AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request";
|
||||
NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response";
|
||||
NSString * const SM_AFURLRequestSerializationErrorDomain = @"com.alamofire.error.serialization.request";
|
||||
NSString * const SM_AFNetworkingOperationFailingURLRequestErrorKey = @"com.alamofire.serialization.request.error.response";
|
||||
|
||||
typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id parameters, NSError *__autoreleasing *error);
|
||||
|
||||
@@ -44,7 +44,7 @@ typedef NSString * (^AFQueryStringSerializationBlock)(NSURLRequest *request, id
|
||||
- parameter string: The string to be percent-escaped.
|
||||
- returns: The percent-escaped string.
|
||||
*/
|
||||
NSString * AFPercentEscapedStringFromString(NSString *string) {
|
||||
NSString * SM_AFPercentEscapedStringFromString(NSString *string) {
|
||||
static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
|
||||
static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
|
||||
|
||||
@@ -78,7 +78,7 @@ NSString * AFPercentEscapedStringFromString(NSString *string) {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFQueryStringPair : NSObject
|
||||
@interface SM_AFQueryStringPair : NSObject
|
||||
@property (readwrite, nonatomic, strong) id field;
|
||||
@property (readwrite, nonatomic, strong) id value;
|
||||
|
||||
@@ -87,7 +87,7 @@ NSString * AFPercentEscapedStringFromString(NSString *string) {
|
||||
- (NSString *)URLEncodedStringValue;
|
||||
@end
|
||||
|
||||
@implementation AFQueryStringPair
|
||||
@implementation SM_AFQueryStringPair
|
||||
|
||||
- (instancetype)initWithField:(id)field value:(id)value {
|
||||
self = [super init];
|
||||
@@ -103,9 +103,9 @@ NSString * AFPercentEscapedStringFromString(NSString *string) {
|
||||
|
||||
- (NSString *)URLEncodedStringValue {
|
||||
if (!self.value || [self.value isEqual:[NSNull null]]) {
|
||||
return AFPercentEscapedStringFromString([self.field description]);
|
||||
return SM_AFPercentEscapedStringFromString([self.field description]);
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
|
||||
return [NSString stringWithFormat:@"%@=%@", SM_AFPercentEscapedStringFromString([self.field description]), SM_AFPercentEscapedStringFromString([self.value description])];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,23 +113,23 @@ NSString * AFPercentEscapedStringFromString(NSString *string) {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary);
|
||||
FOUNDATION_EXPORT NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value);
|
||||
FOUNDATION_EXPORT NSArray * SM_AFQueryStringPairsFromDictionary(NSDictionary *dictionary);
|
||||
FOUNDATION_EXPORT NSArray * SM_AFQueryStringPairsFromKeyAndValue(NSString *key, id value);
|
||||
|
||||
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
|
||||
NSString * SM_AFQueryStringFromParameters(NSDictionary *parameters) {
|
||||
NSMutableArray *mutablePairs = [NSMutableArray array];
|
||||
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
|
||||
for (SM_AFQueryStringPair *pair in SM_AFQueryStringPairsFromDictionary(parameters)) {
|
||||
[mutablePairs addObject:[pair URLEncodedStringValue]];
|
||||
}
|
||||
|
||||
return [mutablePairs componentsJoinedByString:@"&"];
|
||||
}
|
||||
|
||||
NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
|
||||
return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
|
||||
NSArray * SM_AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
|
||||
return SM_AFQueryStringPairsFromKeyAndValue(nil, dictionary);
|
||||
}
|
||||
|
||||
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
||||
NSArray * SM_AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
||||
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
|
||||
|
||||
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
|
||||
@@ -140,21 +140,21 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
||||
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
|
||||
id nestedValue = dictionary[nestedKey];
|
||||
if (nestedValue) {
|
||||
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
|
||||
[mutableQueryStringComponents addObjectsFromArray:SM_AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
|
||||
}
|
||||
}
|
||||
} else if ([value isKindOfClass:[NSArray class]]) {
|
||||
NSArray *array = value;
|
||||
for (id nestedValue in array) {
|
||||
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
|
||||
[mutableQueryStringComponents addObjectsFromArray:SM_AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
|
||||
}
|
||||
} else if ([value isKindOfClass:[NSSet class]]) {
|
||||
NSSet *set = value;
|
||||
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
|
||||
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
|
||||
[mutableQueryStringComponents addObjectsFromArray:SM_AFQueryStringPairsFromKeyAndValue(key, obj)];
|
||||
}
|
||||
} else {
|
||||
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
|
||||
[mutableQueryStringComponents addObject:[[SM_AFQueryStringPair alloc] initWithField:key value:value]];
|
||||
}
|
||||
|
||||
return mutableQueryStringComponents;
|
||||
@@ -162,7 +162,7 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFStreamingMultipartFormData : NSObject <AFMultipartFormData>
|
||||
@interface SM_AFStreamingMultipartFormData : NSObject <AFMultipartFormData>
|
||||
- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
|
||||
stringEncoding:(NSStringEncoding)encoding;
|
||||
|
||||
@@ -171,26 +171,26 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
|
||||
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
|
||||
static NSArray * SM_AFHTTPRequestSerializerObservedKeyPaths() {
|
||||
static NSArray *_SM_AFHTTPRequestSerializerObservedKeyPaths = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
|
||||
_SM_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
|
||||
});
|
||||
|
||||
return _AFHTTPRequestSerializerObservedKeyPaths;
|
||||
return _SM_AFHTTPRequestSerializerObservedKeyPaths;
|
||||
}
|
||||
|
||||
static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext;
|
||||
static void *SM_AFHTTPRequestSerializerObserverContext = &SM_AFHTTPRequestSerializerObserverContext;
|
||||
|
||||
@interface AFHTTPRequestSerializer ()
|
||||
@interface SM_AFHTTPRequestSerializer ()
|
||||
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;
|
||||
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
|
||||
@property (readwrite, nonatomic, assign) AFHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;
|
||||
@property (readwrite, nonatomic, copy) AFQueryStringSerializationBlock queryStringSerialization;
|
||||
@end
|
||||
|
||||
@implementation AFHTTPRequestSerializer
|
||||
@implementation SM_AFHTTPRequestSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
return [[self alloc] init];
|
||||
@@ -239,9 +239,9 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
|
||||
self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
|
||||
|
||||
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
|
||||
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
|
||||
for (NSString *keyPath in SM_AFHTTPRequestSerializerObservedKeyPaths()) {
|
||||
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
|
||||
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
|
||||
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:SM_AFHTTPRequestSerializerObserverContext];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,9 +249,9 @@ static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerOb
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
|
||||
for (NSString *keyPath in SM_AFHTTPRequestSerializerObservedKeyPaths()) {
|
||||
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
|
||||
[self removeObserver:self forKeyPath:keyPath context:AFHTTPRequestSerializerObserverContext];
|
||||
[self removeObserver:self forKeyPath:keyPath context:SM_AFHTTPRequestSerializerObserverContext];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,7 +353,7 @@ forHTTPHeaderField:(NSString *)field
|
||||
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
mutableRequest.HTTPMethod = method;
|
||||
|
||||
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
|
||||
for (NSString *keyPath in SM_AFHTTPRequestSerializerObservedKeyPaths()) {
|
||||
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
|
||||
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
|
||||
}
|
||||
@@ -375,10 +375,10 @@ forHTTPHeaderField:(NSString *)field
|
||||
|
||||
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
|
||||
|
||||
__block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
|
||||
__block SM_AFStreamingMultipartFormData *formData = [[SM_AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
|
||||
|
||||
if (parameters) {
|
||||
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
|
||||
for (SM_AFQueryStringPair *pair in SM_AFQueryStringPairsFromDictionary(parameters)) {
|
||||
NSData *data = nil;
|
||||
if ([pair.value isKindOfClass:[NSData class]]) {
|
||||
data = pair.value;
|
||||
@@ -455,7 +455,7 @@ forHTTPHeaderField:(NSString *)field
|
||||
return mutableRequest;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLRequestSerialization
|
||||
#pragma mark - SM_AFURLRequestSerialization
|
||||
|
||||
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
|
||||
withParameters:(id)parameters
|
||||
@@ -487,7 +487,7 @@ forHTTPHeaderField:(NSString *)field
|
||||
} else {
|
||||
switch (self.queryStringSerializationStyle) {
|
||||
case AFHTTPRequestQueryStringDefaultStyle:
|
||||
query = AFQueryStringFromParameters(parameters);
|
||||
query = SM_AFQueryStringFromParameters(parameters);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -514,7 +514,7 @@ forHTTPHeaderField:(NSString *)field
|
||||
#pragma mark - NSKeyValueObserving
|
||||
|
||||
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
|
||||
if ([AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
|
||||
if ([SM_AFHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -526,7 +526,7 @@ forHTTPHeaderField:(NSString *)field
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context
|
||||
{
|
||||
if (context == AFHTTPRequestSerializerObserverContext) {
|
||||
if (context == SM_AFHTTPRequestSerializerObserverContext) {
|
||||
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
|
||||
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
|
||||
} else {
|
||||
@@ -561,7 +561,7 @@ forHTTPHeaderField:(NSString *)field
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFHTTPRequestSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
serializer.mutableHTTPRequestHeaders = [self.mutableHTTPRequestHeaders mutableCopyWithZone:zone];
|
||||
serializer.queryStringSerializationStyle = self.queryStringSerializationStyle;
|
||||
serializer.queryStringSerialization = self.queryStringSerialization;
|
||||
@@ -601,10 +601,10 @@ static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
|
||||
}
|
||||
}
|
||||
|
||||
NSUInteger const kAFUploadStream3GSuggestedPacketSize = 1024 * 16;
|
||||
NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
NSUInteger const SM_kAFUploadStream3GSuggestedPacketSize = 1024 * 16;
|
||||
NSTimeInterval const SM_kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
|
||||
@interface AFHTTPBodyPart : NSObject
|
||||
@interface SM_AFHTTPBodyPart : NSObject
|
||||
@property (nonatomic, assign) NSStringEncoding stringEncoding;
|
||||
@property (nonatomic, strong) NSDictionary *headers;
|
||||
@property (nonatomic, copy) NSString *boundary;
|
||||
@@ -622,7 +622,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
maxLength:(NSUInteger)length;
|
||||
@end
|
||||
|
||||
@interface AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
|
||||
@interface SM_AFMultipartBodyStream : NSInputStream <NSStreamDelegate>
|
||||
@property (nonatomic, assign) NSUInteger numberOfBytesInPacket;
|
||||
@property (nonatomic, assign) NSTimeInterval delay;
|
||||
@property (nonatomic, strong) NSInputStream *inputStream;
|
||||
@@ -631,19 +631,19 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
|
||||
- (instancetype)initWithStringEncoding:(NSStringEncoding)encoding;
|
||||
- (void)setInitialAndFinalBoundaries;
|
||||
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart;
|
||||
- (void)appendHTTPBodyPart:(SM_AFHTTPBodyPart *)bodyPart;
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFStreamingMultipartFormData ()
|
||||
@interface SM_AFStreamingMultipartFormData ()
|
||||
@property (readwrite, nonatomic, copy) NSMutableURLRequest *request;
|
||||
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
|
||||
@property (readwrite, nonatomic, copy) NSString *boundary;
|
||||
@property (readwrite, nonatomic, strong) AFMultipartBodyStream *bodyStream;
|
||||
@property (readwrite, nonatomic, strong) SM_AFMultipartBodyStream *bodyStream;
|
||||
@end
|
||||
|
||||
@implementation AFStreamingMultipartFormData
|
||||
@implementation SM_AFStreamingMultipartFormData
|
||||
|
||||
- (instancetype)initWithURLRequest:(NSMutableURLRequest *)urlRequest
|
||||
stringEncoding:(NSStringEncoding)encoding
|
||||
@@ -656,7 +656,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
self.request = urlRequest;
|
||||
self.stringEncoding = encoding;
|
||||
self.boundary = AFCreateMultipartFormBoundary();
|
||||
self.bodyStream = [[AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
|
||||
self.bodyStream = [[SM_AFMultipartBodyStream alloc] initWithStringEncoding:encoding];
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -686,16 +686,16 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
NSParameterAssert(mimeType);
|
||||
|
||||
if (![fileURL isFileURL]) {
|
||||
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"AFNetworking", nil)};
|
||||
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a file URL", @"SM_AFNetworking", nil)};
|
||||
if (error) {
|
||||
*error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
|
||||
*error = [[NSError alloc] initWithDomain:SM_AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
|
||||
}
|
||||
|
||||
return NO;
|
||||
} else if ([fileURL checkResourceIsReachableAndReturnError:error] == NO) {
|
||||
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"AFNetworking", nil)};
|
||||
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"File URL not reachable.", @"SM_AFNetworking", nil)};
|
||||
if (error) {
|
||||
*error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
|
||||
*error = [[NSError alloc] initWithDomain:SM_AFURLRequestSerializationErrorDomain code:NSURLErrorBadURL userInfo:userInfo];
|
||||
}
|
||||
|
||||
return NO;
|
||||
@@ -710,7 +710,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
|
||||
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
|
||||
|
||||
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
||||
SM_AFHTTPBodyPart *bodyPart = [[SM_AFHTTPBodyPart alloc] init];
|
||||
bodyPart.stringEncoding = self.stringEncoding;
|
||||
bodyPart.headers = mutableHeaders;
|
||||
bodyPart.boundary = self.boundary;
|
||||
@@ -735,7 +735,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
[mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"; filename=\"%@\"", name, fileName] forKey:@"Content-Disposition"];
|
||||
[mutableHeaders setValue:mimeType forKey:@"Content-Type"];
|
||||
|
||||
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
||||
SM_AFHTTPBodyPart *bodyPart = [[SM_AFHTTPBodyPart alloc] init];
|
||||
bodyPart.stringEncoding = self.stringEncoding;
|
||||
bodyPart.headers = mutableHeaders;
|
||||
bodyPart.boundary = self.boundary;
|
||||
@@ -778,7 +778,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
{
|
||||
NSParameterAssert(body);
|
||||
|
||||
AFHTTPBodyPart *bodyPart = [[AFHTTPBodyPart alloc] init];
|
||||
SM_AFHTTPBodyPart *bodyPart = [[SM_AFHTTPBodyPart alloc] init];
|
||||
bodyPart.stringEncoding = self.stringEncoding;
|
||||
bodyPart.headers = headers;
|
||||
bodyPart.boundary = self.boundary;
|
||||
@@ -819,16 +819,16 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
@property (readwrite, copy) NSError *streamError;
|
||||
@end
|
||||
|
||||
@interface AFMultipartBodyStream () <NSCopying>
|
||||
@interface SM_AFMultipartBodyStream () <NSCopying>
|
||||
@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
|
||||
@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;
|
||||
@property (readwrite, nonatomic, strong) NSEnumerator *HTTPBodyPartEnumerator;
|
||||
@property (readwrite, nonatomic, strong) AFHTTPBodyPart *currentHTTPBodyPart;
|
||||
@property (readwrite, nonatomic, strong) SM_AFHTTPBodyPart *currentHTTPBodyPart;
|
||||
@property (readwrite, nonatomic, strong) NSOutputStream *outputStream;
|
||||
@property (readwrite, nonatomic, strong) NSMutableData *buffer;
|
||||
@end
|
||||
|
||||
@implementation AFMultipartBodyStream
|
||||
@implementation SM_AFMultipartBodyStream
|
||||
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1100)
|
||||
@synthesize delegate;
|
||||
#endif
|
||||
@@ -850,7 +850,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
|
||||
- (void)setInitialAndFinalBoundaries {
|
||||
if ([self.HTTPBodyParts count] > 0) {
|
||||
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||
for (SM_AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||
bodyPart.hasInitialBoundary = NO;
|
||||
bodyPart.hasFinalBoundary = NO;
|
||||
}
|
||||
@@ -860,7 +860,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appendHTTPBodyPart:(AFHTTPBodyPart *)bodyPart {
|
||||
- (void)appendHTTPBodyPart:(SM_AFHTTPBodyPart *)bodyPart {
|
||||
[self.HTTPBodyParts addObject:bodyPart];
|
||||
}
|
||||
|
||||
@@ -950,7 +950,7 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
|
||||
- (unsigned long long)contentLength {
|
||||
unsigned long long length = 0;
|
||||
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||
for (SM_AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||
length += [bodyPart contentLength];
|
||||
}
|
||||
|
||||
@@ -976,9 +976,9 @@ NSTimeInterval const kAFUploadStream3GSuggestedDelay = 0.2;
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
|
||||
SM_AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone:zone] initWithStringEncoding:self.stringEncoding];
|
||||
|
||||
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||
for (SM_AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
|
||||
[bodyStreamCopy appendHTTPBodyPart:[bodyPart copy]];
|
||||
}
|
||||
|
||||
@@ -996,10 +996,10 @@ typedef enum {
|
||||
AFHeaderPhase = 2,
|
||||
AFBodyPhase = 3,
|
||||
AFFinalBoundaryPhase = 4,
|
||||
} AFHTTPBodyPartReadPhase;
|
||||
} SM_AFHTTPBodyPartReadPhase;
|
||||
|
||||
@interface AFHTTPBodyPart () <NSCopying> {
|
||||
AFHTTPBodyPartReadPhase _phase;
|
||||
@interface SM_AFHTTPBodyPart () <NSCopying> {
|
||||
SM_AFHTTPBodyPartReadPhase _phase;
|
||||
NSInputStream *_inputStream;
|
||||
unsigned long long _phaseReadOffset;
|
||||
}
|
||||
@@ -1010,7 +1010,7 @@ typedef enum {
|
||||
maxLength:(NSUInteger)length;
|
||||
@end
|
||||
|
||||
@implementation AFHTTPBodyPart
|
||||
@implementation SM_AFHTTPBodyPart
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
@@ -1182,7 +1182,7 @@ typedef enum {
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFHTTPBodyPart *bodyPart = [[[self class] allocWithZone:zone] init];
|
||||
|
||||
bodyPart.stringEncoding = self.stringEncoding;
|
||||
bodyPart.headers = self.headers;
|
||||
@@ -1197,7 +1197,7 @@ typedef enum {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation AFJSONRequestSerializer
|
||||
@implementation SM_AFJSONRequestSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
return [self serializerWithWritingOptions:(NSJSONWritingOptions)0];
|
||||
@@ -1205,13 +1205,13 @@ typedef enum {
|
||||
|
||||
+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions
|
||||
{
|
||||
AFJSONRequestSerializer *serializer = [[self alloc] init];
|
||||
SM_AFJSONRequestSerializer *serializer = [[self alloc] init];
|
||||
serializer.writingOptions = writingOptions;
|
||||
|
||||
return serializer;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLRequestSerialization
|
||||
#pragma mark - SM_AFURLRequestSerialization
|
||||
|
||||
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
|
||||
withParameters:(id)parameters
|
||||
@@ -1264,7 +1264,7 @@ typedef enum {
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFJSONRequestSerializer *serializer = [super copyWithZone:zone];
|
||||
SM_AFJSONRequestSerializer *serializer = [super copyWithZone:zone];
|
||||
serializer.writingOptions = self.writingOptions;
|
||||
|
||||
return serializer;
|
||||
@@ -1274,7 +1274,7 @@ typedef enum {
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation AFPropertyListRequestSerializer
|
||||
@implementation SM_AFPropertyListRequestSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 writeOptions:0];
|
||||
@@ -1283,7 +1283,7 @@ typedef enum {
|
||||
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
|
||||
writeOptions:(NSPropertyListWriteOptions)writeOptions
|
||||
{
|
||||
AFPropertyListRequestSerializer *serializer = [[self alloc] init];
|
||||
SM_AFPropertyListRequestSerializer *serializer = [[self alloc] init];
|
||||
serializer.format = format;
|
||||
serializer.writeOptions = writeOptions;
|
||||
|
||||
@@ -1345,7 +1345,7 @@ typedef enum {
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone];
|
||||
SM_AFPropertyListRequestSerializer *serializer = [super copyWithZone:zone];
|
||||
serializer.format = self.format;
|
||||
serializer.writeOptions = self.writeOptions;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFURLResponseSerialization.h
|
||||
// SM_AFURLResponseSerialization.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -25,11 +25,11 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data.
|
||||
The `SM_AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data.
|
||||
|
||||
For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object.
|
||||
*/
|
||||
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
|
||||
@protocol SM_AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
|
||||
|
||||
/**
|
||||
The response object decoded from the data associated with a specified response.
|
||||
@@ -49,11 +49,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
`AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
|
||||
`SM_AFHTTPResponseSerializer` conforms to the `SM_AFURLRequestSerialization` & `SM_AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
|
||||
|
||||
Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior.
|
||||
Any request or response serializer dealing with HTTP is encouraged to subclass `SM_AFHTTPResponseSerializer` in order to ensure consistent default behavior.
|
||||
*/
|
||||
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>
|
||||
@interface SM_AFHTTPResponseSerializer : NSObject <SM_AFURLResponseSerialization>
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@@ -104,15 +104,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
/**
|
||||
`AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses.
|
||||
`SM_AFJSONResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes JSON responses.
|
||||
|
||||
By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types:
|
||||
By default, `SM_AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types:
|
||||
|
||||
- `application/json`
|
||||
- `text/json`
|
||||
- `text/javascript`
|
||||
*/
|
||||
@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
|
||||
@interface SM_AFJSONResponseSerializer : SM_AFHTTPResponseSerializer
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@@ -138,14 +138,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
`AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects.
|
||||
`SM_AFXMLParserResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects.
|
||||
|
||||
By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
|
||||
By default, `SM_AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
|
||||
|
||||
- `application/xml`
|
||||
- `text/xml`
|
||||
*/
|
||||
@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer
|
||||
@interface SM_AFXMLParserResponseSerializer : SM_AFHTTPResponseSerializer
|
||||
|
||||
@end
|
||||
|
||||
@@ -154,14 +154,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
||||
/**
|
||||
`AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
|
||||
`AFXMLDocumentResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
|
||||
|
||||
By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
|
||||
|
||||
- `application/xml`
|
||||
- `text/xml`
|
||||
*/
|
||||
@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer
|
||||
@interface AFXMLDocumentResponseSerializer : SM_AFHTTPResponseSerializer
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@@ -184,13 +184,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
`AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
|
||||
`SM_AFPropertyListResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
|
||||
|
||||
By default, `AFPropertyListResponseSerializer` accepts the following MIME types:
|
||||
By default, `SM_AFPropertyListResponseSerializer` accepts the following MIME types:
|
||||
|
||||
- `application/x-plist`
|
||||
*/
|
||||
@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer
|
||||
@interface SM_AFPropertyListResponseSerializer : SM_AFHTTPResponseSerializer
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@@ -218,9 +218,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
`AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses.
|
||||
`SM_AFImageResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes image responses.
|
||||
|
||||
By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage:
|
||||
By default, `SM_AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage:
|
||||
|
||||
- `image/tiff`
|
||||
- `image/jpeg`
|
||||
@@ -233,7 +233,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- `image/x-xbitmap`
|
||||
- `image/x-win-bitmap`
|
||||
*/
|
||||
@interface AFImageResponseSerializer : AFHTTPResponseSerializer
|
||||
@interface SM_AFImageResponseSerializer : SM_AFHTTPResponseSerializer
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
||||
/**
|
||||
@@ -252,21 +252,21 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
`AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer.
|
||||
`AFCompoundSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that delegates the response serialization to the first `SM_AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `SM_AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer.
|
||||
*/
|
||||
@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer
|
||||
@interface SM_AFCompoundResponseSerializer : SM_AFHTTPResponseSerializer
|
||||
|
||||
/**
|
||||
The component response serializers.
|
||||
*/
|
||||
@property (readonly, nonatomic, copy) NSArray <id<AFURLResponseSerialization>> *responseSerializers;
|
||||
@property (readonly, nonatomic, copy) NSArray <id<SM_AFURLResponseSerialization>> *responseSerializers;
|
||||
|
||||
/**
|
||||
Creates and returns a compound serializer comprised of the specified response serializers.
|
||||
|
||||
@warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`.
|
||||
@warning Each response serializer specified must be a subclass of `SM_AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`.
|
||||
*/
|
||||
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<AFURLResponseSerialization>> *)responseSerializers;
|
||||
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<SM_AFURLResponseSerialization>> *)responseSerializers;
|
||||
|
||||
@end
|
||||
|
||||
@@ -279,33 +279,40 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
The following error domain is predefined.
|
||||
|
||||
- `NSString * const AFURLResponseSerializationErrorDomain`
|
||||
- `NSString * const SM_AFURLResponseSerializationErrorDomain`
|
||||
|
||||
### Constants
|
||||
|
||||
`AFURLResponseSerializationErrorDomain`
|
||||
AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
|
||||
`SM_AFURLResponseSerializationErrorDomain`
|
||||
AFURLResponseSerializer errors. Error codes for `SM_AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFURLResponseSerializationErrorDomain;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFURLResponseSerializationErrorDomain;
|
||||
|
||||
/**
|
||||
## User info dictionary keys
|
||||
|
||||
These keys may exist in the user info dictionary, in addition to those defined for NSError.
|
||||
|
||||
- `NSString * const AFNetworkingOperationFailingURLResponseErrorKey`
|
||||
- `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey`
|
||||
- `NSString * const SM_AFNetworkingOperationFailingURLResponseErrorKey`
|
||||
- `NSString * const SM_AFNetworkingOperationFailingURLResponseDataErrorKey`
|
||||
|
||||
### Constants
|
||||
|
||||
`AFNetworkingOperationFailingURLResponseErrorKey`
|
||||
The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`.
|
||||
`SM_AFNetworkingOperationFailingURLResponseErrorKey`
|
||||
The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `SM_AFURLResponseSerializationErrorDomain`.
|
||||
|
||||
`AFNetworkingOperationFailingURLResponseDataErrorKey`
|
||||
The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`.
|
||||
`SM_AFNetworkingOperationFailingURLResponseDataErrorKey`
|
||||
The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `SM_AFURLResponseSerializationErrorDomain`.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseErrorKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingOperationFailingURLResponseErrorKey;
|
||||
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingOperationFailingURLResponseDataErrorKey;
|
||||
|
||||
/**
|
||||
`SM_AFNetworkingOperationFailingURLResponseBodyErrorKey`
|
||||
The corresponding value is an `NSString` containing the decoded error message.
|
||||
*/
|
||||
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingOperationFailingURLResponseBodyErrorKey;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFURLResponseSerialization.m
|
||||
// SM_AFURLResponseSerialization.m
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -19,7 +19,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "AFURLResponseSerialization.h"
|
||||
#import "SM_AFURLResponseSerialization.h"
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
@@ -31,9 +31,10 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
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 SM_AFURLResponseSerializationErrorDomain = @"com.alamofire.error.serialization.response";
|
||||
NSString * const SM_AFNetworkingOperationFailingURLResponseErrorKey = @"com.alamofire.serialization.response.error.response";
|
||||
NSString * const SM_AFNetworkingOperationFailingURLResponseDataErrorKey = @"com.alamofire.serialization.response.error.data";
|
||||
NSString * const SM_AFNetworkingOperationFailingURLResponseBodyErrorKey = @"com.alamofire.serialization.response.error.body";
|
||||
|
||||
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
|
||||
if (!error) {
|
||||
@@ -85,7 +86,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
return JSONObject;
|
||||
}
|
||||
|
||||
@implementation AFHTTPResponseSerializer
|
||||
@implementation SM_AFHTTPResponseSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
return [[self alloc] init];
|
||||
@@ -120,15 +121,15 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
|
||||
if ([data length] > 0 && [response URL]) {
|
||||
NSMutableDictionary *mutableUserInfo = [@{
|
||||
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
|
||||
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"SM_AFNetworking", nil), [response MIMEType]],
|
||||
NSURLErrorFailingURLErrorKey:[response URL],
|
||||
AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
} mutableCopy];
|
||||
if (data) {
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
}
|
||||
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
|
||||
}
|
||||
|
||||
responseIsValid = NO;
|
||||
@@ -136,16 +137,16 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
|
||||
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],
|
||||
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"SM_AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
|
||||
NSURLErrorFailingURLErrorKey:[response URL],
|
||||
AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
} mutableCopy];
|
||||
|
||||
if (data) {
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
}
|
||||
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
|
||||
responseIsValid = NO;
|
||||
}
|
||||
@@ -158,7 +159,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
return responseIsValid;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
#pragma mark - SM_AFURLResponseSerialization
|
||||
|
||||
- (id)responseObjectForResponse:(NSURLResponse *)response
|
||||
data:(NSData *)data
|
||||
@@ -195,7 +196,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone];
|
||||
serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone];
|
||||
|
||||
@@ -206,14 +207,14 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation AFJSONResponseSerializer
|
||||
@implementation SM_AFJSONResponseSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
|
||||
}
|
||||
|
||||
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
|
||||
AFJSONResponseSerializer *serializer = [[self alloc] init];
|
||||
SM_AFJSONResponseSerializer *serializer = [[self alloc] init];
|
||||
serializer.readingOptions = readingOptions;
|
||||
|
||||
return serializer;
|
||||
@@ -230,14 +231,14 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
#pragma mark - SM_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)) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -288,7 +289,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
serializer.readingOptions = self.readingOptions;
|
||||
serializer.removesKeysWithNullValues = self.removesKeysWithNullValues;
|
||||
|
||||
@@ -299,10 +300,10 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation AFXMLParserResponseSerializer
|
||||
@implementation SM_AFXMLParserResponseSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
AFXMLParserResponseSerializer *serializer = [[self alloc] init];
|
||||
SM_AFXMLParserResponseSerializer *serializer = [[self alloc] init];
|
||||
|
||||
return serializer;
|
||||
}
|
||||
@@ -318,14 +319,14 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
#pragma mark - SM_AFURLResponseSerialization
|
||||
|
||||
- (id)responseObjectForResponse:(NSHTTPURLResponse *)response
|
||||
data:(NSData *)data
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -363,14 +364,14 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
#pragma mark - SM_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)) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -419,7 +420,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation AFPropertyListResponseSerializer
|
||||
@implementation SM_AFPropertyListResponseSerializer
|
||||
|
||||
+ (instancetype)serializer {
|
||||
return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0];
|
||||
@@ -428,7 +429,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
|
||||
readOptions:(NSPropertyListReadOptions)readOptions
|
||||
{
|
||||
AFPropertyListResponseSerializer *serializer = [[self alloc] init];
|
||||
SM_AFPropertyListResponseSerializer *serializer = [[self alloc] init];
|
||||
serializer.format = format;
|
||||
serializer.readOptions = readOptions;
|
||||
|
||||
@@ -446,14 +447,14 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
#pragma mark - SM_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)) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -496,7 +497,7 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
serializer.format = self.format;
|
||||
serializer.readOptions = self.readOptions;
|
||||
|
||||
@@ -511,13 +512,13 @@ static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingO
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (AFNetworkingSafeImageLoading)
|
||||
@interface UIImage (SM_AFNetworkingSafeImageLoading)
|
||||
+ (UIImage *)af_safeImageWithData:(NSData *)data;
|
||||
@end
|
||||
|
||||
static NSLock* imageLock = nil;
|
||||
|
||||
@implementation UIImage (AFNetworkingSafeImageLoading)
|
||||
@implementation UIImage (SM_AFNetworkingSafeImageLoading)
|
||||
|
||||
+ (UIImage *)af_safeImageWithData:(NSData *)data {
|
||||
UIImage* image = nil;
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -637,7 +638,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
#endif
|
||||
|
||||
|
||||
@implementation AFImageResponseSerializer
|
||||
@implementation SM_AFImageResponseSerializer
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
@@ -665,7 +666,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -722,7 +723,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
||||
serializer.imageScale = self.imageScale;
|
||||
@@ -736,27 +737,27 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFCompoundResponseSerializer ()
|
||||
@interface SM_AFCompoundResponseSerializer ()
|
||||
@property (readwrite, nonatomic, copy) NSArray *responseSerializers;
|
||||
@end
|
||||
|
||||
@implementation AFCompoundResponseSerializer
|
||||
@implementation SM_AFCompoundResponseSerializer
|
||||
|
||||
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers {
|
||||
AFCompoundResponseSerializer *serializer = [[self alloc] init];
|
||||
SM_AFCompoundResponseSerializer *serializer = [[self alloc] init];
|
||||
serializer.responseSerializers = responseSerializers;
|
||||
|
||||
return serializer;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
#pragma mark - SM_AFURLResponseSerialization
|
||||
|
||||
- (id)responseObjectForResponse:(NSURLResponse *)response
|
||||
data:(NSData *)data
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
for (id <AFURLResponseSerialization> serializer in self.responseSerializers) {
|
||||
if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
|
||||
for (id <SM_AFURLResponseSerialization> serializer in self.responseSerializers) {
|
||||
if (![serializer isKindOfClass:[SM_AFHTTPResponseSerializer class]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -796,7 +797,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
|
||||
#pragma mark - NSCopying
|
||||
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
SM_AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
|
||||
serializer.responseSerializers = self.responseSerializers;
|
||||
|
||||
return serializer;
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFURLSessionManager.h
|
||||
// SM_AFURLSessionManager.h
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -22,23 +22,23 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AFURLResponseSerialization.h"
|
||||
#import "AFURLRequestSerialization.h"
|
||||
#import "AFSecurityPolicy.h"
|
||||
#import "SM_AFURLResponseSerialization.h"
|
||||
#import "SM_AFURLRequestSerialization.h"
|
||||
#import "SM_AFSecurityPolicy.h"
|
||||
#if !TARGET_OS_WATCH
|
||||
#import "AFNetworkReachabilityManager.h"
|
||||
#import "SM_AFNetworkReachabilityManager.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
`AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to `<NSURLSessionTaskDelegate>`, `<NSURLSessionDataDelegate>`, `<NSURLSessionDownloadDelegate>`, and `<NSURLSessionDelegate>`.
|
||||
`SM_AFURLSessionManager` creates and manages an `NSURLSession` object based on a specified `NSURLSessionConfiguration` object, which conforms to `<NSURLSessionTaskDelegate>`, `<NSURLSessionDataDelegate>`, `<NSURLSessionDownloadDelegate>`, and `<NSURLSessionDelegate>`.
|
||||
|
||||
## Subclassing Notes
|
||||
|
||||
This is the base class for `AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `AFURLSessionManager` specifically for HTTP, consider subclassing `AFHTTPSessionManager` instead.
|
||||
This is the base class for `SM_AFHTTPSessionManager`, which adds functionality specific to making HTTP requests. If you are looking to extend `SM_AFURLSessionManager` specifically for HTTP, consider subclassing `SM_AFHTTPSessionManager` instead.
|
||||
|
||||
## NSURLSession & NSURLSessionTask Delegate Methods
|
||||
|
||||
`AFURLSessionManager` implements the following delegate methods:
|
||||
`SM_AFURLSessionManager` implements the following delegate methods:
|
||||
|
||||
### `NSURLSessionDelegate`
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
## Network Reachability Monitoring
|
||||
|
||||
Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `AFNetworkReachabilityManager` for more details.
|
||||
Network reachability status and change monitoring is available through the `reachabilityManager` property. Applications may choose to monitor network reachability conditions in order to prevent or suspend any outbound requests. See `SM_AFNetworkReachabilityManager` for more details.
|
||||
|
||||
## NSCoding Caveats
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
|
||||
@interface SM_AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
|
||||
|
||||
/**
|
||||
The managed session.
|
||||
@@ -100,20 +100,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
|
||||
|
||||
/**
|
||||
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
|
||||
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `SM_AFJSONResponseSerializer`.
|
||||
|
||||
@warning `responseSerializer` must not be `nil`.
|
||||
*/
|
||||
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
|
||||
@property (nonatomic, strong) id <SM_AFURLResponseSerialization> responseSerializer;
|
||||
|
||||
///-------------------------------
|
||||
/// @name Managing Security Policy
|
||||
///-------------------------------
|
||||
|
||||
/**
|
||||
The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified.
|
||||
The security policy used by created session to evaluate server trust for secure connections. `SM_AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified.
|
||||
*/
|
||||
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
|
||||
@property (nonatomic, strong) SM_AFSecurityPolicy *securityPolicy;
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
///--------------------------------------
|
||||
@@ -121,9 +121,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
///--------------------------------------
|
||||
|
||||
/**
|
||||
The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default.
|
||||
The network reachability manager. `SM_AFURLSessionManager` uses the `sharedManager` by default.
|
||||
*/
|
||||
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
|
||||
@property (readwrite, nonatomic, strong) SM_AFNetworkReachabilityManager *reachabilityManager;
|
||||
#endif
|
||||
|
||||
///----------------------------
|
||||
@@ -423,7 +423,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`.
|
||||
|
||||
@param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error.
|
||||
@param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `SM_AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error.
|
||||
*/
|
||||
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
|
||||
|
||||
@@ -450,51 +450,51 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
Posted when a task resumes.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidResumeNotification;
|
||||
|
||||
/**
|
||||
Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteNotification;
|
||||
|
||||
/**
|
||||
Posted when a task suspends its execution.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidSuspendNotification;
|
||||
|
||||
/**
|
||||
Posted when a session is invalidated.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFURLSessionDidInvalidateNotification;
|
||||
|
||||
/**
|
||||
Posted when a session download task encountered an error when moving the temporary download file to a specified destination.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFURLSessionDownloadTaskDidFailToMoveFileNotification;
|
||||
|
||||
/**
|
||||
The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if response data exists for the task.
|
||||
The raw response data of the task. Included in the userInfo dictionary of the `SM_AFNetworkingTaskDidCompleteNotification` if response data exists for the task.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteResponseDataKey;
|
||||
|
||||
/**
|
||||
The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the response was serialized.
|
||||
The serialized response object of the task. Included in the userInfo dictionary of the `SM_AFNetworkingTaskDidCompleteNotification` if the response was serialized.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteSerializedResponseKey;
|
||||
|
||||
/**
|
||||
The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the task has an associated response serializer.
|
||||
The response serializer used to serialize the response. Included in the userInfo dictionary of the `SM_AFNetworkingTaskDidCompleteNotification` if the task has an associated response serializer.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteResponseSerializerKey;
|
||||
|
||||
/**
|
||||
The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk.
|
||||
The file path associated with the download task. Included in the userInfo dictionary of the `SM_AFNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteAssetPathKey;
|
||||
|
||||
/**
|
||||
Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an error exists.
|
||||
Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `SM_AFNetworkingTaskDidCompleteNotification` if an error exists.
|
||||
*/
|
||||
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey;
|
||||
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteErrorKey;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,4 +1,4 @@
|
||||
// AFURLSessionManager.m
|
||||
// SM_AFURLSessionManager.m
|
||||
// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
@@ -19,7 +19,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "AFURLSessionManager.h"
|
||||
#import "SM_AFURLSessionManager.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#ifndef NSFoundationVersionNumber_iOS_8_0
|
||||
@@ -69,19 +69,19 @@ static dispatch_group_t url_session_manager_completion_group() {
|
||||
return af_url_session_manager_completion_group;
|
||||
}
|
||||
|
||||
NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
|
||||
NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
|
||||
NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
|
||||
NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
|
||||
NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
|
||||
NSString * const SM_AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
|
||||
NSString * const SM_AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
|
||||
NSString * const SM_AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
|
||||
NSString * const SM_AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
|
||||
NSString * const SM_AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
|
||||
|
||||
NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
|
||||
NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer";
|
||||
NSString * const AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
|
||||
NSString * const AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
|
||||
NSString * const AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
|
||||
NSString * const SM_AFNetworkingTaskDidCompleteSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse";
|
||||
NSString * const SM_AFNetworkingTaskDidCompleteResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer";
|
||||
NSString * const SM_AFNetworkingTaskDidCompleteResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata";
|
||||
NSString * const SM_AFNetworkingTaskDidCompleteErrorKey = @"com.alamofire.networking.task.complete.error";
|
||||
NSString * const SM_AFNetworkingTaskDidCompleteAssetPathKey = @"com.alamofire.networking.task.complete.assetpath";
|
||||
|
||||
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
|
||||
static NSString * const SM_AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
|
||||
|
||||
static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
|
||||
|
||||
@@ -111,8 +111,8 @@ typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id re
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
|
||||
@property (nonatomic, weak) AFURLSessionManager *manager;
|
||||
@interface SM_AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
|
||||
@property (nonatomic, weak) SM_AFURLSessionManager *manager;
|
||||
@property (nonatomic, strong) NSMutableData *mutableData;
|
||||
@property (nonatomic, strong) NSProgress *uploadProgress;
|
||||
@property (nonatomic, strong) NSProgress *downloadProgress;
|
||||
@@ -123,7 +123,7 @@ typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id re
|
||||
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
|
||||
@end
|
||||
|
||||
@implementation AFURLSessionManagerTaskDelegate
|
||||
@implementation SM_AFURLSessionManagerTaskDelegate
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
@@ -249,12 +249,12 @@ typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id re
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(NSError *)error
|
||||
{
|
||||
__strong AFURLSessionManager *manager = self.manager;
|
||||
__strong SM_AFURLSessionManager *manager = self.manager;
|
||||
|
||||
__block id responseObject = nil;
|
||||
|
||||
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
|
||||
userInfo[SM_AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
|
||||
|
||||
//Performance Improvement from #2672
|
||||
NSData *data = nil;
|
||||
@@ -265,13 +265,13 @@ didCompleteWithError:(NSError *)error
|
||||
}
|
||||
|
||||
if (self.downloadFileURL) {
|
||||
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
|
||||
userInfo[SM_AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
|
||||
} else if (data) {
|
||||
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
|
||||
userInfo[SM_AFNetworkingTaskDidCompleteResponseDataKey] = data;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
|
||||
userInfo[SM_AFNetworkingTaskDidCompleteErrorKey] = error;
|
||||
|
||||
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
if (self.completionHandler) {
|
||||
@@ -279,7 +279,7 @@ didCompleteWithError:(NSError *)error
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SM_AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
|
||||
});
|
||||
});
|
||||
} else {
|
||||
@@ -292,11 +292,11 @@ didCompleteWithError:(NSError *)error
|
||||
}
|
||||
|
||||
if (responseObject) {
|
||||
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
|
||||
userInfo[SM_AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
|
||||
}
|
||||
|
||||
if (serializationError) {
|
||||
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
|
||||
userInfo[SM_AFNetworkingTaskDidCompleteErrorKey] = serializationError;
|
||||
}
|
||||
|
||||
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
@@ -305,7 +305,7 @@ didCompleteWithError:(NSError *)error
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SM_AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -336,7 +336,7 @@ didFinishDownloadingToURL:(NSURL *)location
|
||||
[[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
|
||||
|
||||
if (fileManagerError) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SM_AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,11 +368,11 @@ static inline BOOL af_addMethod(Class theClass, SEL selector, Method method) {
|
||||
static NSString * const AFNSURLSessionTaskDidResumeNotification = @"com.alamofire.networking.nsurlsessiontask.resume";
|
||||
static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofire.networking.nsurlsessiontask.suspend";
|
||||
|
||||
@interface _AFURLSessionTaskSwizzling : NSObject
|
||||
@interface _SM_AFURLSessionTaskSwizzling : NSObject
|
||||
|
||||
@end
|
||||
|
||||
@implementation _AFURLSessionTaskSwizzling
|
||||
@implementation _SM_AFURLSessionTaskSwizzling
|
||||
|
||||
+ (void)load {
|
||||
/**
|
||||
@@ -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];
|
||||
}
|
||||
@@ -473,7 +473,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface AFURLSessionManager ()
|
||||
@interface SM_AFURLSessionManager ()
|
||||
@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
|
||||
@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
|
||||
@property (readwrite, nonatomic, strong) NSURLSession *session;
|
||||
@@ -497,7 +497,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
|
||||
@end
|
||||
|
||||
@implementation AFURLSessionManager
|
||||
@implementation SM_AFURLSessionManager
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithSessionConfiguration:nil];
|
||||
@@ -511,6 +511,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
if (!configuration) {
|
||||
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
configuration.URLCache = nil;
|
||||
}
|
||||
|
||||
self.sessionConfiguration = configuration;
|
||||
@@ -520,18 +521,18 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
|
||||
|
||||
self.responseSerializer = [AFJSONResponseSerializer serializer];
|
||||
self.responseSerializer = [SM_AFJSONResponseSerializer serializer];
|
||||
|
||||
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
|
||||
self.securityPolicy = [SM_AFSecurityPolicy defaultPolicy];
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
|
||||
self.reachabilityManager = [SM_AFNetworkReachabilityManager sharedManager];
|
||||
#endif
|
||||
|
||||
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
|
||||
|
||||
self.lock = [[NSLock alloc] init];
|
||||
self.lock.name = AFURLSessionManagerLockName;
|
||||
self.lock.name = SM_AFURLSessionManagerLockName;
|
||||
|
||||
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
|
||||
for (NSURLSessionDataTask *task in dataTasks) {
|
||||
@@ -565,7 +566,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
if ([task respondsToSelector:@selector(taskDescription)]) {
|
||||
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SM_AFNetworkingTaskDidResumeNotification object:task];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -576,7 +577,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
if ([task respondsToSelector:@selector(taskDescription)]) {
|
||||
if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SM_AFNetworkingTaskDidSuspendNotification object:task];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -584,10 +585,10 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
|
||||
- (SM_AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
|
||||
NSParameterAssert(task);
|
||||
|
||||
AFURLSessionManagerTaskDelegate *delegate = nil;
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = nil;
|
||||
[self.lock lock];
|
||||
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
|
||||
[self.lock unlock];
|
||||
@@ -595,7 +596,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
return delegate;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
|
||||
- (void)setDelegate:(SM_AFURLSessionManagerTaskDelegate *)delegate
|
||||
forTask:(NSURLSessionTask *)task
|
||||
{
|
||||
NSParameterAssert(task);
|
||||
@@ -613,7 +614,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [[SM_AFURLSessionManagerTaskDelegate alloc] init];
|
||||
delegate.manager = self;
|
||||
delegate.completionHandler = completionHandler;
|
||||
|
||||
@@ -628,7 +629,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
|
||||
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [[SM_AFURLSessionManagerTaskDelegate alloc] init];
|
||||
delegate.manager = self;
|
||||
delegate.completionHandler = completionHandler;
|
||||
|
||||
@@ -644,7 +645,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
|
||||
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [[SM_AFURLSessionManagerTaskDelegate alloc] init];
|
||||
delegate.manager = self;
|
||||
delegate.completionHandler = completionHandler;
|
||||
|
||||
@@ -664,7 +665,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
|
||||
NSParameterAssert(task);
|
||||
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
|
||||
[self.lock lock];
|
||||
[delegate cleanUpProgressForTask:task];
|
||||
[self removeNotificationObserverForTask:task];
|
||||
@@ -726,7 +727,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer {
|
||||
- (void)setResponseSerializer:(id <SM_AFURLResponseSerialization>)responseSerializer {
|
||||
NSParameterAssert(responseSerializer);
|
||||
|
||||
_responseSerializer = responseSerializer;
|
||||
@@ -956,7 +957,7 @@ didBecomeInvalidWithError:(NSError *)error
|
||||
self.sessionDidBecomeInvalid(session, error);
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SM_AFURLSessionDidInvalidateNotification object:session];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
@@ -978,7 +979,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
@@ -1025,7 +1026,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
@@ -1078,7 +1079,7 @@ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(NSError *)error
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
|
||||
|
||||
// delegate may be nil when completing a task in the background
|
||||
if (delegate) {
|
||||
@@ -1114,7 +1115,7 @@ didReceiveResponse:(NSURLResponse *)response
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
|
||||
if (delegate) {
|
||||
[self removeDelegateForTask:dataTask];
|
||||
[self setDelegate:delegate forTask:downloadTask];
|
||||
@@ -1130,7 +1131,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
didReceiveData:(NSData *)data
|
||||
{
|
||||
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
|
||||
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
|
||||
|
||||
if (self.dataTaskDidReceiveData) {
|
||||
@@ -1168,7 +1169,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
downloadTask:(NSURLSessionDownloadTask *)downloadTask
|
||||
didFinishDownloadingToURL:(NSURL *)location
|
||||
{
|
||||
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
|
||||
SM_AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
|
||||
if (self.downloadTaskDidFinishDownloading) {
|
||||
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
|
||||
if (fileURL) {
|
||||
@@ -1176,7 +1177,7 @@ didFinishDownloadingToURL:(NSURL *)location
|
||||
NSError *error = nil;
|
||||
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
|
||||
if (error) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SM_AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1,7 +1,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFURLRequestSerialization.h"
|
||||
#import "SM_AFURLRequestSerialization.h"
|
||||
|
||||
@interface TextRequestSerializer : AFHTTPRequestSerializer
|
||||
@interface TextRequestSerializer : SM_AFHTTPRequestSerializer
|
||||
|
||||
+ (instancetype)serializer;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
return serializer;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLRequestSerialization
|
||||
#pragma mark - SM_AFURLRequestSerialization
|
||||
|
||||
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
|
||||
withParameters:(id)parameters
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "AFURLResponseSerialization.h"
|
||||
#import "SM_AFURLResponseSerialization.h"
|
||||
|
||||
@interface TextResponseSerializer : AFHTTPResponseSerializer
|
||||
@interface TextResponseSerializer : SM_AFHTTPResponseSerializer
|
||||
|
||||
+ (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]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,26 +94,26 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
|
||||
if (data && !*decoded) {
|
||||
NSMutableDictionary *mutableUserInfo = [@{
|
||||
NSURLErrorFailingURLErrorKey:[response URL],
|
||||
AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
AFNetworkingOperationFailingURLResponseDataErrorKey: data,
|
||||
AFNetworkingOperationFailingURLResponseBodyKey: @"Could not decode response data due to invalid or unknown charset encoding",
|
||||
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
SM_AFNetworkingOperationFailingURLResponseDataErrorKey: data,
|
||||
SM_AFNetworkingOperationFailingURLResponseBodyErrorKey: @"Could not decode response data due to invalid or unknown charset encoding",
|
||||
} mutableCopy];
|
||||
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
responseIsValid = NO;
|
||||
} else 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],
|
||||
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"SM_AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
|
||||
NSURLErrorFailingURLErrorKey: [response URL],
|
||||
AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
|
||||
} mutableCopy];
|
||||
|
||||
if (data) {
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyKey] = *decoded;
|
||||
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
|
||||
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey] = *decoded;
|
||||
}
|
||||
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
|
||||
responseIsValid = NO;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +125,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
|
||||
return responseIsValid;
|
||||
}
|
||||
|
||||
#pragma mark - AFURLResponseSerialization
|
||||
#pragma mark - SM_AFURLResponseSerialization
|
||||
|
||||
- (id)responseObjectForResponse:(NSURLResponse *)response
|
||||
data:(NSData *)data
|
||||
@@ -132,7 +134,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
|
||||
NSString* decoded = nil;
|
||||
|
||||
if (![self validateResponse:(NSHTTPURLResponse *)response data:data decoded:&decoded error:error]) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
|
||||
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(definition.description, function() {
|
||||
return clickNext()
|
||||
.then(() => validateTestIndex(index))
|
||||
.then(() => validateTestTitle(this.test.title))
|
||||
.then(() => waitToBeFinished(definition.timeout || 10000))
|
||||
.then(() => validateResult(definition))
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "com.ilkimen.http.demo",
|
||||
"displayName": "HttpDemo",
|
||||
"version": "1.0.0",
|
||||
"description": "A sample Apache Cordova application that demonstrates advanced HTTP plugin.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "scripts/build.sh",
|
||||
"test": "npm run build && scripts/test.sh"
|
||||
},
|
||||
"author": "Sefa Ilkimen",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cordova": "7.0.1",
|
||||
"cordova-android": "6.2.3",
|
||||
"cordova-ios": "4.4.0"
|
||||
},
|
||||
"cordova": {
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
Binary file not shown.
@@ -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,472 +0,0 @@
|
||||
const hooks = {
|
||||
onBeforeEachTest: function(done) {
|
||||
cordova.plugin.http.clearCookies();
|
||||
cordova.plugin.http.acceptAllCerts(false, function() {
|
||||
cordova.plugin.http.enableSSLPinning(false, done, done);
|
||||
}, done);
|
||||
}
|
||||
};
|
||||
|
||||
const helpers = {
|
||||
acceptAllCerts: function(done) { cordova.plugin.http.acceptAllCerts(true, done, done); },
|
||||
enableSSLPinning: function(done) { cordova.plugin.http.enableSSLPinning(true, 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.acceptAllCerts,
|
||||
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.acceptAllCerts,
|
||||
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.acceptAllCerts,
|
||||
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.acceptAllCerts,
|
||||
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.acceptAllCerts,
|
||||
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.acceptAllCerts,
|
||||
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.enableSSLPinning,
|
||||
func: function(resolve, reject) {
|
||||
cordova.plugin.http.get('https://httpbin.org', {}, {}, resolve, reject);
|
||||
},
|
||||
validationFunc: function(driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
}
|
||||
},{
|
||||
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');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,16 @@
|
||||
<allow-intent href="mailto:*" />
|
||||
<allow-intent href="geo:*" />
|
||||
<platform name="android">
|
||||
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:networkSecurityConfig="@xml/network_security_config" />
|
||||
</edit-config>
|
||||
<resource-file src="network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
|
||||
<allow-intent href="market:*" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
<engine name="android" spec="6.2.3" />
|
||||
<engine name="browser" spec="5.0.0" />
|
||||
<engine name="ios" spec="4.4.0" />
|
||||
<plugin name="cordova-plugin-file" spec="4.3.3" />
|
||||
<preference name="AndroidPersistentFileLocation" value="Internal" />
|
||||
<preference name="AndroidBlacklistSecureSocketProtocols" value="SSLv3,TLSv1" />
|
||||
</widget>
|
||||
9
test/e2e-app-template/network_security_config.xml
Normal file
9
test/e2e-app-template/network_security_config.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">localhost</domain>
|
||||
<domain includeSubdomains="true">httpbin.org</domain>
|
||||
<domain includeSubdomains="true">httpbingo.org</domain>
|
||||
<domain includeSubdomains="true">www.columbia.edu</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
29
test/e2e-app-template/package.json
Normal file
29
test/e2e-app-template/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "com.ilkimen.http.demo",
|
||||
"displayName": "HttpDemo",
|
||||
"version": "1.0.0",
|
||||
"description": "A sample Apache Cordova application that demonstrates advanced HTTP plugin.",
|
||||
"scripts": {
|
||||
"build": "scripts/build.sh",
|
||||
"test": "npm run build && scripts/test.sh"
|
||||
},
|
||||
"author": "Sefa Ilkimen",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cordova": "12.0.0",
|
||||
"cordova-android": "12.0.1",
|
||||
"cordova-browser": "7.0.0",
|
||||
"cordova-ios": "7.1.1",
|
||||
"cordova-plugin-device": "2.0.3"
|
||||
},
|
||||
"cordova": {
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios",
|
||||
"browser"
|
||||
],
|
||||
"plugins": {
|
||||
"cordova-plugin-device": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
test/e2e-app-template/www/certificates/.gitkeep
Normal file
0
test/e2e-app-template/www/certificates/.gitkeep
Normal file
@@ -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>
|
||||
170
test/e2e-app-template/www/index.js
Normal file
170
test/e2e-app-template/www/index.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/* global hooks, tests */
|
||||
|
||||
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
|
||||
};
|
||||
},
|
||||
|
||||
skip: function (content) {
|
||||
document.getElementById('statusInput').value = 'finished';
|
||||
app.printResult('result - skipped', content);
|
||||
|
||||
app.lastResult = {
|
||||
type: 'skipped',
|
||||
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, app.skip);
|
||||
} 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();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user