Compare commits

...

109 Commits

Author SHA1 Message Date
Sefa Ilkimen
5b8f20e1c4 release v3.2.2 2021-10-15 15:12:50 +02:00
Sefa Ilkimen
b3b97306f4 fix: #438 requests not working correctly on browser platform 2021-10-15 14:49:34 +02:00
Sefa Ilkimen
b20faa9c3c release v3.2.1 2021-09-09 16:34:33 +02:00
Sefa Ilkimen
d66b4b7645 fix: vulnerable npm dependecies 2021-09-09 16:29:46 +02:00
Sefa Ilkimen
fb77849d10 docu: update Travis CI badge 2021-09-09 14:54:03 +02:00
Sefa Ilkimen
e2919e51e1 fix: missing break statement in public-interface leads to broken upload and download function 2021-09-09 14:32:44 +02:00
Sefa Ilkimen
ad1d500eae - docu: list known issues
- test: disable `abort` tests
2021-09-09 13:16:07 +02:00
Sefa Ilkimen
1bafe6cdd9 fix: compile error in fix #404 2021-08-20 05:01:39 +02:00
Sefa Ilkimen
1dd5ee59c8 Merge branch 'YouYue123-setTimeout' 2021-08-20 04:38:52 +02:00
Sefa Ilkimen
6f68aab736 Merge branch 'setTimeout' of https://github.com/YouYue123/cordova-plugin-advanced-http into YouYue123-setTimeout
# Conflicts:
#	README.md
#	src/android/com/silkimen/cordovahttp/CordovaHttpBase.java
#	src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java
#	src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
#	src/ios/CordovaHttpPlugin.m
#	www/public-interface.js
2021-08-20 04:37:49 +02:00
Sefa Ilkimen
c2a2ebc31b chore: test on another device 2021-08-20 03:47:23 +02:00
Sefa Ilkimen
c9bff478d8 chore: update changelog 2021-08-20 03:41:35 +02:00
Sefa Ilkimen
9ff32f1892 Merge pull request #422 from meiram-tr/master
Replacing AFNewtworking files and classes names to prevent conflicts with the real pod library
2021-08-20 03:38:13 +02:00
Sefa Ilkimen
16ac763a2a fix: #425 plugin crashes on Android SDK levels < 24 2021-08-20 03:29:21 +02:00
Sefa Ilkimen
80315156ad chore: test on another device 2021-08-19 19:13:10 +02:00
Sefa Ilkimen
5499f2a8b8 chore: test on another device 2021-08-19 19:07:25 +02:00
Sefa Ilkimen
9d870e7748 chore: disable network logging on browserstack 2021-08-08 14:52:06 +02:00
Sefa Ilkimen
b6a8acd25b release v3.2.0 2021-07-26 01:22:31 +02:00
Meir Amar
c2a0317b8f replacing AFNewtworking file and classes names to prevent conflicts with the real AFNetworking pod 2021-07-25 14:59:57 +03:00
Sefa Ilkimen
0ccf64e289 chore: add workaround for missing DX files in build-tools 31 2021-07-22 15:40:38 +02:00
Sefa Ilkimen
986ddeefd4 chore: remove obsolete mipsel workaround 2021-07-22 14:36:34 +02:00
Sefa Ilkimen
67c2c733f5 chore: enable network logging on browserstack 2021-07-15 15:34:34 +02:00
Sefa Ilkimen
d077d02062 Merge pull request #421 from silkimen/feat/#420-implement-blacklist-to-disable-TLS-protocols-on-Android
feat: #420 implement blacklist to disable unsafe SSL/TLS protocol ver…
2021-07-15 14:35:24 +02:00
Sefa Ilkimen
060794354d docu: add docu for secure socket protocol blacklisting 2021-07-15 14:19:10 +02:00
Sefa Ilkimen
4ebd259c7e Merge branch 'feat/#420-implement-blacklist-to-disable-TLS-protocols-on-Android' of https://github.com/silkimen/cordova-plugin-advanced-http into feat/#420-implement-blacklist-to-disable-TLS-protocols-on-Android 2021-07-15 12:53:39 +02:00
Sefa Ilkimen
bbd4cf01ab refactor: apply review feddback 2021-07-15 12:53:12 +02:00
Sefa Ilkimen
7eb3395aa4 Update test/e2e-specs.js
Co-authored-by: Robin Hartmann <contact.robin.hartmann@gmail.com>
2021-07-15 12:38:09 +02:00
Sefa Ilkimen
05abdfcd91 Update CHANGELOG.md
Co-authored-by: Robin Hartmann <contact.robin.hartmann@gmail.com>
2021-07-15 12:36:10 +02:00
Sefa Ilkimen
bda4eedfb9 feat: #420 implement blacklist to disable unsafe SSL/TLS protocol versions on Android 2021-07-15 04:00:35 +02:00
Sefa Ilkimen
9d6005af29 - chore: implement new test reporter to see more details on failed tests
- fix: fix broken e2e tests on Android 8+
2021-07-15 03:14:46 +02:00
Sefa Ilkimen
94126b9021 release v3.1.1 2021-07-07 12:41:00 +02:00
Sefa Ilkimen
007d5c609f chore: reduce false positives in e2e tests 2021-07-07 05:07:34 +02:00
Sefa Ilkimen
badf6dcdc2 fix: #372 [Bug] Android: malformed empty multipart requests 2021-07-07 03:16:43 +02:00
Sefa Ilkimen
c081060a9e fix: broken e2e tests caused by wrong plugin entries in app template 2021-07-07 03:15:21 +02:00
Sefa Ilkimen
2ce130133c - chore: update Travis CI badge
- chore: update Travis CI Xcode image version
2021-07-06 22:40:58 +02:00
Sefa Ilkimen
cfcc572ca0 chore(CI): remove slack integration 2021-07-06 19:08:54 +02:00
Moon
7c1836e87f Set default connectTimeout and readTimeout as 60s 2021-03-25 00:02:29 +08:00
Moon
87ddbbe3b1 Update test case description 2021-03-25 00:01:46 +08:00
Sefa Ilkimen
d7688b485d fix: e2e test returns false positive due to test timing (request finished to early) 2021-03-24 06:54:59 +01:00
Sefa Ilkimen
81ba667e37 fix: broken e2e spec 2021-03-24 06:43:42 +01:00
Sefa Ilkimen
5ad1967b46 Merge branch 'master' of https://github.com/silkimen/cordova-plugin-advanced-http 2021-03-24 06:24:39 +01:00
Sefa Ilkimen
90ed474b29 docs: update changelog 2021-03-24 06:24:20 +01:00
Sefa Ilkimen
1947906c4c Merge pull request #399 from avargaskun/master
Fix: Memory leak on iOS
2021-03-24 06:22:08 +01:00
Sefa Ilkimen
b25b07a2a7 chore: update appium caps for local testing 2021-03-24 06:18:33 +01:00
Sefa Ilkimen
a01625ecfb chore: update requested appium version for saucelabs build 2021-03-24 06:06:58 +01:00
Sefa Ilkimen
b03ae7d6d1 chore: update appium capabilites for testing on saucelabs 2021-03-24 05:53:22 +01:00
Sefa Ilkimen
fdaea418be chore: update capabilities for e2e tests running on browser stack
- increase test device OS versions
2021-03-24 05:35:45 +01:00
Sefa Ilkimen
d84df0fb00 chore: update travis CI pipeline
- use node 14
- use `xcode12.2` image for iOS build
2021-03-24 05:25:20 +01:00
Sefa Ilkimen
baedd60329 chore: update cordova cli and cordova platform versions in e2e template 2021-03-24 05:12:00 +01:00
YouYue123
4687bad20d 1. Add connectTimeout getter, setter
2. Add readTimeout getter, setter
3. Modify android and ios part to take from connect and readtimeout
4. update docs
2021-03-18 22:18:00 +08:00
Antonio Vargas Garcia
80b22d4202 Fix: Memory leak on iOS
- Requests are leaking instances of the AFHTTPSessionManager
- Over time this causes iOS to terminate the app
- Inspiration for the fix: https://stackoverflow.com/a/41345142
2021-02-10 22:16:41 -08:00
Sefa Ilkimen
89ac260ef6 Merge pull request #390 from silkimen/dependabot/npm_and_yarn/ini-1.3.8
chore(deps): bump ini from 1.3.5 to 1.3.8
2021-01-18 03:58:02 +01:00
dependabot[bot]
6a266218fb chore(deps): bump ini from 1.3.5 to 1.3.8
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-18 02:49:36 +00:00
Sefa Ilkimen
6050830423 Merge pull request #389 from ath0mas/fix/344-redirect-specs
Resolve #344 restore and fix redirect specs
2021-01-18 03:31:31 +01:00
Sefa Ilkimen
95c9eb85d0 Merge pull request #394 from ath0mas/feature/docs-local-testing
docs: update readme to include Local Testing
2021-01-18 02:28:45 +01:00
Alexis THOMAS
7e3ff25195 ignore idea files 2021-01-17 21:31:13 +01:00
Alexis THOMAS
8a6b9e583e docs: update readme to include Local Testing 2021-01-17 21:26:11 +01:00
Alexis THOMAS
b32dfc7143 fix #344: "disableRedirect" removed since 3.0, use "setFollowRedirect" 2021-01-17 18:26:37 +01:00
Alexis THOMAS
a72bf758c6 docs: link to go-httpbin 2020-12-10 00:06:32 +01:00
Alexis THOMAS
45f9cbfaa4 fix #344: change redirect specs to httpbingo.org 2020-12-10 00:04:18 +01:00
Sefa Ilkimen
f8f52e1e97 chore: add FUNDING.yml 2020-12-09 00:54:33 +01:00
Sefa Ilkimen
9b995724f4 chore: use node v12 in Travis CI builds 2020-10-19 04:32:01 +02:00
Sefa Ilkimen
25a0a9a7ae fix: #372 [Bug] Android: malformed empty multipart requests 2020-10-19 04:11:49 +02:00
Sefa Ilkimen
e44def06a5 refactor: some minor cleanup and refactoring 2020-10-19 01:35:05 +02:00
Sefa Ilkimen
3d288951bf chore: update dev dependencies 2020-10-19 00:19:48 +02:00
Sefa Ilkimen
f39063cec9 chore: disable CodeQL for Java 2020-10-19 00:10:49 +02:00
Sefa Ilkimen
1bcb8016ff Setup CodeQL Analysis 2020-10-18 23:58:36 +02:00
Sefa Ilkimen
6339b9b83d release v3.1.0 2020-10-16 00:58:40 +02:00
Sefa Ilkimen
389534d661 chore: update changelog 2020-10-13 00:46:48 +02:00
Sefa Ilkimen
c10722eca0 Merge branch 'mmig-feature-abort' 2020-10-13 00:23:34 +02:00
Sefa Ilkimen
6a60058fc6 chore: auto fix some indentations 2020-10-13 00:22:42 +02:00
russa
fcedfae1ac FIX do not evaluate test result if test is skipped 2020-10-09 16:51:43 +02:00
russa
aca165b900 added unsupported warning for abort for Android versions < 6 2020-10-08 21:17:26 +02:00
russa
09c2b383ff skip abort-tests if Android version is < 6 2020-10-08 21:10:45 +02:00
russa
6918a2ed15 added support for skipping tests 2020-10-08 21:08:30 +02:00
russa
5b827d500d improve handling for race condition that request finished before adding it to pending requests map 2020-10-08 21:06:56 +02:00
russa
1c27b62148 added configuration for allowing http (cleartext) requests on Android 7 and later (sdk >= 24) 2020-10-08 18:12:59 +02:00
Sefa Ilkimen
f33a911e7e Merge pull request #364 from silkimen/dependabot/npm_and_yarn/bl-4.0.3
chore(deps): bump bl from 4.0.2 to 4.0.3
2020-10-01 02:20:41 +02:00
russa
097faad07a removed unsupported warning for aborting requests on ios from README 2020-09-25 18:59:45 +02:00
russa
62c400c6db added support for aborting requests for ios platform 2020-09-25 18:56:15 +02:00
russa
f823d24438 added tests for abort 2020-09-16 19:51:29 +02:00
russa
64a7148444 FIX use same error message for abort as on android platform 2020-09-16 19:48:23 +02:00
russa
f6736d9150 added usage description for abort() 2020-09-16 18:37:15 +02:00
russa
bc90ae85fb added 'unsupported' feedback for abort() on ios platform 2020-09-16 18:36:55 +02:00
russa
389e860125 added support for abort() on browser platform 2020-09-16 18:35:01 +02:00
russa
2367d264c1 added support for abort() on android platform 2020-09-16 18:05:30 +02:00
russa
269d5d4c8a added abort() function to js-module 2020-09-16 17:44:59 +02:00
dependabot[bot]
b6ee4de379 chore(deps): bump bl from 4.0.2 to 4.0.3
Bumps [bl](https://github.com/rvagg/bl) from 4.0.2 to 4.0.3.
- [Release notes](https://github.com/rvagg/bl/releases)
- [Commits](https://github.com/rvagg/bl/compare/v4.0.2...v4.0.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-02 15:37:00 +00:00
Sefa Ilkimen
5f327dc82a release v3.0.1 2020-08-18 12:52:01 +02:00
Sefa Ilkimen
1639efe8d0 fix: #355 [Bug] [Browser] responseType "json" not working with valid JSON response 2020-08-18 02:16:03 +02:00
Sefa Ilkimen
9bb0c58e35 chore: bump version and update readme 2020-08-17 15:59:23 +02:00
Sefa Ilkimen
57562a0dcf fix: #359 [Bug] [Android] memory leakage leads to app crashes 2020-08-17 03:02:05 +02:00
Sefa Ilkimen
ad4079625e Merge pull request #354 from silkimen/dependabot/npm_and_yarn/lodash-4.17.19
chore(deps): bump lodash from 4.17.15 to 4.17.19
2020-08-17 00:47:29 +02:00
dependabot[bot]
98d3d38e07 chore(deps): bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-18 20:24:07 +00:00
Sefa Ilkimen
c748406090 release v3.0.0 2020-07-14 03:13:05 +02:00
Sefa Ilkimen
dc6cf4d45b chore: fix update-plugin-xml.js 2020-07-14 03:12:29 +02:00
Sefa Ilkimen
7661e02598 chore: accept android sdk licenses 2020-07-14 02:39:59 +02:00
Sefa Ilkimen
20ec4bee31 chore: use node 10.x for TravisCI build 2020-07-14 01:33:22 +02:00
Sefa Ilkimen
4eed89e530 chore: update change log 2020-07-14 00:50:49 +02:00
Sefa Ilkimen
f43b2f9f5c Merge pull request #345 from ikosta/master
fix(): default filename for blob
2020-07-07 17:43:46 +02:00
Konstantinos Tsanakas
7b4d37acd9 Update www/ponyfills.js
Co-authored-by: Sefa Ilkimen <silkimen@users.noreply.github.com>
2020-07-07 11:03:36 +02:00
Konstantinos Tsanakas
ca5306cb47 fix(ponyfills): default filename for blob 2020-07-06 15:23:02 +02:00
Konstantinos Tsanakas
c0c1af5ee6 fix(): default filename for blob 2020-06-25 13:36:38 +02:00
Sefa Ilkimen
42e629e34d chore: update readme 2020-06-25 10:35:29 +02:00
Sefa Ilkimen
3bec8dde5f feat: #158 support removing headers which were previously set via "setHeader" 2020-06-25 10:28:43 +02:00
Sefa Ilkimen
8a3bc17810 chore: update dependencies 2020-06-25 09:35:06 +02:00
Sefa Ilkimen
1eb83478dc - deprecate: #297 drop support for Android < 5.1
- bump version
- remove deprecated functions
2020-06-25 07:47:51 +02:00
Sefa Ilkimen
1f0df54111 workaround #344: disable two specs until https://github.com/postmanlabs/httpbin/issues/617 is fixed 2020-06-25 07:09:53 +02:00
Pascal Brogle
6797d2c3e0 apply timeout correctly on android
Also set connectTimeout
2020-03-05 15:46:25 +01:00
61 changed files with 12973 additions and 6040 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
github: silkimen

View File

@@ -53,8 +53,8 @@ jobs:
java-version: 1.8
- name: Update test cert for httpbin.org
run: npm run updatecert
- name: Add workaround for mipsel reference
run: sudo mkdir -p $ANDROID_HOME/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64
- name: Add workaround for missing DX files in build-tools 31 (https://stackoverflow.com/a/68430992)
run: ln -s $ANDROID_HOME/build-tools/31.0.0/d8 $ANDROID_HOME/build-tools/31.0.0/dx && ln -s $ANDROID_HOME/build-tools/31.0.0/lib/d8.jar $ANDROID_HOME/build-tools/31.0.0/lib/dx.jar
- name: Build test app
run: scripts/build-test-app.sh --android --device
- name: Upload artifact to BrowserStack

62
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
# 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.
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 20 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['javascript']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# 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.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@ npm-debug.log
/temp
/android-sdk-macosx.zip
/android-sdk-macosx/**
.idea/
*.iml

View File

@@ -1,7 +1,3 @@
notifications:
slack:
secure: twDT06GAiu0jsKizow7TcghZj70KbuuTrlo02QGmbSxBk2rJfsXSrHAsA3+s/9Q4mudENk6na7fs1aPCxz+u2etUGp+2PaJKVKR5n3jrNNt3SnYeWsBgVo7o7H1aLXatX3a+TdPXh1F5gQ4Ycr93nTYbW/077jsOholwbOHDZE3VcU9dzNPwFaEvhrDbr/ei3tef0ZiM1qxIad74TgwWMKClwai3I7HCVkZOPsyV+ve6cdIJ8Dt47JzFUHSW3SZuoe5Kywxvp0VvMo/QAJw95y3edNafx4EXHwbaN71rpGWSJXIKSZzcSQalZJ9DxGYspIBkWvGsNuQRzG9CzIoNQK10iERlIVC5vKDfKX22gayOQPSDkswJzIduylBUC8zdTPCndXyNEM/Lrj6hg+ksFWN58vYNPgfUeiga7X+LV5HytftsMFW+xx2kbnGeU8doGeX8Q8G7h9OIkHCTTG7R0ldYMIqTm8YJGPkRIv4OReC5ZOhiZD+wSg4KQ0wmMeRi+hyn+I5UPnKEOHAIN8FmLNCZFbgr1wuPFp9xnJIOcumQnQVZ2t6vk6IjIbwhYPWCnf7Sr4BvJxE8eyiLrEaXK0FiPb3My9wK9tLFjj1zdD7e4+SLq+WFMeCxp2eXOGF0Bu+2VK2tGjgWhaudaIpjbRQAAQ5nPa43h16NruEvNWI=
cache:
directories:
- node_modules
@@ -15,10 +11,11 @@ matrix:
language: objective-c
sudo: false
os: osx
osx_image: xcode10.1
osx_image: xcode12.5
before_install:
- export LANG=en_US.UTF-8
- export LANG=en_US.UTF-8 &&
nvm use 14
install:
- npm install
@@ -36,17 +33,19 @@ matrix:
android:
components:
- tools
- platform-tools
- build-tools-28.0.3
- android-27
- android-28
- extra-android-support
- extra-android-m2repository
- extra-google-m2repository
before_install:
- export LANG=en_US.UTF-8 &&
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - &&
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - &&
sudo apt-get install -y nodejs
- yes | sdkmanager --update
install:
- npm install

View File

@@ -1,5 +1,43 @@
# Changelog
# 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)

103
README.md
View File

@@ -4,7 +4,7 @@ Cordova Advanced HTTP
[![MIT Licence](https://img.shields.io/badge/license-MIT-blue?style=flat)](https://opensource.org/licenses/mit-license.php)
[![downloads/month](https://img.shields.io/npm/dm/cordova-plugin-advanced-http.svg)](https://www.npmjs.com/package/cordova-plugin-advanced-http)
[![Travis Build Status](https://img.shields.io/travis/silkimen/cordova-plugin-advanced-http/master?label=Travis%20CI)](https://travis-ci.org/silkimen/cordova-plugin-advanced-http)
[![Travis Build Status](https://img.shields.io/travis/com/silkimen/cordova-plugin-advanced-http/master?label=Travis%20CI)](https://app.travis-ci.com/silkimen/cordova-plugin-advanced-http)
[![GitHub Build Status](https://img.shields.io/github/workflow/status/silkimen/cordova-plugin-advanced-http/Cordova%20HTTP%20Plugin%20CI/master?label=GitHub%20Actions)](https://github.com/silkimen/cordova-plugin-advanced-http/actions)
@@ -34,6 +34,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
@@ -61,7 +74,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');
@@ -115,12 +128,26 @@ This defaults to `urlencoded`. You can also override the default content type he
: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 the "read" timeout in seconds. This is the timeout interval to use when waiting for additional data.
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.
@@ -210,12 +237,6 @@ Configure X.509 client certificate authentication. Takes mode and options. `mode
cordova.plugin.http.setClientAuthMode('none', {}, success, fail);
```
### disableRedirect (deprecated)
This function was deprecated in 2.0.9. Use ["setFollowRedirect"](#setFollowRedirect) instead.
### setSSLCertMode (deprecated)
This function was deprecated in 2.0.8. Use ["setServerTrustMode"](#setServerTrustMode) instead.
### removeCookies
Remove all cookies associated with a given URL.
@@ -410,6 +431,46 @@ cordova.plugin.http.downloadFile("https://google.com/", {
});
```
### 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) {
// prints the filename
console.log(entry.name);
// prints the filePath
console.log(entry.fullPath);
}, 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);
});
```
## Browser support<a name="browserSupport"></a>
This plugin supports a very restricted set of functions on the browser platform.
@@ -443,6 +504,28 @@ This plugin uses amazing cloud services to maintain quality. CI Builds and E2E t
* [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 testjs
```
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 testandroid
npm run testios
```
## Contribute & Develop

16827
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-advanced-http",
"version": "2.5.1",
"version": "3.2.2",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
@@ -58,15 +58,12 @@
},
"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": "8.1.2",
"mocha": "4.0.0",
"mock-require": "2.0.2",
"mz": "2.7.0",
"chai": "4.2.0",
"colors": "1.4.0",
"cordova": "10.0.0",
"mocha": "8.2.0",
"umd-tough-cookie": "2.4.3",
"wd": "1.4.1",
"xml2js": "0.4.19"
"wd": "1.12.1",
"xml2js": "0.4.23"
}
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.5.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.2.2">
<name>Advanced HTTP plugin</name>
<description>
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
@@ -8,6 +8,7 @@
<engine name="cordova" version=">=4.0.0"/>
</engines>
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
<preference name="AndroidBlacklistSecureSocketProtocols" default="SSLv3,TLSv1"/>
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
<js-module src="www/dependency-validator.js" name="dependency-validator"/>
<js-module src="www/error-codes.js" name="error-codes"/>
@@ -35,25 +36,25 @@
<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"/>
@@ -74,16 +75,14 @@
<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/OkConnectionFactory.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/TLSConfiguration.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
<preference name="OKHTTP_VERSION" default="3.10.0"/>
<framework src="com.squareup.okhttp3:okhttp-urlconnection:$OKHTTP_VERSION"/>
</platform>
<platform name="browser">
<config-file target="config.xml" parent="/*">

View File

@@ -10,5 +10,5 @@ fi
printf 'Running e2e tests\n'
pushd $ROOT
./node_modules/.bin/mocha ./test/e2e-tooling/test.js "$@"
./node_modules/.bin/mocha --reporter ./test/e2e-tooling/reporter.js ./test/e2e-tooling/test.js "$@"
popd

View File

@@ -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]);

View File

@@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
@@ -16,11 +17,8 @@ import com.silkimen.http.HttpBodyDecoder;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.HttpRequest.HttpRequestException;
import com.silkimen.http.JsonUtils;
import com.silkimen.http.OkConnectionFactory;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -37,34 +35,37 @@ abstract class CordovaHttpBase implements Runnable {
protected String responseType;
protected Object data;
protected JSONObject headers;
protected int timeout;
protected int connectTimeout;
protected int readTimeout;
protected boolean followRedirects;
protected TLSConfiguration tlsConfiguration;
protected CallbackContext callbackContext;
protected CordovaObservableCallbackContext callbackContext;
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout,
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
CallbackContext 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.timeout = timeout;
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 timeout, boolean followRedirects,
String responseType, TLSConfiguration tlsConfiguration, 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.timeout = timeout;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.followRedirects = followRedirects;
this.responseType = responseType;
this.tlsConfiguration = tlsConfiguration;
@@ -74,30 +75,39 @@ abstract class CordovaHttpBase implements Runnable {
@Override
public void run() {
CordovaHttpResponse response = new CordovaHttpResponse();
HttpRequest request = null;
try {
HttpRequest request = this.createRequest();
request = this.createRequest();
this.prepareRequest(request);
this.sendBody(request);
this.processResponse(request, response);
request.disconnect();
} catch (HttpRequestException e) {
if (e.getCause() instanceof SSLException) {
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 (e.getCause() instanceof UnknownHostException) {
} 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 (e.getCause() instanceof SocketTimeoutException) {
} 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: " + e.getCause().getMessage());
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());
@@ -121,10 +131,10 @@ abstract class CordovaHttpBase implements Runnable {
protected void prepareRequest(HttpRequest request) throws JSONException, IOException {
request.followRedirects(this.followRedirects);
request.readTimeout(this.timeout);
request.connectTimeout(this.connectTimeout);
request.readTimeout(this.readTimeout);
request.acceptCharset("UTF-8");
request.uncompress(true);
HttpRequest.setConnectionFactory(new OkConnectionFactory());
if (this.tlsConfiguration.getHostnameVerifier() != null) {
request.setHostnameVerifier(this.tlsConfiguration.getHostnameVerifier());
@@ -148,7 +158,7 @@ abstract class CordovaHttpBase implements Runnable {
} else if ("urlencoded".equals(this.serializer)) {
// intentionally left blank, because content type is set in HttpRequest.form()
} else if ("multipart".equals(this.serializer)) {
request.contentType("multipart/form-data");
// intentionally left blank, because content type is set in HttpRequest.part()
}
}
@@ -181,6 +191,12 @@ abstract class CordovaHttpBase implements Runnable {
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");
}
}
}
@@ -203,4 +219,19 @@ abstract class CordovaHttpBase implements Runnable {
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");
}
}

View File

@@ -9,17 +9,16 @@ import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.file.FileUtils;
import org.json.JSONObject;
class CordovaHttpDownload extends CordovaHttpBase {
private String filePath;
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout, boolean followRedirects,
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int connectTimeout, int readTimeout,
boolean followRedirects, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
super("GET", url, headers, timeout, followRedirects, "text", tlsConfiguration, callbackContext);
super("GET", url, headers, connectTimeout, readTimeout, followRedirects, "text", tlsConfiguration, callbackContext);
this.filePath = filePath;
}

View File

@@ -5,21 +5,20 @@ import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.json.JSONObject;
class CordovaHttpOperation extends CordovaHttpBase {
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
int timeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
CallbackContext callbackContext) {
int connectTimeout, int readTimeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
CordovaObservableCallbackContext callbackContext) {
super(method, url, serializer, data, headers, timeout, followRedirects, responseType, tlsConfiguration,
super(method, url, serializer, data, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration,
callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
String responseType, TLSConfiguration tlsConfiguration, CallbackContext 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, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
super(method, url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
}
}

View File

@@ -1,6 +1,10 @@
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;
@@ -17,17 +21,22 @@ import android.util.Base64;
import javax.net.ssl.TrustManagerFactory;
public class CordovaHttpPlugin extends CordovaPlugin {
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();
@@ -38,6 +47,13 @@ public class CordovaHttpPlugin extends CordovaPlugin {
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);
}
@@ -73,6 +89,8 @@ public class CordovaHttpPlugin extends CordovaPlugin {
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);
} else {
return false;
}
@@ -83,14 +101,18 @@ public class CordovaHttpPlugin extends CordovaPlugin {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
int timeout = args.getInt(2) * 1000;
boolean followRedirect = args.getBoolean(3);
String responseType = args.getString(4);
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);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout, followRedirect,
responseType, this.tlsConfiguration, callbackContext);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
cordova.getThreadPool().execute(request);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, connectTimeout, readTimeout,
followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
startRequest(reqId, observableCallbackContext, request);
return true;
}
@@ -102,14 +124,18 @@ public class CordovaHttpPlugin extends CordovaPlugin {
Object data = args.get(1);
String serializer = args.getString(2);
JSONObject headers = args.getJSONObject(3);
int timeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
String responseType = args.getString(6);
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,
timeout, followRedirect, responseType, this.tlsConfiguration, callbackContext);
connectTimeout, readTimeout, followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
cordova.getThreadPool().execute(request);
startRequest(reqId, observableCallbackContext, request);
return true;
}
@@ -119,14 +145,18 @@ public class CordovaHttpPlugin extends CordovaPlugin {
JSONObject headers = args.getJSONObject(1);
JSONArray filePaths = args.getJSONArray(2);
JSONArray uploadNames = args.getJSONArray(3);
int timeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
String responseType = args.getString(6);
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);
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, timeout, followRedirect,
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), callbackContext);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
cordova.getThreadPool().execute(upload);
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;
}
@@ -135,17 +165,29 @@ public class CordovaHttpPlugin extends CordovaPlugin {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
String filePath = args.getString(2);
int timeout = args.getInt(3) * 1000;
boolean followRedirect = args.getBoolean(4);
int connectTimeout = args.getInt(3) * 1000;
int readTimeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
Integer reqId = args.getInt(6);
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout, followRedirect,
this.tlsConfiguration, callbackContext);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
cordova.getThreadPool().execute(download);
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);
@@ -166,4 +208,45 @@ public class CordovaHttpPlugin extends CordovaPlugin {
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());
}
}
}
}

View File

@@ -17,7 +17,6 @@ import java.net.URI;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -26,11 +25,11 @@ class CordovaHttpUpload extends CordovaHttpBase {
private JSONArray uploadNames;
private Context applicationContext;
public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int timeout,
public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int connectTimeout, int readTimeout,
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
Context applicationContext, CallbackContext callbackContext) {
Context applicationContext, CordovaObservableCallbackContext callbackContext) {
super("POST", url, headers, timeout, followRedirects, responseType, tlsConfiguration, callbackContext);
super("POST", url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
this.filePaths = filePaths;
this.uploadNames = uploadNames;
this.applicationContext = applicationContext;

View File

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

View File

@@ -1,26 +0,0 @@
package com.silkimen.http;
import okhttp3.OkHttpClient;
import okhttp3.OkUrlFactory;
import java.net.URL;
import java.net.HttpURLConnection;
import java.net.URLStreamHandler;
import java.net.Proxy;
public class OkConnectionFactory implements HttpRequest.ConnectionFactory {
private final OkHttpClient client = new OkHttpClient();
public HttpURLConnection create(URL url) {
OkUrlFactory urlFactory = new OkUrlFactory(this.client);
return (HttpURLConnection) urlFactory.open(url);
}
public HttpURLConnection create(URL url, Proxy proxy) {
OkHttpClient clientWithProxy = new OkHttpClient.Builder().proxy(proxy).build();
OkUrlFactory urlFactory = new OkUrlFactory(clientWithProxy);
return (HttpURLConnection) urlFactory.open(url);
}
}

View File

@@ -13,9 +13,10 @@ import javax.net.ssl.TrustManager;
import com.silkimen.http.TLSSocketFactory;
public class TLSConfiguration {
private TrustManager[] trustManagers;
private KeyManager[] keyManagers;
private HostnameVerifier hostnameVerifier;
private TrustManager[] trustManagers = null;
private KeyManager[] keyManagers = null;
private HostnameVerifier hostnameVerifier = null;
private String[] blacklistedProtocols = {};
private SSLSocketFactory socketFactory;
@@ -33,6 +34,11 @@ public class TLSConfiguration {
this.socketFactory = null;
}
public void setBlacklistedProtocols(String[] protocols) {
this.blacklistedProtocols = protocols;
this.socketFactory = null;
}
public HostnameVerifier getHostnameVerifier() {
return this.hostnameVerifier;
}
@@ -46,12 +52,7 @@ public class TLSConfiguration {
SSLContext context = SSLContext.getInstance("TLS");
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
if (android.os.Build.VERSION.SDK_INT < 20) {
this.socketFactory = new TLSSocketFactory(context);
} else {
this.socketFactory = context.getSocketFactory();
}
this.socketFactory = new TLSSocketFactory(context, this.blacklistedProtocols);
return this.socketFactory;
} catch (GeneralSecurityException e) {

View File

@@ -5,6 +5,9 @@ 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;
@@ -12,9 +15,15 @@ import javax.net.ssl.SSLSocketFactory;
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
private List<String> blacklistedProtocols;
public TLSSocketFactory(SSLContext context) {
delegate = context.getSocketFactory();
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
@@ -55,9 +64,21 @@ public class TLSSocketFactory extends SSLSocketFactory {
}
private Socket enableTLSOnSocket(Socket socket) {
if (socket != null && (socket instanceof SSLSocket)) {
((SSLSocket) socket).setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
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;
}
}

View File

@@ -3,6 +3,8 @@ var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var cordovaProxy = require('cordova/exec/proxy');
var jsUtil = require(pluginId + '.js-util');
var reqMap = {};
function serializeJsonData(data) {
try {
return JSON.stringify(data);
@@ -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,7 +30,7 @@ function serializeArray(key, values) {
function serializeParams(params) {
if (params === null) return '';
return Object.keys(params).map(function(key) {
return Object.keys(params).map(function (key) {
if (jsUtil.getTypeOf(params[key]) === 'Array') {
return serializeArray(key, params[key]);
}
@@ -38,11 +40,11 @@ function serializeParams(params) {
}
function decodeB64(dataString) {
var binarString = atob(dataString);
var bytes = new Uint8Array(binarString.length);
var binaryString = atob(dataString);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binarString.length; ++i) {
bytes[i] = binarString.charCodeAt(i);
for (var i = 0; i < binaryString.length; ++i) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
@@ -60,7 +62,7 @@ function processMultipartData(data) {
var type = data.types[i];
if (fileName) {
fd.append(name, new Blob([decodeB64(buffer)], {type: type}), 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));
@@ -115,10 +117,17 @@ 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) {
Object.keys(headers).forEach(function (key) {
if (key.toLowerCase() === headerName.toLowerCase()) {
result = headers[key];
}
@@ -134,7 +143,7 @@ function setDefaultContentType(headers, contentType) {
}
function setHeaders(xhr, headers) {
Object.keys(headers).forEach(function(key) {
Object.keys(headers).forEach(function (key) {
if (key.toLowerCase() === 'cookie') return;
xhr.setRequestHeader(key, headers[key]);
@@ -142,35 +151,45 @@ function setHeaders(xhr, headers) {
}
function sendRequest(method, withData, opts, success, failure) {
var data, serializer, headers, timeout, followRedirect, responseType;
var data, serializer, headers, readTimeout, followRedirect, responseType, reqId;
var url = opts[0];
if (withData) {
data = opts[1];
serializer = opts[2];
headers = opts[3];
timeout = opts[4];
followRedirect = opts[5];
responseType = opts[6];
// connect timeout not applied
// connectTimeout = opts[4];
readTimeout = opts[5];
followRedirect = opts[6];
responseType = opts[7];
reqId = opts[8];
} else {
headers = opts[1];
timeout = opts[2];
followRedirect = opts[3];
responseType = opts[4];
// 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 failure('advanced-http: disabling follow redirect not supported on browser platform');
return onFail('advanced-http: disabling follow redirect not supported on browser platform');
}
switch (serializer) {
@@ -179,7 +198,7 @@ function sendRequest(method, withData, opts, success, failure) {
processedData = serializeJsonData(data);
if (processedData === null) {
return failure('advanced-http: failed serializing data');
return onFail('advanced-http: failed serializing data');
}
break;
@@ -196,7 +215,7 @@ function sendRequest(method, withData, opts, success, failure) {
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) {
@@ -212,16 +231,29 @@ function sendRequest(method, withData, opts, success, failure) {
break;
}
xhr.timeout = timeout * 1000;
xhr.responseType = responseType;
// requesting text instead of JSON because it's parsed in the response handler
xhr.responseType = responseType === 'json' ? 'text' : responseType;
// we can't set connect timeout and read timeout separately on browser platform
xhr.timeout = readTimeout * 1000;
setHeaders(xhr, headers);
xhr.onerror = function () {
return failure(createXhrFailureObject(xhr));
return onFail(createXhrFailureObject(xhr));
};
xhr.ontimeout = function () {
return failure({
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,
@@ -233,15 +265,28 @@ function sendRequest(method, withData, opts, success, failure) {
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, failure) {
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 = {
get: function (success, failure, opts) {
return sendRequest('get', false, opts, success, failure);
@@ -261,6 +306,9 @@ var browserInterface = {
patch: function (success, failure, opts) {
return sendRequest('patch', true, opts, success, failure);
},
abort: function (success, failure, opts) {
return abort(opts, success, failure);
},
uploadFile: function (success, failure, opts) {
return failure('advanced-http: function "uploadFile" not supported on browser platform');
},

View File

@@ -1,5 +1,5 @@
#import <Foundation/Foundation.h>
#import "AFURLRequestSerialization.h"
#import "SM_AFURLRequestSerialization.h"
@interface BinaryRequestSerializer : AFHTTPRequestSerializer

View File

@@ -8,7 +8,7 @@
return serializer;
}
#pragma mark - AFURLRequestSerialization
#pragma mark - SM_AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters

View File

@@ -1,5 +1,5 @@
#import <Foundation/Foundation.h>
#import "AFURLResponseSerialization.h"
#import "SM_AFURLResponseSerialization.h"
@interface BinaryResponseSerializer : AFHTTPResponseSerializer

View File

@@ -85,20 +85,20 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
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;
// trying to decode error message in body
mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
}
if (error) {
*error = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo];
*error = [NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo];
}
return NO;
@@ -108,14 +108,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
return YES;
}
#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;
}
}

View File

@@ -15,5 +15,6 @@
- (void)options:(CDVInvokedUrlCommand*)command;
- (void)uploadFiles:(CDVInvokedUrlCommand*)command;
- (void)downloadFile:(CDVInvokedUrlCommand*)command;
- (void)abort:(CDVInvokedUrlCommand*)command;
@end

View File

@@ -4,31 +4,43 @@
#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:(bool)redirect forManager:(AFHTTPSessionManager*)manager;
- (void)setTimeout:(NSTimeInterval)timeout forManager:(SM_AFHTTPSessionManager*)manager;
- (void)setRedirect:(bool)redirect forManager:(SM_AFHTTPSessionManager*)manager;
@end
@implementation CordovaHttpPlugin {
AFSecurityPolicy *securityPolicy;
SM_AFSecurityPolicy *securityPolicy;
NSURLCredential *x509Credential;
NSMutableDictionary *reqDict;
}
- (void)pluginInitialize {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
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];
} else if ([serializerName isEqualToString:@"utf8"]) {
@@ -40,7 +52,7 @@
}
}
- (void)setupAuthChallengeBlock:(AFHTTPSessionManager*)manager {
- (void)setupAuthChallengeBlock:(SM_AFHTTPSessionManager*)manager {
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(
NSURLSession * _Nonnull session,
NSURLAuthenticationChallenge * _Nonnull challenge,
@@ -52,7 +64,7 @@
if (![self->securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
return NSURLSessionAuthChallengeRejectProtectionSpace;
}
if (credential) {
return NSURLSessionAuthChallengeUseCredential;
}
@@ -62,18 +74,18 @@
*credential = self->x509Credential;
return NSURLSessionAuthChallengeUseCredential;
}
return NSURLSessionAuthChallengePerformDefaultHandling;
}];
}
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(AFHTTPSessionManager*)manager {
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(SM_AFHTTPSessionManager*)manager {
[headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[manager.requestSerializer setValue:obj forHTTPHeaderField:key];
}];
}
- (void)setRedirect:(bool)followRedirect forManager:(AFHTTPSessionManager*)manager {
- (void)setRedirect:(bool)followRedirect forManager:(SM_AFHTTPSessionManager*)manager {
[manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest * _Nonnull(NSURLSession * _Nonnull session,
NSURLSessionTask * _Nonnull task, NSURLResponse * _Nonnull response, NSURLRequest * _Nonnull request) {
@@ -85,11 +97,11 @@
}];
}
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
- (void)setTimeout:(NSTimeInterval)timeout forManager:(SM_AFHTTPSessionManager*)manager {
[manager.requestSerializer setTimeoutInterval:timeout];
}
- (void)setResponseSerializer:(NSString*)responseType forManager:(AFHTTPSessionManager*)manager {
- (void)setResponseSerializer:(NSString*)responseType forManager:(SM_AFHTTPSessionManager*)manager {
if ([responseType isEqualToString: @"text"] || [responseType isEqualToString: @"json"]) {
manager.responseSerializer = [TextResponseSerializer serializer];
} else {
@@ -111,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[AFNetworkingOperationFailingURLResponseBodyErrorKey]) {
[dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyErrorKey] 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"];
}
@@ -174,18 +193,20 @@
}
- (void)executeRequestWithoutData:(CDVInvokedUrlCommand*)command withMethod:(NSString*) method {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
NSString *responseType = [command.arguments objectAtIndex:4];
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:timeoutInSeconds forManager:manager];
[self setTimeout:readTimeout forManager:manager];
[self setRedirect:followRedirect forManager:manager];
[self setResponseSerializer:responseType forManager:manager];
@@ -194,30 +215,37 @@
@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];
};
[manager downloadTaskWithHTTPMethod:method URLString:url parameters:nil progress:nil success:onSuccess failure:onFailure];
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];
@@ -226,49 +254,53 @@
}
- (void)executeRequestWithData:(CDVInvokedUrlCommand*)command withMethod:(NSString*)method {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *data = [command.arguments objectAtIndex:1];
NSString *serializerName = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
NSString *responseType = [command.arguments objectAtIndex:6];
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:timeoutInSeconds 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"];
@@ -278,30 +310,38 @@
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"]) {
[manager uploadTaskWithHTTPMethod:method URLString:url parameters:nil constructingBodyWithBlock:constructBody progress:nil success:onSuccess failure:onFailure];
task = [manager uploadTaskWithHTTPMethod:method URLString:url parameters:nil constructingBodyWithBlock:constructBody progress:nil success:onSuccess failure:onFailure];
} else {
[manager uploadTaskWithHTTPMethod:method URLString:url parameters:data progress:nil success:onSuccess failure:onFailure];
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];
@@ -313,15 +353,15 @@
NSString *certMode = [command.arguments objectAtIndex:0];
if ([certMode isEqualToString: @"default"] || [certMode isEqualToString: @"legacy"]) {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
} else if ([certMode isEqualToString: @"nocheck"]) {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName = NO;
} else if ([certMode isEqualToString: @"pinned"]) {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
}
@@ -333,27 +373,27 @@
- (void)setClientAuthMode:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult;
NSString *mode = [command.arguments objectAtIndex:0];
if ([mode isEqualToString:@"none"]) {
x509Credential = nil;
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
}
if ([mode isEqualToString:@"systemstore"]) {
NSString *alias = [command.arguments objectAtIndex:1];
// TODO
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);
@@ -367,7 +407,7 @@
self->x509Credential = [NSURLCredential credentialWithIdentity:identity certificates: nil persistence:NSURLCredentialPersistenceForSession];
CFRelease(items);
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
}
}
@@ -404,19 +444,20 @@
}
- (void)uploadFiles:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSArray *filePaths = [command.arguments objectAtIndex: 2];
NSArray *names = [command.arguments objectAtIndex: 3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
NSString *responseType = [command.arguments objectAtIndex:6];
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 setupAuthChallengeBlock: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setTimeout:readTimeout forManager:manager];
[self setRedirect:followRedirect forManager:manager];
[self setResponseSerializer:responseType forManager:manager];
@@ -424,7 +465,7 @@
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
[manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSURLSessionDataTask *task = [manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSError *error;
for (int i = 0; i < [filePaths count]; i++) {
NSString *filePath = (NSString *) [filePaths objectAtIndex:i];
@@ -433,6 +474,8 @@
[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"];
@@ -442,6 +485,8 @@
return;
}
} progress:nil success:^(NSURLSessionTask *task, id responseObject) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
@@ -449,6 +494,8 @@
[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];
@@ -456,6 +503,7 @@
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
[self addRequest:reqId forTask:task];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
@@ -464,18 +512,20 @@
}
- (void)downloadFile:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSString *filePath = [command.arguments objectAtIndex: 2];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
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 setupAuthChallengeBlock: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setTimeout:readTimeout forManager:manager];
[self setRedirect:followRedirect forManager:manager];
if ([filePath hasPrefix:@"file://"]) {
@@ -486,7 +536,8 @@
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
[manager GET:url parameters:nil 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
@@ -547,6 +598,7 @@
[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"];
@@ -555,6 +607,7 @@
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
[self addRequest:reqId forTask:task];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
@@ -562,4 +615,32 @@
}
}
- (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

View File

@@ -1,4 +1,4 @@
// AFHTTPSessionManager.h
// SM_AFHTTPSessionManager.h
// Copyright (c) 20112016 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.
@@ -85,26 +85,26 @@ NS_ASSUME_NONNULL_BEGIN
@warning `requestSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic, strong) 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`.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
@property (nonatomic, strong) 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.

View File

@@ -1,4 +1,4 @@
// AFHTTPSessionManager.m
// SM_AFHTTPSessionManager.m
// Copyright (c) 20112016 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 {
@@ -86,13 +86,13 @@
#pragma mark -
- (void)setRequestSerializer:(AFHTTPRequestSerializer <AFURLRequestSerialization> *)requestSerializer {
- (void)setRequestSerializer:(AFHTTPRequestSerializer <SM_AFURLRequestSerialization> *)requestSerializer {
NSParameterAssert(requestSerializer);
_requestSerializer = requestSerializer;
}
- (void)setResponseSerializer:(AFHTTPResponseSerializer <AFURLResponseSerialization> *)responseSerializer {
- (void)setResponseSerializer:(AFHTTPResponseSerializer <SM_AFURLResponseSerialization> *)responseSerializer {
NSParameterAssert(responseSerializer);
[super setResponseSerializer:responseSerializer];
@@ -428,7 +428,7 @@
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))];
SM_AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[SM_AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))];
if (decodedPolicy) {
self.securityPolicy = decodedPolicy;
}
@@ -453,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];

View File

@@ -1,4 +1,4 @@
// AFNetworkReachabilityManager.h
// SM_AFNetworkReachabilityManager.h
// Copyright (c) 20112016 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

View File

@@ -1,4 +1,4 @@
// AFNetworkReachabilityManager.m
// SM_AFNetworkReachabilityManager.m
// Copyright (c) 20112016 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) {
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);

View File

@@ -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_ */

View File

@@ -1,4 +1,4 @@
// AFSecurityPolicy.h
// SM_AFSecurityPolicy.h
// Copyright (c) 20112016 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.
*/

View File

@@ -1,4 +1,4 @@
// AFSecurityPolicy.m
// SM_AFSecurityPolicy.m
// Copyright (c) 20112016 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,12 +148,12 @@ 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
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"www/certificates"];
@@ -179,7 +179,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
}
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
SM_AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
@@ -190,7 +190,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];
@@ -341,7 +341,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;

View File

@@ -1,4 +1,4 @@
// AFURLRequestSerialization.h
// SM_AFURLRequestSerialization.h
// Copyright (c) 20112016 Alamofire Software Foundation ( http://alamofire.org/ )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -56,11 +56,11 @@ FOUNDATION_EXPORT NSString * AFPercentEscapedStringFromString(NSString *string);
FOUNDATION_EXPORT NSString * 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.
`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.
*/
@interface AFHTTPRequestSerializer : NSObject <AFURLRequestSerialization>
@interface 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
*/
@@ -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

View File

@@ -1,4 +1,4 @@
// AFURLRequestSerialization.m
// SM_AFURLRequestSerialization.m
// Copyright (c) 20112016 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);
@@ -455,7 +455,7 @@ forHTTPHeaderField:(NSString *)field
return mutableRequest;
}
#pragma mark - AFURLRequestSerialization
#pragma mark - SM_AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
@@ -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;
@@ -1211,7 +1211,7 @@ typedef enum {
return serializer;
}
#pragma mark - AFURLRequestSerialization
#pragma mark - SM_AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters

View File

@@ -1,4 +1,4 @@
// AFURLResponseSerialization.h
// SM_AFURLResponseSerialization.h
// Copyright (c) 20112016 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.
`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.
*/
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>
@interface AFHTTPResponseSerializer : NSObject <SM_AFURLResponseSerialization>
- (instancetype)init;
@@ -259,14 +259,14 @@ NS_ASSUME_NONNULL_BEGIN
/**
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:`.
*/
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<AFURLResponseSerialization>> *)responseSerializers;
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<SM_AFURLResponseSerialization>> *)responseSerializers;
@end
@@ -279,40 +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;
/**
`AFNetworkingOperationFailingURLResponseBodyErrorKey`
`SM_AFNetworkingOperationFailingURLResponseBodyErrorKey`
The corresponding value is an `NSString` containing the decoded error message.
*/
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyErrorKey;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingOperationFailingURLResponseBodyErrorKey;
NS_ASSUME_NONNULL_END

View File

@@ -1,4 +1,4 @@
// AFURLResponseSerialization.m
// SM_AFURLResponseSerialization.m
// Copyright (c) 20112016 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,10 +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 AFNetworkingOperationFailingURLResponseBodyErrorKey = @"com.alamofire.serialization.response.error.body";
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) {
@@ -121,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;
@@ -137,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;
}
@@ -159,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
@@ -231,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;
}
}
@@ -319,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;
}
}
@@ -364,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;
}
}
@@ -447,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;
}
}
@@ -512,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;
@@ -666,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;
}
}
@@ -750,13 +750,13 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
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) {
for (id <SM_AFURLResponseSerialization> serializer in self.responseSerializers) {
if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
continue;
}

View File

@@ -1,4 +1,4 @@
// AFURLSessionManager.h
// SM_AFURLSessionManager.h
// Copyright (c) 20112016 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.
@@ -104,16 +104,16 @@ NS_ASSUME_NONNULL_BEGIN
@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
///----------------------------
@@ -450,17 +450,17 @@ 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.
@@ -473,28 +473,28 @@ FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;
FOUNDATION_EXPORT NSString * const 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

View File

@@ -1,4 +1,4 @@
// AFURLSessionManager.m
// SM_AFURLSessionManager.m
// Copyright (c) 20112016 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 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 AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
NSString * const 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];
});
});
});
@@ -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];
@@ -522,16 +522,16 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
self.responseSerializer = [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 +565,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 +576,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 +584,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 +595,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 +613,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 +628,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 +644,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 +664,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 +726,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
#pragma mark -
- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer {
- (void)setResponseSerializer:(id <SM_AFURLResponseSerialization>)responseSerializer {
NSParameterAssert(responseSerializer);
_responseSerializer = responseSerializer;
@@ -1078,7 +1078,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 +1114,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 +1130,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 +1168,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) {

View File

@@ -1,5 +1,5 @@
#import <Foundation/Foundation.h>
#import "AFURLRequestSerialization.h"
#import "SM_AFURLRequestSerialization.h"
@interface TextRequestSerializer : AFHTTPRequestSerializer

View File

@@ -8,7 +8,7 @@
return serializer;
}
#pragma mark - AFURLRequestSerialization
#pragma mark - SM_AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters

View File

@@ -1,5 +1,5 @@
#import <Foundation/Foundation.h>
#import "AFURLResponseSerialization.h"
#import "SM_AFURLResponseSerialization.h"
@interface TextResponseSerializer : AFHTTPResponseSerializer

View File

@@ -94,26 +94,26 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
if (data && !*decoded) {
NSMutableDictionary *mutableUserInfo = [@{
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
AFNetworkingOperationFailingURLResponseDataErrorKey: data,
AFNetworkingOperationFailingURLResponseBodyErrorKey: @"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[AFNetworkingOperationFailingURLResponseBodyErrorKey] = *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;
}
}
@@ -125,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
@@ -134,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;
}
}

View File

@@ -16,15 +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="7.1.0" />
<engine name="browser" spec="5.0.0" />
<engine name="ios" spec="5.0.1" />
<plugin name="cordova-plugin-file" spec="6.0.1" />
<preference name="AndroidPersistentFileLocation" value="Internal" />
<preference name="AndroidBlacklistSecureSocketProtocols" value="SSLv3,TLSv1" />
</widget>

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

View File

@@ -1,26 +1,35 @@
{
"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": "7.1.0",
"cordova-browser": "5.0.0",
"cordova-ios": "5.0.1"
},
"cordova": {
"platforms": [
"android",
"ios"
]
},
"devDependencies": {}
}
"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": "10.0.0",
"cordova-android": "9.0.0",
"cordova-browser": "6.0.0",
"cordova-ios": "6.2.0",
"cordova-plugin-device": "2.0.3",
"cordova-plugin-wkwebview-file-xhr": "3.0.0"
},
"cordova": {
"platforms": [
"android",
"ios",
"browser"
],
"plugins": {
"cordova-plugin-device": {},
"cordova-plugin-wkwebview-file-xhr": {}
}
},
"devDependencies": {
"cordova-plugin-device": "2.0.3"
}
}

View File

@@ -10,12 +10,12 @@ const app = {
var onlyFlaggedTests = [];
var enabledTests = [];
tests.forEach(function (test) {
if (test.only) {
onlyFlaggedTests.push(test);
}
if (!test.disabled) {
enabledTests.push(test);
}
@@ -50,6 +50,16 @@ const app = {
};
},
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);
@@ -126,7 +136,7 @@ const app = {
const execTest = function () {
try {
testDefinition.func(app.resolve, app.reject);
testDefinition.func(app.resolve, app.reject, app.skip);
} catch (error) {
app.throw(error);
}

View File

@@ -83,10 +83,37 @@ const helpers = {
}
result.type.should.be.equal(expected);
},
isAbortSupported: function () {
// abort is not working reliably; will be documented in known issues
return false;
if (window.cordova && window.cordova.platformId === 'android') {
var version = device.version; // NOTE will throw error if cordova is present without cordova-plugin-device
var major = parseInt(/^(\d+)(\.|$)/.exec(version)[1], 10);
return isFinite(major) && major >= 6;
}
return true;
},
getAbortDelay: function () { return 0; },
getDemoArrayBuffer: function(size) {
var demoText = [73, 39, 109, 32, 97, 32, 100, 117, 109, 109, 121, 32, 102, 105, 108, 101, 33, 32, 73, 39, 109, 32, 117, 115, 101, 100, 32, 102, 111, 114, 32, 116, 101, 115, 116, 105, 110, 103, 32, 112, 117, 114, 112, 111, 115, 101, 115, 46, 32, 82, 97, 110, 100, 111, 109, 32, 100, 97, 116, 97, 32, 105, 115, 32, 102, 111, 108, 108, 111, 119, 105, 110, 103, 58, 32];
var buffer = new ArrayBuffer(size);
var view = new Uint8Array(buffer);
for (var i = 0; i < size; ++i) {
view[i] = demoText[i];
}
return buffer;
},
isTlsBlacklistSupported: function () {
return window.cordova && window.cordova.platformId === 'android';
}
};
const messageFactory = {
handshakeFailed: function() { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: Handshake failed' },
sslTrustAnchor: function () { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
invalidCertificate: function (domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
}
@@ -291,7 +318,7 @@ const tests = [
{
description: 'should resolve correct URL after redirect (GET) #33',
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbingo.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');
@@ -300,8 +327,8 @@ const tests = [
{
description: 'should not follow 302 redirect when following redirects is disabled',
expected: 'rejected: {"status": 302, ...',
before: function (resolve, reject) { cordova.plugin.http.disableRedirect(true, resolve, reject) },
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
before: function (resolve, reject) { cordova.plugin.http.setFollowRedirect(false); resolve(); },
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbingo.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.status.should.be.equal(302);
@@ -900,7 +927,7 @@ const tests = [
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
should.equal(null, result.data.data);
should.equal(true, result.data.data === null || result.data.data === undefined);
}
},
{
@@ -988,6 +1015,157 @@ const tests = [
JSON.parse(result.data.data).cookies.should.be.eql({});
}
},
{
description: 'should be able to abort (POST)',
expected: 'rejected: {"status":-8, "error": "Request ...}',
before: helpers.setRawSerializer,
func: function (resolve, reject, skip) {
if (!helpers.isAbortSupported()) {
return skip();
}
var targetUrl = 'http://httpbin.org/post';
var fileContent = helpers.getDemoArrayBuffer(10000);
var reqId = cordova.plugin.http.post(targetUrl, fileContent, {}, resolve, reject);
setTimeout(function () {
cordova.plugin.http.abort(reqId);
}, helpers.getAbortDelay());
},
validationFunc: function (driver, result) {
helpers.checkResult(result, 'rejected');
result.data.status.should.be.equal(-8);
}
},
{
description: 'should be able to abort (GET)',
expected: 'rejected: {"status":-8, "error": "Request ...}',
func: function (resolve, reject, skip) {
if (!helpers.isAbortSupported()) {
return skip();
}
var url = 'https://httpbin.org/drip?duration=2&numbytes=10&code=200';
var options = { method: 'get', responseType: 'blob' };
var success = function (response) {
resolve({
isBlob: response.data.constructor === Blob,
type: response.data.type,
byteLength: response.data.size
});
};
var reqId = cordova.plugin.http.sendRequest(url, options, success, reject);
setTimeout(function () {
cordova.plugin.http.abort(reqId);
}, helpers.getAbortDelay());
},
validationFunc: function (driver, result) {
helpers.checkResult(result, 'rejected');
result.data.status.should.be.equal(-8);
}
},
{
description: 'should be able to abort downloading a file',
expected: 'rejected: {"status":-8, "error": "Request ...}',
func: function (resolve, reject, skip) {
if (!helpers.isAbortSupported()) {
return skip();
}
var sourceUrl = 'http://httpbin.org/xml';
var targetPath = cordova.file.cacheDirectory + 'test.xml';
var reqId = 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);
setTimeout(function () {
cordova.plugin.http.abort(reqId);
}, helpers.getAbortDelay());
},
validationFunc: function (driver, result) {
helpers.checkResult(result, 'rejected');
result.data.status.should.be.equal(-8);
}
},
{
description: 'should be able to abort uploading a file',
expected: 'rejected: {"status":-8, "error": "Request ...}',
func: function (resolve, reject, skip) {
if (!helpers.isAbortSupported()) {
return skip();
}
var fileName = 'test-file.txt';
var fileContent = helpers.getDemoArrayBuffer(10000);
var sourcePath = cordova.file.cacheDirectory + fileName;
var targetUrl = 'http://httpbin.org/post';
helpers.writeToFile(function () {
var reqId = cordova.plugin.http.uploadFile(targetUrl, {}, {}, sourcePath, fileName, resolve, reject);
setTimeout(function () {
cordova.plugin.http.abort(reqId);
}, helpers.getAbortDelay());
}, fileName, fileContent);
},
validationFunc: function (driver, result) {
helpers.checkResult(result, 'rejected');
result.data.status.should.be.equal(-8);
}
},
{
description: 'should not send malformed request when FormData is empty #372',
expected: 'resolved: {"status":200, ...',
before: helpers.setMultipartSerializer,
func: function (resolve, reject) {
var ponyfills = cordova.plugin.http.ponyfills;
var formData = new ponyfills.FormData();
var url = 'http://httpbin.org/anything';
var options = { method: 'post', data: formData };
cordova.plugin.http.sendRequest(url, options, resolve, reject);
},
validationFunc: function (driver, result, targetInfo) {
helpers.checkResult(result, 'resolved');
var parsed = JSON.parse(result.data.data);
if (targetInfo.isAndroid) {
// boundary should be sent correctly on Android
parsed.headers['Content-Type'].should.be.equal('multipart/form-data; boundary=00content0boundary00');
} else {
// falling back to empty url encoded request on iOS
parsed.headers['Content-Type'].should.be.equal('application/x-www-form-urlencoded');
}
}
},
{
description: 'should reject connecting to server with blacklisted SSL version #420',
expected: 'rejected: {"status":-2, ...',
func: function (resolve, reject, skip) {
if (!helpers.isTlsBlacklistSupported()) {
return skip();
}
cordova.plugin.http.get('https://tls-v1-0.badssl.com:1010/', {}, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: messageFactory.handshakeFailed() });
}
},
];
if (typeof module !== 'undefined' && module.exports) {

View File

@@ -14,7 +14,7 @@ const configs = {
},
localIosEmulator: {
platformName: 'iOS',
platformVersion: '13.2',
platformVersion: '14.3',
automationName: 'XCUITest',
deviceName: 'iPhone 8',
autoWebview: true,
@@ -22,7 +22,7 @@ const configs = {
},
localAndroidEmulator: {
platformName: 'Android',
platformVersion: '5',
platformVersion: '9',
deviceName: 'Android Emulator',
autoWebview: true,
fullReset: true,
@@ -32,27 +32,27 @@ const configs = {
// testing on SauceLabs
saucelabsIosDevice: {
browserName: '',
'appium-version': '1.9.1',
'appium-version': '1.20.1',
platformName: 'iOS',
platformVersion: '10.3',
platformVersion: '14.3',
deviceName: 'iPhone 6',
autoWebview: true,
app: 'sauce-storage:HttpDemo.app.zip'
},
saucelabsIosEmulator: {
browserName: '',
'appium-version': '1.9.1',
'appium-version': '1.20.1',
platformName: 'iOS',
platformVersion: '10.3',
platformVersion: '14.3',
deviceName: 'iPhone Simulator',
autoWebview: true,
app: 'sauce-storage:HttpDemo.app.zip'
},
saucelabsAndroidEmulator: {
browserName: '',
'appium-version': '1.9.1',
'appium-version': '1.20.1',
platformName: 'Android',
platformVersion: '5.1',
platformVersion: '8.0',
deviceName: 'Android Emulator',
autoWebview: true,
app: 'sauce-storage:HttpDemo.apk'
@@ -60,18 +60,20 @@ const configs = {
// testing on BrowserStack
browserstackIosDevice: {
device: 'iPhone 7',
os_version: '10',
device: 'iPhone 12',
os_version: '14',
project: 'HTTP Test App',
autoWebview: true,
app: 'HttpTestAppAndroid'
app: 'HttpTestAppAndroid',
'browserstack.networkLogs': false
},
browserstackAndroidDevice: {
device: 'Google Nexus 6',
os_version: '5.0',
os_version: '6.0',
project: 'HTTP Test App',
autoWebview: true,
app: 'HttpTestAppAndroid'
app: 'HttpTestAppAndroid',
'browserstack.networkLogs': false
}
};

View File

@@ -0,0 +1,223 @@
'use strict';
const Mocha = require('mocha');
const milliseconds = require('ms');
const Base = Mocha.reporters.Base;
const color = Base.color;
const {
isString,
stringify,
inherits
} = Mocha.utils;
const {
EVENT_RUN_BEGIN,
EVENT_RUN_END,
EVENT_TEST_FAIL,
EVENT_TEST_PASS,
EVENT_TEST_PENDING,
EVENT_SUITE_BEGIN,
EVENT_SUITE_END
} = Mocha.Runner.constants;
function Spec(runner, options) {
Base.call(this, runner, options);
var self = this;
var indents = 0;
var n = 0;
function indent() {
return Array(indents).join(' ');
}
runner.on(EVENT_RUN_BEGIN, function() {
Base.consoleLog();
});
runner.on(EVENT_SUITE_BEGIN, function(suite) {
++indents;
Base.consoleLog(color('suite', '%s%s'), indent(), suite.title);
});
runner.on(EVENT_SUITE_END, function() {
--indents;
if (indents === 1) {
Base.consoleLog();
}
});
runner.on(EVENT_TEST_PENDING, function(test) {
var fmt = indent() + color('pending', ' - %s');
Base.consoleLog(fmt, test.title);
});
runner.on(EVENT_TEST_PASS, function(test) {
var fmt;
if (test.speed === 'fast') {
fmt =
indent() +
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s');
Base.consoleLog(fmt, test.title);
} else {
fmt =
indent() +
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s') +
color(test.speed, ' (%dms)');
Base.consoleLog(fmt, test.title, test.duration);
}
});
runner.on(EVENT_TEST_FAIL, function(test) {
Base.consoleLog(indent() + color('fail', ' %d) %s'), ++n, test.title);
});
runner.once(EVENT_RUN_END, self.epilogue.bind(self));
}
/**
* Inherit from `Base.prototype`.
*/
inherits(Spec, Base);
Spec.description = 'custom reporter for HTTP plugin testing';
Spec.prototype.epilogue = function() {
var stats = this.stats;
var fmt;
Base.consoleLog();
// passes
fmt =
color('bright pass', ' ') +
color('green', ' %d passing') +
color('light', ' (%s)');
Base.consoleLog(fmt, stats.passes || 0, milliseconds(stats.duration));
// pending
if (stats.pending) {
fmt = color('pending', ' ') + color('pending', ' %d pending');
Base.consoleLog(fmt, stats.pending);
}
// failures
if (stats.failures) {
fmt = color('fail', ' %d failing');
Base.consoleLog(fmt, stats.failures);
this.showList(this.failures);
Base.consoleLog();
}
Base.consoleLog();
};
Spec.prototype.showList = function(failures) {
var multipleErr, multipleTest;
var self = this;
Base.consoleLog();
failures.forEach(function(test, i) {
// format
var fmt =
color('error title', ' %s) %s:\n') +
color('error message', ' %s') +
color('error stack', '\n%s\n');
// msg
var msg;
var err;
if (test.err && test.err.multiple) {
if (multipleTest !== test) {
multipleTest = test;
multipleErr = [test.err].concat(test.err.multiple);
}
err = multipleErr.shift();
} else {
err = test.err;
}
var message;
if (err.message && typeof err.message.toString === 'function') {
message = err.message + '';
} else if (typeof err.inspect === 'function') {
message = err.inspect() + '';
} else {
message = '';
}
var stack = err.stack || message;
var index = message ? stack.indexOf(message) : -1;
if (index === -1) {
msg = message;
} else {
index += message.length;
msg = stack.slice(0, index);
// remove msg from stack
stack = stack.slice(index + 1);
}
// uncaught
if (err.uncaught) {
msg = 'Uncaught ' + msg;
}
// explicitly show diff
if (Base.showDiff(err)) {
self.stringifyDiffObjs(err);
fmt =
color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
var match = message.match(/^([^:]+): expected/);
msg = '\n ' + color('error message', match ? match[1] : msg);
msg += Base.generateDiff(err.actual, err.expected);
}
// indent stack trace
stack = stack.replace(/^/gm, ' ');
// indented test title
var testTitle = '';
test.titlePath().forEach(function(str, index) {
if (index !== 0) {
testTitle += '\n ';
}
for (var i = 0; i < index; i++) {
testTitle += ' ';
}
testTitle += str;
});
Base.consoleLog(fmt, i + 1, testTitle, msg, stack);
self.showDetails(err);
});
};
Spec.prototype.stringifyDiffObjs = function(err) {
if (!isString(err.actual) || !isString(err.expected)) {
err.actual = stringify(err.actual);
err.expected = stringify(err.expected);
}
}
Spec.prototype.showDetails = function(err) {
if (!err.details) {
return;
}
const details = JSON
.stringify(err.details, null, 2)
.replace(/^/gm, ' ');
Base.consoleLog(
color('error stack', '\n Details:\n%s'),
details
);
}
exports = module.exports = Spec;

View File

@@ -1,15 +1,15 @@
const wd = require('wd');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const logging = require('./logging');
const capsConfig = require('./caps');
const serverConfig = require('./server');
const testDefinitions = require('../e2e-specs');
chai.use(chaiAsPromised);
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
global.should = chai.should();
let driver;
let allPassed = true;
describe('Advanced HTTP e2e test suite', function () {
const isSauceLabs = !!process.env.SAUCE_USERNAME;
const isBrowserStack = !!process.env.BROWSERSTACK_USERNAME;
@@ -20,9 +20,6 @@ describe('Advanced HTTP e2e test suite', function () {
const targetInfo = { isSauceLabs, isBrowserStack, isDevice, isAndroid };
const environment = isSauceLabs ? 'saucelabs' : isBrowserStack ? 'browserstack' : 'local';
let driver;
let allPassed = true;
this.timeout(15000);
this.slow(4000);
@@ -52,12 +49,19 @@ describe('Advanced HTTP e2e test suite', function () {
});
const defineTestForMocha = (test, index) => {
it(index + ': ' + test.description, async () => {
it(index + ': ' + test.description, async function () {
await clickNext(driver);
await validateTestIndex(driver, index);
await validateTestTitle(driver, test.description);
await waitToBeFinished(driver, test.timeout || 10000);
await validateResult(driver, test.validationFunc, targetInfo);
const skipped = await checkSkipped(driver);
if (skipped) {
this.skip();
} else {
await validateResult(driver, test.validationFunc, targetInfo);
}
});
};
@@ -117,7 +121,20 @@ async function waitToBeFinished(driver, timeout) {
async function validateResult(driver, validationFunc, targetInfo) {
const result = await driver.safeExecute('app.lastResult');
validationFunc(driver, result, targetInfo);
try {
validationFunc(driver, result, targetInfo);
} catch (error) {
allPassed = false;
error.details = result;
throw error;
}
}
async function checkSkipped(driver) {
const result = await driver.safeExecute('app.lastResult');
return result.type === 'skipped';
}
function sleep(ms) {

View File

@@ -1,5 +1,4 @@
const chai = require('chai');
const mock = require('mock-require');
const util = require('util');
const should = chai.should();
@@ -50,6 +49,13 @@ describe('Advanced HTTP public interface', function () {
http.getHeaders('*').myKey.should.equal('myValue');
});
it('clears global headers correctly when value is undefined', () => {
http.setHeader('*', 'myKey', 'myValue');
http.setHeader('*', 'myKey', null);
should.equal(undefined, http.getHeaders('*').myKey);
Object.keys(http.getHeaders('*')).length.should.be.equal(0);
});
it('sets host headers correctly #24', () => {
http.setHeader('www.google.de', 'myKey', 'myValue');
http.getHeaders('www.google.de').myKey.should.equal('myValue');
@@ -142,12 +148,32 @@ describe('Advanced HTTP public interface', function () {
it('configures global timeout value correctly with given valid value', () => {
http.setRequestTimeout(10);
http.getRequestTimeout().should.equal(10);
http.getConnectTimeout().should.equal(10);
http.getReadTimeout().should.equal(10);
});
it('throws an Error when you try to configure global timeout with a string', () => {
(() => { http.setRequestTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
});
it('configures connect timeout value correctly with given valid value', () => {
http.setConnectTimeout(10);
http.getConnectTimeout().should.equal(10);
})
it('throws an Error when you try to configure connect timeout with a string', () => {
(() => { http.setConnectTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
})
it('configures read timeout value correctly with given valid value', () => {
http.setReadTimeout(10);
http.getReadTimeout().should.equal(10);
})
it('throws an Error when you try to configure connect timeout with a string', () => {
(() => { http.setReadTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
})
it('sets global option for following redirects correctly', () => {
http.setFollowRedirect(false);
http.getFollowRedirect().should.equal(false);
@@ -375,6 +401,8 @@ describe('Common helpers', function () {
serializer: 'urlencoded',
followRedirect: true,
timeout: 60.0,
connectTimeout: 30.0,
readTimeout: 30.0
}
it('adds missing "followRedirect" option correctly', () => {
@@ -623,6 +651,44 @@ describe('Common helpers', function () {
});
});
it('processes data correctly when serializer "multipart" is configured and form data contains file value (filename set)', (cb) => {
const formData = new FormDataMock();
formData.append('myFile', new BlobMock([testString], { type: 'application/octet-stream' }), 'file.name');
helpers.processData(formData, 'multipart', (data) => {
data.buffers.length.should.be.equal(1);
data.names.length.should.be.equal(1);
data.fileNames.length.should.be.equal(1);
data.types.length.should.be.equal(1);
data.buffers[0].should.be.eql(testStringBase64);
data.names[0].should.be.equal('myFile');
data.fileNames[0].should.be.equal('file.name');
data.types[0].should.be.equal('application/octet-stream');
cb();
});
});
it('processes data correctly when serializer "multipart" is configured and form data contains file value (filename empty)', (cb) => {
const formData = new FormDataMock();
formData.append('myFile', new BlobMock([testString], { type: 'application/octet-stream' }), '');
helpers.processData(formData, 'multipart', (data) => {
data.buffers.length.should.be.equal(1);
data.names.length.should.be.equal(1);
data.fileNames.length.should.be.equal(1);
data.types.length.should.be.equal(1);
data.buffers[0].should.be.eql(testStringBase64);
data.names[0].should.be.equal('myFile');
data.fileNames[0].should.be.equal('');
data.types[0].should.be.equal('application/octet-stream');
cb();
});
});
it('processes data correctly when serializer "raw" is configured', (cb) => {
const byteArray = new Uint8Array([1, 2, 3]);
helpers.processData(byteArray, 'raw', (data) => {
@@ -632,6 +698,20 @@ describe('Common helpers', function () {
})
});
});
describe('nextRequestId()', function () {
const helpers = require('../www/helpers')(null, null, null, null, null, null);
it('returns number requestIds', () => {
helpers.nextRequestId().should.be.a('number');
});
it('returns unique requestIds', () => {
const ids = [helpers.nextRequestId(), helpers.nextRequestId(), helpers.nextRequestId()];
const set = new Set(ids);
ids.should.to.deep.equal(Array.from(set));
});
});
});
describe('Dependency Validator', function () {

View File

@@ -3,7 +3,7 @@ const BlobMock = require('./Blob.mock');
module.exports = class FileMock extends BlobMock {
constructor(blob, fileName) {
super(blob, { type: blob.type });
this._fileName = fileName || '';
this._fileName = fileName !== undefined ? fileName : 'blob';
this.__lastModifiedDate = new Date();
}

View File

@@ -6,4 +6,5 @@ module.exports = {
UNSUPPORTED_URL: -5,
NOT_CONNECTED: -6,
POST_PROCESSING_FAILED: -7,
ABORTED: -8,
};

View File

@@ -3,6 +3,8 @@ var globalConfigs = {
serializer: 'urlencoded',
followRedirect: true,
timeout: 60.0,
connectTimeout: 60.0,
readTimeout: 60.0
};
module.exports = globalConfigs;

View File

@@ -5,6 +5,13 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'options', 'upload', 'download'];
var validResponseTypes = ['text', 'json', 'arraybuffer', 'blob'];
var nextRequestId = (function(){
var currReqId = 0;
return function nextRequestId() {
return ++currReqId;
}
})();
var interface = {
b64EncodeUnicode: b64EncodeUnicode,
checkClientAuthMode: checkClientAuthMode,
@@ -24,6 +31,7 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
injectCookieHandler: injectCookieHandler,
injectFileEntryHandler: injectFileEntryHandler,
injectRawResponseHandler: injectRawResponseHandler,
nextRequestId: nextRequestId,
};
// expose all functions for testing purposes
@@ -187,7 +195,9 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
}
function checkForInvalidHeaderValue(value) {
if (jsUtil.getTypeOf(value) !== 'String') {
var type = jsUtil.getTypeOf(value);
if (type !== 'String' && type !== 'Null') {
throw new Error(messages.INVALID_HEADER_VALUE);
}
@@ -451,7 +461,7 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
reader.onload = function () {
result.buffers.push(base64.fromArrayBuffer(reader.result));
result.names.push(entry.value[0]);
result.fileNames.push(entry.value[1].name || 'blob');
result.fileNames.push(entry.value[1].name !== undefined ? entry.value[1].name : 'blob');
result.types.push(entry.value[1].type || '');
processFormDataIterator(iterator, textEncoder, result, onFinished);
};
@@ -495,7 +505,9 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
params: checkParamsObject(options.params || {}),
responseType: checkResponseType(options.responseType || validResponseTypes[0]),
serializer: checkSerializer(options.serializer || globals.serializer),
timeout: checkTimeoutValue(options.timeout || globals.timeout),
connectTimeout: checkTimeoutValue(options.connectTimeout || globals.connectTimeout),
readTimeout: checkTimeoutValue(options.readTimeout || globals.readTimeout),
timeout: checkTimeoutValue(options.timeout || globals.timeout)
};
}
};

View File

@@ -11,7 +11,7 @@ module.exports = {
INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
INVALID_DOWNLOAD_FILE_PATH: 'advanced-http: invalid "filePath" value, needs to be a string, <filePath: string>',
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value, <followRedirect: boolean>',
INVALID_HEADER_VALUE: 'advanced-http: invalid header value, needs to be a string, <header: string>',
INVALID_HEADER_VALUE: 'advanced-http: invalid header value, needs to be a string or null, <header: string | null>',
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
INVALID_RESPONSE_TYPE: 'advanced-http: invalid response type, supported types are:',
INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',

View File

@@ -16,7 +16,7 @@ module.exports = function init(global) {
} else if (global.Blob && value instanceof global.Blob) {
// mimic File instance by adding missing properties
value.lastModifiedDate = new Date();
value.name = filename || '';
value.name = filename !== undefined ? filename : 'blob';
} else {
value = String(value);
}
@@ -44,4 +44,4 @@ module.exports = function init(global) {
}
return interface;
};
};

View File

@@ -14,10 +14,12 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
setRequestTimeout: setRequestTimeout,
getFollowRedirect: getFollowRedirect,
setFollowRedirect: setFollowRedirect,
// @DEPRECATED
disableRedirect: disableRedirect,
// @DEPRECATED
setSSLCertMode: setServerTrustMode,
// @Android Only
getConnectTimeout: getConnectTimeout,
// @Android Only
setConnectTimeout: setConnectTimeout,
getReadTimeout: getReadTimeout,
setReadTimeout: setReadTimeout,
setServerTrustMode: setServerTrustMode,
setClientAuthMode: setClientAuthMode,
sendRequest: sendRequest,
@@ -30,6 +32,7 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
options: options,
uploadFile: uploadFile,
downloadFile: downloadFile,
abort: abort,
ErrorCode: errorCodes,
ponyfills: ponyfills
};
@@ -62,7 +65,12 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
helpers.checkForInvalidHeaderValue(value);
globalConfigs.headers[host] = globalConfigs.headers[host] || {};
globalConfigs.headers[host][header] = value;
if (value === null) {
delete globalConfigs.headers[host][header];
} else {
globalConfigs.headers[host][header] = value;
}
}
function getDataSerializer() {
@@ -95,6 +103,24 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
function setRequestTimeout(timeout) {
globalConfigs.timeout = helpers.checkTimeoutValue(timeout);
globalConfigs.connectTimeout = helpers.checkTimeoutValue(timeout);
globalConfigs.readTimeout = helpers.checkTimeoutValue(timeout);
}
function getConnectTimeout() {
return globalConfigs.connectTimeout;
}
function setConnectTimeout(timeout) {
globalConfigs.connectTimeout = helpers.checkTimeoutValue(timeout);
}
function getReadTimeout() {
return globalConfigs.readTimeout;
}
function setReadTimeout(timeout) {
globalConfigs.readTimeout = helpers.checkTimeoutValue(timeout);
}
function getFollowRedirect() {
@@ -105,14 +131,6 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
globalConfigs.followRedirect = helpers.checkFollowRedirectValue(follow);
}
// @DEPRECATED
function disableRedirect(disable, success, failure) {
helpers.handleMissingCallbacks(success, failure);
setFollowRedirect(!disable);
success();
}
function setServerTrustMode(mode, success, failure) {
helpers.handleMissingCallbacks(success, failure);
@@ -150,23 +168,31 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
var onFail = helpers.injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, helpers.injectRawResponseHandler(options.responseType, success, failure));
var reqId = helpers.nextRequestId();
switch (options.method) {
case 'post':
case 'put':
case 'patch':
return helpers.processData(options.data, options.serializer, function(data) {
exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect, options.responseType]);
helpers.processData(options.data, options.serializer, function (data) {
exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, reqId]);
});
break;
case 'upload':
var fileOptions = helpers.checkUploadFileOptions(options.filePath, options.name);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.timeout, options.followRedirect, options.responseType]);
exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFiles', [url, headers, fileOptions.filePaths, fileOptions.names, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, reqId]);
break;
case 'download':
var filePath = helpers.checkDownloadFilePath(options.filePath);
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, filePath, options.timeout, options.followRedirect]);
exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, filePath, options.connectTimeout, options.readTimeout, options.followRedirect, reqId]);
break;
default:
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect, options.responseType]);
exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.connectTimeout, options.readTimeout, options.followRedirect, options.responseType, reqId]);
break;
}
return reqId;
}
function post(url, data, headers, success, failure) {
@@ -205,5 +231,9 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
}
function abort(requestId , success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'abort', [requestId]);
}
return publicInterface;
}