Compare commits

..

590 Commits

Author SHA1 Message Date
Sefa Ilkimen
1f5b0f5b49 chore: add JS linter 2025-12-02 08:27:04 +01:00
Sefa Ilkimen
af5e06902e chore: prepare iOS testing on BrowserStack 2025-12-02 07:57:28 +01:00
Sefa Ilkimen
73f37d6a35 chore: remove Travis CI builds 2025-12-02 06:52:28 +01:00
Sefa Ilkimen
3678f5f6e1 chore: fix Android CI build 2025-12-02 06:45:43 +01:00
Sefa Ilkimen
41861aa2ec chore: use npm caching in CI builds 2025-12-02 05:09:54 +01:00
Sefa Ilkimen
91573159e3 chore: update CodeQL workflow 2025-12-02 04:36:14 +01:00
Sefa Ilkimen
b4ff7735de chore: enable manual dispatch of GH Actions workflow 2025-12-02 03:45:13 +01:00
Sefa Ilkimen
60c042164a test: fix broken tests due to changes to BrowserStack 2023-06-14 17:29:22 +02:00
Sefa Ilkimen
342022d3a7 test: fix broken tests due to changes to BrowserStack 2023-06-14 13:50:29 +02:00
Sefa Ilkimen
dbe28b284f test: fix broken tests due to changes to BrowserStack 2023-06-06 03:31:17 +02:00
Sefa Ilkimen
fe1a0fdbf7 test: fix broken test due to changes at BrowserStack 2023-06-06 03:02:20 +02:00
Sefa Ilkimen
32a250fbd5 Merge pull request #504 from ciknowles/bugfix-482
Update CordovaHttpBase.java - Bugfix 482, omit Accept-Charset header
2023-06-06 02:47:36 +02:00
Sefa Ilkimen
cb84860bb1 chore: clean up 2023-06-06 02:47:07 +02:00
Sefa Ilkimen
7f5345eafd Merge pull request #491 from RonnieRen/bugfix/certificates_path
support public asset path name instead of www for capacitor engine
2023-05-24 02:00:55 +02:00
Sefa Ilkimen
54cbb840e2 Merge pull request #494 from silkimen/dependabot/npm_and_yarn/http-cache-semantics-4.1.1
chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1
2023-05-24 01:55:21 +02:00
Sefa Ilkimen
866034ea18 Merge pull request #502 from silkimen/dependabot/npm_and_yarn/xml2js-0.5.0
chore(deps-dev): bump xml2js from 0.4.23 to 0.5.0
2023-05-24 01:49:14 +02:00
ciknowles
6615f1bf53 Update CordovaHttpBase.java 2023-04-24 18:30:36 +01:00
dependabot[bot]
3ee42a41e4 chore(deps-dev): bump xml2js from 0.4.23 to 0.5.0
Bumps [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js) from 0.4.23 to 0.5.0.
- [Release notes](https://github.com/Leonidas-from-XIV/node-xml2js/releases)
- [Commits](https://github.com/Leonidas-from-XIV/node-xml2js/commits/0.5.0)

---
updated-dependencies:
- dependency-name: xml2js
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 23:26:48 +00:00
uf200387
8acefd2791 for capacitor, we need to find the certificates from main bundle 2023-03-05 00:50:42 +08:00
uf200387
f6adf1e920 use correct asset name for ios platform if we are running capacitor 2023-03-05 00:37:17 +08:00
uf200387
0c26ada847 use the correct asset dir based on we are running on capacitor or cordova 2023-03-05 00:13:06 +08:00
dependabot[bot]
3d20ab6b8c chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-03 18:09:14 +00:00
Sefa Ilkimen
6fc8a7e06d chore: improve travis build for android 2022-12-29 04:59:16 +01:00
Sefa Ilkimen
00dc85cdac chore: update android build tools for Travis CI build 2022-12-29 04:34:34 +01:00
Sefa Ilkimen
c86874a01a test: fix broken tests due to changes at httpbingo.org 2022-12-29 03:14:10 +01:00
Sefa Ilkimen
86fc3892a4 chore: update GitHub Actions badge 2022-12-29 01:19:40 +01:00
ronnie ren
469ace48b5 support public asset path for capacitor engine 2022-12-28 18:34:46 +08:00
Sefa Ilkimen
b1ad996585 Merge pull request #480 from shaneAPT/master
Update READ.md setCookie with removal message
2022-10-19 11:24:17 +02:00
Shane Tully
1218a57e5f update READ.md setCookie 2022-09-22 21:48:40 +01:00
Sefa Ilkimen
320ed35022 Merge pull request #469 from silkimen/dependabot/npm_and_yarn/async-2.6.4
chore(deps): bump async from 2.6.3 to 2.6.4
2022-09-16 04:42:41 +02:00
dependabot[bot]
845f90d43f chore(deps): bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-29 02:30:39 +00:00
Sefa Ilkimen
f6c3ea7f66 release v3.3.1 2022-03-31 01:22:23 +02:00
Sefa Ilkimen
d52eb08982 docu: update changelog 2022-03-31 01:16:49 +02:00
Sefa Ilkimen
57c87db5a1 fix: broken network connection check on Android 2022-03-31 01:02:09 +02:00
Sefa Ilkimen
8ea1f3aed6 Merge pull request #427 from moshe5745/master
Add "No connection" error response with status code -6
2022-03-31 00:37:46 +02:00
Sefa Ilkimen
e41684cc79 Merge pull request #459 from spoonconsulting/remove_dup_symbol_base
AFNetworking: Remove Duplicate Symbol Base
2022-03-31 00:11:40 +02:00
Sefa Ilkimen
d393d3ca78 Merge pull request #429 from inited/master
Added support for intermediate certificates on iOS
2022-03-30 23:56:22 +02:00
Sefa Ilkimen
479c644b1d Merge pull request #444 from alexander-gruenewald/patch-1
[Browser] Fixing error propagation in setHeaders
2022-03-30 23:36:36 +02:00
Sefa Ilkimen
bfa446c7fd Merge pull request #461 from dtarnawsky/master
Resolves #441 disable url cache
2022-03-30 23:33:42 +02:00
Sefa Ilkimen
c92f07775b chore: update all dependencies 2022-03-30 23:14:37 +02:00
Sefa Ilkimen
9870dde50a release v3.3.0 2022-03-30 17:37:14 +02:00
Sefa Ilkimen
ed08534cf3 chore: fix broken CI build / test 2022-03-30 16:19:46 +02:00
Sefa Ilkimen
ea15617fae docu: update changelog 2022-03-29 14:39:54 +02:00
Sefa Ilkimen
87c915f7c0 Merge pull request #451 from h4ck-rOOt/download-response-ext
feat: response object on file download
2022-03-29 14:04:05 +02:00
Damian Tarnawsky
1f4ccd37b4 Resolves #441 disable url cache 2022-03-11 06:30:34 -08:00
zafirskthelifehacker
343c536a3b New Line 2022-03-10 10:26:50 +04:00
zafirskthelifehacker
38da25bb75 Reset Package-Lock and Revert Version 2022-03-10 10:26:08 +04:00
zafirskthelifehacker
42175e27ad Remove Spoon Consulting Ltd 2022-03-10 10:16:21 +04:00
Tobias Becht
43b65fa887 docs: extend downloadFile example code 2022-02-01 16:48:10 +01:00
Tobias Becht
48445786b6 chore: review feedback, response object 2022-02-01 16:44:08 +01:00
Tobias Becht
08ec3203e9 feat: response object on file download 2022-02-01 14:56:33 +01:00
Alexander Grünewald
9684c03685 Update cordova-http-plugin.js 2022-01-03 08:00:04 +01:00
YushraJewon
8f3cfb11d0 forking reason in readme 2021-12-17 14:38:46 +04:00
d48512c8df package name 2021-12-16 23:18:11 +04:00
ee2d727c2b rename symbol 2021-12-16 23:09:55 +04:00
0ae3756a74 remove duplicate symbol of afnetworking 2021-12-16 18:49:57 +04:00
Alexander Grünewald
935e7d6ba3 [Browser] Fixing error propagation in setHeaders
If the headers contain certain special characters (€, “, ≠, ∑, †, Ω etc..), calling the setHeaders method will throw an "Value is not a valid ByteString" error which unfortunately is neither propagated to the caller of the http-plugin, nor is the xhr request cancelled in this case.

By wrapping the setHeader-call into a try/catch, and fail the xhr request in case of an error, we make sure the caller of the http-plugin will receive a proper error and the xhr request gets canceled.
2021-11-30 17:04:50 +01:00
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
Jaroslav Pavlicek
6a9a22f81e Added support for intermediate certificates on iOS 2021-09-14 11:42:01 +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
moshe_ch
36d7e1813c Add "No connection" error response with status code -6 2021-08-26 01:04:22 +03: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
Sefa Ilkimen
ef09e466ec release v2.5.1 2020-06-04 01:32:33 +02:00
Sefa Ilkimen
88de0550f4 chore: update README 2020-05-30 18:39:20 +02:00
Sefa Ilkimen
7f680b07ec chore: update changelog 2020-05-29 05:03:12 +02:00
Sefa Ilkimen
a259e7cf8d Merge branch 'clear-cookies-ios'
# Conflicts:
#	test/e2e-specs.js
2020-05-29 05:00:25 +02:00
Sefa Ilkimen
1e3c9e6645 chore: update changelog 2020-05-29 04:22:50 +02:00
Sefa Ilkimen
e0cf458179 Merge branch 'antikalk-master' 2020-05-29 04:15:02 +02:00
Sefa Ilkimen
4f4e7ffa33 fix: broken handling for empty strings 2020-05-29 04:03:20 +02:00
Sefa Ilkimen
65e4a4d4dc fix: e2e spec "should allow empty response body even though responseType is set #334"
- `cordova.plugin.http.get` does not support passing an options object => using `cordova.plugin.http.sendRequest` instead
- but still fails on test "should return empty body string correctly (GET)"
2020-05-29 03:35:20 +02:00
antikalk
fed2b03cc6 Revert "+ dont manipulate response data"
This reverts commit bfc6ba2008.
2020-05-21 22:41:49 +02:00
antikalk
29e0b385de Merge branch 'master' of https://github.com/antikalk/cordova-plugin-advanced-http 2020-05-16 16:55:10 +02:00
antikalk
bfc6ba2008 + dont manipulate response data 2020-05-16 16:54:58 +02:00
Oliver
060aa088f5 Merge branch 'master' into master 2020-05-15 08:32:03 +02:00
antikalk
e775057389 + added e2e test 2020-05-14 22:22:03 +02:00
antikalk
81874c6bc8 + test if empty response data is handled correctly 2020-05-13 09:13:51 +02:00
antikalk
658bbd8b28 + set data to null if response data is not set 2020-05-13 09:12:59 +02:00
antikalk
a1e4be37d4 + early return pattern 2020-05-13 08:07:13 +02:00
antikalk
5be52d78d1 + handle empty success responses 2020-05-12 14:44:22 +02:00
Sefa Ilkimen
0a23e29403 chore: document X.509 client cert feature 2020-05-02 00:48:52 +02:00
Sefa Ilkimen
6009ae82f7 chore: update readme to prevent confusion on request timeout 2020-05-02 00:06:58 +02:00
Sefa Ilkimen
0bfb81c57f chore: fix browserstack testing for android 2020-05-01 23:56:39 +02:00
Pascal Brogle
6797d2c3e0 apply timeout correctly on android
Also set connectTimeout
2020-03-05 15:46:25 +01:00
Sefa Ilkimen
f8f4bdc9df fix: auth challenge block isn't handling server trust settings correctly 2020-03-05 03:23:28 +01:00
Sefa Ilkimen
d84e3995d2 chore: update appium caps 2020-03-05 02:04:48 +01:00
Sefa Ilkimen
f5597dd176 WIP: implement setClientAuthMode()for iOS 2020-03-05 01:36:27 +01:00
Sefa Ilkimen
b25b7db4be release v2.4.1 2020-02-18 01:04:57 +01:00
Sefa Ilkimen
92be8f8e96 fix #248: clearCookies() does not work on iOS 2020-02-03 03:58:09 +01:00
Sefa Ilkimen
9ffdc5d2eb test: implement e2e test for #248 2020-02-03 03:23:50 +01:00
Sefa Ilkimen
23a98ae491 fix #300: FormData object containing null or undefined value is not serialized correctly 2020-02-03 02:46:31 +01:00
Sefa Ilkimen
ab9dad8f46 Merge branch 'antikalk-master' 2020-02-03 01:38:00 +01:00
Sefa Ilkimen
69344b5357 chore: update changelog 2020-02-03 01:37:21 +01:00
Sefa Ilkimen
c88d1b6cc7 test: implement e2e test for #301 2020-02-03 01:30:28 +01:00
antikalk
b6f369b868 fix for issues #220 and #286
When responseType is set to json, the data should be returned as plain json string, not as a base64 encoded string.
2020-01-29 20:14:06 +01:00
Sefa Ilkimen
39fa17e4ed update changelog 2020-01-27 23:57:45 +01:00
Sefa Ilkimen
5a19c6ad06 Merge branch 'fix/#296-multipart-serializer-on-browser-platform' 2020-01-27 23:45:53 +01:00
Sefa Ilkimen
192059d34e release v2.4.0 2020-01-27 04:49:11 +01:00
Sefa Ilkimen
7193b636f1 feat #283: improve error message on timeout on browser platform 2020-01-27 04:44:13 +01:00
Sefa Ilkimen
662b4352f5 update readme and changelog 2020-01-27 02:58:45 +01:00
Sefa Ilkimen
e18f9eee51 implement feat #155 for iOS 2020-01-27 02:22:29 +01:00
Sefa Ilkimen
c7eb60e670 implement feat #155 for android 2020-01-27 02:10:32 +01:00
Sefa Ilkimen
6a930de82f implement www interface for #155 2020-01-27 02:10:01 +01:00
Sefa Ilkimen
588e4a0e57 implement e2e specs for #155 2020-01-27 02:09:21 +01:00
Sefa Ilkimen
78db1dc516 fix #296: [Bug] [browser] multipart requests are not serialized correctly 2020-01-27 01:29:04 +01:00
Sefa Ilkimen
aded59e3d1 implement raw serializer support for browser target 2020-01-26 18:41:31 +01:00
Sefa Ilkimen
9ef582b37f Merge branch 'chuchuva-raw-request-body' 2020-01-26 18:00:47 +01:00
Sefa Ilkimen
33fea67603 update readme and changelog 2020-01-26 17:55:04 +01:00
Sefa Ilkimen
faffe0e078 fix e2e tests for raw serializer 2020-01-26 17:50:40 +01:00
Sefa Ilkimen
99c7f5d331 add default content type for raw serializer 2020-01-26 17:50:18 +01:00
Pavel Chuchuva
b74ad73742 Add documentation for 'raw' serializer 2020-01-18 08:44:58 +11:00
Pavel Chuchuva
7fd7ab223c Add tests for 'raw' serializer 2020-01-18 08:23:43 +11:00
Pavel Chuchuva
937010bd4e Add support for sending 'raw' requests on iOS too 2020-01-14 16:09:47 +11:00
Pavel Chuchuva
7ab4b634ca Add support for sending 'raw' requests on Android
request.data can be `Uint8Array` or `ArrayBuffer`. The plugin will send it as is, without any processing.
2020-01-12 08:18:02 +11:00
Sefa Ilkimen
32187a12fe chore: fix #281 broken builds in forks 2019-12-15 05:18:58 +01:00
Sefa Ilkimen
a4f121728c release v2.3.1 2019-12-14 04:36:31 +01:00
Sefa Ilkimen
21d991b04e chore: setup testing android version on BrowserStack 2019-12-14 04:09:11 +01:00
Sefa Ilkimen
31b1eee355 chore: update version and changelog 2019-12-14 02:15:49 +01:00
Sefa Ilkimen
3d1b195831 Merge pull request #275 from ath0mas/patch-1
Fix WebStorageCookieStore getAllCookies()
2019-12-14 02:08:23 +01:00
Alexis THOMAS
014753e127 fix(cookieStore): error in getAllCookies
typo in Array.prototype call was throwing a TypeError
2019-12-05 15:38:30 +01:00
Sefa Ilkimen
5ee26bc2cc Update badges in readme 2019-12-02 02:46:21 +01:00
Sefa Ilkimen
7d7f02b4b3 release v2.3.0 2019-12-02 02:20:52 +01:00
Sefa Ilkimen
afc9e3e944 Update readme 2019-12-01 07:30:45 +01:00
Sefa Ilkimen
9af2d1c21a Update license and readme file 2019-12-01 06:47:29 +01:00
Sefa Ilkimen
9dce2fb964 Update badges in readme 2019-12-01 06:28:21 +01:00
Sefa Ilkimen
eb946b49ab Add GitHub Actions CI workflow 2019-12-01 06:08:16 +01:00
Sefa Ilkimen
72ca81b515 Update changelog 2019-11-30 17:36:49 +01:00
Sefa Ilkimen
68633f1bb8 chore: update changelog and readme 2019-11-18 04:22:37 +01:00
Sefa Ilkimen
ee26b78e0d chore: update mocha timeout values 2019-11-18 03:09:32 +01:00
Sefa Ilkimen
d924f98844 chore: update slack notifications for travis builds 2019-11-18 02:59:22 +01:00
Sefa Ilkimen
8fceb4df97 chore: configure mocha threshold values 2019-11-18 02:28:52 +01:00
Sefa Ilkimen
7a09fa9460 feat: add ponyfills to support multipart requests on android webview versions < 50 and iOS versions < 13.2 2019-11-18 02:01:02 +01:00
Sefa Ilkimen
3e5c941fdd refactor: iOS implementation 2019-11-17 22:46:51 +01:00
Sefa Ilkimen
0f273f401b feat(ios): implement multipart requests
- expose new AFHTTPSessionManager method "uploadTaskWithHTTPMethod"
2019-11-17 21:29:38 +01:00
Sefa Ilkimen
684874184d - refactor: iOS implementation
- chore: add flags to run only one spec or disable spec in e2e tests
2019-11-17 19:33:19 +01:00
Sefa Ilkimen
19e1e7206b implement e2e tests for #101 2019-11-14 04:45:20 +01:00
Sefa Ilkimen
21bec76c11 refactor e2e test scripts 2019-11-14 02:06:33 +01:00
Sefa Ilkimen
594d03aa41 WIP: major progress for #101
- feat(www): implement preprocessor for FormData instances
- feat(www): implement API checks for multipart requests
- feat(android): implement multipart requests
- chore(specs): implement www specs for new prprocessor
2019-11-11 04:49:35 +01:00
Sefa Ilkimen
b3276ad2d4 update cordova-ios in test app template 2019-11-10 17:05:52 +01:00
Sefa Ilkimen
3b4a5b7c26 add missing module reference for "dependency-validator.js" 2019-11-10 06:55:38 +01:00
Sefa Ilkimen
867b8ea202 - WIP: implement data pre-processor for #101
- implement checks for #101
- add some specs
2019-11-10 06:40:31 +01:00
Sefa Ilkimen
cc3e70771c release v2.2.0 2019-09-29 23:39:40 +02:00
Sefa Ilkimen
9e892119cc feat(#127): adding multiple file upload
- implement multi uploading files for ios
- update readme
2019-09-29 21:43:58 +02:00
Sefa Ilkimen
f93f69e0aa feat(#127): adding multiple file upload
- remove www interface function "uploadFiles" as it confuses more than it helps
- implement multi uploading files for android
- add e2e spec
2019-09-29 20:37:36 +02:00
Sefa Ilkimen
4ace394464 feat(#127): adding multiple file upload
- fix missing option params
2019-09-27 13:25:14 +02:00
Sefa Ilkimen
ae3e821639 feat(#127): adding multiple file upload
- prepare www interface
- implement options validation
- implement specs for js interface
- improve error messages
2019-09-26 16:37:19 +02:00
Sefa Ilkimen
e17768041c fix: missing file reference in plugin.xml 2019-09-26 04:44:40 +02:00
Sefa Ilkimen
34a559bc6e feature #253: add support for response type "json" 2019-09-26 04:31:08 +02:00
Sefa Ilkimen
2ce1fc407d feature #239: add enumeration style object for error codes 2019-09-26 03:00:30 +02:00
Sefa Ilkimen
4b73c3762c Merge pull request #243 from joellimberg/patch-1
Readme typo fix: ”plugin.” → “plugin.http.”
2019-07-30 23:46:05 +02:00
Joel Limberg
5a9c176bca Readme typo fix: ”plugin.” → “plugin.http.” 2019-07-26 22:07:46 +03:00
Sefa Ilkimen
40aa944d0f fix #231: remove broken link 2019-07-23 23:02:34 +02:00
Sefa Ilkimen
c7feb7aca3 improve readme 2019-07-23 22:39:56 +02:00
Sefa Ilkimen
680105175b Update issue templates 2019-06-25 02:26:16 +02:00
Sefa Ilkimen
07811d0380 Update issue templates 2019-06-25 02:25:20 +02:00
Sefa Ilkimen
026e8589c2 Update issue templates 2019-06-24 17:49:08 +02:00
Sefa Ilkimen
e1592001af Update issue templates 2019-06-24 17:36:30 +02:00
Sefa Ilkimen
f057b126ba release v2.1.1 2019-06-14 17:01:30 +02:00
Sefa Ilkimen
2b567cdf32 Fix #224: responseType "arraybuffer" and "blob" not working on browser platform 2019-06-14 16:43:36 +02:00
Sefa Ilkimen
d99c7bb154 release v2.1.0 2019-06-14 02:57:16 +02:00
Sefa Ilkimen
336a56be1c update changelog 2019-06-14 02:56:59 +02:00
Sefa Ilkimen
8b962cc350 Merge pull request #205 from BeMyEye/master
add OKHTTP_VERSION plugin variable
2019-06-14 02:37:52 +02:00
Sefa Ilkimen
13bf4666b0 feature #171: support for responseType "blob" 2019-06-14 02:29:45 +02:00
Sefa Ilkimen
1fc3d6230c - update readme
- implement response type support for browser platform
2019-06-14 00:52:27 +02:00
Sefa Ilkimen
85346e0381 Merge branch 'binary_response'
# Conflicts:
#	test/e2e-specs.js
2019-06-13 16:44:35 +02:00
Sefa Ilkimen
c9f8c8b66a release v2.0.11 2019-06-13 16:12:50 +02:00
Sefa Ilkimen
9b26a0f031 fix #221: headers are not set on Android when request fails due to non-success status code 2019-06-13 16:00:26 +02:00
Sefa Ilkimen
298c031433 release v2.0.10 2019-06-13 00:20:01 +02:00
Sefa Ilkimen
eab6acf85c - update changelog
- increment version
2019-06-13 00:12:09 +02:00
Sefa Ilkimen
8cf0d21a7a add response type "arraybuffer" support for iOS 2019-06-12 23:43:40 +02:00
Sefa Ilkimen
f91727e14a add response type "arraybuffer" support for android 2019-06-12 23:01:15 +02:00
Sefa Ilkimen
7b485507dc - use appium 1.9.1 for saucelabs
- update readme
2019-06-03 13:49:51 +02:00
Sefa Ilkimen
03b0abb74e using updated appium version for saucelabs 2019-06-03 13:32:45 +02:00
Sefa Ilkimen
c3d60c37bf fix #218: headers are used as params on browser platform 2019-06-03 11:59:35 +02:00
Sefa Ilkimen
54ef8ae169 release v2.0.9 2019-04-15 18:35:51 +02:00
Sefa Ilkimen
c83940a8f6 deprecate "disableRedirect" in favor of "setFollowRedirect" 2019-04-15 18:09:34 +02:00
Sefa Ilkimen
ccabbf2a29 - add some specs
- prepare new "follow redirect" config interface
2019-04-15 04:23:03 +02:00
Sefa Ilkimen
7ba0852698 fix JS specs 2019-04-15 03:59:47 +02:00
Sefa Ilkimen
4f3ff9097f WIP: implementing X509 client cert authentication (android "buffer" mode) 2019-04-15 03:18:59 +02:00
Sefa Ilkimen
620ce3f81c Fix #204: broken support for cordova-android < 7.0 2019-04-15 02:47:14 +02:00
cvaliere
6e68bf4dfe Merge pull request #1 from BeMyEye/use-preference-OKHTTP_VERSION
use OKHTTP_VERSION for okhttp-urlconnection
2019-04-11 17:35:14 +02:00
Kevin Simon
87f0f3600c use OKHTTP_VERSION for okhttp-urlconnection 2019-04-11 17:24:27 +02:00
Sefa Ilkimen
0bf39650e1 release v2.0.8 2019-04-10 12:06:17 +02:00
Sefa Ilkimen
d444363a8e - fix #198: Cookie header is always passed even if there is no cookie
- some cleanup
2019-04-09 17:25:55 +02:00
Sefa Ilkimen
13976bbe43 Update change log and version number 2019-04-08 20:16:24 +02:00
Sefa Ilkimen
70c8b9bb32 Fix #201: browser implementation broken due to broken dependency 2019-04-08 20:12:36 +02:00
Sefa Ilkimen
4b964e8b7c Fix #197: iOS crashes on multiple HTTPS downloadFile() calls (reverting a8e3637) 2019-04-08 19:14:39 +02:00
Sefa Ilkimen
56f8b41f4f Fix #189: error code mappings for TLS handshake problems are not precise 2019-04-08 17:58:39 +02:00
Sefa Ilkimen
e1e720fe2d Add missing handler on browser platform 2019-04-08 17:31:38 +02:00
Sefa Ilkimen
a1b6ea94f0 Fix #200: remove string switch and use ugly if ... else 😢 to be compatible with Java target 6 2019-04-05 16:48:18 +02:00
Sefa Ilkimen
d977392a49 - some cleanup
- deprecate "setSSLCertMode" in favor of "setServerTrustMode"
2019-04-05 16:26:04 +02:00
Sefa Ilkimen
8d28f4ab80 WIP: implementing X509 client cert authentication 2019-04-05 05:22:34 +02:00
Sefa Ilkimen
38b3e6ffb1 release v2.0.7 2019-04-03 19:20:58 +02:00
Sefa Ilkimen
7d984bcf89 add spec for #197 2019-04-03 19:14:39 +02:00
Sefa Ilkimen
2cfac2026b - increment version
- update changelog
- improve query params spec
2019-04-03 18:58:19 +02:00
Sefa Ilkimen
8bdbf7ca78 fix query params handling for iOS 2019-04-03 14:28:12 +02:00
Sefa Ilkimen
d5ce3ed76b fix query param url encoding 2019-04-03 13:03:49 +02:00
Sefa Ilkimen
8a83a940cb WIP: support deeply structured query params (fix #48) 2019-04-02 12:51:20 +02:00
Sefa Ilkimen
230de36618 WIP:
- some refactoring for future features
- move params serializer into JS code
2019-04-02 12:04:49 +02:00
Sefa Ilkimen
0af726d8ce align iOS error codes with Android error codes 2019-03-28 15:37:56 +01:00
Sefa Ilkimen
ba57d3cba1 re-integrate OkHttp3 connection factory 2019-03-22 18:09:25 +01:00
Sefa Ilkimen
508b1b4f83 re-implement disabling verification of hostname 2019-03-22 05:03:24 +01:00
Sefa Ilkimen
7992bd0991 WIP: started re-implementing SSL cert modes 2019-03-22 04:06:50 +01:00
Sefa Ilkimen
a331b57ab2 WIP: fix disableRedirect() 2019-03-22 02:18:43 +01:00
Sefa Ilkimen
ee30160921 WIP: reduce duplicate code & cleanup 2019-03-22 02:06:39 +01:00
Sefa Ilkimen
e8e1c4273f WIP: re-implement Upload and Download Runnables 2019-03-21 17:45:24 +01:00
Sefa Ilkimen
752b2cdcb7 WIP: started refactoring
- one HTTP request class implementing Runnable for all request methods
 - broken SSL cert handling
2019-03-21 15:12:53 +01:00
Sefa Ilkimen
314314d7f9 release v2.0.6 2019-03-11 14:20:57 +01:00
Sefa Ilkimen
a8e3637f27 Merge branch 'chrisjdev-master' 2019-03-11 14:01:37 +01:00
Sefa Ilkimen
56272b9a5d Update changelog 2019-03-11 13:59:17 +01:00
Sefa Ilkimen
8f859db57f Merge branch 'master' of https://github.com/chrisjdev/cordova-plugin-advanced-http into chrisjdev-master 2019-03-11 11:34:10 +01:00
Sefa Ilkimen
e673754b13 Test for #184 2019-03-01 18:12:46 +01:00
Sefa Ilkimen
a0f376233c Fix #187: setSSLCertMode with "default" throws an error on Android 2019-03-01 18:12:06 +01:00
Sefa Ilkimen
fcd142a70b release v2.0.5 2019-02-25 13:08:25 +01:00
Sefa Ilkimen
807400bc63 Fix #185: need more detailed SSL error message 2019-02-22 19:20:12 +01:00
Sefa Ilkimen
1fd857f1d9 release v2.0.4 2019-01-17 17:30:43 +01:00
Sefa Ilkimen
f801d2a283 Update changelog 2019-01-17 17:26:24 +01:00
Sefa Ilkimen
6033ea4b76 Fix #179: Can't send empty string with utf8 serializer 2019-01-17 17:03:12 +01:00
Sefa Ilkimen
dadbf97d0c release v2.0.3 2019-01-15 17:00:54 +01:00
Sefa Ilkimen
f06788d199 Update cordova to fix peer dependency issues 2019-01-15 16:58:00 +01:00
Sefa Ilkimen
a195de409d Fix #172: plugin does not respect user installed CA certs on Android 2018-12-23 19:00:51 +01:00
Chris J
0fade8351d Merge pull request #1 from chrisjdev/keepSessionManager
Keep AFHTTPSessionManager instance with the plugin
2018-12-20 10:49:05 -05:00
Chris J
ebd6ae9793 Keep AFHTTPSessionManager instance with the plugin
Persist the AFHTTPSessionManager instance for the life of the plugin to allow reusing the underlying sockets, for example, with "Connection: keep-alive" headers.
2018-12-20 10:47:13 -05:00
Sefa Ilkimen
49f219723d Fix test scripts for linux bash 2018-12-12 19:46:58 +01:00
Sefa Ilkimen
f1bb4f36d0 Fix android test suite 2018-12-12 15:29:27 +01:00
Sefa Ilkimen
e2a869bbd2 Running android tests based on android image instead of xcode image (travis config) 2018-12-12 15:23:05 +01:00
Sefa Ilkimen
d2f79352ae release v2.0.2 2018-12-10 18:52:43 +01:00
Sefa Ilkimen
cca1be9d91 Update android SDK and build tools 2018-12-10 18:03:11 +01:00
Sefa Ilkimen
b6d8763a18 Update cordova-android version in test app 2018-12-10 15:57:47 +01:00
Sefa Ilkimen
c70ce750ed Using Xcode 9.3 image for tests because higher versions don't include JDK v1.8 and therefore crash android builds 2018-12-10 15:54:19 +01:00
Sefa Ilkimen
54abb2ba37 Fix #170: Can't build iOS Test App with Xcode 10.1 2018-12-10 15:17:36 +01:00
Sefa Ilkimen
8d433d050f update changelog 2018-12-06 18:47:55 +01:00
Sefa Ilkimen
bf616fc6e5 Merge pull request #164 from ath0mas/patch-1
Fix argument double encoding for HttpHead
2018-12-06 18:03:16 +01:00
Alexis THOMAS
c31765bbcd Fix argument double encoding for HttpHead
To follow #157 that fixed double encoding on file download
2018-11-26 10:37:21 +01:00
Sefa Ilkimen
b5ec508c30 Merge pull request #157 from TheZopo/double-arg-encoding-fix
Fix argument double encoding on file download
2018-11-25 23:06:04 +01:00
Bastien Marsaud
6fcf4dc8eb Fix argument double encoding on file download
Disable argument encoding in get request
2018-10-18 12:33:05 +02:00
Sefa Ilkimen
f0e1522982 Fix #142: Plugin affected by REDoS Issue of tough-cookie 2018-09-04 20:27:45 +02:00
Sefa Ilkimen
c387e52ea3 release v2.0.1 2018-09-04 02:20:15 +02:00
Sefa Ilkimen
f87afa2217 - fix #136: Content-Type header not overwritable on browser platform
- remove obsolete methods "enableSSLPinning" and "acceptAllCerts"
2018-09-04 01:56:17 +02:00
Sefa Ilkimen
516aa6b61d release v2.0.0 2018-09-03 16:03:46 +02:00
Sefa Ilkimen
a6bf9041a5 add missing "updatecert" task to travis config 2018-09-03 15:25:11 +02:00
Sefa Ilkimen
c41fc11888 SSL certs must be places in "www/certificates"; project root folder is not scanned for certs anymore 2018-09-03 15:02:26 +02:00
Sefa Ilkimen
91515d30bd add test for gzipped content 2018-09-03 14:18:22 +02:00
Sefa Ilkimen
c8638ff204 exclude test certs from repository 2018-09-03 14:17:22 +02:00
Sefa Ilkimen
0acf2e2574 fix test #34 "should pin SSL cert correctly (GET)" 2018-09-03 13:52:09 +02:00
Sefa Ilkimen
f4674028b5 Merge pull request #118 from 0verrfl0w/ios-networking-indicator
Ios networking indicator
2018-07-23 15:18:45 +02:00
Florian Leitmann
5919d90698 Merge remote-tracking branch 'upstream/master' into ios-networking-indicator 2018-05-31 17:29:12 +02:00
Sefa Ilkimen
27e959800a remove deprecated AngularJS integration service 2018-05-27 20:33:02 +02:00
Sefa Ilkimen
96f45d7274 feature #103: implement HTTP SSL cert modes 2018-05-27 19:30:42 +02:00
Florian Leitmann
3dbd07c9df Merge branch 'master' into ios-networking-indicator 2018-05-27 14:01:04 +02:00
Florian Leitmann
cb597c0d30 Added SDNetworkActivityIndicator library and integrated it in plugin.xml definition as well as CordovaHttpPlugin.m
This library allows to show the network activity indicator on iOS devices, to indicate native networking
It uses a counter to hide the indicator depending on whether all request have been fulfilled or not
2018-05-27 12:45:52 +02:00
Sefa Ilkimen
60189a68b3 update httpbin.org SSL cert 2018-05-25 18:58:52 +02:00
Sefa Ilkimen
8e4bfdbc70 remove explicit host name verification setting on Android 2018-05-25 18:45:10 +02:00
Sefa Ilkimen
2ae4c7cf39 refactor cert handling for Android (preparing for v2 changes) 2018-04-11 03:24:27 +02:00
Sefa Ilkimen
32fdf49d31 fix misleading examples in README 2018-04-08 23:59:11 +02:00
Sefa Ilkimen
db0f233737 update README (missing url property in callback signature) 2018-03-21 22:35:31 +01:00
Sefa Ilkimen
2263950de9 release v1.11.1 2018-03-21 13:18:57 +01:00
Sefa Ilkimen
2a9d3296cc update httpbin.org SSL cert 2018-03-21 12:56:11 +01:00
Sefa Ilkimen
55dd751cec fix #92: headers not deserialized on platform "browser" 2018-03-16 03:04:03 +01:00
Sefa Ilkimen
af239d3194 release v1.11.0 2018-03-02 01:09:23 +01:00
Sefa Ilkimen
291e8fe547 browser: fix content type header 2018-03-02 01:00:22 +01:00
Sefa Ilkimen
8b3466e3c3 - update readme
- update version number
2018-03-02 00:46:01 +01:00
Sefa Ilkimen
73e76f6333 feature #11: implement support for "browser" platform 2018-03-01 23:45:54 +01:00
Sefa Ilkimen
3fcac64597 update OkHttp dependency 2018-03-01 16:16:02 +01:00
Sefa Ilkimen
4a437f9435 fix docu for "disableRedirect" 2018-02-28 04:08:32 +01:00
Sefa Ilkimen
2d15b86cb5 add documentation for feature #77 2018-02-28 04:05:13 +01:00
Sefa Ilkimen
63d859ad54 - implement all HTTP operations as shorthand functions of "sendRequest"
- add some more type checking
2018-02-28 03:24:27 +01:00
Sefa Ilkimen
6b9ed72b9f WIP: new API which allows overriding global options 2018-02-28 01:31:35 +01:00
Sefa Ilkimen
1719334d39 release v1.10.2 2018-02-27 22:32:22 +01:00
Sefa Ilkimen
8db15c6c96 - update change log
- update version number
2018-02-27 22:20:48 +01:00
Sefa Ilkimen
e58a398c33 update change log 2018-02-27 17:28:52 +01:00
Sefa Ilkimen
a637d70ec1 Merge pull request #76 from Johny101/master
Modify Android HTTP upload sequence for IOS uniformity
2018-02-27 17:23:28 +01:00
Sefa Ilkimen
6f40ed5cd8 skip Saucelabs tests on PRs, because secret ENV vars are not supported currently for PRs 2018-02-27 15:33:59 +01:00
Sefa Ilkimen
8b09e159fa update change log 2018-02-27 14:57:55 +01:00
Sefa Ilkimen
1d5642c8ae fix #83: handle error during file download correctly on iOS 2018-02-27 14:50:30 +01:00
Sefa Ilkimen
a580ce2067 Merge branch 'patch-1' of https://github.com/troyanskiy/cordova-HTTP into troyanskiy-patch-1 2018-02-27 14:32:35 +01:00
Sefa Ilkimen
35dc0205aa Merge pull request #82 from chax/master
Replaced connectionFactory with OkHttp implementation
2018-02-27 12:53:03 +01:00
Raman Rasliuk
2dbf0d452a App Crash on error body does not exists
Prevent crash if body does not exist on error
2018-02-27 12:04:37 +01:00
chax
8a9381ffaa refactored to use Singleton instance of ConnectionFactory 2018-02-26 11:19:29 +01:00
chax
f1c95ef6fd fixes #79 2018-02-23 17:36:22 +01:00
Sefa Ilkimen
6877fbdb65 fix #78: override header "Content-Type" correctly on Android 2018-02-21 23:44:36 +01:00
Johny101
a52e243e5f Merge pull request #1 from Johny101/HttpUploadSequenceEdition
Modify android HTTP upload sequence for uniformity with IOS
2018-02-16 13:09:54 -05:00
Johny101
9c34f47624 Modify android HTTP upload sequence for uniformity with IOS
Date: Feb2018

Description
Android cordova-plugin-advanced-http 'upload' sequence was modified:
- to respect the order of operations some sites requires, like amazonaws.com
- to have the same behaviour as the IOS counterpart.

Tests
Manual manipulations on Android and PC
2018-02-16 12:59:45 -05:00
Sefa Ilkimen
3f1ee62088 add some specs 2018-02-16 02:50:35 +01:00
Sefa Ilkimen
c73b15b620 release v1.10.1 2018-02-16 00:54:17 +01:00
Sefa Ilkimen
86698ffdcc add a spec to ensure empty body strings are handled correctly (not throwing an decoder error) 2018-02-15 17:01:56 +01:00
Sefa Ilkimen
4a396c28e5 iOS: call failure handler when body can not be decoded 2018-02-15 16:47:49 +01:00
Sefa Ilkimen
53224ed875 - update version number
- update changelog
2018-02-15 12:34:11 +01:00
Sefa Ilkimen
3adec712c9 remove jwt plugin for saucelabs integration 2018-02-15 11:39:53 +01:00
Sefa Ilkimen
a49bcac81b Android: support guessing charset when header value is not set 2018-02-15 11:10:19 +01:00
Sefa Ilkimen
2f01d48abb - Android: support latin1 (iso-8859-1) encoded data
- Android: some refactoring
2018-02-13 16:38:46 +01:00
Sefa Ilkimen
53df68f154 fix #72 for iOS: support latin1 (iso-8859-1) encoded data 2018-02-13 15:03:05 +01:00
Sefa Ilkimen
441268586b fix #71: encode query string in URL correctly on Android 2018-02-11 16:37:51 +01:00
Sefa Ilkimen
df86929148 release v1.10.0 2018-02-09 10:54:42 +01:00
Sefa Ilkimen
f691d09eb0 update docu and change log 2018-02-09 10:45:03 +01:00
Sefa Ilkimen
b68cbcd157 replace all typeof calls 2018-02-09 10:06:32 +01:00
Sefa Ilkimen
a9bdca2d6d make internals private (not directly accessible via public interface) 2018-02-09 10:01:48 +01:00
Sefa Ilkimen
5186b375a4 feature #34: add support for transfering raw string as utf-8 encoded data 2018-02-09 09:16:30 +01:00
Sefa Ilkimen
d28eedebd0 some refactoring:
- new module for messages
 - new module for helper functions
2018-02-08 16:44:16 +01:00
Sefa Ilkimen
25b4a283ea - add data type validation for data param
- support strings as data when "utf8" serializer is configured
2018-02-07 18:24:39 +01:00
Sefa Ilkimen
ebef9a2944 Android: fix interface for "utf8" serializer 2018-02-07 16:57:48 +01:00
Sefa Ilkimen
c452b29433 WIP commit for iOS:
- failure handler gets called if an exception occurs in native code
- rename "raw" serializer to "utf8"
- extract serializer into separate file
2018-02-07 16:31:51 +01:00
Sefa Ilkimen
60e7debbdf Merge branch 'master' of https://github.com/robertocapuano/cordova-plugin-advanced-http into robertocapuano-master 2018-01-21 15:11:44 +01:00
Sefa Ilkimen
c7820c8185 release v1.9.1 2018-01-04 16:34:21 +01:00
Sefa Ilkimen
f6a15190f3 add another cookie spec to validate #59 2018-01-04 13:17:52 +01:00
Sefa Ilkimen
419283c5dd add another spec for cookie handling 2018-01-04 04:10:30 +01:00
Sefa Ilkimen
7a8f9a0dba add a spec for cookie handling 2018-01-04 03:53:39 +01:00
Sefa Ilkimen
64731a3794 fix #58: white-list of allowed content-types should be removed for iOS 2018-01-04 02:05:55 +01:00
Sefa Ilkimen
73097b9db9 fix: bug fix #54 causes a bug on Android (success and failure handler are called both) 2018-01-04 01:23:49 +01:00
Sefa Ilkimen
a9c3be124e fixed #54: Requests are not responding on iOS with non-string values in header object 2018-01-04 00:40:40 +01:00
Roberto Capuano
5d5a859cfc added 'raw' serializer 2017-12-12 16:42:03 +01:00
Sefa Ilkimen
d67e80dfe0 fix #45: does not encode arrays correctly as HTTP GET parameter on Android 2017-11-26 03:42:22 +01:00
Sefa Ilkimen
55ba423d2f update readme to include tough-cookie 2017-11-24 19:48:39 +01:00
Sefa Ilkimen
7f34201e8d release v1.9.0 2017-11-24 19:26:03 +01:00
Sefa Ilkimen
01c43c4e29 throw an error when "setHeader" is used to add a custom cookie header 2017-11-24 19:14:14 +01:00
Sefa Ilkimen
d34196ff4e feature #46: support adding custom cookies 2017-11-24 18:47:00 +01:00
Sefa Ilkimen
1957cf4ef7 update change log 2017-11-24 17:46:44 +01:00
Sefa Ilkimen
681c384b3e feature #43: add support for Content-Type "application/javascript"
Add support for Content-Type application/javascript
2017-11-23 01:03:33 +01:00
Sefa Ilkimen
9d6406277c - feature #44: expose "getCookieString" method
- update version number
2017-11-16 23:22:29 +01:00
Sefa Ilkimen
b9bc1b0054 - update cordova (dev dependency)
- pin version of cordova file plugin dependency (v4.3.3)
2017-11-13 17:47:35 +01:00
Sefa Ilkimen
d17f44b641 improve CI build error handling (do not continue when one of the commands fail) 2017-11-08 12:17:27 +01:00
Sefa Ilkimen
11515a8400 add tests for sending url encoded data 2017-11-08 11:57:33 +01:00
Sefa Ilkimen
937296b371 release v1.8.1 2017-11-08 11:17:53 +01:00
Thomas Opiolka
b60086df0d Add support for Content-Type application/javascript 2017-11-08 11:16:42 +01:00
Sefa Ilkimen
a48a91dd14 add OS identifier for saucelabs tests 2017-11-08 11:15:32 +01:00
Sefa Ilkimen
e8a72a53cc split CI build into two jobs 2017-11-08 10:29:59 +01:00
Sefa Ilkimen
efbdc21221 fix: ANDROID_HOME path for CI build 2017-11-07 18:30:42 +01:00
Sefa Ilkimen
06d7d859e7 update change log 2017-11-07 14:53:37 +01:00
Sefa Ilkimen
239b82c08e enable saucelabs testing for android platform 2017-11-07 14:52:11 +01:00
Sefa Ilkimen
e987652d97 fix #40: generic error codes are different on Android and iOS 2017-11-07 13:31:38 +01:00
Sefa Ilkimen
068b25b6f9 - fix #27: "uploadFile" method doesn't return data object on iOS
- update version number
2017-11-03 15:51:19 +01:00
Sefa Ilkimen
8c55572eb8 implement test for "downloadFile" method 2017-11-02 17:01:35 +01:00
Sefa Ilkimen
48afeefbc3 - update version number
- update change log
2017-11-02 15:45:37 +01:00
Sefa Ilkimen
292d726b20 - feature #33: response object contains response url
- annotate tests with issue numbers
- refactor android source code
2017-11-02 14:59:53 +01:00
Sefa Ilkimen
47ac5d3e0a release v1.7.1 2017-10-27 18:48:39 +02:00
Sefa Ilkimen
4db3939bbe - update version number
- update changelog
- enable travis caching for node modules
2017-10-27 16:44:37 +02:00
Sefa Ilkimen
0aee536d5c fix #26: JSON request with array data is not working on Android (JSON error) 2017-10-27 16:20:35 +02:00
Sefa Ilkimen
0d8d7735d9 - improve docu for "setHeader" method
- add test case for "setHeader" call with port number
2017-10-27 12:16:01 +02:00
Sefa Ilkimen
208529003d fix #35: Android headers are not normalized (not returned in lowercase) 2017-10-26 17:34:32 +02:00
Sefa Ilkimen
77c0a887ac fix #36: setting basic authentication not working correctly 2017-10-26 12:11:14 +02:00
Jochen Becker
8c596d80b0 fixed global 'asterisks' header not defined 2017-10-26 11:27:30 +02:00
Sefa Ilkimen
0407a561fc fix broken links in contribution guidelines 2017-10-24 17:07:25 +02:00
Sefa Ilkimen
ebe6205e50 add contribution guidelines 2017-10-24 17:00:40 +02:00
Sefa Ilkimen
3ea9fd4fd1 release v1.7.0 2017-10-24 14:08:06 +02:00
Sefa Ilkimen
56791016e6 updated version number 2017-10-24 14:06:19 +02:00
Sefa Ilkimen
9655c89486 - added indicator for running tests
- validation function is triggered when test is finished
2017-10-23 16:54:34 +02:00
Sefa Ilkimen
15bf6281bc added missing documentation for "patch " method 2017-10-20 17:50:16 +02:00
Sefa Ilkimen
d342ca054a updated readme and changelog 2017-10-20 17:36:46 +02:00
Sefa Ilkimen
d7e13b046b - feature #24: extended behaviour of "setHeader" method
- added test suite for www interface of plugin (js files)
2017-10-20 17:22:51 +02:00
Sefa Ilkimen
1375727316 release v1.6.2 2017-10-19 17:58:48 +02:00
Sefa Ilkimen
d136d90893 - improved release process (using npm script)
- updated version number
2017-10-19 17:56:30 +02:00
Sefa Ilkimen
a8f1d2b1c2 changed behaviour #29: removed "validateDomainName" function and disabling it when "acceptAllCerts" is enabled 2017-10-19 16:24:54 +02:00
Sefa Ilkimen
c29ce85df3 Merge pull request #28 from ryandegruyter/patch-1
(iOS) Add support for Content-Type application/hal+json
2017-10-19 09:42:13 +02:00
Sefa Ilkimen
aeefc9f5ef tests: increased timeout value because test vm takes long time to spin up 2017-10-16 10:57:05 +02:00
Sefa Ilkimen
4ea9c24d6d travis config: added travis_wait to prevent timeout issue 2017-10-16 10:17:05 +02:00
Sefa Ilkimen
7037fd2fa7 - improved test app
- defined new tests
2017-10-13 16:30:48 +02:00
Sefa Ilkimen
6f210a70fb fixed issue #31 - request fails throwing error on erroneous cookies 2017-10-13 12:16:29 +02:00
Sefa Ilkimen
10eb102d43 fixed travis config (jwt section) 2017-10-12 09:08:03 +02:00
Sefa Ilkimen
9dc0bd56e0 WIP: integrating saucelabs tests 2017-10-11 16:33:07 +02:00
Ryan de Gruyter
190c44b30a Add support for Content-Type application/hal+json
Currently when the server responds with a content type of application/hal+json, the response might be 200 but the AFNetwork will call the fail callback anyway because the content type is not acceptable.
2017-10-09 13:34:40 +02:00
Sefa Ilkimen
807af7adfa release 1.6.1 2017-10-04 13:01:50 +02:00
Sefa Ilkimen
dd66698f96 updated version number 2017-10-04 13:00:32 +02:00
Sefa Ilkimen
3476ff7ffb added script which is updating version number in plugin.xml automatically 2017-10-04 12:59:33 +02:00
Sefa Ilkimen
52b2efe71b fixed PATCH method on android 2017-10-02 16:17:29 +02:00
Sefa Ilkimen
8b4c6de4b9 implemented PATCH method for iOS 2017-10-02 12:29:55 +02:00
Sefa Ilkimen
3138d423ee temporary removed patch method in www interface 2017-10-02 11:09:05 +02:00
Sefa Ilkimen
f0194ad55a Merge pull request #22 from akhatri/feature-patch-method-android
Feature patch method android
2017-10-02 10:50:12 +02:00
Akshat Khatri - AK
09da3c9a97 patch method android 2017-10-01 17:26:20 +11:00
Akshat Khatri - AK
52ccfc6aca Merge remote-tracking branch 'refs/remotes/silkimen/master' into feature-patch-method-android 2017-10-01 17:11:41 +11:00
Sefa Ilkimen
fc89a64675 update changelog and version number 2017-09-29 16:42:36 +02:00
Sefa Ilkimen
4954385361 - fix issue #16: removed build script and prebuild hook
- implement release script
2017-09-29 16:35:34 +02:00
Sefa Ilkimen
08554c02cb Merge pull request #21 from kesozjura/master
Add global redirection control
2017-09-29 15:51:56 +02:00
Martin Kasa
18a2fc0c69 temporary umd-tough-cookie.js fix 2017-09-29 14:44:44 +02:00
Martin Kasa
a4a6440bcb Add global redirection control
copied from https://github.com/wymsee/cordova-HTTP/pull/137
2017-09-29 14:36:00 +02:00
Akshat Khatri - AK
792515e1c6 patch method android 2017-09-29 21:02:44 +10:00
Sefa Ilkimen
66dc8ff2ca changed readme file 2017-09-26 10:48:06 +02:00
Sefa Ilkimen
91b35d561c - updated version number
- updated change log
2017-09-26 10:38:16 +02:00
Sefa Ilkimen
45c0f8937f Merge pull request #14 from RangerRick/fix-serialization
change setParamSerializer to setDataSerializer (fixes #13)
2017-09-26 11:15:19 +03:00
Sefa Ilkimen
b1c8427986 Merge pull request #15 from RangerRick/documentation-updates
add missing documentation
2017-09-26 11:05:34 +03:00
Benjamin Reed
91470725ee add missing documentation for setRequestTimeout, removeCookies, and head 2017-09-25 16:12:40 -04:00
Benjamin Reed
7cb41e0c1b change setParamSerializer to setDataSerializer (fixes #13) 2017-09-25 14:51:57 -04:00
Sefa Ilkimen
ee8cae88a9 updated change log 2017-09-12 19:05:23 +03:00
Sefa Ilkimen
aa8105948e Merge pull request #10 from DayBr3ak/master
fix gzip decompression when request header accepts gzip compression
2017-09-12 19:00:31 +03:00
ben
325a2e6200 fix gzip decompression when request header accepts gzip compression 2017-09-12 14:17:52 +02:00
Sefa Ilkimen
8ac8b95246 updated version number 2017-08-21 00:35:24 +02:00
Sefa Ilkimen
5a7f09bc44 updated change log 2017-08-21 00:31:50 +02:00
Sefa Ilkimen
308c49635b added build batch 2017-08-21 00:23:32 +02:00
Sefa Ilkimen
d2a5a113c6 added slack integration 2017-08-21 00:17:30 +02:00
Sefa Ilkimen
2c4ce6f8c7 fixed travis filename 2017-08-20 23:49:58 +02:00
Sefa Ilkimen
613593ba51 added travis integration (doing test installation) 2017-08-20 23:44:21 +02:00
Jochen Becker
a20821378b - if a request timed out the same error codes are returned for iOS and Android
- updated change log
2017-08-14 17:05:44 +02:00
Sefa Ilkimen
5535464db2 updated change log 2017-08-13 03:05:22 +02:00
Sefa Ilkimen
7ded66d4b8 fix issue #2: handling "expires" key case-insensitive 2017-08-13 03:01:52 +02:00
Jochen Becker
d84a222dda - added function "setRequestTimeout" to set the timeout for all further requests and to prevent to "wait forever" on Android in case the server is not responding
- updated change log
- incremented version for release
2017-08-10 10:03:38 +02:00
Jochen Becker
14eef1ca66 - convert response header keys to lowercase (iOS only)
- updated change log
- incremented version for release
2017-05-31 08:50:13 +02:00
Jochen Becker
cf5a684d51 - added a function to remove all cookies for a URL
- updated change log
- incremented version for release
2017-05-23 10:26:51 +02:00
Jochen Becker
c153378b4b - fixed an error if the response has no "headers" field
- updated change log
- incremented version for release
2017-03-03 11:11:04 +01:00
Sefa Ilkimen
8092e8b025 - updated change log
- incremented version for release
2016-12-19 15:32:13 +01:00
Sefa Ilkimen
afb4ea6199 - handles cookies correctly on non-success response from server
- throws error when a callback function is missing
2016-12-12 14:09:41 +01:00
Sefa Ilkimen
2c8c8a14a5 - fixed missing file "umd-tough-cookie.js“
- updated changeling
- incremented version for release
2016-12-07 17:21:03 +01:00
Sefa Ilkimen
8267a02c9d - updated change log
- incremented version for release
2016-12-07 16:48:02 +01:00
Sefa Ilkimen
a1f678a886 fixed case-sensitive path for android source files 2016-12-07 16:20:17 +01:00
Sefa Ilkimen
69cf4d0939 - updated change log
- incremented version for release
2016-12-07 15:32:18 +01:00
Sefa Ilkimen
d90cefcbaa fixed handling of multiple cookies 2016-12-07 15:29:54 +01:00
Sefa Ilkimen
b3118d5ccd removed „String.prototype.startsWith“ call to be compatible with older web view versions 2016-12-06 16:05:48 +01:00
Sefa Ilkimen
6b0f23b2d1 fixed lodash exports 2016-12-06 11:52:38 +01:00
Sefa Ilkimen
d0ece22302 Merge branch 'feature-cookie-handling' 2016-12-06 10:31:54 +01:00
Sefa Ilkimen
a6a1bcaab1 updated readme file 2016-12-06 10:29:35 +01:00
Sefa Ilkimen
4afb375971 added function for clearing cookies 2016-12-05 18:25:14 +01:00
Sefa Ilkimen
20e5bd0ee9 added local storage store for cookies 2016-12-05 17:52:20 +01:00
Sefa Ilkimen
e84d84c358 integrated „umd-tough-cookie“ for cookie handling 2016-12-05 13:19:26 +01:00
Sefa Ilkimen
955c590e42 - incremented version
- updated change log and readme
2016-11-14 15:43:18 +01:00
Sefa Ilkimen
0d627b7a0d Merge branch 'feature-put-delete-methods' 2016-11-14 15:13:55 +01:00
Sefa Ilkimen
8fc4c2b9cb minor changes for cleanup 2016-11-14 15:13:26 +01:00
Sefa Ilkimen
a0edca1b1d updated README 2016-11-14 14:43:24 +01:00
Sefa Ilkimen
d5b2cdaf60 implemented PUT and DELETE methods for Android and iOS 2016-11-14 13:56:54 +01:00
Sefa Ilkimen
0989de20ab renamed param serializer to data serializer 2016-11-14 00:47:24 +01:00
Sefa Ilkimen
050ed894d6 defined www interface for http methods PUT and DELETE 2016-11-14 00:18:16 +01:00
Sefa Ilkimen
4552e9c84b Merge branch 'feature-configurable-serializer' 2016-11-11 19:30:47 +01:00
Sefa Ilkimen
c8df1ebd8b - implemented configurable serializer for Android
- cleaned up Android source structure
2016-11-11 19:24:49 +01:00
Sefa Ilkimen
50cda6a5af updated readme file 2016-11-11 02:22:53 +01:00
Sefa Ilkimen
4600fd0555 implemented configurable serializer for iOS 2016-11-11 01:33:29 +01:00
Sefa Ilkimen
46176500df fixed var name 2016-11-11 01:32:16 +01:00
Sefa Ilkimen
9f515d01d8 defined www interface for configurable parameter serializer 2016-11-10 18:46:30 +01:00
Andrew Stephan
bf8ded59a2 1.2.0 2016-07-20 12:33:52 -04:00
Andrew Stephan
c531ffaa44 Merge pull request #95 from wymsee/tls
Support for TLSv1.1 and TLSv1.2 for android 4.1 - 4.4
2016-07-20 12:33:41 -04:00
Andrew Stephan
0e4624f627 updated changelong 2016-07-20 12:05:11 -04:00
Andrew Stephan
9a65da55c9 only use custom factory for API Levels < 20 2016-07-20 11:55:49 -04:00
Andrew Stephan
4c26dd793e fixed var name 2016-07-18 15:56:11 -04:00
Andrew Stephan
beae854526 updates to only allow tlsv1.1 and tlsv1.2 2016-07-18 15:42:30 -04:00
Andrew Stephan
9cd64b1d9e 1.1.0 2016-05-02 13:49:15 -04:00
Andrew Stephan
d726998e5e Merge pull request #85 from wymsee/ios-errors
updated AFNetworking and edited requests to send back the body for errors
2016-05-02 13:49:00 -04:00
Andrew Stephan
b6976bef18 README, CHANGELOG and version number updates 2016-05-02 13:48:48 -04:00
Andrew Stephan
60220a224b updated AFNetworking and edited requests to send back the body for errors 2016-04-29 14:19:29 -04:00
Andrew Stephan
ba37200d75 updated readme with info on headers 2016-04-28 19:12:36 -04:00
Andrew Stephan
50d40e76eb Merge pull request #8 from SpiderOak/disable-encoding-get-urls
Disables encoding get() URLs by default (android)
2016-04-28 18:58:26 -04:00
Andrew Stephan
99683f1f30 1.0.3 2016-04-26 15:23:21 -04:00
Andrew Stephan
e9070461c1 updated version in plugin.xml 2016-04-26 15:23:15 -04:00
Andrew Stephan
04fdbed1fb 1.0.2 2016-03-03 10:56:20 -05:00
Andrew Stephan
3aab872ec0 1.0.1 2016-03-03 10:55:33 -05:00
Andrew Stephan
b2e7878da4 fixes for useBasicAuth and setHeader for angular use 2016-03-03 10:55:09 -05:00
Andrew Stephan
6808069a18 updated install instructions in readme 2016-02-24 13:52:29 -05:00
Andrew Stephan
fbd5c60c68 Merge pull request #77 from wymsee/update
Update
2016-02-24 13:42:23 -05:00
Andrew Stephan
fc220041d8 updated the changelog 2016-02-23 17:12:48 -05:00
Andrew Stephan
1e61e48628 update docs 2016-02-23 17:08:45 -05:00
Andrew Stephan
bcf6f6ed90 updated http request and removed some android code that is now in js. Fixed upload file in iOS 2016-02-23 12:48:40 -05:00
Dan McNamara
cbe554abe7 Add package.json 2016-02-22 13:16:35 -05:00
Andrew Stephan
c35af633a4 fixed some issues with responseSerializers and moved some functions out of obj-c into javascript 2016-02-19 17:13:48 -05:00
Andrew Stephan
340e45a381 Fixed ios code to work with AFNetwork 3.0.x 2016-02-18 20:31:21 -05:00
Andrew Stephan
434d8dfd2b updated afnetworking 2016-02-18 18:15:57 -05:00
Andrew Stephan
8e27b5b1dd Merge pull request #76 from denisbabineau/wymsee_master
File plugin update and more
2016-02-18 18:02:41 -05:00
Denis Babineau
dbe9d003b8 remove unused #import to non-existant file (now a private header in CordovaLib) 2016-02-16 10:36:06 -04:00
Denis Babineau
c9a9296396 missing file reference in plugin.xml 2016-02-10 14:34:05 -04:00
Dan McNamara
9ed1c45e77 Merge pull request #51 from hideov/patch-1
Update README.md
2015-10-09 16:28:14 -04:00
Denis Babineau
07e0195c91 add HEAD request support and return response headers for all request types both for success and failures. 2015-10-07 13:33:07 -03:00
Denis Babineau
141f996052 fix downloadFile method on ios; invalid native path and not using proper instance of CDVFile 2015-08-26 23:58:50 -03:00
Denis Babineau
cabb0db9de acceptAllHosts can now be called on both platform. 2015-07-28 11:15:32 -03:00
Denis Babineau
43b04a94e2 add acceptAllHosts call to test self-signed certificate in dev environment (still performs pinning but skips host validation)
TODO: iOS
2015-07-28 11:04:09 -03:00
Eddy Verbruggen
1c1f716cf8 #7 android: acceptAllCerts doesn't call callbacks 2015-07-28 11:00:20 -03:00
Denis Babineau
125248c267 Merge commit 'b08f7b43337ab19d6a0b9dce0a3cfb1ea348ca5d' into wymsee_master
* commit 'b08f7b43337ab19d6a0b9dce0a3cfb1ea348ca5d':
  Request Internet permission in AndroidManifest.xml
  #2 iOS doesn't compile because of a missing framework
2015-07-28 10:59:31 -03:00
Denis Babineau
20f39b32f1 update cordova file plugin dependency to new cordova-plugin-file name/version 2015-07-15 15:21:39 -03:00
hideov
31c5ad9bd3 Update README.md
fixed first argument in various examples
2015-05-07 11:51:39 +02:00
Eddy Verbruggen
b08f7b4333 Merge pull request #4 from Icenium/bektchiev/internet-permission
Request Internet permission in AndroidManifest.xml
2015-03-10 16:19:13 +01:00
CSullivan102
6ea22f99e8 Add SystemConfiguration framework to plugin.xml for iOS 2015-03-09 16:01:00 -04:00
Martin Bektchiev
d03c6a5a48 Request Internet permission in AndroidManifest.xml 2015-02-03 15:08:53 +02:00
Andrew Stephan
66b7a31bc6 updated version number to 0.1.4 2014-12-29 09:21:38 -05:00
Andrew Stephan
83e903b409 Update README.md 2014-12-09 14:04:38 -05:00
Andrew Stephan
5ed4dce809 Merge pull request #29 from cvillerm/master
Added section in README about limitations + CA pinning
2014-12-09 14:03:19 -05:00
Claude Villermain
872b5fa013 Added section about limitations + CA pinning 2014-11-12 08:49:04 +01:00
Andrew Stephan
75296f0734 Merge pull request #23 from pvsaikrishna/moduleexports
Exporting http object to use it through clobbers target defined .
2014-11-10 10:32:42 -05:00
Sai Krishna
978708890b Exporting http object to use it through clobbers target defined in plugins.xml.
Doing this helps in using the plugin through ngCordova, a much better way of using Cordova plugins from Angular.
2014-10-29 10:27:34 +00:00
Andrew Stephan
4fc676cc67 Merge pull request #20 from Telerik-Verified-Plugins/load-certificates-from-www-folder
#1 Allow loading certificates from within the www folder
2014-10-22 15:16:32 -04:00
Andrew Stephan
0b01dc8b69 added url for cordova file plugin 2014-10-14 10:28:03 -04:00
EddyVerbruggen
dc123932e3 #2 iOS doesn't compile because of a missing framework 2014-10-14 15:52:33 +02:00
EddyVerbruggen
0d67760abb #1 Allow loading certificates from within the www folder 2014-10-14 15:49:01 +02:00
CSullivan102
5c138dc39f Merge pull request #15 from wymsee/updateAFNetworking
Updated AFNetworking to version 2.4.1 to fix crash when building in Xcode 6
2014-09-22 17:34:17 -04:00
CSullivan102
be660f97d8 Updated AFNetworking to version 2.4.1 to fix crash when building in Xcode 6 2014-09-22 00:05:07 -04:00
Andrew Stephan
e562bd0627 Merge pull request #12 from SpiderOak/report-ssl-pinning-error
Missing imports for SSLHandshakeException reporting
2014-09-15 10:26:55 -04:00
Andrew Stephan
c5f5a33f07 Merge pull request #10 from andrey-tsaplin/fix-filenames-ios
Fixed filenames for case-sensitive filesystems
2014-08-27 10:49:52 -04:00
Tsaplin Andrey
7a20200f8e Fixed filenames for case-sensitive filesystems 2014-08-27 11:47:31 +04:00
Tommy-Carlos Williams
bd8207a390 Missing imports 2014-08-22 12:20:25 +10:00
Andrew Stephan
e507759527 Merge pull request #9 from SpiderOak/report-ssl-pinning-error
Reports SSL Handshake errors when they occur (android)
2014-08-20 13:19:01 -04:00
Andrew Stephan
e83229e837 Merge pull request #6 from SpiderOak/readme-typos
Examples used wrong case for cordovaHTTP object
2014-08-20 13:09:35 -04:00
Tommy-Carlos Williams
03dc55bdf9 Reports SSL Handshake errors when they occur
Instead of just reporting a 500 error with some generic text, I thought it might be useful to know when an actual SSL related error occurred. It the idea is to prevent MitM attacks, it might be important to warn users it might be happening.

Currently doesn't drill down the `getCause()` tree very far, just determines if it was an `SSLHandshakeException`.
2014-08-20 19:15:15 +10:00
Tommy-Carlos Williams
46517ae7f2 Disables encoding get() URLs by default
In our app this is leading to errors on URLs that are already properly encoded (they are being double-encoded... %20 -> %2520, etc)

Feel free to ignore or close this one if you need this functionality, but since `post()` URLs were *not* being encoded, I thought it might have just been a default that didn't affect you one way or the other.
2014-08-20 19:05:11 +10:00
Tommy-Carlos Williams
7d67b295fa Examples used wrong case for cordovaHTTP object
Many or all of the examples used `cordovaHttp` instead of `cordovaHTTP`

This made cutting and pasting them fail and might cause confusion.
2014-08-20 18:50:29 +10:00
125 changed files with 30635 additions and 6180 deletions

19
.editorconfig Normal file
View File

@@ -0,0 +1,19 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

6
.eslintignore Normal file
View File

@@ -0,0 +1,6 @@
node_modules
temp
platforms
**/*.min.js
www/umd-tough-cookie.js
www/lodash.js

25
.eslintrc.json Normal file
View File

@@ -0,0 +1,25 @@
{
"env": {
"browser": true,
"node": true,
"es2020": true
},
"extends": [
"eslint:recommended"
],
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "script"
},
"overrides": [
{
"files": [
"test/**/*.js",
"www/**/*.js"
],
"env": {
"mocha": true
}
}
]
}

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

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

33
.github/ISSUE_TEMPLATE/--bug-report.md vendored Normal file
View File

@@ -0,0 +1,33 @@
---
name: "\U0001F41BBug report"
about: Create a report to help us improve
title: "[Bug] [platform] your issue title"
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is and what you expected to happen.
**System info**
- affected HTTP plugin version: [e.g. 2.1.1]
- affected platform(s) and version(s): [e.g. iOS 12.2]
- affected device(s): [e.g. iPhone 8]
- cordova version: [e.g. 6.5.0]
- cordova platform version(s): [e.g. android 7.0.0, browser 5.0.3]
**Are you using ionic-native-wrapper?**
- ionic-native-wrapper version: [e.g. 5.8.0]
- did you check [ionic-native issue tracker](https://github.com/ionic-team/ionic-native/issues) for your problem?
**Minimum viable code to reproduce**
If applicable, add formatted sample coding to help explain your problem.
e.g.:
```js
cordova.plugin.http.setDataSerializer('urlencoded');
```
**Screenshots**
If applicable, add screenshots to help explain your problem.

View File

@@ -0,0 +1,21 @@
---
name: "\U0001F680Feature request"
about: Suggest an idea for this project
title: "[Feature]"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. e.g. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen. If applicable, add formatted sample coding to help explain your idea.
```js
// do some fancy stuff here
```
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

View File

@@ -0,0 +1,18 @@
---
name: "\U0001F914Support question"
about: Ask the community
title: ''
labels: question
assignees: ''
---
**You've got questions?**
We primarily use GitHub as an issue tracker; for usage and support questions, please check out these resources below. Thanks! 😁
* README.md: https://github.com/silkimen/cordova-plugin-advanced-http/blob/master/README.md
* StackOverflow: https://stackoverflow.com/questions/tagged/cordova-plugin-advanced-http using the tag `cordova-plugin-advanced-http`
* Wiki: https://github.com/silkimen/cordova-plugin-advanced-http/wiki
And don't forget: If you get help, help others. Good karma rulez!

166
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,166 @@
name: Cordova HTTP Plugin CI
on:
push:
workflow_dispatch:
inputs:
ci_reason:
description: "Optional note for manual runs"
required: false
env:
node_version: "22.x"
java_distribution: "zulu"
java_version: "17"
gradle_version: "7.6.1"
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.node_version }}
cache: npm
cache-dependency-path: package-lock.json
- name: Install node modules
run: npm ci
- name: Run ESLint
run: npm run lint
test-www-interface:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.node_version }}
cache: npm
cache-dependency-path: package-lock.json
- name: Install node modules
run: npm ci
- name: Run WWW interface tests
run: npm run test:js
build-ios:
runs-on: macOS-latest
outputs:
run-tests: ${{ steps.should-run-tests.outputs.run_tests }}
steps:
- uses: actions/checkout@v1
- name: Install node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.node_version }}
cache: npm
cache-dependency-path: package-lock.json
- id: should-run-tests
# need to find a solution for signing iOS App so we can build for device target instead simulator
# for now we skip iOS tests on BrowserStack
run: echo "run_tests=false" >> "$GITHUB_OUTPUT"
- name: Install node modules
run: npm ci
- name: Update test cert for httpbin.org
run: npm run update:cert
- name: Build test app
run: scripts/build-test-app.sh --ios --emulator
- name: Upload artifact to BrowserStack
if: steps.should-run-tests.outputs.run_tests == 'true'
run: scripts/upload-browserstack.sh --ios
test-ios:
needs: build-ios
if: needs.build-ios.outputs.run-tests == 'true'
runs-on: macOS-latest
steps:
- uses: actions/checkout@v1
- name: Install node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.node_version }}
cache: npm
cache-dependency-path: package-lock.json
- name: Install node modules
run: npm ci
- name: Run e2e tests (iOS)
run: scripts/test-app.sh --ios --device
build-android:
runs-on: ubuntu-latest
outputs:
run-tests: ${{ steps.should-run-tests.outputs.run_tests }}
steps:
- uses: actions/checkout@v1
- name: Install node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.node_version }}
cache: npm
cache-dependency-path: package-lock.json
- id: should-run-tests
run: |
if [ -n "${BROWSERSTACK_USERNAME}" ]; then
echo "run_tests=true" >> "$GITHUB_OUTPUT"
else
echo "run_tests=false" >> "$GITHUB_OUTPUT"
fi
- name: Install node modules
run: npm ci
- name: Install JDK
uses: actions/setup-java@v2
with:
distribution: ${{ env.java_distribution }}
java-version: ${{ env.java_version }}
java-package: jdk
- name: Ensure Android build-tools 33.0.2
run: yes | "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "build-tools;33.0.2"
- name: Install Gradle
run: |
set -euo pipefail
curl -sSL "https://services.gradle.org/distributions/gradle-${{ env.gradle_version }}-bin.zip" -o gradle.zip
unzip -q gradle.zip -d "$HOME/gradle"
echo "$HOME/gradle/gradle-${{ env.gradle_version }}/bin" >> "$GITHUB_PATH"
rm gradle.zip
- name: Update test cert for httpbin.org
run: npm run update:cert
- name: Shim missing DX binaries (https://stackoverflow.com/a/68430992)
run: |
set -euo pipefail
BUILD_TOOLS_PATH="$(ls -d $ANDROID_HOME/build-tools/*/ | sort -V | tail -n 1)"
D8_BIN="${BUILD_TOOLS_PATH}d8"
DX_BIN="${BUILD_TOOLS_PATH}dx"
D8_JAR="${BUILD_TOOLS_PATH}lib/d8.jar"
DX_JAR="${BUILD_TOOLS_PATH}lib/dx.jar"
if [ -f "$D8_BIN" ] && [ ! -e "$DX_BIN" ]; then
ln -s "$D8_BIN" "$DX_BIN"
fi
if [ -f "$D8_JAR" ] && [ ! -e "$DX_JAR" ]; then
ln -s "$D8_JAR" "$DX_JAR"
fi
- name: Build test app
run: scripts/build-test-app.sh --android --device
- name: Upload artifact to BrowserStack
if: steps.should-run-tests.outputs.run_tests == 'true'
run: scripts/upload-browserstack.sh --android
test-android:
needs: build-android
if: needs.build-android.outputs.run-tests == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.node_version }}
cache: npm
cache-dependency-path: package-lock.json
- name: Install node modules
run: npm ci
- name: Run e2e tests (Android)
run: scripts/test-app.sh --android --device

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

@@ -0,0 +1,103 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
push:
branches: ["master"]
pull_request:
branches: ["master"]
schedule:
- cron: "0 0 * * 0"
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
- language: java-kotlin
build-mode: none # This mode only analyzes Java. Set this to 'autobuild' or 'manual' to analyze Kotlin too.
- language: javascript-typescript
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- name: Run manual build steps
if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"

11
.gitignore vendored
View File

@@ -1,2 +1,11 @@
node_modules/**
test/e2e-app-template/www/certificates/*.cer
test/e2e-app-template/www/certificates/*.pkcs
tags
.zedstate
.zedstate
npm-debug.log
/temp
/android-sdk-macosx.zip
/android-sdk-macosx/**
.idea/
*.iml

5
.npmignore Normal file
View File

@@ -0,0 +1,5 @@
tags
.zedstate
/scripts
/temp
/test

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"editor.tabSize": 2
}

378
CHANGELOG.md Normal file
View File

@@ -0,0 +1,378 @@
# Changelog
# 3.3.1
- Fixed #427: missing connection check on Android (thanks moshe5745)
- Fixed #459: namespace collision on iOS when another plugin is also using AFNetworking (thanks zafirskthelifehacker)
- Fixed #429: intermediate CA certs are not respected on iOS when using client certs (thanks pavrda)
- Fixed #444: errors are not handled when invalid header values are applied on browser (thanks @MobisysGmbH)
- Fixed #441: sensible data can be cached in cache.db on iOS (thanks dtarnawsky)
# 3.3.0
- Feature #451: expose response object on `downloadFile()` (thanks to @MobisysGmbH)
# 3.2.2
- Fixed #438: requests not working correctly on browser platform because request options are not processed correctly
## 3.2.1
- Fixed #425: plugin crashes on Android SDK levels < 24
- Fixed #418: deprecated AFNetworking method causes app crash (thanks meiram-tr)
- Fixed #404: wrong timeout implementation (thanks YouYue123)
## 3.2.0
- Feature #420: implement blacklist feature to disable SSL/TLS versions on Android (thanks to @MobisysGmbH)
## 3.1.1
- Fixed #372: malformed empty multipart request on Android
- Fixed #399: memory leakage leads to app crashes on iOS (thanks avargaskun)
## 3.1.0
- Feature #272: add support for aborting requests (thanks russaa)
## 3.0.1
- Fixed #359: memory leakage leads to app crashes on Android
- Fixed #355: responseType "json" not working with valid JSON response on browser (thanks millerg6711)
## 3.0.0
- Feature #158: support removing headers which were previously set via "setHeader"
- Fixed #345: empty file names are not handled correctly (thanks ikosta)
- :warning: **Breaking Change**: Dropped support for Android < 5.1
- :warning: **Breaking Change**: Removed "disableRedirect", use "setFollowRedirect" instead
- :warning: **Breaking Change**: Removed "setSSLCertMode", use "setServerTrustMode" instead
## 2.5.1
- Fixed #334: empty JSON response triggers error even though request is successful (thanks antikalk)
- Fixed #248: clearCookies() does not work on iOS
## 2.5.0
- Feature #56: add support for X.509 client certificate based authentication
## 2.4.1
- Fixed #296: multipart requests are not serialized on browser platform
- Fixed #301: data is not decoded correctly when responseType is "json" (thanks antikalk)
- Fixed #300: FormData object containing null or undefined value is not serialized correctly
## 2.4.0
- Feature #291: add support for sending 'raw' requests (thanks to jachstet-sea and chuchuva)
- Feature #155: add OPTIONS method
- Feature #283: improve error message on timeout on browser platform
## 2.3.1
- Fixed #275: getAllCookies() is broken because of a typo (thanks ath0mas)
## 2.3.0
- Feature #101: Support "multipart/form-data" requests (thanks SDA SE Open Industry Solutions)
#### Important information
This feature depends on several Web APIs. See https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.
## 2.2.0
- Feature #239: add enumeration style object for error codes
- Feature #253: add support for response type "json"
- Feature #127: add multiple file upload (thanks SDA SE Open Industry Solutions and nilswitschel)
## 2.1.1
- Fixed #224: response type "arraybuffer" and "blob" not working on browser platform
## 2.1.0
- Feature #216: Support for response type `arraybuffer`
- Feature #171: Support for response type `blob`
- Feature #205: Add preference for configuring OKHTTP version (thanks RougeCiel)
## 2.0.11
- Fixed #221: headers not set on Android when request fails due to non-success status code
## 2.0.10
- Fixed #218: headers are used as params on browser platform
## 2.0.9
- Fixed #204: broken support for cordova-android < 7.0
- :warning: **Deprecation**: Deprecated "disableRedirect" in favor of "setFollowRedirect"
## 2.0.8
- Fixed #198: cookie header is always passed even if there is no cookie
- Fixed #201: browser implementation is broken due to broken dependency
- Fixed #197: iOS crashes when multiple request are done simultaneously (reverted a8e3637)
- Fixed #189: error code mappings are not precise
- Fixed #200: compatibility with Java 6 is broken due to string switch on Android
- :warning: **Deprecation**: Deprecated "setSSLCertMode" in favor of "setServerTrustMode"
## 2.0.7
- Fixed #195: URLs are double-encoded on Android
## 2.0.6
- Fixed #187: setSSLCertMode with "default" throws an error on Android
- Fixed #115: HTTP connections are not kept alive on iOS (thanks MorpheusDe97)
## 2.0.5
- Fixed #185: need more detailed SSL error message
## 2.0.4
- Fixed #179: sending empty string with utf8 serializer throws an exception
## 2.0.3
- Fixed #172: plugin does not respect user installed CA certs on Android
#### Important information
We've changed a default behavior on Android. User installed CA certs are respected now.
If you don't want this for your needs, you can switch back to old behavior by setting SSL cert mode to `legacy`.
## 2.0.2
- Fixed #142: Plugin affected by REDoS Issue of tough-cookie
- Fixed #157: Arguments are double URL-encoded on "downloadFile" (thanks TheZopo)
- Fixed #164: Arguments are double URL-encoded on "head" (thanks ath0mas)
## 2.0.1
- Fixed #136: Content-Type header non-overwritable on browser platform
## 2.0.0
- Feature #103: implement HTTP SSL cert modes
- :warning: **Breaking Change**: Removed AngularJS (v1) integration service
- :warning: **Breaking Change**: Removed "enableSSLPinning" and "acceptAllCerts", use "setSSLCertMode" instead
- :warning: **Breaking Change**: Certificates must be placed in "www/certificates" folder
## 1.11.1
- Fixed #92: headers not deserialized on platform "browser"
## 1.11.0
- Feature #77: allow overriding global settings for each single request
- Feature #11: add support for "browser" platform
## 1.10.2
- Fixed #78: overriding header "Content-Type" not working on Android
- Fixed #79: PATCH operation not working on Android API level 19 and older (thanks chax)
- Fixed #83: App crashes on error during download operation on iOS (thanks troyanskiy)
- Fixed #76: upload sequence is not respecting order of operations needed by some sites (thanks Johny101)
- :warning: **Deprecation**: AngularJS service is deprecated now and will be removed anytime soon
## 1.10.1
- Fixed #71: does not encode query string in URL correctly on Android
- Fixed #72: app crashes if response encoding is not UTF-8 (thanks jkfb)
## 1.10.0
- Feature #34: add new serializer "utf8" sending utf-8 encoded plain text (thanks robertocapuano)
## 1.9.1
- Fixed #45: does not encode arrays correctly as HTTP GET parameter on Android
- Fixed #54: requests are not responding on iOS with non-string values in header object
- Fixed #58: white-list of allowed content-types should be removed for iOS
## v1.9.0
- Feature #44: "getCookieString" method is exposed
- Feature #43: added support for content type "application/javascript" on iOS (thanks wh33ler)
- Feature #46: "setCookie" allows adding custom cookies
## v1.8.1
- Fixed #27: "uploadFile" method doesn't return data object on iOS (thanks Faisalali23 and laiyinjie)
- Fixed #40: generic error codes are different on Android and iOS
## v1.8.0
- Feature #33: response object contains response url
## v1.7.1
- Fixed #36: setting basic authentication not working correctly (thanks jkfb)
- Fixed #35: Android headers are not normalized (not returned in lowercase)
- Fixed #26: JSON request with array data is not working on Android (JSON error)
## v1.7.0
- Feature #24: "setHeader" allows configuring headers for specified host
## v1.6.2
- Change #29: removed "validateDomainName" (see info notice)
- Fixed #31: request fails throwing error on erroneous cookies
- Fixed #28: added support for content type "application/hal+json" on iOS (thanks ryandegruyter)
#### Important information
We've decided to remove the `validateDomainName()` method, because people were complaining that `acceptAllCerts(true)` is not behaving as expected. And also it's not a good idea to disable domain name validation while using valid certs, because it pretends having a secure connection, but it isn't.
You should either use valid certs with domain name validation enabled (safe for production use) or accept any certs without domain name validation (only for private dev environments). I strongly discourage using fake certs in public networks.
Therefore we are disabling domain name validation automatically, when you set `acceptAllCerts(true)`. So if you were using `validateDomainName()` function, you need to remove this function call for v1.6.2+.
## v1.6.1
- Fixed #23: PATCH method broken on android
## v1.6.0
- Feature #18: implemented PATCH method (thanks akhatri for android implementation)
- Feature #21: added redirection control (thanks to notsyncing and kesozjura)
- Fixed #16: cordova tries to run build script during plugin install
## v1.5.10
- Fixed #10: fix gzip decompression when request header accepts gzip compression (thanks to DayBr3ak)
- Fixed #13: fix angular integration for `setDataSerializer` (thanks to RangerRick)
- Added some missing documentation (thanks to RangerRick)
## v1.5.9
- Fixed case-sensitive folder name of Android source files
## v1.5.8
- Use the same error codes if a request timed out
## v1.5.7
- Fixed a bug in cookie handling (cookies containing an "Expires" string)
- Added setRequestTimeout function to set the timeout in seconds for all further requests
## v1.5.6
- All response header keys are converted to lowercase (iOS only)
## v1.5.5
- added a function to remove all cookies for a URL
## v1.5.4
- fixed an error if the response has no "headers" field
## v1.5.3
- handles cookies correctly on non-success response from server
- throws error when a callback function is missing
## v1.5.2
- fixed missing file "umd-tough-cookie.js“ (caused by missing file ".npmignore")
## v1.5.1
- fixed case-sensitive path name of android source files ("CordovaHTTP" --> "cordovahttp")
## v1.5.0
- added cookie handling
- cookies are persisted via web storage API
## v1.4.0
- forked from "cordova-plugin-http" v1.2.0 (see https://github.com/wymsee/cordova-HTTP)
- added configuration for data serializer
- added HTTP methods PUT and DELETE
# Previous changelog (cordova-plugin-http)
## v1.2.0
- Added support for TLSv1.1 and TLSv1.2 for android versions 4.1-4.4 (API levels 16-19)
### Potentially Breaking Changes that really shouldn't matter because you shouldn't be using SSLv3
- Dropped SSLv3 support for all API Levels < 20. It will now only work on API Levels 20-22.
## v1.1.0
- Fixed the body of errors not being returned in iOS
- Updated AFNetworking to 3.1.0
### Potentially Breaking Changes
- Disable encoding get() URLS in android (Thanks to devgeeks)
## v1.0.3
- Fixed version number in plugin.xml
## v1.0.2
- Fixed bug using useBasicAuth and setHeader from angular
## v1.0.1
- updated README
## v1.0.0
- Added getBasicAuthHeader function
- Added necessary iOS framework (Thanks to EddyVerbruggen)
- Request internet permission in android (Thanks to mbektchiev)
- Fix acceptAllCerts doesn't call callbacks (Thanks to EddyVerbruggen)
- Add validateDomainName (Thanks to denisbabineau)
- Add HEAD request support (untested) (Thanks to denisbabineau)
### Potentially Breaking Changes
- Update cordova file plugin dependency (Thanks to denisbabineau)
- useBasicAuthHeader and setHeader are now synchronous functions
- updated AFNetworking to 3.0.4 - only iOS 7+ is now supported
- updated http-request to 6.0
## v0.1.4
- Support for certificates in www/certificates folder (Thanks to EddyVerbruggen)
## v0.1.3
- Update AFNetworking to 2.4.1 for iOS bug fix in Xcode 6
## v0.1.2
- Fixed plugin.xml for case sensitive filesystems (Thanks to andrey-tsaplin)
## v0.1.1
- Fixed a bug that prevented building
## v0.1.0
- Initial release
## Contributions not noted above
- Fixed examples (Thanks to devgeeks)
- Reports SSL Handshake errors rather than giving a generic error (Thanks to devgeeks)
- Exporting http as a module (Thanks to pvsaikrishna)
- Added Limitations section to readme (Thanks to cvillerm)
- Fixed examples (Thanks to hideov)

77
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,77 @@
# Contributing to Advanced HTTP Plugin
We'd love for you to contribute to our source code and to make Advanced HTTP even better than it is
today! Here are the guidelines we'd like you to follow:
- [Issues and Bugs](#issue)
- [Feature Requests](#feature)
- [Submission Guidelines](#submit)
## <a name="issue"></a> Found an Issue?
If you find a bug in the source code or a mistake in the documentation, you can help us by
submitting an issue to our [GitHub Repository](https://github.com/silkimen/cordova-plugin-advanced-http/issues).
Even better you can submit a Pull Request with a fix.
## <a name="feature"></a> Want a Feature?
You can request a new feature by submitting an issue to our
[GitHub Repository](https://github.com/silkimen/cordova-plugin-advanced-http/issues).
If you would like to implement a new feature then consider what kind of change it is:
* **Major Changes** that you wish to contribute to the project should be discussed first so that we
can better coordinate our efforts, prevent duplication of work, and help you to craft the change
so that it is successfully accepted into the project. Please submit an issue to our GitHub Repository
for discussion.
* **Small Changes** can be crafted and submitted to the GitHub Repository as a Pull Request.
## <a name="submit"></a> Submission Guidelines
### Submitting an Issue
Before you submit your issue search the archive, maybe your question was already answered.
If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize
the effort we can spend fixing issues and adding new features, by not reporting duplicate issues.
Providing the following information will increase the chances of your issue being dealt with
quickly:
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
* **Motivation for or Use Case** - explain why this is a bug for you
* **Advanced HTTP Version(s)** - is it a regression?
* **Operating System** - is this a problem with all supported OS or only specific ones?
* **Related Issues** - has a similar issue been reported before?
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
causing the problem (line of code or commit)
**If you get help, help others. Good karma rulez!**
### Submitting a Pull Request
Before you submit your pull request consider the following guidelines:
* Search [GitHub](https://github.com/silkimen/cordova-plugin-advanced-http/pulls) for an open or
closed Pull Request that relates to your submission. You don't want to duplicate effort.
* Make your changes in a new git branch:
```shell
git checkout -b my-fix-branch master
```
* Create your patch
* Commit your changes using a descriptive commit message
* Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
In GitHub, send a pull request to `cordova-plugin-advanced-http:master`.
If we suggest changes or the [CI build fails](#cibuild), then:
* Make the required updates.
* Commit your changes to your branch (e.g. `my-fix-branch`).
* Push the changes to your GitHub repository (this will update your Pull Request).
That's it! Thank you for your contribution!
### <a name="cibuild"></a> Pull Request Feedback
You can always check the results of the latest CI builds on [Github Actions](https://github.com/silkimen/cordova-plugin-advanced-http/actions).
You can use this information to inspect failing tests in your PR.

View File

@@ -1,5 +1,7 @@
The MIT License (MIT)
Copyright (c) 2019 Sefa Ilkimen
Copyright (c) 2017 Mobisys GmbH
Copyright (c) 2014 Wymsee, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -18,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

618
README.md
View File

@@ -1,178 +1,542 @@
cordovaHTTP
==================
Cordova Advanced HTTP
=====================
[![npm version](https://img.shields.io/npm/v/cordova-plugin-advanced-http)](https://www.npmjs.com/package/cordova-plugin-advanced-http?activeTab=versions)
[![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)
[![GitHub Build Status](https://img.shields.io/github/actions/workflow/status/silkimen/cordova-plugin-advanced-http/.github/workflows/ci.yml?branch=master)](https://github.com/silkimen/cordova-plugin-advanced-http/actions)
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS and Android.
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS, Android and [Browser](#browserSupport).
This is a fork of [Wymsee's Cordova-HTTP plugin](https://github.com/wymsee/cordova-HTTP).
## Advantages over Javascript requests
- Background threading - all requests are done in a background thread.
- SSL Pinning - read more at [LumberBlog](http://blog.lumberlabs.com/2012/04/why-app-developers-should-care-about.html).
- SSL / TLS Pinning
- CORS restrictions do not apply
- X.509 client certificate based authentication
- Handling of HTTP code 401 - read more at [Issue CB-2415](https://issues.apache.org/jira/browse/CB-2415)
## Updates
Please check [CHANGELOG.md](CHANGELOG.md) for details about updating to a new version.
## Installation
The plugin conforms to the Cordova plugin specification, it can be installed
using the Cordova / Phonegap command line interface.
phonegap plugin add https://github.com/wymsee/cordova-HTTP.git
```shell
phonegap plugin add cordova-plugin-advanced-http
cordova plugin add https://github.com/wymsee/cordova-HTTP.git
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
### AngularJS
### Plain Cordova
This plugin creates a cordovaHTTP service inside of a cordovaHTTP module. You must load the module when you create your app's module.
This plugin registers a global object located at `cordova.plugin.http`.
var app = angular.module('myApp', ['ngRoute', 'ngAnimate', 'cordovaHTTP']);
You can then inject the cordovaHTTP service into your controllers. The functions can then be used identically to the examples shown below except that instead of accepting success and failure callback functions, each function returns a promise. For more information on promises in AngularJS read the [AngularJS docs](http://docs.angularjs.org/api/ng/service/$q). For more info on promises in general check out this article on [html5rocks](http://www.html5rocks.com/en/tutorials/es6/promises/). Make sure that you load cordova.js or phonegap.js after AngularJS is loaded.
### With Ionic-native wrapper
### Not AngularJS
Check the [Ionic docs](https://ionicframework.com/docs/native/http/) for how to use this plugin with Ionic-native.
This plugin registers a `cordovaHTTP` global on window
## Synchronous Functions
### getBasicAuthHeader
This returns an object representing a basic HTTP Authorization header of the form `{'Authorization': 'Basic base64encodedusernameandpassword'}`
## Functions
All available functions are documented below. Every function takes a success and error callback function as the last 2 arguments.
```js
var header = cordova.plugin.http.getBasicAuthHeader('user', 'password');
```
### useBasicAuth
This sets up all future requests to use Basic HTTP authentication with the given username and password.
cordovaHttp.useBasicAuth("user", "password", function() {
console.log('success!');
}, function() {
console.log('error :(');
});
### setHeader
Set a header for all future requests. Takes a header and a value.
```js
cordova.plugin.http.useBasicAuth('user', 'password');
```
cordovaHttp.setHeader("Header", "Value", function() {
console.log('success!');
}, function() {
console.log('error :(');
});
### 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 or null).
### enableSSLPinning
Enable or disable SSL pinning. To use SSL pinning you must include at least one .cer SSL certificate in your app project. For ios include your certificate in the root level of your bundle (just add the .cer file to your project/target at the root level). For android include your certificate in your project's platforms/android/assets folder. In both cases all .cer files found will be loaded automatically. If you only have a .pem certificate see this [stackoverflow answer](http://stackoverflow.com/a/16583429/3182729). You want to convert it to a DER encoded certificate with a .cer extension.
```js
cordova.plugin.http.setHeader('Hostname', 'Header', 'Value');
```
cordovaHttp.enableSSLPinning(true, function() {
console.log('success!');
}, function() {
console.log('error :(');
});
### acceptAllCerts
Accept all SSL certificates. Or disable accepting all certificates.
You can also define headers used for all hosts by using wildcard character "\*" or providing only two params.
```js
cordova.plugin.http.setHeader('*', 'Header', 'Value');
cordova.plugin.http.setHeader('Header', 'Value');
```
The hostname also includes the port number. If you define a header for `www.example.com` it will not match following URL `http://www.example.com:8080`.
```js
// will match http://www.example.com/...
cordova.plugin.http.setHeader('www.example.com', 'Header', 'Value');
// will match http://www.example.com:8080/...
cordova.plugin.http.setHeader('www.example.com:8080', 'Header', 'Value');
```
### setDataSerializer<a name="setDataSerializer"></a>
Set the data serializer which will be used for all future PATCH, POST and PUT requests. Takes a string representing the name of the serializer.
```js
cordova.plugin.http.setDataSerializer('urlencoded');
```
You can choose one of these:
* `urlencoded`: send data as url encoded content in body
* default content type "application/x-www-form-urlencoded"
* data must be an dictionary style `Object`
* `json`: send data as JSON encoded content in body
* default content type "application/json"
* data must be an `Array` or an dictionary style `Object`
* `utf8`: send data as plain UTF8 encoded string in body
* default content type "plain/text"
* data must be a `String`
* `multipart`: send FormData objects as multipart content in body
* default content type "multipart/form-data"
* data must be an `FormData` instance
* `raw`: send data as is, without any processing
* default content type "application/octet-stream"
* data must be an `Uint8Array` or an `ArrayBuffer`
This defaults to `urlencoded`. You can also override the default content type headers by specifying your own headers (see [setHeader](#setHeader)).
:warning: `urlencoded` does not support serializing deep structures whereas `json` does.
:warning: `multipart` depends on several Web API standards which need to be supported in your web view. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.
### setRequestTimeout
Set how long to wait for a request to respond, in seconds.
For Android, this will set both [connectTimeout](https://developer.android.com/reference/java/net/URLConnection#getConnectTimeout()) and [readTimeout](https://developer.android.com/reference/java/net/URLConnection#setReadTimeout(int)).
For iOS, this will set [timeout interval](https://developer.apple.com/documentation/foundation/nsmutableurlrequest/1414063-timeoutinterval).
For browser platform, this will set [timeout](https://developer.mozilla.org/fr/docs/Web/API/XMLHttpRequest/timeout).
```js
cordova.plugin.http.setRequestTimeout(5.0);
```
### setConnectTimeout (Android Only)
Set connect timeout for Android
```js
cordova.plugin.http.setRequestTimeout(5.0);
```
### setReadTimeout (Android Only)
Set read timeout for Android
```js
cordova.plugin.http.setReadTimeout(5.0);
```
### setFollowRedirect<a name="setFollowRedirect"></a>
Configure if it should follow redirects automatically. This defaults to true.
```js
cordova.plugin.http.setFollowRedirect(true);
```
### getCookieString
Returns saved cookies (as string) matching given URL.
```js
cordova.plugin.http.getCookieString(url);
```
### setCookie
Add a custom cookie. Takes a URL, a cookie string and an options object. See [ToughCookie documentation](https://github.com/salesforce/tough-cookie#setcookiecookieorstring-currenturl-options-cberrcookie) for allowed options. Cookie will persist until removed with [removeCookies](#removecookies) or [clearCookies](#clearcookies).
```js
cordova.plugin.http.setCookie(url, cookie, options);
```
### clearCookies
Clear the cookie store.
```js
cordova.plugin.http.clearCookies();
```
## Asynchronous Functions
These functions all take success and error callbacks as their last 2 arguments.
### setServerTrustMode<a name="setServerTrustMode"></a>
Set server trust mode, being one of the following values:
* `default`: default SSL trustship and hostname verification handling using system's CA certs
* `legacy`: use legacy default behavior (< 2.0.3), excluding user installed CA certs (only for Android)
* `nocheck`: disable SSL certificate checking and hostname verification, trusting all certs (meant to be used only for testing purposes)
* `pinned`: trust only provided certificates
To use SSL pinning you must include at least one `.cer` SSL certificate in your app project. You can pin to your server certificate or to one of the issuing CA certificates. Include your certificate in the `www/certificates` folder. All `.cer` files found there will be loaded automatically.
:warning: Your certificate must be DER encoded! If you only have a PEM encoded certificate read this [stackoverflow answer](http://stackoverflow.com/a/16583429/3182729). You want to convert it to a DER encoded certificate with a .cer extension.
```js
// enable SSL pinning
cordova.plugin.http.setServerTrustMode('pinned', function() {
console.log('success!');
}, function() {
console.log('error :(');
});
// use system's default CA certs
cordova.plugin.http.setServerTrustMode('default', function() {
console.log('success!');
}, function() {
console.log('error :(');
});
// disable SSL cert checking, only meant for testing purposes, do NOT use in production!
cordova.plugin.http.setServerTrustMode('nocheck', function() {
console.log('success!');
}, function() {
console.log('error :(');
});
```
### setClientAuthMode<a name="setClientAuthMode"></a>
Configure X.509 client certificate authentication. Takes mode and options. `mode` being one of following values:
* `none`: disable client certificate authentication
* `systemstore` (only on Android): use client certificate installed in the Android system store; user will be presented with a list of all installed certificates
* `buffer`: use given client certificate; you will need to provide an options object:
* `rawPkcs`: ArrayBuffer containing raw PKCS12 container with client certificate and private key
* `pkcsPassword`: password of the PKCS container
```js
// enable client auth using PKCS12 container given in ArrayBuffer `myPkcs12ArrayBuffer`
cordova.plugin.http.setClientAuthMode('buffer', {
rawPkcs: myPkcs12ArrayBuffer,
pkcsPassword: 'mySecretPassword'
}, success, fail);
// enable client auth using certificate in system store (only on Android)
cordova.plugin.http.setClientAuthMode('systemstore', {}, success, fail);
// disable client auth
cordova.plugin.http.setClientAuthMode('none', {}, success, fail);
```
### removeCookies
Remove all cookies associated with a given URL.
```js
cordova.plugin.http.removeCookies(url, callback);
```
### sendRequest<a name="sendRequest"></a>
Execute a HTTP request. Takes a URL and an options object. This is the internally used implementation of the following shorthand functions ([post](#post), [get](#get), [put](#put), [patch](#patch), [delete](#delete), [head](#head), [uploadFile](#uploadFile) and [downloadFile](#downloadFile)). You can use this function, if you want to override global settings for each single request. Check the documentation of the respective shorthand function for details on what is returned on success and failure.
:warning: You need to encode the base URL yourself if it contains special characters like whitespaces. You can use `encodeURI()` for this purpose.
The options object contains following keys:
* `method`: HTTP method to be used, defaults to `get`, needs to be one of the following values:
* `get`, `post`, `put`, `patch`, `head`, `delete`, `options`, `upload`, `download`
* `data`: payload to be send to the server (only applicable on `post`, `put` or `patch` methods)
* `params`: query params to be appended to the URL (only applicable on `get`, `head`, `delete`, `upload` or `download` methods)
* `serializer`: data serializer to be used (only applicable on `post`, `put` or `patch` methods), defaults to global serializer value, see [setDataSerializer](#setDataSerializer) for supported values
* `responseType`: expected response type, defaults to `text`, needs to be one of the following values:
* `text`: data is returned as decoded string, use this for all kinds of string responses (e.g. XML, HTML, plain text, etc.)
* `json` data is treated as JSON and returned as parsed object, returns `undefined` when response body is empty
* `arraybuffer`: data is returned as [ArrayBuffer instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), returns `null` when response body is empty
* `blob`: data is returned as [Blob instance](https://developer.mozilla.org/en-US/docs/Web/API/Blob), returns `null` when response body is empty
* `timeout`: timeout value for the request in seconds, defaults to global timeout value
* `followRedirect`: enable or disable automatically following redirects
* `headers`: headers object (key value pair), will be merged with global values
* `filePath`: file path(s) to be used during upload and download see [uploadFile](#uploadFile) and [downloadFile](#downloadFile) for detailed information
* `name`: name(s) to be used during upload see [uploadFile](#uploadFile) for detailed information
Here's a quick example:
```js
const options = {
method: 'post',
data: { id: 12, message: 'test' },
headers: { Authorization: 'OAuth2: token' }
};
cordova.plugin.http.sendRequest('https://google.com/', options, function(response) {
// prints 200
console.log(response.status);
}, function(response) {
// prints 403
console.log(response.status);
//prints Permission denied
console.log(response.error);
});
```
cordovaHttp.acceptAllCerts(true, function() {
console.log('success!');
}, function() {
console.log('error :(');
});
### post<a name="post"></a>
Execute a POST request. Takes a URL, parameters, and headers.
Execute a POST request. Takes a URL, data, and headers.
```js
cordova.plugin.http.post('https://google.com/', {
test: 'testString'
}, {
Authorization: 'OAuth2: token'
}, function(response) {
console.log(response.status);
}, function(response) {
console.error(response.error);
});
```
#### success
The success function receives a response object with 2 properties: status and data. Status is the HTTP response code and data is the response from the server as a string. Here's a quick example:
The success function receives a response object with 4 properties: status, data, url, and headers. **status** is the HTTP response code as numeric value. **data** is the response from the server as a string. **url** is the final URL obtained after any redirects as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
Here's a quick example:
```js
{
status: 200,
data: '{"id": 12, "message": "test"}',
url: 'http://example.net/rest'
headers: {
'content-length': '247'
}
}
```
{
status: 200,
data: "{'id': 12, 'message': 'test'}"
}
Most apis will return JSON meaning you'll want to parse the data like in the example below:
cordovaHttp.post("https://google.com/, {
id: 12,
message: "test"
}, { Authorization: "OAuth2: token" }, function(response) {
// prints 200
console.log(response.status);
try {
response.data = JSON.parse(response.data);
// prints test
console.log(response.data.message);
} catch(e) {
console.error("JSON parsing error");
}
}, function(response) {
// prints 403
console.log(response.status);
//prints Permission denied
console.log(response.error);
});
#### failure
The error function receives a response object with 2 properties: status and error. Status is the HTTP response code. Error is the error response from the server as a string. Here's a quick example:
```js
cordova.plugin.http.post('https://google.com/', {
id: 12,
message: 'test'
}, { Authorization: 'OAuth2: token' }, function(response) {
// prints 200
console.log(response.status);
try {
response.data = JSON.parse(response.data);
// prints test
console.log(response.data.message);
} catch(e) {
console.error('JSON parsing error');
}
}, function(response) {
// prints 403
console.log(response.status);
{
status: 403,
error: "Permission denied"
}
### get
//prints Permission denied
console.log(response.error);
});
```
#### failure
The error function receives a response object with 4 properties: status, error, url, and headers (url and headers being optional). **status** is a HTTP response code or an internal error code. Positive values are HTTP status codes whereas negative values do represent internal error codes. **error** is the error response from the server as a string or an internal error message. **url** is the final URL obtained after any redirects as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
Here's a quick example:
```js
{
status: 403,
error: 'Permission denied',
url: 'http://example.net/noperm'
headers: {
'content-length': '247'
}
}
```
:warning: An enumeration style object is exposed as `cordova.plugin.http.ErrorCode`. You can use it to check against internal error codes.
### get<a name="get"></a>
Execute a GET request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
cordovaHttp.get("https://google.com/, {
id: 12,
message: "test"
}, { Authorization: "OAuth2: token" }, function(response) {
console.log(response.status);
}, function(response) {
console.error(response.error);
});
### uploadFile
Uploads a file saved on the device. Takes a URL, parameters, headers, filePath, and the name of the parameter to pass the file along as. See the [post](#post) documentation for details on what is returned on success and failure.
```js
cordova.plugin.http.get('https://google.com/', {
id: '12',
message: 'test'
}, { Authorization: 'OAuth2: token' }, function(response) {
console.log(response.status);
}, function(response) {
console.error(response.error);
});
```
cordovaHttp.uploadFile("https://google.com/, {
id: 12,
message: "test"
}, { Authorization: "OAuth2: token" }, "file:///somepicture.jpg", "picture", function(response) {
console.log(response.status);
}, function(response) {
console.error(response.error);
});
### downloadFile
Downloads a file and saves it to the device. Takes a URL, parameters, headers, and a filePath. See [post](#post) documentation for details on what is returned on failure. On success this function returns a cordova [FileEntry object](http://cordova.apache.org/docs/en/3.3.0/cordova_file_file.md.html#FileEntry).
### put<a name="put"></a>
Execute a PUT request. Takes a URL, data, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
cordovaHttp.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) {
console.error(response.error);
});
### patch<a name="patch"></a>
Execute a PATCH request. Takes a URL, data, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### delete<a name="delete"></a>
Execute a DELETE request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### head<a name="head"></a>
Execute a HEAD request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### options<a name="options"></a>
Execute a OPTIONS request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### uploadFile<a name="uploadFile"></a>
Uploads one or more file(s) saved on the device. Takes a URL, parameters, headers, filePath(s), and the name(s) of the parameter to pass the file along as. See the [post](#post) documentation for details on what is returned on success and failure.
```js
// e.g. for single file
const filePath = 'file:///somepicture.jpg';
const name = 'picture';
// e.g. for multiple files
const filePath = ['file:///somepicture.jpg', 'file:///somedocument.doc'];
const name = ['picture', 'document'];
cordova.plugin.http.uploadFile("https://google.com/", {
id: '12',
message: 'test'
}, { Authorization: 'OAuth2: token' }, filePath, name, function(response) {
console.log(response.status);
}, function(response) {
console.error(response.error);
});
```
### downloadFile<a name="downloadFile"></a>
Downloads a file and saves it to the device. Takes a URL, parameters, headers, and a filePath. See [post](#post) documentation for details on what is returned on failure. On success this function returns a cordova [FileEntry object](http://cordova.apache.org/docs/en/3.3.0/cordova_file_file.md.html#FileEntry) as first and the response object as second parameter.
```js
cordova.plugin.http.downloadFile(
"https://google.com/",
{ id: '12', message: 'test' },
{ Authorization: 'OAuth2: token' },
'file:///somepicture.jpg',
// success callback
function(entry, response) {
// prints the filename
console.log(entry.name);
// prints the filePath
console.log(entry.fullPath);
// prints all header key/value pairs
Object.keys(response.headers).forEach(function (key) {
console.log(key, response.headers[key]);
});
},
// error callback
function(response) {
console.error(response.error);
}
);
```
### abort<a name="abort"></a>
Abort a HTTP request. Takes the `requestId` which is returned by [sendRequest](#sendRequest) and its shorthand functions ([post](#post), [get](#get), [put](#put), [patch](#patch), [delete](#delete), [head](#head), [uploadFile](#uploadFile) and [downloadFile](#downloadFile)).
If the request already has finished, the request will finish normally and the abort call result will be `{ aborted: false }`.
If the request is still in progress, the request's `failure` callback will be invoked with response `{ status: -8 }`, and the abort call result `{ aborted: true }`.
:warning: Not supported for Android < 6 (API level < 23). For Android 5.1 and below, calling `abort(reqestId)` will have no effect, i.e. the requests will finish as if the request was not cancelled.
```js
// start a request and get its requestId
var requestId = cordova.plugin.http.downloadFile("https://google.com/", {
id: '12',
message: 'test'
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry, response) {
// prints the filename
console.log(entry.name);
// prints the filePath
console.log(entry.fullPath);
// prints the status code
console.log(response.status);
}, function(response) {
// if request was actually aborted, failure callback with status -8 will be invoked
if(response.status === -8){
console.log('download aborted');
} else {
console.error(response.error);
}
});
//...
// abort request
cordova.plugin.http.abort(requestId, function(result) {
// prints if request was aborted: true | false
console.log(result.aborted);
}, function(response) {
console.error(response.error);
});
```
## Browser support<a name="browserSupport"></a>
This plugin supports a very restricted set of functions on the browser platform.
It's meant for testing purposes, not for production grade usage.
Following features are *not* supported:
* Manipulating Cookies
* Uploading and Downloading files
* Pinning SSL certificate
* Disabling SSL certificate check
* Disabling transparently following redirects (HTTP codes 3xx)
* Circumventing CORS restrictions
## Libraries
This plugin utilizes some awesome open source networking libraries. These are both MIT licensed:
This plugin utilizes some awesome open source libraries:
- iOS - [AFNetworking](https://github.com/AFNetworking/AFNetworking)
- Android - [http-request](https://github.com/kevinsawicki/http-request)
- iOS - [AFNetworking](https://github.com/AFNetworking/AFNetworking) (MIT licensed)
- Android - [http-request](https://github.com/kevinsawicki/http-request) (MIT licensed)
- Cookie handling - [tough-cookie](https://github.com/salesforce/tough-cookie) (BSD-3-Clause licensed)
We made a few modifications to http-request. They can be found in a separate repo here: https://github.com/wymsee/http-request
We made a few modifications to the networking libraries.
## License
## CI Builds & E2E Testing
The MIT License
This plugin uses amazing cloud services to maintain quality. CI Builds and E2E testing are powered by:
Copyright (c) 2014 Wymsee, Inc
* [GitHub Actions](https://github.com/features/actions)
* [BrowserStack](https://www.browserstack.com/)
* [Sauce Labs](https://saucelabs.com/)
* [httpbin.org](https://httpbin.org/)
* [go-httpbin](https://httpbingo.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
### Local Testing
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
First, install current package with `npm install` to fetch dev dependencies.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Then, to execute Javascript tests:
```shell
npm run test:js
```
And, to execute E2E tests:
- setup local Android sdk and emulators, or Xcode and simulators for iOS
- launch emulator or simulator
- install [Appium](http://appium.io/) (see [Getting Started](https://github.com/appium/appium/blob/HEAD/docs/en/about-appium/getting-started.md))
- start `appium`
- run
- updating client and server certificates, building test app, and running e2e tests
```shell
npm run test:android
npm run test:ios
```
## Contribute & Develop
We've set up a separate document for our [contribution guidelines](CONTRIBUTING.md).

12732
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

71
package.json Normal file
View File

@@ -0,0 +1,71 @@
{
"name": "cordova-plugin-advanced-http",
"version": "3.3.1",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
"update:cert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
"build:browser": "./scripts/build-test-app.sh --browser",
"build:android": "./scripts/build-test-app.sh --android --emulator",
"build:ios": "./scripts/build-test-app.sh --ios --emulator",
"lint": "eslint . --ext .js",
"test:android": "npm run update:cert && npm run build:android && ./scripts/test-app.sh --android --emulator",
"test:ios": "npm run update:cert && npm run build:ios && ./scripts/test-app.sh --ios --emulator",
"test:app": "npm run test:android && npm run test:ios",
"test:js": "mocha ./test/js-specs.js",
"test": "npm run test:js && npm run test:app",
"release": "npm run test && ./scripts/release.sh"
},
"cordova": {
"id": "cordova-plugin-advanced-http",
"platforms": [
"ios",
"android"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/silkimen/cordova-plugin-advanced-http.git"
},
"keywords": [
"cordova",
"device",
"ecosystem:cordova",
"cordova-ios",
"cordova-android",
"ssl",
"tls"
],
"engines": [
{
"name": "cordova",
"version": ">=4.0.0"
}
],
"author": "Wymsee",
"contributors": [
"devgeeks",
"EddyVerbruggen",
"mbektchiev",
"denisbabineau",
"andrey-tsaplin",
"pvsaikrishna",
"cvillerm",
"hideov",
"silkimen"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/silkimen/cordova-plugin-advanced-http/issues"
},
"homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme",
"devDependencies": {
"chai": "4.3.6",
"colors": "1.4.0",
"cordova": "11.0.0",
"eslint": "^8.57.0",
"mocha": "9.2.2",
"umd-tough-cookie": "3.0.0",
"wd": "1.14.0",
"xml2js": "0.5.0"
}
}

View File

@@ -1,88 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="com.synconset.cordovaHTTP"
version="0.1.1">
<name>SSL Pinning</name>
<description>
<?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="3.3.1">
<name>Advanced HTTP plugin</name>
<description>
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
</description>
<engines>
<engine name="cordova" version=">=3.0.0" />
</engines>
<dependency id="org.apache.cordova.file" commit="r0.2.5" />
<js-module src="www/cordovaHTTP.js" name="CordovaHttpPlugin">
<clobbers target="plugins.CordovaHttpPlugin" />
</description>
<engines>
<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"/>
<js-module src="www/global-configs.js" name="global-configs"/>
<js-module src="www/helpers.js" name="helpers"/>
<js-module src="www/js-util.js" name="js-util"/>
<js-module src="www/local-storage-store.js" name="local-storage-store"/>
<js-module src="www/lodash.js" name="lodash"/>
<js-module src="www/messages.js" name="messages"/>
<js-module src="www/ponyfills.js" name="ponyfills"/>
<js-module src="www/public-interface.js" name="public-interface"/>
<js-module src="www/umd-tough-cookie.js" name="tough-cookie"/>
<js-module src="www/url-util.js" name="url-util"/>
<js-module src="www/advanced-http.js" name="http">
<clobbers target="cordova.plugin.http"/>
</js-module>
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="CordovaHttpPlugin">
<param name="ios-package" value="CordovaHttpPlugin"/>
</feature>
</config-file>
<header-file src="src/ios/CordovaHttpPlugin.h"/>
<header-file src="src/ios/BinaryRequestSerializer.h"/>
<header-file src="src/ios/BinaryResponseSerializer.h"/>
<header-file src="src/ios/TextResponseSerializer.h"/>
<header-file src="src/ios/TextRequestSerializer.h"/>
<header-file src="src/ios/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/SM_AFNetworking/SM_AFHTTPSessionManager.m"/>
<source-file src="src/ios/SM_AFNetworking/SM_AFNetworkReachabilityManager.m"/>
<source-file src="src/ios/SM_AFNetworking/SM_AFSecurityPolicy.m"/>
<source-file src="src/ios/SM_AFNetworking/SM_AFURLRequestSerialization.m"/>
<source-file src="src/ios/SM_AFNetworking/SM_AFURLResponseSerialization.m"/>
<source-file src="src/ios/SM_AFNetworking/SM_AFURLSessionManager.m"/>
<source-file src="src/ios/SDNetworkActivityIndicator/SDNetworkActivityIndicator.m"/>
<framework src="Security.framework"/>
<framework src="SystemConfiguration.framework"/>
</platform>
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="CordovaHttpPlugin">
<param name="android-package" value="com.silkimen.cordovahttp.CordovaHttpPlugin"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</config-file>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaClientAuth.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpBase.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpDownload.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaObservableCallbackContext.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/cordovahttp/CordovaServerTrust.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/http/HttpBodyDecoder.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/HttpRequest.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/JsonUtils.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/KeyChainKeyManager.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/TLSConfiguration.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
</platform>
<platform name="browser">
<config-file target="config.xml" parent="/*">
<feature name="CordovaHttpPlugin">
<param name="browser-package" value="CordovaHttpPlugin"/>
</feature>
</config-file>
<js-module src="src/browser/cordova-http-plugin.js" name="http-proxy">
<runs/>
</js-module>
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="CordovaHttpPlugin">
<param name="ios-package" value="CordovaHttpPlugin"/>
</feature>
</config-file>
<header-file src="src/ios/CordovaHttpPlugin.h" />
<source-file src="src/ios/CordovaHttpPlugin.m" />
<header-file src="src/ios/HttpManager.h" />
<source-file src="src/ios/HttpManager.m" />
<header-file src="src/ios/TextResponseSerializer.h" />
<source-file src="src/ios/TextResponseSerializer.m" />
<header-file src="src/ios/AFNetworking/AFHTTPRequestOperation.h" />
<source-file src="src/ios/AFNetworking/AFHTTPRequestOperation.m" />
<header-file src="src/ios/AFNetworking/AFHTTPRequestOperationManager.h" />
<source-file src="src/ios/AFNetworking/AFHTTPRequestOperationManager.m" />
<header-file src="src/ios/AFNetworking/AFHTTPSessionManager.h" />
<source-file src="src/ios/AFNetworking/AFHTTPSessionManager.m" />
<header-file src="src/ios/AFNetworking/AFNetworking.h" />
<header-file src="src/ios/AFNetworking/AFNetworkReachabilityManager.h" />
<source-file src="src/ios/AFNetworking/AFNetworkReachabilityManager.m" />
<header-file src="src/ios/AFNetworking/AFSecurityPolicy.h" />
<source-file src="src/ios/AFNetworking/AFSecurityPolicy.m" />
<header-file src="src/ios/AFNetworking/AFURLConnectionOperation.h" />
<source-file src="src/ios/AFNetworking/AFURLConnectionOperation.m" />
<header-file src="src/ios/AFNetworking/AFURLRequestSerialization.h" />
<source-file src="src/ios/AFNetworking/AFURLRequestSerialization.m" />
<header-file src="src/ios/AFNetworking/AFURLResponseSerialization.h" />
<source-file src="src/ios/AFNetworking/AFURLResponseSerialization.m" />
<header-file src="src/ios/AFNetworking/AFURLSessionManager.h" />
<source-file src="src/ios/AFNetworking/AFURLSessionManager.m" />
<framework src="Security.framework" />
</platform>
<!--android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="CordovaHttpPlugin">
<param name="android-package" value="com.synconset.CordovaHttpPlugin"/>
</feature>
</config-file>
<source-file src="src/android/com/synconset/CordovaHTTP/CordovaHttp.java" target-dir="src/com/synconset" />
<source-file src="src/android/com/synconset/CordovaHTTP/CordovaHttpGet.java" target-dir="src/com/synconset" />
<source-file src="src/android/com/synconset/CordovaHTTP/CordovaHttpPost.java" target-dir="src/com/synconset" />
<source-file src="src/android/com/synconset/CordovaHTTP/CordovaHttpUpload.java" target-dir="src/com/synconset" />
<source-file src="src/android/com/synconset/CordovaHTTP/CordovaHttpDownload.java" target-dir="src/com/synconset" />
<source-file src="src/android/com/synconset/CordovaHTTP/CordovaHttpPlugin.java" target-dir="src/com/synconset" />
<source-file src="src/android/com/synconset/CordovaHTTP/HttpRequest.java" target-dir="src/com/github/kevinsawicki/http" />
</platform>
</platform>
</plugin>

47
scripts/build-test-app.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -e
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
WORKINGCOPY=$ROOT/temp/workingcopy
CDV=$ROOT/node_modules/.bin/cordova
PLATFORM=ios
TARGET=emulator
while :; do
case $1 in
--android)
PLATFORM=android
;;
--browser)
PLATFORM=browser
;;
--ios)
PLATFORM=ios
;;
--device)
TARGET=device
;;
--emulator)
TARGET=emulator
;;
-?*)
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
*)
break
esac
shift
done
printf 'Building test app for %s\n' $PLATFORM
rm -rf $ROOT/temp
mkdir $ROOT/temp
cp -r $ROOT/test/e2e-app-template/. $ROOT/temp/
cp $ROOT/test/e2e-specs.js $ROOT/temp/www/
rsync -ax --exclude node_modules --exclude scripts --exclude temp --exclude test $ROOT/. $WORKINGCOPY
cd $ROOT/temp
$CDV prepare
$CDV plugins add $WORKINGCOPY
$CDV build $PLATFORM --$TARGET --buildConfig build.json

13
scripts/release.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
pushd $ROOT
VERSION=$(node -e "console.log(require('./package.json').version)")
./scripts/update-tough-cookie.sh
node ./scripts/update-plugin-xml.js $VERSION
git commit -a -m "release v$VERSION"
git tag "v$VERSION"
npm publish
popd

14
scripts/test-app.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -e
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
if [ $CI == "true" ] && ([ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]) && ([ -z $BROWSERSTACK_USERNAME ] || [ -z $BROWSERSTACK_ACCESS_KEY ]); then
echo "Skipping CI tests, because Saucelabs and BrowserStack credentials are not set.";
exit 0;
fi
printf 'Running e2e tests\n'
pushd $ROOT
./node_modules/.bin/mocha --reporter ./test/e2e-tooling/reporter.js ./test/e2e-tooling/test.js "$@"
popd

View File

@@ -0,0 +1,29 @@
const fs = require('fs');
const https = require('https');
const path = require('path');
const SOURCE_URL = 'https://badssl.com/certs/badssl.com-client.p12';
const TARGET_PATH = path.join(__dirname, '../test/e2e-app-template/www/certificates/badssl-client-cert.pkcs');
const downloadPkcsContainer = (source, target) => new Promise((resolve, reject) => {
const file = fs.createWriteStream(target);
const req = https.get(source, response => {
response.pipe(file)
resolve(target);
});
req.on('error', error => {
return reject(error)
});
req.end();
});
console.log(`Updating client certificate from ${SOURCE_URL}`);
downloadPkcsContainer(SOURCE_URL, TARGET_PATH)
.catch(error => {
console.error(`Updating client certificate failed: ${error}`);
process.exit(1);
});

View File

@@ -0,0 +1,40 @@
const fs = require('fs');
const https = require('https');
const path = require('path');
const SOURCE_HOST = 'httpbin.org';
const TARGET_PATH = path.join(__dirname, '../test/e2e-app-template/www/certificates/httpbin.org.cer');
const getCert = hostname => new Promise((resolve, reject) => {
const options = {
hostname,
agent: false,
rejectUnauthorized: false,
ciphers: 'ALL'
};
const req = https.get(options, response => {
const certificate = response.socket.getPeerCertificate();
if (certificate === null) {
return reject({ message: 'The website did not provide a certificate' });
}
resolve(certificate);
});
req.on('error', error => {
return reject(error)
});
req.end();
});
console.log(`Updating server certificate from ${SOURCE_HOST}`);
getCert(SOURCE_HOST)
.then(cert => fs.writeFileSync(TARGET_PATH, cert.raw))
.catch(error => {
console.error(`Updating server certificate failed: ${error}`);
process.exit(1);
});

View File

@@ -0,0 +1,33 @@
const args = process.argv.slice(2);
const fs = require('fs');
const path = require('path');
const xml2js = require('xml2js');
const xmlPath = path.join(__dirname, '..', 'plugin.xml');
const parse = xml => new Promise((resolve, reject) => {
const parser = new xml2js.Parser();
parser.parseString(xml, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
const stringify = obj => {
const builder = new xml2js.Builder();
return builder.buildObject(obj);
};
const update = async (version) => {
const xml = fs.readFileSync(xmlPath, 'utf-8');
const parsed = await parse(xml);
parsed.plugin.$.version = version;
fs.writeFileSync(xmlPath, stringify(parsed));
};
return update(args[0]);

8
scripts/update-tough-cookie.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -e
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
cd $ROOT
npm i
cp node_modules/umd-tough-cookie/lib/umd-tough-cookie.js www/umd-tough-cookie.js

40
scripts/upload-browserstack.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -e
PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
TEMP=$ROOT/temp
if [ -z $BROWSERSTACK_USERNAME ] || [ -z $BROWSERSTACK_ACCESS_KEY ]; then
echo "Skipping uploading artifact, because BrowserStack credentials are not set.";
exit 0;
fi
if [ $PLATFORM = "android" ]; then
curl -u $BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY \
-X POST \
https://api-cloud.browserstack.com/app-automate/upload \
-F "file=@$TEMP/platforms/android/app/build/outputs/apk/debug/app-debug.apk" \
-F "data={\"custom_id\": \"HttpTestAppAndroid\"}"
else
rm -rf "$TEMP/HttpDemo.ipa"
IOS_BUILD_DIR="$TEMP/platforms/ios/build/Debug-iphonesimulator"
APP_PATH="$IOS_BUILD_DIR/HttpDemo.app"
if [ ! -d "$APP_PATH" ]; then
echo "Unable to locate $APP_PATH" >&2
exit 1
fi
PAYLOAD_DIR="$TEMP/Payload"
rm -rf "$PAYLOAD_DIR"
mkdir -p "$PAYLOAD_DIR"
cp -R "$APP_PATH" "$PAYLOAD_DIR/HttpDemo.app"
(cd "$TEMP" && zip -qr HttpDemo.ipa Payload)
rm -rf "$PAYLOAD_DIR"
curl -u $BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY \
-X POST \
https://api-cloud.browserstack.com/app-automate/upload \
-F "file=@$TEMP/HttpDemo.ipa" \
-F "data={\"custom_id\": \"HttpTestAppIos\"}"
fi

30
scripts/upload-saucelabs.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -e
PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
TEMP=$ROOT/temp
if [ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]; then
echo "Skipping uploading artifact, because Saucelabs credentials are not set.";
exit 0;
fi
if [ $PLATFORM = "android" ]; then
curl -u $SAUCE_USERNAME:$SAUCE_ACCESS_KEY \
-X POST \
-H "Content-Type: application/octet-stream" \
https://saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/HttpDemo.apk?overwrite=true \
--data-binary @$TEMP/platforms/android/app/build/outputs/apk/debug/app-debug.apk
else
rm -rf $TEMP/HttpDemo.app.zip
pushd $TEMP/platforms/ios/build/emulator
zip -r $TEMP/HttpDemo.app.zip ./HttpDemo.app
popd
curl -u $SAUCE_USERNAME:$SAUCE_ACCESS_KEY \
-X POST \
-H "Content-Type: application/octet-stream" \
https://saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/HttpDemo.app.zip?overwrite=true \
--data-binary @$TEMP/HttpDemo.app.zip
fi

View File

@@ -0,0 +1,113 @@
package com.silkimen.cordovahttp;
import android.app.Activity;
import android.content.Context;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import org.apache.cordova.CallbackContext;
import com.silkimen.http.KeyChainKeyManager;
import com.silkimen.http.TLSConfiguration;
class CordovaClientAuth implements Runnable, KeyChainAliasCallback {
private static final String TAG = "Cordova-Plugin-HTTP";
private String mode;
private String aliasString;
private byte[] rawPkcs;
private String pkcsPassword;
private Activity activity;
private Context context;
private TLSConfiguration tlsConfiguration;
private CallbackContext callbackContext;
public CordovaClientAuth(final String mode, final String aliasString, final byte[] rawPkcs,
final String pkcsPassword, final Activity activity, final Context context, final TLSConfiguration configContainer,
final CallbackContext callbackContext) {
this.mode = mode;
this.aliasString = aliasString;
this.rawPkcs = rawPkcs;
this.pkcsPassword = pkcsPassword;
this.activity = activity;
this.tlsConfiguration = configContainer;
this.context = context;
this.callbackContext = callbackContext;
}
@Override
public void run() {
if ("systemstore".equals(this.mode)) {
this.loadFromSystemStore();
} else if ("buffer".equals(this.mode)) {
this.loadFromBuffer();
} else {
this.disableClientAuth();
}
}
private void loadFromSystemStore() {
if (this.aliasString == null) {
KeyChain.choosePrivateKeyAlias(this.activity, this, null, null, null, -1, null);
} else {
this.alias(this.aliasString);
}
}
private void loadFromBuffer() {
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
ByteArrayInputStream stream = new ByteArrayInputStream(this.rawPkcs);
keyStore.load(stream, this.pkcsPassword.toCharArray());
keyManagerFactory.init(keyStore, this.pkcsPassword.toCharArray());
this.tlsConfiguration.setKeyManagers(keyManagerFactory.getKeyManagers());
this.callbackContext.success();
} catch (Exception e) {
Log.e(TAG, "Couldn't load given PKCS12 container for authentication", e);
this.callbackContext.error("Couldn't load given PKCS12 container for authentication");
}
}
private void disableClientAuth() {
this.tlsConfiguration.setKeyManagers(null);
this.callbackContext.success();
}
@Override
public void alias(final String alias) {
try {
if (alias == null) {
throw new Exception("Couldn't get a consent for private key access");
}
PrivateKey key = KeyChain.getPrivateKey(this.context, alias);
X509Certificate[] chain = KeyChain.getCertificateChain(this.context, alias);
KeyManager keyManager = new KeyChainKeyManager(alias, key, chain);
this.tlsConfiguration.setKeyManagers(new KeyManager[] { keyManager });
this.callbackContext.success(alias);
} catch (Exception e) {
Log.e(TAG, "Couldn't load private key and certificate pair with given alias \"" + alias + "\" for authentication",
e);
this.callbackContext.error(
"Couldn't load private key and certificate pair with given alias \"" + alias + "\" for authentication");
}
}
}

View File

@@ -0,0 +1,236 @@
package com.silkimen.cordovahttp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLException;
import com.silkimen.http.HttpBodyDecoder;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.HttpRequest.HttpRequestException;
import com.silkimen.http.JsonUtils;
import com.silkimen.http.TLSConfiguration;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Base64;
import android.util.Log;
abstract class CordovaHttpBase implements Runnable {
protected static final String TAG = "Cordova-Plugin-HTTP";
protected String method;
protected String url;
protected String serializer = "none";
protected String responseType;
protected Object data;
protected JSONObject headers;
protected int connectTimeout;
protected int readTimeout;
protected boolean followRedirects;
protected TLSConfiguration tlsConfiguration;
protected CordovaObservableCallbackContext callbackContext;
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int connectTimeout,
int readTimeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
CordovaObservableCallbackContext callbackContext) {
this.method = method;
this.url = url;
this.serializer = serializer;
this.data = data;
this.headers = headers;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.followRedirects = followRedirects;
this.responseType = responseType;
this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
public CordovaHttpBase(String method, String url, JSONObject headers, int connectTimeout, int readTimeout, boolean followRedirects,
String responseType, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
this.method = method;
this.url = url;
this.headers = headers;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.followRedirects = followRedirects;
this.responseType = responseType;
this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
@Override
public void run() {
CordovaHttpResponse response = new CordovaHttpResponse();
HttpRequest request = null;
try {
request = this.createRequest();
this.prepareRequest(request);
this.sendBody(request);
this.processResponse(request, response);
request.disconnect();
} catch (HttpRequestException e) {
Throwable cause = e.getCause();
String message = cause.getMessage();
if (cause instanceof SSLException) {
response.setStatus(-2);
response.setErrorMessage("TLS connection could not be established: " + e.getMessage());
Log.w(TAG, "TLS connection could not be established", e);
} else if (cause instanceof UnknownHostException) {
response.setStatus(-3);
response.setErrorMessage("Host could not be resolved: " + e.getMessage());
Log.w(TAG, "Host could not be resolved", e);
} else if (cause instanceof SocketTimeoutException) {
response.setStatus(-4);
response.setErrorMessage("Request timed out: " + e.getMessage());
Log.w(TAG, "Request timed out", e);
} else if (cause instanceof InterruptedIOException && "thread interrupted".equals(message.toLowerCase())) {
this.setAborted(request, response);
} else {
response.setStatus(-1);
response.setErrorMessage("There was an error with the request: " + message);
Log.w(TAG, "Generic request error", e);
}
} catch (InterruptedException ie) {
this.setAborted(request, response);
} catch (Exception e) {
response.setStatus(-1);
response.setErrorMessage(e.getMessage());
Log.e(TAG, "An unexpected error occured", e);
}
try {
if (response.hasFailed()) {
this.callbackContext.error(response.toJSON());
} else {
this.callbackContext.success(response.toJSON());
}
} catch (JSONException e) {
Log.e(TAG, "An unexpected error occured while creating HTTP response object", e);
}
}
protected HttpRequest createRequest() throws JSONException {
return new HttpRequest(this.url, this.method);
}
protected void prepareRequest(HttpRequest request) throws JSONException, IOException {
request.followRedirects(this.followRedirects);
request.connectTimeout(this.connectTimeout);
request.readTimeout(this.readTimeout);
request.uncompress(true);
if (this.tlsConfiguration.getHostnameVerifier() != null) {
request.setHostnameVerifier(this.tlsConfiguration.getHostnameVerifier());
}
request.setSSLSocketFactory(this.tlsConfiguration.getTLSSocketFactory());
// setup content type before applying headers, so user can override it
this.setContentType(request);
request.headers(JsonUtils.getStringMap(this.headers));
}
protected void setContentType(HttpRequest request) {
if ("json".equals(this.serializer)) {
request.contentType("application/json", "UTF-8");
} else if ("utf8".equals(this.serializer)) {
request.contentType("text/plain", "UTF-8");
} else if ("raw".equals(this.serializer)) {
request.contentType("application/octet-stream");
} else if ("urlencoded".equals(this.serializer)) {
// intentionally left blank, because content type is set in HttpRequest.form()
} else if ("multipart".equals(this.serializer)) {
// intentionally left blank, because content type is set in HttpRequest.part()
}
}
protected void sendBody(HttpRequest request) throws Exception {
if (this.data == null) {
return;
}
if ("json".equals(this.serializer)) {
request.send(this.data.toString());
} else if ("utf8".equals(this.serializer)) {
request.send(((JSONObject) this.data).getString("text"));
} else if ("raw".equals(this.serializer)) {
request.send(Base64.decode((String)this.data, Base64.DEFAULT));
} else if ("urlencoded".equals(this.serializer)) {
request.form(JsonUtils.getObjectMap((JSONObject) this.data));
} else if ("multipart".equals(this.serializer)) {
JSONArray buffers = ((JSONObject) this.data).getJSONArray("buffers");
JSONArray names = ((JSONObject) this.data).getJSONArray("names");
JSONArray fileNames = ((JSONObject) this.data).getJSONArray("fileNames");
JSONArray types = ((JSONObject) this.data).getJSONArray("types");
for (int i = 0; i < buffers.length(); ++i) {
byte[] bytes = Base64.decode(buffers.getString(i), Base64.DEFAULT);
String name = names.getString(i);
if (fileNames.isNull(i)) {
request.part(name, new String(bytes, "UTF-8"));
} else {
request.part(name, fileNames.getString(i), types.getString(i), new ByteArrayInputStream(bytes));
}
}
// prevent sending malformed empty multipart requests (#372)
if (buffers.length() == 0) {
request.contentType("multipart/form-data; boundary=00content0boundary00");
request.send("\r\n--00content0boundary00--\r\n");
}
}
}
protected void processResponse(HttpRequest request, CordovaHttpResponse response) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
request.receive(outputStream);
response.setStatus(request.code());
response.setUrl(request.url().toString());
response.setHeaders(request.headers());
if (request.code() >= 200 && request.code() < 300) {
if ("text".equals(this.responseType) || "json".equals(this.responseType)) {
String decoded = HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset());
response.setBody(decoded);
} else {
response.setData(outputStream.toByteArray());
}
} else {
response.setErrorMessage(HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset()));
}
}
protected void setAborted(HttpRequest request, CordovaHttpResponse response) {
response.setStatus(-8);
response.setErrorMessage("Request was aborted");
if (request != null) {
try {
request.disconnect();
} catch(Exception any){
Log.w(TAG, "Failed to close aborted request", any);
}
}
Log.i(TAG, "Request was aborted");
}
}

View File

@@ -0,0 +1,41 @@
package com.silkimen.cordovahttp;
import java.io.File;
import java.net.URI;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.file.FileUtils;
import org.json.JSONObject;
class CordovaHttpDownload extends CordovaHttpBase {
private String filePath;
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int connectTimeout, int readTimeout,
boolean followRedirects, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
super("GET", url, headers, connectTimeout, readTimeout, followRedirects, "text", tlsConfiguration, callbackContext);
this.filePath = filePath;
}
@Override
protected void processResponse(HttpRequest request, CordovaHttpResponse response) throws Exception {
response.setStatus(request.code());
response.setUrl(request.url().toString());
response.setHeaders(request.headers());
if (request.code() >= 200 && request.code() < 300) {
File file = new File(new URI(this.filePath));
JSONObject fileEntry = FileUtils.getFilePlugin().getEntryForFile(file);
request.receive(file);
response.setFileEntry(fileEntry);
} else {
response.setErrorMessage("There was an error downloading the file");
}
}
}

View File

@@ -0,0 +1,24 @@
package com.silkimen.cordovahttp;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.TLSConfiguration;
import org.json.JSONObject;
class CordovaHttpOperation extends CordovaHttpBase {
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
int connectTimeout, int readTimeout, boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
CordovaObservableCallbackContext callbackContext) {
super(method, url, serializer, data, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration,
callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject headers, int connectTimeout, int readTimeout, boolean followRedirects,
String responseType, TLSConfiguration tlsConfiguration, CordovaObservableCallbackContext callbackContext) {
super(method, url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
}
}

View File

@@ -0,0 +1,273 @@
package com.silkimen.cordovahttp;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.Future;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.util.Base64;
import javax.net.ssl.TrustManagerFactory;
public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
private static final String TAG = "Cordova-Plugin-HTTP";
private TLSConfiguration tlsConfiguration;
private HashMap<Integer, Future<?>> reqMap;
private final Object reqMapLock = new Object();
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
this.tlsConfiguration = new TLSConfiguration();
this.reqMap = new HashMap<Integer, Future<?>>();
try {
KeyStore store = KeyStore.getInstance("AndroidCAStore");
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
store.load(null);
tmf.init(store);
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
if (this.preferences.contains("androidblacklistsecuresocketprotocols")) {
this.tlsConfiguration.setBlacklistedProtocols(
this.preferences.getString("androidblacklistsecuresocketprotocols", "").split(",")
);
}
} catch (Exception e) {
Log.e(TAG, "An error occured while loading system's CA certificates", e);
}
}
@Override
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext)
throws JSONException {
if (action == null) {
return false;
}
if ("setServerTrustMode".equals(action)) {
return this.setServerTrustMode(args, callbackContext);
} else if ("setClientAuthMode".equals(action)) {
return this.setClientAuthMode(args, callbackContext);
} else if ("abort".equals(action)) {
return this.abort(args, callbackContext);
}
if (!isNetworkAvailable()) {
CordovaHttpResponse response = new CordovaHttpResponse();
response.setStatus(-6);
response.setErrorMessage("No network connection available");
callbackContext.error(response.toJSON());
return true;
}
if ("get".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("head".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("delete".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("options".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("post".equals(action)) {
return this.executeHttpRequestWithData(action, args, callbackContext);
} else if ("put".equals(action)) {
return this.executeHttpRequestWithData(action, args, callbackContext);
} else if ("patch".equals(action)) {
return this.executeHttpRequestWithData(action, args, callbackContext);
} else if ("uploadFiles".equals(action)) {
return this.uploadFiles(args, callbackContext);
} else if ("downloadFile".equals(action)) {
return this.downloadFile(args, callbackContext);
} else {
return false;
}
}
private boolean executeHttpRequestWithoutData(final String method, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
int connectTimeout = args.getInt(2) * 1000;
int readTimeout = args.getInt(3) * 1000;
boolean followRedirect = args.getBoolean(4);
String responseType = args.getString(5);
Integer reqId = args.getInt(6);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, connectTimeout, readTimeout,
followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
startRequest(reqId, observableCallbackContext, request);
return true;
}
private boolean executeHttpRequestWithData(final String method, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
Object data = args.get(1);
String serializer = args.getString(2);
JSONObject headers = args.getJSONObject(3);
int connectTimeout = args.getInt(4) * 1000;
int readTimeout = args.getInt(5) * 1000;
boolean followRedirect = args.getBoolean(6);
String responseType = args.getString(7);
Integer reqId = args.getInt(8);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
connectTimeout, readTimeout, followRedirect, responseType, this.tlsConfiguration, observableCallbackContext);
startRequest(reqId, observableCallbackContext, request);
return true;
}
private boolean uploadFiles(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
JSONArray filePaths = args.getJSONArray(2);
JSONArray uploadNames = args.getJSONArray(3);
int connectTimeout = args.getInt(4) * 1000;
int readTimeout = args.getInt(5) * 1000;
boolean followRedirect = args.getBoolean(6);
String responseType = args.getString(7);
Integer reqId = args.getInt(8);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePaths, uploadNames, connectTimeout, readTimeout, followRedirect,
responseType, this.tlsConfiguration, this.cordova.getActivity().getApplicationContext(), observableCallbackContext);
startRequest(reqId, observableCallbackContext, upload);
return true;
}
private boolean downloadFile(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
String filePath = args.getString(2);
int connectTimeout = args.getInt(3) * 1000;
int readTimeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
Integer reqId = args.getInt(6);
CordovaObservableCallbackContext observableCallbackContext = new CordovaObservableCallbackContext(callbackContext, reqId);
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, connectTimeout, readTimeout,
followRedirect, this.tlsConfiguration, observableCallbackContext);
startRequest(reqId, observableCallbackContext, download);
return true;
}
private void startRequest(Integer reqId, CordovaObservableCallbackContext observableCallbackContext, CordovaHttpBase request) {
synchronized (reqMapLock) {
observableCallbackContext.setObserver(this);
Future<?> task = cordova.getThreadPool().submit(request);
this.addReq(reqId, task, observableCallbackContext);
}
}
private boolean setServerTrustMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
CordovaServerTrust runnable = new CordovaServerTrust(args.getString(0), this.cordova.getActivity(),
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(runnable);
return true;
}
private boolean setClientAuthMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
byte[] pkcs = args.isNull(2) ? null : Base64.decode(args.getString(2), Base64.DEFAULT);
CordovaClientAuth runnable = new CordovaClientAuth(args.getString(0), args.isNull(1) ? null : args.getString(1),
pkcs, args.getString(3), this.cordova.getActivity(), this.cordova.getActivity().getApplicationContext(),
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(runnable);
return true;
}
private boolean abort(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
int reqId = args.getInt(0);
boolean result = false;
// NOTE no synchronized (reqMapLock), since even if the req was already removed from reqMap,
// the worst that would happen calling task.cancel(true) is a result of false
// (i.e. same result as locking & not finding the req in reqMap)
Future<?> task = this.reqMap.get(reqId);
if (task != null && !task.isDone()) {
result = task.cancel(true);
}
callbackContext.success(new JSONObject().put("aborted", result));
return true;
}
private void addReq(final Integer reqId, final Future<?> task, final CordovaObservableCallbackContext observableCallbackContext) {
synchronized (reqMapLock) {
if (!task.isDone()){
this.reqMap.put(reqId, task);
}
}
}
private void removeReq(final Integer reqId) {
synchronized (reqMapLock) {
this.reqMap.remove(reqId);
}
}
@Override
public void update(Observable o, Object arg) {
synchronized (reqMapLock) {
CordovaObservableCallbackContext c = (CordovaObservableCallbackContext) arg;
if (c.getCallbackContext().isFinished()) {
removeReq(c.getRequestId());
}
}
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) cordova.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
}

View File

@@ -0,0 +1,100 @@
package com.silkimen.cordovahttp;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import android.text.TextUtils;
import android.util.Log;
import android.util.Base64;
class CordovaHttpResponse {
private int status;
private String url;
private Map<String, List<String>> headers;
private String body;
private byte[] rawData;
private JSONObject fileEntry;
private boolean hasFailed;
private boolean isFileOperation;
private boolean isRawResponse;
private String error;
public void setStatus(int status) {
this.status = status;
}
public void setUrl(String url) {
this.url = url;
}
public void setHeaders(Map<String, List<String>> headers) {
this.headers = headers;
}
public void setBody(String body) {
this.body = body;
}
public void setData(byte[] rawData) {
this.isRawResponse = true;
this.rawData = rawData;
}
public void setFileEntry(JSONObject entry) {
this.isFileOperation = true;
this.fileEntry = entry;
}
public void setErrorMessage(String message) {
this.hasFailed = true;
this.error = message;
}
public boolean hasFailed() {
return this.hasFailed;
}
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
json.put("status", this.status);
json.put("url", this.url);
if (this.headers != null && !this.headers.isEmpty()) {
json.put("headers", new JSONObject(getFilteredHeaders()));
}
if (this.hasFailed) {
json.put("error", this.error);
} else if (this.isFileOperation) {
json.put("file", this.fileEntry);
} else if (this.isRawResponse) {
json.put("data", Base64.encodeToString(this.rawData, Base64.DEFAULT));
} else {
json.put("data", this.body);
}
return json;
}
private Map<String, String> getFilteredHeaders() throws JSONException {
Map<String, String> filteredHeaders = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
String key = entry.getKey();
List<String> value = entry.getValue();
if ((key != null) && (!value.isEmpty())) {
filteredHeaders.put(key.toLowerCase(), TextUtils.join(", ", value));
}
}
return filteredHeaders;
}
}

View File

@@ -0,0 +1,91 @@
package com.silkimen.cordovahttp;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.TLSConfiguration;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import org.json.JSONArray;
import org.json.JSONObject;
class CordovaHttpUpload extends CordovaHttpBase {
private JSONArray filePaths;
private JSONArray uploadNames;
private Context applicationContext;
public CordovaHttpUpload(String url, JSONObject headers, JSONArray filePaths, JSONArray uploadNames, int connectTimeout, int readTimeout,
boolean followRedirects, String responseType, TLSConfiguration tlsConfiguration,
Context applicationContext, CordovaObservableCallbackContext callbackContext) {
super("POST", url, headers, connectTimeout, readTimeout, followRedirects, responseType, tlsConfiguration, callbackContext);
this.filePaths = filePaths;
this.uploadNames = uploadNames;
this.applicationContext = applicationContext;
}
@Override
protected void sendBody(HttpRequest request) throws Exception {
for (int i = 0; i < this.filePaths.length(); ++i) {
String uploadName = this.uploadNames.getString(i);
String filePath = this.filePaths.getString(i);
Uri fileUri = Uri.parse(filePath);
// File Scheme
if (ContentResolver.SCHEME_FILE.equals(fileUri.getScheme())) {
File file = new File(new URI(filePath));
String fileName = file.getName().trim();
String mimeType = this.getMimeTypeFromFileName(fileName);
request.part(uploadName, fileName, mimeType, file);
}
// Content Scheme
if (ContentResolver.SCHEME_CONTENT.equals(fileUri.getScheme())) {
InputStream inputStream = this.applicationContext.getContentResolver().openInputStream(fileUri);
String fileName = this.getFileNameFromContentScheme(fileUri, this.applicationContext).trim();
String mimeType = this.getMimeTypeFromFileName(fileName);
request.part(uploadName, fileName, mimeType, inputStream);
}
}
}
private String getFileNameFromContentScheme(Uri contentSchemeUri, Context applicationContext) {
Cursor returnCursor = applicationContext.getContentResolver().query(contentSchemeUri, null, null, null, null);
if (returnCursor == null || !returnCursor.moveToFirst()) {
return null;
}
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
String fileName = returnCursor.getString(nameIndex);
returnCursor.close();
return fileName;
}
private String getMimeTypeFromFileName(String fileName) {
if (fileName == null || !fileName.contains(".")) {
return null;
}
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
int extIndex = fileName.lastIndexOf('.') + 1;
String extension = fileName.substring(extIndex).toLowerCase();
return mimeTypeMap.getMimeTypeFromExtension(extension);
}
}

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

@@ -0,0 +1,132 @@
package com.silkimen.cordovahttp;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import android.app.Activity;
import android.util.Log;
import android.content.res.AssetManager;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
class CordovaServerTrust implements Runnable {
private static final String TAG = "Cordova-Plugin-HTTP";
private final TrustManager[] noOpTrustManagers;
private final HostnameVerifier noOpVerifier;
private String mode;
private Activity activity;
private TLSConfiguration tlsConfiguration;
private CallbackContext callbackContext;
public CordovaServerTrust(final String mode, final Activity activity, final TLSConfiguration configContainer,
final CallbackContext callbackContext) {
this.mode = mode;
this.activity = activity;
this.tlsConfiguration = configContainer;
this.callbackContext = callbackContext;
this.noOpTrustManagers = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] chain, String authType) {
// intentionally left blank
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
// intentionally left blank
}
} };
this.noOpVerifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
}
@Override
public void run() {
try {
if ("legacy".equals(this.mode)) {
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(null);
} else if ("nocheck".equals(this.mode)) {
this.tlsConfiguration.setHostnameVerifier(this.noOpVerifier);
this.tlsConfiguration.setTrustManagers(this.noOpTrustManagers);
} else if ("pinned".equals(this.mode)) {
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromBundle(getWebAssetDir() + "/certificates")));
} else {
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(this.getTrustManagers(this.getCertsFromKeyStore("AndroidCAStore")));
}
callbackContext.success();
} catch (Exception e) {
Log.e(TAG, "An error occured while configuring SSL cert mode", e);
callbackContext.error("An error occured while configuring SSL cert mode");
}
}
private String getWebAssetDir() {
return isRunningOnCapacitor()? "public" : "www";
}
private boolean isRunningOnCapacitor() {
return this.activity.getClass().getSuperclass().getName().contains("com.getcapacitor");
}
private TrustManager[] getTrustManagers(KeyStore store) throws GeneralSecurityException {
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(store);
return tmf.getTrustManagers();
}
private KeyStore getCertsFromBundle(String path) throws GeneralSecurityException, IOException {
AssetManager assetManager = this.activity.getAssets();
String[] files = assetManager.list(path);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
for (int i = 0; i < files.length; i++) {
int index = files[i].lastIndexOf('.');
if (index == -1 || !files[i].substring(index).equals(".cer")) {
continue;
}
keyStore.setCertificateEntry("CA" + i, cf.generateCertificate(assetManager.open(path + "/" + files[i])));
}
return keyStore;
}
private KeyStore getCertsFromKeyStore(String storeType) throws GeneralSecurityException, IOException {
KeyStore store = KeyStore.getInstance(storeType);
store.load(null);
return store;
}
}

View File

@@ -0,0 +1,55 @@
package com.silkimen.http;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.MalformedInputException;
public class HttpBodyDecoder {
private static final String[] ACCEPTED_CHARSETS = new String[] { "UTF-8", "ISO-8859-1" };
public static String decodeBody(byte[] body, String charsetName)
throws CharacterCodingException, MalformedInputException {
return decodeBody(ByteBuffer.wrap(body), charsetName);
}
public static String decodeBody(ByteBuffer body, String charsetName)
throws CharacterCodingException, MalformedInputException {
if (charsetName == null) {
return tryDecodeByteBuffer(body);
} else {
return decodeByteBuffer(body, charsetName);
}
}
private static String tryDecodeByteBuffer(ByteBuffer buffer)
throws CharacterCodingException, MalformedInputException {
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
try {
return decodeByteBuffer(buffer, ACCEPTED_CHARSETS[i]);
} catch (MalformedInputException e) {
continue;
} catch (CharacterCodingException e) {
continue;
}
}
return decodeBody(buffer, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
}
private static String decodeByteBuffer(ByteBuffer buffer, String charsetName)
throws CharacterCodingException, MalformedInputException {
return createCharsetDecoder(charsetName).decode(buffer).toString();
}
private static CharsetDecoder createCharsetDecoder(String charsetName) {
return Charset.forName(charsetName).newDecoder().onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
}

View File

@@ -0,0 +1,58 @@
package com.silkimen.http;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class JsonUtils {
public static HashMap<String, String> getStringMap(JSONObject object) throws JSONException {
HashMap<String, String> map = new HashMap<String, String>();
if (object == null) {
return map;
}
Iterator<?> i = object.keys();
while (i.hasNext()) {
String key = (String) i.next();
map.put(key, object.getString(key));
}
return map;
}
public static HashMap<String, Object> getObjectMap(JSONObject object) throws JSONException {
HashMap<String, Object> map = new HashMap<String, Object>();
if (object == null) {
return map;
}
Iterator<?> i = object.keys();
while (i.hasNext()) {
String key = (String) i.next();
Object value = object.get(key);
if (value instanceof JSONArray) {
map.put(key, getObjectList((JSONArray) value));
} else {
map.put(key, object.get(key));
}
}
return map;
}
public static ArrayList<Object> getObjectList(JSONArray array) throws JSONException {
ArrayList<Object> list = new ArrayList<Object>();
for (int i = 0; i < array.length(); i++) {
list.add(array.get(i));
}
return list;
}
}

View File

@@ -0,0 +1,57 @@
package com.silkimen.http;
import android.content.Context;
import android.security.KeyChain;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509ExtendedKeyManager;
public class KeyChainKeyManager extends X509ExtendedKeyManager {
private final String alias;
private final X509Certificate[] chain;
private final PrivateKey key;
public KeyChainKeyManager(String alias, PrivateKey key, X509Certificate[] chain) {
this.alias = alias;
this.key = key;
this.chain = chain;
}
@Override
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
return this.alias;
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
return chain;
}
@Override
public PrivateKey getPrivateKey(String alias) {
return key;
}
@Override
public final String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
@Override
public final String[] getClientAliases(String keyType, Principal[] issuers) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
@Override
public final String[] getServerAliases(String keyType, Principal[] issuers) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,64 @@
package com.silkimen.http;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import com.silkimen.http.TLSSocketFactory;
public class TLSConfiguration {
private TrustManager[] trustManagers = null;
private KeyManager[] keyManagers = null;
private HostnameVerifier hostnameVerifier = null;
private String[] blacklistedProtocols = {};
private SSLSocketFactory socketFactory;
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
}
public void setKeyManagers(KeyManager[] keyManagers) {
this.keyManagers = keyManagers;
this.socketFactory = null;
}
public void setTrustManagers(TrustManager[] trustManagers) {
this.trustManagers = trustManagers;
this.socketFactory = null;
}
public void setBlacklistedProtocols(String[] protocols) {
this.blacklistedProtocols = protocols;
this.socketFactory = null;
}
public HostnameVerifier getHostnameVerifier() {
return this.hostnameVerifier;
}
public SSLSocketFactory getTLSSocketFactory() throws IOException {
if (this.socketFactory != null) {
return this.socketFactory;
}
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
this.socketFactory = new TLSSocketFactory(context, this.blacklistedProtocols);
return this.socketFactory;
} catch (GeneralSecurityException e) {
IOException ioException = new IOException("Security exception occured while configuring TLS context");
ioException.initCause(e);
throw ioException;
}
}
}

View File

@@ -0,0 +1,84 @@
package com.silkimen.http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
private List<String> blacklistedProtocols;
public TLSSocketFactory(SSLContext context, String[] blacklistedProtocols) {
this.delegate = context.getSocketFactory();
this.blacklistedProtocols = new ArrayList();
for (int i = 0; i < blacklistedProtocols.length; ++i) {
this.blacklistedProtocols.add(blacklistedProtocols[i].trim());
}
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(delegate.createSocket(socket, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if (socket == null || !(socket instanceof SSLSocket)) {
return socket;
}
String[] supported = ((SSLSocket) socket).getSupportedProtocols();
List<String> filtered = new ArrayList();
for (int i = 0; i < supported.length; ++i) {
if (!this.blacklistedProtocols.contains(supported[i])) {
filtered.add(supported[i]);
}
}
((SSLSocket) socket).setEnabledProtocols(filtered.toArray(new String[0]));
return socket;
}
}

View File

@@ -1,106 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.HostnameVerifier;
import java.util.Iterator;
import android.util.Log;
import com.github.kevinsawicki.http.HttpRequest;
public abstract class CordovaHttp {
protected static final String TAG = "CordovaHTTP";
protected static final String CHARSET = "UTF-8";
private static AtomicBoolean sslPinning = new AtomicBoolean(false);
private static AtomicBoolean acceptAllCerts = new AtomicBoolean(false);
private String urlString;
private Map<?, ?> params;
private Map<String, String> headers;
private CallbackContext callbackContext;
public CordovaHttp(String urlString, Map<?, ?> params, Map<String, String> headers, CallbackContext callbackContext) {
this.urlString = urlString;
this.params = params;
this.headers = headers;
this.callbackContext = callbackContext;
}
public static void enableSSLPinning(boolean enable) {
sslPinning.set(enable);
if (enable) {
acceptAllCerts.set(false);
}
}
public static void acceptAllCerts(boolean accept) {
acceptAllCerts.set(accept);
if (accept) {
sslPinning.set(false);
}
}
protected String getUrlString() {
return this.urlString;
}
protected Map<?, ?> getParams() {
return this.params;
}
protected Map<String, String> getHeaders() {
return this.headers;
}
protected CallbackContext getCallbackContext() {
return this.callbackContext;
}
protected HttpRequest setupSecurity(HttpRequest request) {
if (acceptAllCerts.get()) {
request.trustAllCerts();
request.trustAllHosts();
}
if (sslPinning.get()) {
request.pinToCerts();
}
return request;
}
protected void respondWithError(int status, String msg) {
try {
JSONObject response = new JSONObject();
response.put("status", status);
response.put("error", msg);
this.callbackContext.error(response);
} catch (JSONException e) {
this.callbackContext.error(msg);
}
}
protected void respondWithError(String msg) {
this.respondWithError(500, msg);
}
}

View File

@@ -1,65 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset;
import android.util.Log;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
import java.io.File;
import java.net.UnknownHostException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.file.FileUtils;
import org.json.JSONException;
import org.json.JSONObject;
public class CordovaHttpDownload extends CordovaHttp implements Runnable {
private String filePath;
public CordovaHttpDownload(String urlString, Map<?, ?> params, Map<String, String> headers, CallbackContext callbackContext, String filePath) {
super(urlString, params, headers, callbackContext);
this.filePath = filePath;
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.get(this.getUrlString(), this.getParams(), true);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeaders());
int code = request.code();
JSONObject response = new JSONObject();
response.put("status", code);
if (code >= 200 && code < 300) {
URI uri = new URI(filePath);
File file = new File(uri);
request.receive(file);
JSONObject fileEntry = FileUtils.getEntry(file);
response.put("file", fileEntry);
this.getCallbackContext().success(response);
} else {
response.put("error", "There was an error downloading the file");
this.getCallbackContext().error(response);
}
} catch(URISyntaxException e) {
this.respondWithError("There was an error with the given filePath");
} catch (JSONException e) {
this.respondWithError("There was an error generating the response");
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,59 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.HostnameVerifier;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
public class CordovaHttpGet extends CordovaHttp implements Runnable {
public CordovaHttpGet(String urlString, Map<?, ?> params, Map<String, String> headers, CallbackContext callbackContext) {
super(urlString, params, headers, callbackContext);
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.get(this.getUrlString(), this.getParams(), true);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeaders());
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
response.put("status", code);
if (code >= 200 && code < 300) {
response.put("data", body);
this.getCallbackContext().success(response);
} else {
response.put("error", body);
this.getCallbackContext().error(response);
}
} catch (JSONException e) {
this.respondWithError("There was an error generating the response");
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,173 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.HashMap;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.HostnameVerifier;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.res.AssetManager;
import android.util.Base64;
import android.util.Log;
import com.github.kevinsawicki.http.HttpRequest;
public class CordovaHttpPlugin extends CordovaPlugin {
private static final String TAG = "CordovaHTTP";
private HashMap<String, String> globalHeaders;
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
this.globalHeaders = new HashMap<String, String>();
}
@Override
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals("get")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
HashMap<?, ?> paramsMap = this.getMapFromJSONObject(params);
HashMap<String, String> headersMap = this.addToMap(this.globalHeaders, headers);
CordovaHttpGet get = new CordovaHttpGet(urlString, paramsMap, headersMap, callbackContext);
cordova.getThreadPool().execute(get);
} else if (action.equals("post")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
HashMap<?, ?> paramsMap = this.getMapFromJSONObject(params);
HashMap<String, String> headersMap = this.addToMap(this.globalHeaders, headers);
CordovaHttpPost post = new CordovaHttpPost(urlString, paramsMap, headersMap, callbackContext);
cordova.getThreadPool().execute(post);
} else if (action.equals("useBasicAuth")) {
String username = args.getString(0);
String password = args.getString(1);
this.useBasicAuth(username, password);
callbackContext.success();
} else if (action.equals("enableSSLPinning")) {
try {
boolean enable = args.getBoolean(0);
this.enableSSLPinning(enable);
callbackContext.success();
} catch(Exception e) {
e.printStackTrace();
callbackContext.error("There was an error setting up ssl pinning");
}
} else if (action.equals("acceptAllCerts")) {
boolean accept = args.getBoolean(0);
CordovaHttp.acceptAllCerts(accept);
} else if (action.equals("setHeader")) {
String header = args.getString(0);
String value = args.getString(1);
this.setHeader(header, value);
callbackContext.success();
} else if (action.equals("uploadFile")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
HashMap<?, ?> paramsMap = this.getMapFromJSONObject(params);
HashMap<String, String> headersMap = this.addToMap(this.globalHeaders, headers);
String filePath = args.getString(3);
String name = args.getString(4);
CordovaHttpUpload upload = new CordovaHttpUpload(urlString, paramsMap, headersMap, callbackContext, filePath, name);
cordova.getThreadPool().execute(upload);
} else if (action.equals("downloadFile")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
HashMap<?, ?> paramsMap = this.getMapFromJSONObject(params);
HashMap<String, String> headersMap = this.addToMap(this.globalHeaders, headers);
String filePath = args.getString(3);
CordovaHttpDownload download = new CordovaHttpDownload(urlString, paramsMap, headersMap, callbackContext, filePath);
cordova.getThreadPool().execute(download);
} else {
return false;
}
return true;
}
private void useBasicAuth(String username, String password) {
String loginInfo = username + ":" + password;
loginInfo = "Basic " + Base64.encodeToString(loginInfo.getBytes(), Base64.NO_WRAP);
this.globalHeaders.put("Authorization", loginInfo);
}
private void setHeader(String header, String value) {
this.globalHeaders.put(header, value);
}
private void enableSSLPinning(boolean enable) throws GeneralSecurityException, IOException {
if (enable) {
AssetManager assetManager = cordova.getActivity().getAssets();
String[] files = assetManager.list("");
int index;
ArrayList<String> cerFiles = new ArrayList<String>();
for (int i = 0; i < files.length; i++) {
index = files[i].lastIndexOf('.');
if (index != -1) {
if (files[i].substring(index).equals(".cer")) {
cerFiles.add(files[i]);
}
}
}
for (int i = 0; i < cerFiles.size(); i++) {
InputStream in = cordova.getActivity().getAssets().open(cerFiles.get(i));
InputStream caInput = new BufferedInputStream(in);
HttpRequest.addCert(caInput);
}
CordovaHttp.enableSSLPinning(true);
} else {
CordovaHttp.enableSSLPinning(false);
}
}
private HashMap<String, String> addToMap(HashMap<String, String> map, JSONObject object) throws JSONException {
HashMap<String, String> newMap = (HashMap<String, String>)map.clone();
Iterator<?> i = object.keys();
while (i.hasNext()) {
String key = (String)i.next();
newMap.put(key, object.getString(key));
}
return newMap;
}
private HashMap<String, Object> getMapFromJSONObject(JSONObject object) throws JSONException {
HashMap<String, Object> map = new HashMap<String, Object>();
Iterator<?> i = object.keys();
while(i.hasNext()) {
String key = (String)i.next();
map.put(key, object.get(key));
}
return map;
}
}

View File

@@ -1,52 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset;
import java.net.UnknownHostException;
import java.util.Map;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
public class CordovaHttpPost extends CordovaHttp implements Runnable {
public CordovaHttpPost(String urlString, Map<?, ?> params, Map<String, String> headers, CallbackContext callbackContext) {
super(urlString, params, headers, callbackContext);
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.post(this.getUrlString());
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeaders());
request.form(this.getParams());
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
response.put("status", code);
if (code >= 200 && code < 300) {
response.put("data", body);
this.getCallbackContext().success(response);
} else {
response.put("error", body);
this.getCallbackContext().error(response);
}
} catch (JSONException e) {
this.respondWithError("There was an error generating the response");
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,91 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset;
import java.io.File;
import java.net.UnknownHostException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import android.webkit.MimeTypeMap;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
public class CordovaHttpUpload extends CordovaHttp implements Runnable {
private String filePath;
private String name;
public CordovaHttpUpload(String urlString, Map<?, ?> params, Map<String, String> headers, CallbackContext callbackContext, String filePath, String name) {
super(urlString, params, headers, callbackContext);
this.filePath = filePath;
this.name = name;
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.post(this.getUrlString());
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeaders());
URI uri = new URI(filePath);
int index = filePath.lastIndexOf('/');
String filename = filePath.substring(index + 1);
index = filePath.lastIndexOf('.');
String ext = filePath.substring(index + 1);
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeType = mimeTypeMap.getMimeTypeFromExtension(ext);
request.part(this.name, filename, mimeType, new File(uri));
Set<?> set = (Set<?>)this.getParams().entrySet();
Iterator<?> i = set.iterator();
while (i.hasNext()) {
Entry<?, ?> e = (Entry<?, ?>)i.next();
String key = (String)e.getKey();
Object value = e.getValue();
if (value instanceof Number) {
request.part(key, (Number)value);
} else if (value instanceof String) {
request.part(key, (String)value);
} else {
this.respondWithError("All parameters must be Numbers or Strings");
return;
}
}
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
response.put("status", code);
if (code >= 200 && code < 300) {
response.put("data", body);
this.getCallbackContext().success(response);
} else {
response.put("error", body);
this.getCallbackContext().error(response);
}
} catch (URISyntaxException e) {
this.respondWithError("There was an error loading the file");
} catch (JSONException e) {
this.respondWithError("There was an error generating the response");
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

344
src/browser/cordova-http-plugin.js vendored Normal file
View File

@@ -0,0 +1,344 @@
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);
} catch (err) {
return null;
}
}
function serializePrimitive(key, value) {
if (value === null || value === undefined) {
return encodeURIComponent(key) + '=';
}
return encodeURIComponent(key) + '=' + encodeURIComponent(value);
}
function serializeArray(key, values) {
return values.map(function (value) {
return encodeURIComponent(key) + '[]=' + encodeURIComponent(value);
}).join('&');
}
function serializeParams(params) {
if (params === null) return '';
return Object.keys(params).map(function (key) {
if (jsUtil.getTypeOf(params[key]) === 'Array') {
return serializeArray(key, params[key]);
}
return serializePrimitive(key, params[key]);
}).join('&');
}
function decodeB64(dataString) {
var binaryString = atob(dataString);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; ++i) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
function processMultipartData(data) {
if (!data) return null;
var fd = new FormData();
for (var i = 0; i < data.buffers.length; ++i) {
var buffer = data.buffers[i];
var name = data.names[i];
var fileName = data.fileNames[i];
var type = data.types[i];
if (fileName) {
fd.append(name, new Blob([decodeB64(buffer)], { type: type }), fileName);
} else {
// we assume it's plain text if no filename was given
fd.append(name, atob(buffer));
}
}
return fd;
}
function deserializeResponseHeaders(headers) {
var headerMap = {};
var arr = headers.trim().split(/[\r\n]+/);
arr.forEach(function (line) {
var parts = line.split(': ');
var header = parts.shift().toLowerCase();
var value = parts.join(': ');
headerMap[header] = value;
});
return headerMap;
}
function getResponseData(xhr) {
if (xhr.responseType !== 'text' || jsUtil.getTypeOf(xhr.responseText) !== 'String') {
return xhr.response;
}
return xhr.responseText;
}
function createXhrSuccessObject(xhr) {
return {
url: xhr.responseURL,
status: xhr.status,
data: getResponseData(xhr),
headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
};
}
function createXhrFailureObject(xhr) {
var obj = {};
obj.headers = xhr.getAllResponseHeaders();
obj.error = getResponseData(xhr);
obj.error = obj.error || 'advanced-http: please check browser console for error messages';
if (xhr.responseURL) obj.url = xhr.responseURL;
if (xhr.status) obj.status = xhr.status;
return obj;
}
function injectRequestIdHandler(reqId, cb) {
return function (response) {
delete reqMap[reqId];
cb(response);
}
}
function getHeaderValue(headers, headerName) {
let result = null;
Object.keys(headers).forEach(function (key) {
if (key.toLowerCase() === headerName.toLowerCase()) {
result = headers[key];
}
});
return result;
}
function setDefaultContentType(headers, contentType) {
if (getHeaderValue(headers, 'Content-Type') === null) {
headers['Content-Type'] = contentType;
}
}
function setHeaders(xhr, headers) {
Object.keys(headers).forEach(function (key) {
if (key.toLowerCase() === 'cookie') return;
xhr.setRequestHeader(key, headers[key]);
});
}
function sendRequest(method, withData, opts, success, failure) {
var data, serializer, headers, readTimeout, followRedirect, responseType, reqId;
var url = opts[0];
if (withData) {
data = opts[1];
serializer = opts[2];
headers = opts[3];
// connect timeout not applied
// connectTimeout = opts[4];
readTimeout = opts[5];
followRedirect = opts[6];
responseType = opts[7];
reqId = opts[8];
} else {
headers = opts[1];
// connect timeout not applied
// connectTimeout = opts[2];
readTimeout = opts[3];
followRedirect = opts[4];
responseType = opts[5];
reqId = opts[6];
}
var onSuccess = injectRequestIdHandler(reqId, success);
var onFail = injectRequestIdHandler(reqId, failure);
var processedData = null;
var xhr = new XMLHttpRequest();
reqMap[reqId] = xhr;
xhr.open(method, url);
if (headers.Cookie && headers.Cookie.length > 0) {
return onFail('advanced-http: custom cookies not supported on browser platform');
}
if (!followRedirect) {
return onFail('advanced-http: disabling follow redirect not supported on browser platform');
}
switch (serializer) {
case 'json': {
setDefaultContentType(headers, 'application/json; charset=utf8');
processedData = serializeJsonData(data);
if (processedData === null) {
return onFail('advanced-http: failed serializing data');
}
break;
}
case 'utf8': {
setDefaultContentType(headers, 'text/plain; charset=utf8');
processedData = data.text;
break;
}
case 'urlencoded': {
setDefaultContentType(headers, 'application/x-www-form-urlencoded');
processedData = serializeParams(data);
break;
}
case 'multipart': {
const contentType = getHeaderValue(headers, 'Content-Type');
// intentionally don't set a default content type
// it's set by the browser together with the content disposition string
if (contentType) {
headers['Content-Type'] = contentType;
}
processedData = processMultipartData(data);
break;
}
case 'raw': {
setDefaultContentType(headers, 'application/octet-stream');
processedData = data;
break;
}
}
// 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;
try {
setHeaders(xhr, headers);
} catch(error) {
return onFail({
status: -1,
error: error,
url: url,
headers: headers
});
}
xhr.onerror = function () {
return onFail(createXhrFailureObject(xhr));
};
xhr.onabort = function () {
return onFail({
status: -8,
error: 'Request was aborted',
url: url,
headers: {}
});
};
xhr.ontimeout = function () {
return onFail({
status: -4,
error: 'Request timed out',
url: url,
headers: {}
});
};
xhr.onload = function () {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status < 200 || xhr.status > 299) {
return onFail(createXhrFailureObject(xhr));
}
return onSuccess(createXhrSuccessObject(xhr));
};
xhr.send(processedData);
}
function abort(opts, success) {
var reqId = opts[0];
var result = false;
var xhr = reqMap[reqId];
if(xhr && xhr.readyState !== xhr.DONE){
xhr.abort();
result = true;
}
success({aborted: result});
}
var browserInterface = {
get: function (success, failure, opts) {
return sendRequest('get', false, opts, success, failure);
},
head: function (success, failure, opts) {
return sendRequest('head', false, opts, success, failure);
},
delete: function (success, failure, opts) {
return sendRequest('delete', false, opts, success, failure);
},
post: function (success, failure, opts) {
return sendRequest('post', true, opts, success, failure);
},
put: function (success, failure, opts) {
return sendRequest('put', true, opts, success, failure);
},
patch: function (success, failure, opts) {
return sendRequest('patch', true, opts, success, failure);
},
abort: function (success, failure, opts) {
return abort(opts, success, failure);
},
uploadFile: function (success, failure) {
return failure('advanced-http: function "uploadFile" not supported on browser platform');
},
uploadFiles: function (success, failure) {
return failure('advanced-http: function "uploadFiles" not supported on browser platform');
},
downloadFile: function (success, failure) {
return failure('advanced-http: function "downloadFile" not supported on browser platform');
},
setServerTrustMode: function (success, failure) {
return failure('advanced-http: function "setServerTrustMode" not supported on browser platform');
},
setClientAuthMode: function (success, failure) {
return failure('advanced-http: function "setClientAuthMode" not supported on browser platform');
}
};
module.exports = browserInterface;
cordovaProxy.add('CordovaHttpPlugin', browserInterface);

View File

@@ -1,68 +0,0 @@
// AFHTTPRequestOperation.h
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "AFURLConnectionOperation.h"
#import "AFURLResponseSerialization.h"
/**
`AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request.
*/
@interface AFHTTPRequestOperation : AFURLConnectionOperation
///------------------------------------------------
/// @name Getting HTTP URL Connection Information
///------------------------------------------------
/**
The last HTTP response received by the operation's connection.
*/
@property (readonly, nonatomic, strong) NSHTTPURLResponse *response;
/**
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 AFHTTPResponse serializer, which uses the raw data as its response object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed.
@warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value
*/
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
/**
An object constructed by the `responseSerializer` from the response and response data. Returns `nil` unless the operation `isFinished`, has a `response`, and has `responseData` with non-zero content length. If an error occurs during serialization, `nil` will be returned, and the `error` property will be populated with the serialization error.
*/
@property (readonly, nonatomic, strong) id responseObject;
///-----------------------------------------------------------
/// @name Setting Completion Block Success / Failure Callbacks
///-----------------------------------------------------------
/**
Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed.
This method should be overridden in subclasses in order to specify the response object passed into the success block.
@param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request.
@param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request.
*/
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
@end

View File

@@ -1,202 +0,0 @@
// AFHTTPRequestOperation.m
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFHTTPRequestOperation.h"
static dispatch_queue_t http_request_operation_processing_queue() {
static dispatch_queue_t af_http_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_http_request_operation_processing_queue;
}
static dispatch_group_t http_request_operation_completion_group() {
static dispatch_group_t af_http_request_operation_completion_group;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_http_request_operation_completion_group = dispatch_group_create();
});
return af_http_request_operation_completion_group;
}
#pragma mark -
@interface AFURLConnectionOperation ()
@property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSURLResponse *response;
@end
@interface AFHTTPRequestOperation ()
@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response;
@property (readwrite, nonatomic, strong) id responseObject;
@property (readwrite, nonatomic, strong) NSError *responseSerializationError;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
@end
@implementation AFHTTPRequestOperation
@dynamic lock;
- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
self = [super initWithRequest:urlRequest];
if (!self) {
return nil;
}
self.responseSerializer = [AFHTTPResponseSerializer serializer];
return self;
}
- (void)setResponseSerializer:(AFHTTPResponseSerializer <AFURLResponseSerialization> *)responseSerializer {
NSParameterAssert(responseSerializer);
[self.lock lock];
_responseSerializer = responseSerializer;
self.responseObject = nil;
self.responseSerializationError = nil;
[self.lock unlock];
}
- (id)responseObject {
[self.lock lock];
if (!_responseObject && [self isFinished] && !self.error) {
NSError *error = nil;
self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error];
if (error) {
self.responseSerializationError = error;
}
}
[self.lock unlock];
return _responseObject;
}
- (NSError *)error {
if (_responseSerializationError) {
return _responseSerializationError;
} else {
return [super error];
}
}
#pragma mark - AFHTTPRequestOperation
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^{
if (self.completionGroup) {
dispatch_group_enter(self.completionGroup);
}
dispatch_async(http_request_operation_processing_queue(), ^{
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
id responseObject = self.responseObject;
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
success(self, responseObject);
});
}
}
}
if (self.completionGroup) {
dispatch_group_leave(self.completionGroup);
}
});
};
#pragma clang diagnostic pop
}
#pragma mark - AFURLRequestOperation
- (void)pause {
u_int64_t offset = 0;
if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {
offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue];
} else {
offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length];
}
NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy];
if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) {
[mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"];
}
[mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"];
self.request = mutableURLRequest;
[super pause];
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
self.responseSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializer))];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFHTTPRequestOperation *operation = [[[self class] allocWithZone:zone] initWithRequest:self.request];
operation.responseSerializer = [self.responseSerializer copyWithZone:zone];
operation.completionQueue = self.completionQueue;
operation.completionGroup = self.completionGroup;
return operation;
}
@end

View File

@@ -1,294 +0,0 @@
// AFHTTPRequestOperationManager.h
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <Availability.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <CoreServices/CoreServices.h>
#endif
#import "AFHTTPRequestOperation.h"
#import "AFURLResponseSerialization.h"
#import "AFURLRequestSerialization.h"
#import "AFSecurityPolicy.h"
#import "AFNetworkReachabilityManager.h"
/**
`AFHTTPRequestOperationManager` encapsulates the common patterns of communicating with a web application over HTTP, including request creation, response serialization, network reachability monitoring, and security, as well as request operation management.
## 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.
For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect.
## Methods to Override
To change the behavior of all request operation construction for an `AFHTTPRequestOperationManager` subclass, override `HTTPRequestOperationWithRequest:success:failure`.
## 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>`.
Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `<AFURLResponseSerialization>`
## URL Construction Using Relative Paths
For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`.
Below are a few examples of how `baseURL` and relative paths interact:
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz
[NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo
[NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/
[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash.
## 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.
## NSCoding & NSCopying Caveats
`AFHTTPRequestOperationManager` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. There are a few minor caveats to keep in mind, however:
- Archives and copies of HTTP clients will be initialized with an empty operation queue.
- NSCoding cannot serialize / deserialize block properties, so an archive of an HTTP client will not include any reachability callback block that may be set.
*/
@interface AFHTTPRequestOperationManager : NSObject <NSCoding, NSCopying>
/**
The URL used to monitor reachability, and construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods.
*/
@property (readonly, nonatomic, strong) NSURL *baseURL;
/**
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
@warning `requestSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPRequestSerializer <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 a JSON serializer, which serializes data from responses with a `application/json` MIME type, and falls back to the raw data object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
/**
The operation queue on which request operations are scheduled and run.
*/
@property (nonatomic, strong) NSOperationQueue *operationQueue;
///-------------------------------
/// @name Managing URL Credentials
///-------------------------------
/**
Whether request operations should consult the credential storage for authenticating the connection. `YES` by default.
@see AFURLConnectionOperation -shouldUseCredentialStorage
*/
@property (nonatomic, assign) BOOL shouldUseCredentialStorage;
/**
The credential used by request operations for authentication challenges.
@see AFURLConnectionOperation -credential
*/
@property (nonatomic, strong) NSURLCredential *credential;
///-------------------------------
/// @name Managing Security Policy
///-------------------------------
/**
The security policy used by created request operations to evaluate server trust for secure connections. `AFHTTPRequestOperationManager` uses the `defaultPolicy` unless otherwise specified.
*/
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
///------------------------------------
/// @name Managing Network Reachability
///------------------------------------
/**
The network reachability manager. `AFHTTPRequestOperationManager` uses the `sharedManager` by default.
*/
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
///---------------------------------------------
/// @name Creating and Initializing HTTP Clients
///---------------------------------------------
/**
Creates and returns an `AFHTTPRequestOperationManager` object.
*/
+ (instancetype)manager;
/**
Initializes an `AFHTTPRequestOperationManager` object with the specified base URL.
This is the designated initializer.
@param url The base URL for the HTTP client.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(NSURL *)url;
///---------------------------------------
/// @name Managing HTTP Request Operations
///---------------------------------------
/**
Creates an `AFHTTPRequestOperation`, and sets the response serializers to that of the HTTP client.
@param request The request object to be loaded asynchronously during execution of the operation.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
*/
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
///---------------------------
/// @name Making HTTP Requests
///---------------------------
/**
Creates and runs an `AFHTTPRequestOperation` with a `GET` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates and runs an `AFHTTPRequestOperation` with a `HEAD` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single arguments: the request operation.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (AFHTTPRequestOperation *)HEAD:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates and runs an `AFHTTPRequestOperation` with a `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates and runs an `AFHTTPRequestOperation` with a multipart `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates and runs an `AFHTTPRequestOperation` with a `PUT` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (AFHTTPRequestOperation *)PUT:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates and runs an `AFHTTPRequestOperation` with a `PATCH` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (AFHTTPRequestOperation *)PATCH:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates and runs an `AFHTTPRequestOperation` with a `DELETE` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the request operation, and the response object created by the client response serializer.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the request operation and the error describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (AFHTTPRequestOperation *)DELETE:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
@end

View File

@@ -1,238 +0,0 @@
// AFHTTPRequestOperationManager.m
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "AFHTTPRequestOperationManager.h"
#import "AFHTTPRequestOperation.h"
#import <Availability.h>
#import <Security/Security.h>
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <UIKit/UIKit.h>
#endif
@interface AFHTTPRequestOperationManager ()
@property (readwrite, nonatomic, strong) NSURL *baseURL;
@end
@implementation AFHTTPRequestOperationManager
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
- (instancetype)init {
return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
self = [super init];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
self.operationQueue = [[NSOperationQueue alloc] init];
self.shouldUseCredentialStorage = YES;
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.operationQueue];
}
#pragma mark -
#ifdef _SYSTEMCONFIGURATION_H
#endif
- (void)setRequestSerializer:(AFHTTPRequestSerializer <AFURLRequestSerialization> *)requestSerializer {
NSParameterAssert(requestSerializer);
_requestSerializer = requestSerializer;
}
- (void)setResponseSerializer:(AFHTTPResponseSerializer <AFURLResponseSerialization> *)responseSerializer {
NSParameterAssert(responseSerializer);
_responseSerializer = responseSerializer;
}
#pragma mark -
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = self.responseSerializer;
operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage;
operation.credential = self.credential;
operation.securityPolicy = self.securityPolicy;
[operation setCompletionBlockWithSuccess:success failure:failure];
return operation;
}
#pragma mark -
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
- (AFHTTPRequestOperation *)HEAD:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *requestOperation, __unused id responseObject) {
if (success) {
success(requestOperation);
}
} failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
- (AFHTTPRequestOperation *)PUT:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PUT" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
- (AFHTTPRequestOperation *)PATCH:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PATCH" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
- (AFHTTPRequestOperation *)DELETE:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"DELETE" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))];
self = [self initWithBaseURL:baseURL];
if (!self) {
return nil;
}
self.requestSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(requestSerializer))];
self.responseSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializer))];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))];
[coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))];
[coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFHTTPRequestOperationManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL];
HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone];
HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone];
return HTTPClient;
}
@end

View File

@@ -1,238 +0,0 @@
// AFHTTPSessionManager.h
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <Availability.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <CoreServices/CoreServices.h>
#endif
#import "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.
## 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.
For developers targeting iOS 6 or Mac OS X 10.8 or earlier, `AFHTTPRequestOperationManager` may be used to similar effect.
## Methods to Override
To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`.
## 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>`.
Responses received from the server are automatically validated and serialized by the `responseSerializers` property, which is an object conforming to `<AFURLResponseSerialization>`
## URL Construction Using Relative Paths
For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`.
Below are a few examples of how `baseURL` and relative paths interact:
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz
[NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo
[NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/
[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash.
*/
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
@interface AFHTTPSessionManager : AFURLSessionManager <NSCoding, NSCopying>
/**
The URL used to monitor reachability, and construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods.
*/
@property (readonly, nonatomic, strong) NSURL *baseURL;
/**
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
@warning `requestSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPRequestSerializer <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;
///---------------------
/// @name Initialization
///---------------------
/**
Creates and returns an `AFHTTPSessionManager` object.
*/
+ (instancetype)manager;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
@param url The base URL for the HTTP client.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(NSURL *)url;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
This is the designated initializer.
@param url The base URL for the HTTP client.
@param configuration The configuration used to create the managed session.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration;
///---------------------------
/// @name Making HTTP Requests
///---------------------------
/**
Creates and runs an `NSURLSessionDataTask` with a `GET` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
@end
#endif

View File

@@ -1,328 +0,0 @@
// AFHTTPSessionManager.m
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFHTTPSessionManager.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
#import "AFHTTPRequestOperation.h"
#import <Availability.h>
#import <Security/Security.h>
#ifdef _SYSTEMCONFIGURATION_H
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#endif
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <UIKit/UIKit.h>
#endif
@interface AFHTTPSessionManager ()
@property (readwrite, nonatomic, strong) NSURL *baseURL;
@end
@implementation AFHTTPSessionManager
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
- (instancetype)init {
return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue];
}
#pragma mark -
#ifdef _SYSTEMCONFIGURATION_H
#endif
- (void)setRequestSerializer:(AFHTTPRequestSerializer <AFURLRequestSerialization> *)requestSerializer {
NSParameterAssert(requestSerializer);
_requestSerializer = requestSerializer;
}
- (void)setResponseSerializer:(AFHTTPResponseSerializer <AFURLResponseSerialization> *)responseSerializer {
NSParameterAssert(responseSerializer);
[super setResponseSerializer:responseSerializer];
}
#pragma mark -
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
__block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
__block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id __unused responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
__block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:nil];
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PUT" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
__block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"PATCH" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
__block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(NSDictionary *)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"DELETE" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
__block NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))];
NSURLSessionConfiguration *configuration = [decoder decodeObjectForKey:@"sessionConfiguration"];
if (!configuration) {
NSString *configurationIdentifier = [decoder decodeObjectForKey:@"identifier"];
if (configurationIdentifier) {
configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier];
}
}
self = [self initWithBaseURL:baseURL sessionConfiguration:configuration];
if (!self) {
return nil;
}
self.requestSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(requestSerializer))];
self.responseSerializer = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializer))];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))];
if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) {
[coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
} else {
[coder encodeObject:self.session.configuration.identifier forKey:@"identifier"];
}
[coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))];
[coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
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];
return HTTPClient;
}
@end
#endif

View File

@@ -1,44 +0,0 @@
// AFNetworking.h
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <Availability.h>
#ifndef _AFNETWORKING_
#define _AFNETWORKING_
#import "AFURLRequestSerialization.h"
#import "AFURLResponseSerialization.h"
#import "AFSecurityPolicy.h"
#import "AFNetworkReachabilityManager.h"
#import "AFURLConnectionOperation.h"
#import "AFHTTPRequestOperation.h"
#import "AFHTTPRequestOperationManager.h"
#if ( ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || \
( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 ) )
#import "AFURLSessionManager.h"
#import "AFHTTPSessionManager.h"
#endif
#endif /* _AFNETWORKING_ */

View File

@@ -1,295 +0,0 @@
// AFSecurity.m
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFSecurityPolicy.h"
#if !defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
static NSData * AFSecKeyGetData(SecKeyRef key) {
CFDataRef data = NULL;
#if defined(NS_BLOCK_ASSERTIONS)
SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data);
#else
OSStatus status = SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data);
NSCAssert(status == errSecSuccess, @"SecItemExport error: %ld", (long int)status);
#endif
NSCParameterAssert(data);
return (__bridge_transfer NSData *)data;
}
#endif
static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
return [(__bridge id)key1 isEqual:(__bridge id)key2];
#else
return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];
#endif
}
static id AFPublicKeyForCertificate(NSData *certificate) {
SecCertificateRef allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
NSCParameterAssert(allowedCertificate);
SecCertificateRef allowedCertificates[] = {allowedCertificate};
CFArrayRef tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef allowedTrust = NULL;
#if defined(NS_BLOCK_ASSERTIONS)
SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust);
#else
OSStatus status = SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust);
NSCAssert(status == errSecSuccess, @"SecTrustCreateWithCertificates error: %ld", (long int)status);
#endif
SecTrustResultType result = 0;
#if defined(NS_BLOCK_ASSERTIONS)
SecTrustEvaluate(allowedTrust, &result);
#else
status = SecTrustEvaluate(allowedTrust, &result);
NSCAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status);
#endif
SecKeyRef allowedPublicKey = SecTrustCopyPublicKey(allowedTrust);
NSCParameterAssert(allowedPublicKey);
CFRelease(allowedTrust);
CFRelease(policy);
CFRelease(tempCertificates);
CFRelease(allowedCertificate);
return (__bridge_transfer id)allowedPublicKey;
}
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
SecTrustResultType result = 0;
#if defined(NS_BLOCK_ASSERTIONS)
SecTrustEvaluate(serverTrust, &result);
#else
OSStatus status = SecTrustEvaluate(serverTrust, &result);
NSCAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status);
#endif
return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
}
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
}
return [NSArray arrayWithArray:trustChain];
}
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
SecCertificateRef someCertificates[] = {certificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
SecTrustRef trust = NULL;
#if defined(NS_BLOCK_ASSERTIONS)
SecTrustCreateWithCertificates(certificates, policy, &trust);
SecTrustResultType result;
SecTrustEvaluate(trust, &result);
#else
OSStatus status = SecTrustCreateWithCertificates(certificates, policy, &trust);
NSCAssert(status == errSecSuccess, @"SecTrustCreateWithCertificates error: %ld", (long int)status);
SecTrustResultType result;
status = SecTrustEvaluate(trust, &result);
NSCAssert(status == errSecSuccess, @"SecTrustEvaluate error: %ld", (long int)status);
#endif
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
CFRelease(trust);
CFRelease(certificates);
}
CFRelease(policy);
return [NSArray arrayWithArray:trustChain];
}
#pragma mark -
@interface AFSecurityPolicy()
@property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys;
@end
@implementation AFSecurityPolicy
+ (NSArray *)defaultPinnedCertificates {
static NSArray *_defaultPinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
_defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates];
});
return _defaultPinnedCertificates;
}
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = pinningMode;
securityPolicy.validatesDomainName = YES;
[securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]];
return securityPolicy;
}
- (id)init {
self = [super init];
if (!self) {
return nil;
}
self.validatesCertificateChain = YES;
return self;
}
#pragma mark -
- (void)setPinnedCertificates:(NSArray *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
[mutablePinnedPublicKeys addObject:AFPublicKeyForCertificate(certificate)];
}
self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
#pragma mark -
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust {
return [self evaluateServerTrust:serverTrust forDomain:nil];
}
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
return YES;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
if (!self.validatesCertificateChain) {
return YES;
}
NSUInteger trustedCertificateCount = 0;
for (NSData *trustChainCertificate in serverCertificates) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
trustedCertificateCount++;
}
}
return trustedCertificateCount == [serverCertificates count];
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
if (!self.validatesCertificateChain && [publicKeys count] > 0) {
publicKeys = @[[publicKeys firstObject]];
}
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1));
}
}
return NO;
}
#pragma mark - NSKeyValueObserving
+ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {
return [NSSet setWithObject:@"pinnedCertificates"];
}
@end

View File

@@ -1,357 +0,0 @@
// AFURLConnectionOperation.h
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <Availability.h>
#import "AFSecurityPolicy.h"
/**
`AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods.
## Subclassing Notes
This is the base class of all network request operations. You may wish to create your own subclass in order to implement additional `NSURLConnection` delegate methods (see "`NSURLConnection` Delegate Methods" below), or to provide additional properties and/or class constructors.
If you are creating a subclass that communicates over the HTTP or HTTPS protocols, you may want to consider subclassing `AFHTTPRequestOperation` instead, as it supports specifying acceptable content types or status codes.
## NSURLConnection Delegate Methods
`AFURLConnectionOperation` implements the following `NSURLConnection` delegate methods:
- `connection:didReceiveResponse:`
- `connection:didReceiveData:`
- `connectionDidFinishLoading:`
- `connection:didFailWithError:`
- `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:`
- `connection:willCacheResponse:`
- `connectionShouldUseCredentialStorage:`
- `connection:needNewBodyStream:`
- `connection:willSendRequestForAuthenticationChallenge:`
If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first.
## Callbacks and Completion Blocks
The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this.
Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/).
## SSL Pinning
Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle.
SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice.
Connections will be validated on all matching certificates with a `.cer` extension in the bundle root.
## NSCoding & NSCopying Conformance
`AFURLConnectionOperation` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. However, because of the intrinsic limitations of capturing the exact state of an operation at a particular moment, there are some important caveats to keep in mind:
### NSCoding Caveats
- Encoded operations do not include any block or stream properties. Be sure to set `completionBlock`, `outputStream`, and any callback blocks as necessary when using `-initWithCoder:` or `NSKeyedUnarchiver`.
- Operations are paused on `encodeWithCoder:`. If the operation was encoded while paused or still executing, its archived state will return `YES` for `isReady`. Otherwise, the state of an operation when encoding will remain unchanged.
### NSCopying Caveats
- `-copy` and `-copyWithZone:` return a new operation with the `NSURLRequest` of the original. So rather than an exact copy of the operation at that particular instant, the copying mechanism returns a completely new instance, which can be useful for retrying operations.
- A copy of an operation will not include the `outputStream` of the original.
- Operation copies do not include `completionBlock`, as it often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied.
*/
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate, NSURLConnectionDataDelegate, NSCoding, NSCopying>
///-------------------------------
/// @name Accessing Run Loop Modes
///-------------------------------
/**
The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`.
*/
@property (nonatomic, strong) NSSet *runLoopModes;
///-----------------------------------------
/// @name Getting URL Connection Information
///-----------------------------------------
/**
The request used by the operation's connection.
*/
@property (readonly, nonatomic, strong) NSURLRequest *request;
/**
The last response received by the operation's connection.
*/
@property (readonly, nonatomic, strong) NSURLResponse *response;
/**
The error, if any, that occurred in the lifecycle of the request.
*/
@property (readonly, nonatomic, strong) NSError *error;
///----------------------------
/// @name Getting Response Data
///----------------------------
/**
The data received during the request.
*/
@property (readonly, nonatomic, strong) NSData *responseData;
/**
The string representation of the response data.
*/
@property (readonly, nonatomic, copy) NSString *responseString;
/**
The string encoding of the response.
If the response does not specify a valid string encoding, `responseStringEncoding` will return `NSUTF8StringEncoding`.
*/
@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding;
///-------------------------------
/// @name Managing URL Credentials
///-------------------------------
/**
Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default.
This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`.
*/
@property (nonatomic, assign) BOOL shouldUseCredentialStorage;
/**
The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
*/
@property (nonatomic, strong) NSURLCredential *credential;
///-------------------------------
/// @name Managing Security Policy
///-------------------------------
/**
The security policy used to evaluate server trust for secure connections.
*/
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
///------------------------
/// @name Accessing Streams
///------------------------
/**
The input stream used to read data to be sent during the request.
This property acts as a proxy to the `HTTPBodyStream` property of `request`.
*/
@property (nonatomic, strong) NSInputStream *inputStream;
/**
The output stream that is used to write data received until the request is finished.
By default, data is accumulated into a buffer that is stored into `responseData` upon completion of the request. When `outputStream` is set, the data will not be accumulated into an internal buffer, and as a result, the `responseData` property of the completed request will be `nil`. The output stream will be scheduled in the network thread runloop upon being set.
*/
@property (nonatomic, strong) NSOutputStream *outputStream;
///---------------------------------
/// @name Managing Callback Queues
///---------------------------------
/**
The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, strong) dispatch_queue_t completionQueue;
/**
The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
*/
@property (nonatomic, strong) dispatch_group_t completionGroup;
///---------------------------------------------
/// @name Managing Request Operation Information
///---------------------------------------------
/**
The user info dictionary for the receiver.
*/
@property (nonatomic, strong) NSDictionary *userInfo;
///------------------------------------------------------
/// @name Initializing an AFURLConnectionOperation Object
///------------------------------------------------------
/**
Initializes and returns a newly allocated operation object with a url connection configured with the specified url request.
This is the designated initializer.
@param urlRequest The request object to be used by the operation connection.
*/
- (instancetype)initWithRequest:(NSURLRequest *)urlRequest;
///----------------------------------
/// @name Pausing / Resuming Requests
///----------------------------------
/**
Pauses the execution of the request operation.
A paused operation returns `NO` for `-isReady`, `-isExecuting`, and `-isFinished`. As such, it will remain in an `NSOperationQueue` until it is either cancelled or resumed. Pausing a finished, cancelled, or paused operation has no effect.
*/
- (void)pause;
/**
Whether the request operation is currently paused.
@return `YES` if the operation is currently paused, otherwise `NO`.
*/
- (BOOL)isPaused;
/**
Resumes the execution of the paused request operation.
Pause/Resume behavior varies depending on the underlying implementation for the operation class. In its base implementation, resuming a paused requests restarts the original request. However, since HTTP defines a specification for how to request a specific content range, `AFHTTPRequestOperation` will resume downloading the request from where it left off, instead of restarting the original request.
*/
- (void)resume;
///----------------------------------------------
/// @name Configuring Backgrounding Task Behavior
///----------------------------------------------
/**
Specifies that the operation should continue execution after the app has entered the background, and the expiration handler for that background task.
@param handler A handler to be called shortly before the applications remaining background time reaches 0. The handler is wrapped in a block that cancels the operation, and cleans up and marks the end of execution, unlike the `handler` parameter in `UIApplication -beginBackgroundTaskWithExpirationHandler:`, which expects this to be done in the handler itself. The handler is called synchronously on the main thread, thus blocking the applications suspension momentarily while the application is notified.
*/
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler;
#endif
///---------------------------------
/// @name Setting Progress Callbacks
///---------------------------------
/**
Sets a callback to be called when an undetermined number of bytes have been uploaded to the server.
@param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread.
*/
- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))block;
/**
Sets a callback to be called when an undetermined number of bytes have been downloaded from the server.
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread.
*/
- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead))block;
///-------------------------------------------------
/// @name Setting NSURLConnection Delegate Callbacks
///-------------------------------------------------
/**
Sets a block to be executed when the connection will authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequestForAuthenticationChallenge:`.
@param block A block object to be executed when the connection will authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated. This block must invoke one of the challenge-responder methods (NSURLAuthenticationChallengeSender protocol).
If `allowsInvalidSSLCertificate` is set to YES, `connection:willSendRequestForAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates.
*/
- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block;
/**
Sets a block to be executed when the server redirects the request from one URL to another URL, or when the request URL changed by the `NSURLProtocol` subclass handling the request in order to standardize its format, as handled by the `NSURLConnectionDataDelegate` method `connection:willSendRequest:redirectResponse:`.
@param block A block object to be executed when the request URL was changed. The block returns an `NSURLRequest` object, the URL request to redirect, and takes three arguments: the URL connection object, the the proposed redirected request, and the URL response that caused the redirect.
*/
- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block;
/**
Sets a block to be executed to modify the response a connection will cache, if any, as handled by the `NSURLConnectionDelegate` method `connection:willCacheResponse:`.
@param block A block object to be executed to determine what response a connection will cache, if any. The block returns an `NSCachedURLResponse` object, the cached response to store in memory or `nil` to prevent the response from being cached, and takes two arguments: the URL connection object, and the cached response provided for the request.
*/
- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;
///
/**
*/
+ (NSArray *)batchOfRequestOperations:(NSArray *)operations
progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))completionBlock;
@end
///----------------
/// @name Constants
///----------------
/**
## 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 AFNetworkingOperationFailingURLResponseErrorKey`
### 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 `AFNetworkingErrorDomain`.
`AFNetworkingOperationFailingURLResponseErrorKey`
The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFNetworkingErrorDomain`.
## Error Domains
The following error domain is predefined.
- `NSString * const AFNetworkingErrorDomain`
### Constants
`AFNetworkingErrorDomain`
AFNetworking errors. Error codes for `AFNetworkingErrorDomain` correspond to codes in `NSURLErrorDomain`.
*/
extern NSString * const AFNetworkingErrorDomain;
extern NSString * const AFNetworkingOperationFailingURLRequestErrorKey;
extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey;
///--------------------
/// @name Notifications
///--------------------
/**
Posted when an operation begins executing.
*/
extern NSString * const AFNetworkingOperationDidStartNotification;
/**
Posted when an operation finishes.
*/
extern NSString * const AFNetworkingOperationDidFinishNotification;

View File

@@ -1,752 +0,0 @@
// AFURLConnectionOperation.m
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFURLConnectionOperation.h"
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <UIKit/UIKit.h>
#endif
#if !__has_feature(objc_arc)
#error AFNetworking must be built with ARC.
// You can turn on ARC for only AFNetworking files by adding -fobjc-arc to the build phase for each of its files.
#endif
typedef NS_ENUM(NSInteger, AFOperationState) {
AFOperationPausedState = -1,
AFOperationReadyState = 1,
AFOperationExecutingState = 2,
AFOperationFinishedState = 3,
};
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
typedef UIBackgroundTaskIdentifier AFBackgroundTaskIdentifier;
#else
typedef id AFBackgroundTaskIdentifier;
#endif
static dispatch_group_t url_request_operation_completion_group() {
static dispatch_group_t af_url_request_operation_completion_group;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_request_operation_completion_group = dispatch_group_create();
});
return af_url_request_operation_completion_group;
}
static dispatch_queue_t url_request_operation_completion_queue() {
static dispatch_queue_t af_url_request_operation_completion_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT );
});
return af_url_request_operation_completion_queue;
}
static NSString * const kAFNetworkingLockName = @"com.alamofire.networking.operation.lock";
NSString * const AFNetworkingErrorDomain = @"AFNetworkingErrorDomain";
NSString * const AFNetworkingOperationFailingURLRequestErrorKey = @"AFNetworkingOperationFailingURLRequestErrorKey";
NSString * const AFNetworkingOperationFailingURLResponseErrorKey = @"AFNetworkingOperationFailingURLResponseErrorKey";
NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start";
NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish";
typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, NSInteger totalBytes, NSInteger totalBytesExpected);
typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge);
typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse);
typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse);
static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {
switch (state) {
case AFOperationReadyState:
return @"isReady";
case AFOperationExecutingState:
return @"isExecuting";
case AFOperationFinishedState:
return @"isFinished";
case AFOperationPausedState:
return @"isPaused";
default: {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
return @"state";
#pragma clang diagnostic pop
}
}
}
static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) {
switch (fromState) {
case AFOperationReadyState:
switch (toState) {
case AFOperationPausedState:
case AFOperationExecutingState:
return YES;
case AFOperationFinishedState:
return isCancelled;
default:
return NO;
}
case AFOperationExecutingState:
switch (toState) {
case AFOperationPausedState:
case AFOperationFinishedState:
return YES;
default:
return NO;
}
case AFOperationFinishedState:
return NO;
case AFOperationPausedState:
return toState == AFOperationReadyState;
default:
return YES;
}
}
@interface AFURLConnectionOperation ()
@property (readwrite, nonatomic, assign) AFOperationState state;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
@property (readwrite, nonatomic, strong) NSURLConnection *connection;
@property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSURLResponse *response;
@property (readwrite, nonatomic, strong) NSError *error;
@property (readwrite, nonatomic, strong) NSData *responseData;
@property (readwrite, nonatomic, copy) NSString *responseString;
@property (readwrite, nonatomic, assign) NSStringEncoding responseStringEncoding;
@property (readwrite, nonatomic, assign) NSInteger totalBytesRead;
@property (readwrite, nonatomic, assign) AFBackgroundTaskIdentifier backgroundTaskIdentifier;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock uploadProgress;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationProgressBlock downloadProgress;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationAuthenticationChallengeBlock authenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse;
@property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse;
- (void)operationDidStart;
- (void)finish;
- (void)cancelConnection;
@end
@implementation AFURLConnectionOperation
@synthesize outputStream = _outputStream;
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
- (instancetype)initWithRequest:(NSURLRequest *)urlRequest {
NSParameterAssert(urlRequest);
self = [super init];
if (!self) {
return nil;
}
self.lock = [[NSRecursiveLock alloc] init];
self.lock.name = kAFNetworkingLockName;
self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
self.request = urlRequest;
self.shouldUseCredentialStorage = YES;
self.state = AFOperationReadyState;
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
return self;
}
- (void)dealloc {
if (_outputStream) {
[_outputStream close];
_outputStream = nil;
}
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
if (_backgroundTaskIdentifier) {
[[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier];
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
#endif
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, state: %@, cancelled: %@ request: %@, response: %@>", NSStringFromClass([self class]), self, AFKeyPathFromOperationState(self.state), ([self isCancelled] ? @"YES" : @"NO"), self.request, self.response];
}
- (void)setCompletionBlock:(void (^)(void))block {
[self.lock lock];
if (!block) {
[super setCompletionBlock:nil];
} else {
__weak __typeof(self)weakSelf = self;
[super setCompletionBlock:^ {
__strong __typeof(weakSelf)strongSelf = weakSelf;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();
dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop
dispatch_group_async(group, queue, ^{
block();
});
dispatch_group_notify(group, url_request_operation_completion_queue(), ^{
[strongSelf setCompletionBlock:nil];
});
}];
}
[self.lock unlock];
}
- (NSInputStream *)inputStream {
return self.request.HTTPBodyStream;
}
- (void)setInputStream:(NSInputStream *)inputStream {
NSMutableURLRequest *mutableRequest = [self.request mutableCopy];
mutableRequest.HTTPBodyStream = inputStream;
self.request = mutableRequest;
}
- (NSOutputStream *)outputStream {
if (!_outputStream) {
self.outputStream = [NSOutputStream outputStreamToMemory];
}
return _outputStream;
}
- (void)setOutputStream:(NSOutputStream *)outputStream {
[self.lock lock];
if (outputStream != _outputStream) {
if (_outputStream) {
[_outputStream close];
}
_outputStream = outputStream;
}
[self.lock unlock];
}
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
[self.lock lock];
if (!self.backgroundTaskIdentifier) {
UIApplication *application = [UIApplication sharedApplication];
__weak __typeof(self)weakSelf = self;
self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (handler) {
handler();
}
if (strongSelf) {
[strongSelf cancel];
[application endBackgroundTask:strongSelf.backgroundTaskIdentifier];
strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}
}];
}
[self.lock unlock];
}
#endif
- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite))block {
self.uploadProgress = block;
}
- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead))block {
self.downloadProgress = block;
}
- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block {
self.authenticationChallenge = block;
}
- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block {
self.cacheResponse = block;
}
- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block {
self.redirectResponse = block;
}
- (void)setState:(AFOperationState)state {
if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {
return;
}
[self.lock lock];
NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
NSString *newStateKey = AFKeyPathFromOperationState(state);
[self willChangeValueForKey:newStateKey];
[self willChangeValueForKey:oldStateKey];
_state = state;
[self didChangeValueForKey:oldStateKey];
[self didChangeValueForKey:newStateKey];
[self.lock unlock];
}
- (NSString *)responseString {
[self.lock lock];
if (!_responseString && self.response && self.responseData) {
self.responseString = [[NSString alloc] initWithData:self.responseData encoding:self.responseStringEncoding];
}
[self.lock unlock];
return _responseString;
}
- (NSStringEncoding)responseStringEncoding {
[self.lock lock];
if (!_responseStringEncoding && self.response) {
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
if (self.response.textEncodingName) {
CFStringEncoding IANAEncoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName);
if (IANAEncoding != kCFStringEncodingInvalidId) {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(IANAEncoding);
}
}
self.responseStringEncoding = stringEncoding;
}
[self.lock unlock];
return _responseStringEncoding;
}
- (void)pause {
if ([self isPaused] || [self isFinished] || [self isCancelled]) {
return;
}
[self.lock lock];
if ([self isExecuting]) {
[self performSelector:@selector(operationDidPause) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
dispatch_async(dispatch_get_main_queue(), ^{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
}
self.state = AFOperationPausedState;
[self.lock unlock];
}
- (void)operationDidPause {
[self.lock lock];
[self.connection cancel];
[self.lock unlock];
}
- (BOOL)isPaused {
return self.state == AFOperationPausedState;
}
- (void)resume {
if (![self isPaused]) {
return;
}
[self.lock lock];
self.state = AFOperationReadyState;
[self start];
[self.lock unlock];
}
#pragma mark - NSOperation
- (BOOL)isReady {
return self.state == AFOperationReadyState && [super isReady];
}
- (BOOL)isExecuting {
return self.state == AFOperationExecutingState;
}
- (BOOL)isFinished {
return self.state == AFOperationFinishedState;
}
- (BOOL)isConcurrent {
return YES;
}
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
- (void)operationDidStart {
[self.lock lock];
if (![self isCancelled]) {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
[self.connection start];
}
[self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
});
}
- (void)finish {
[self.lock lock];
self.state = AFOperationFinishedState;
[self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];
});
}
- (void)cancel {
[self.lock lock];
if (![self isFinished] && ![self isCancelled]) {
[super cancel];
if ([self isExecuting]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
}
[self.lock unlock];
}
- (void)cancelConnection {
NSDictionary *userInfo = nil;
if ([self.request URL]) {
userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
}
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];
if (![self isFinished]) {
if (self.connection) {
[self.connection cancel];
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error];
} else {
// Accomodate race condition where `self.connection` has not yet been set before cancellation
self.error = error;
[self finish];
}
}
}
#pragma mark -
+ (NSArray *)batchOfRequestOperations:(NSArray *)operations
progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))completionBlock
{
if (!operations || [operations count] == 0) {
return @[[NSBlockOperation blockOperationWithBlock:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(@[]);
}
});
}]];
}
__block dispatch_group_t group = dispatch_group_create();
NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(operations);
}
});
}];
for (AFURLConnectionOperation *operation in operations) {
operation.completionGroup = group;
void (^originalCompletionBlock)(void) = [operation.completionBlock copy];
__weak __typeof(operation)weakOperation = operation;
operation.completionBlock = ^{
__strong __typeof(weakOperation)strongOperation = weakOperation;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue();
#pragma clang diagnostic pop
dispatch_group_async(group, queue, ^{
if (originalCompletionBlock) {
originalCompletionBlock();
}
NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx, BOOL __unused *stop) {
return [op isFinished];
}] count];
if (progressBlock) {
progressBlock(numberOfFinishedOperations, [operations count]);
}
dispatch_group_leave(group);
});
};
dispatch_group_enter(group);
[batchedOperation addDependency:operation];
}
return [operations arrayByAddingObject:batchedOperation];
}
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if (self.authenticationChallenge) {
self.authenticationChallenge(connection, challenge);
return;
}
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
} else {
if ([challenge previousFailureCount] == 0) {
if (self.credential) {
[[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge];
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
} else {
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
}
}
}
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection {
return self.shouldUseCredentialStorage;
}
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
if (self.redirectResponse) {
return self.redirectResponse(connection, request, redirectResponse);
} else {
return request;
}
}
- (void)connection:(NSURLConnection __unused *)connection
didSendBodyData:(NSInteger)bytesWritten
totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
if (self.uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
});
}
}
- (void)connection:(NSURLConnection __unused *)connection
didReceiveResponse:(NSURLResponse *)response
{
self.response = response;
[self.outputStream open];
}
- (void)connection:(NSURLConnection __unused *)connection
didReceiveData:(NSData *)data
{
NSUInteger length = [data length];
while (YES) {
NSInteger totalNumberOfBytesWritten = 0;
if ([self.outputStream hasSpaceAvailable]) {
const uint8_t *dataBuffer = (uint8_t *)[data bytes];
NSInteger numberOfBytesWritten = 0;
while (totalNumberOfBytesWritten < (NSInteger)length) {
numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)];
if (numberOfBytesWritten == -1) {
break;
}
totalNumberOfBytesWritten += numberOfBytesWritten;
}
break;
}
if (self.outputStream.streamError) {
[self.connection cancel];
[self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];
return;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
self.totalBytesRead += (NSInteger)length;
if (self.downloadProgress) {
self.downloadProgress(length, self.totalBytesRead, (NSInteger)self.response.expectedContentLength);
}
});
}
- (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {
self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
[self.outputStream close];
[self finish];
self.connection = nil;
}
- (void)connection:(NSURLConnection __unused *)connection
didFailWithError:(NSError *)error
{
self.error = error;
[self.outputStream close];
[self finish];
self.connection = nil;
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
if (self.cacheResponse) {
return self.cacheResponse(connection, cachedResponse);
} else {
if ([self isCancelled]) {
return nil;
}
return cachedResponse;
}
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
NSURLRequest *request = [decoder decodeObjectForKey:NSStringFromSelector(@selector(request))];
self = [self initWithRequest:request];
if (!self) {
return nil;
}
self.state = (AFOperationState)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(state))];
self.response = [decoder decodeObjectForKey:NSStringFromSelector(@selector(response))];
self.error = [decoder decodeObjectForKey:NSStringFromSelector(@selector(error))];
self.responseData = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseData))];
self.totalBytesRead = [decoder decodeIntegerForKey:NSStringFromSelector(@selector(totalBytesRead))];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[self pause];
[coder encodeObject:self.request forKey:NSStringFromSelector(@selector(request))];
switch (self.state) {
case AFOperationExecutingState:
case AFOperationPausedState:
[coder encodeInteger:AFOperationReadyState forKey:NSStringFromSelector(@selector(state))];
break;
default:
[coder encodeInteger:self.state forKey:NSStringFromSelector(@selector(state))];
break;
}
[coder encodeObject:self.response forKey:NSStringFromSelector(@selector(response))];
[coder encodeObject:self.error forKey:NSStringFromSelector(@selector(error))];
[coder encodeObject:self.responseData forKey:NSStringFromSelector(@selector(responseData))];
[coder encodeInt64:self.totalBytesRead forKey:NSStringFromSelector(@selector(totalBytesRead))];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFURLConnectionOperation *operation = [(AFURLConnectionOperation *)[[self class] allocWithZone:zone] initWithRequest:self.request];
operation.uploadProgress = self.uploadProgress;
operation.downloadProgress = self.downloadProgress;
operation.authenticationChallenge = self.authenticationChallenge;
operation.cacheResponse = self.cacheResponse;
operation.redirectResponse = self.redirectResponse;
operation.completionQueue = self.completionQueue;
operation.completionGroup = self.completionGroup;
return operation;
}
@end

View File

@@ -1,936 +0,0 @@
// AFURLSessionManager.m
//
// Copyright (c) 2013 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
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFURLSessionManager.h"
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
static dispatch_queue_t url_session_manager_processing_queue() {
static dispatch_queue_t af_url_session_manager_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_url_session_manager_processing_queue;
}
static dispatch_group_t url_session_manager_completion_group() {
static dispatch_group_t af_url_session_manager_completion_group;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_completion_group = dispatch_group_create();
});
return af_url_session_manager_completion_group;
}
NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";
NSString * const AFNetworkingTaskDidCompleteNotification = @"com.alamofire.networking.task.complete";
NSString * const AFNetworkingTaskDidSuspendNotification = @"com.alamofire.networking.task.suspend";
NSString * const AFURLSessionDidInvalidateNotification = @"com.alamofire.networking.session.invalidate";
NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification = @"com.alamofire.networking.session.download.file-manager-error";
NSString * const AFNetworkingTaskDidStartNotification = @"com.alamofire.networking.task.resume"; // Deprecated
NSString * const AFNetworkingTaskDidFinishNotification = @"com.alamofire.networking.task.complete"; // Deprecated
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 AFNetworkingTaskDidFinishSerializedResponseKey = @"com.alamofire.networking.task.complete.serializedresponse"; // Deprecated
NSString * const AFNetworkingTaskDidFinishResponseSerializerKey = @"com.alamofire.networking.task.complete.responseserializer"; // Deprecated
NSString * const AFNetworkingTaskDidFinishResponseDataKey = @"com.alamofire.networking.complete.finish.responsedata"; // Deprecated
NSString * const AFNetworkingTaskDidFinishErrorKey = @"com.alamofire.networking.task.complete.error"; // Deprecated
NSString * const AFNetworkingTaskDidFinishAssetPathKey = @"com.alamofire.networking.task.complete.assetpath"; // Deprecated
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
static void * AFTaskStateChangedContext = &AFTaskStateChangedContext;
typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef NSURLRequest * (^AFURLSessionTaskWillPerformHTTPRedirectionBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request);
typedef NSURLSessionAuthChallengeDisposition (^AFURLSessionTaskDidReceiveAuthenticationChallengeBlock)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential);
typedef NSInputStream * (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession *session, NSURLSessionTask *task);
typedef void (^AFURLSessionTaskDidSendBodyDataBlock)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend);
typedef void (^AFURLSessionTaskDidCompleteBlock)(NSURLSession *session, NSURLSessionTask *task, NSError *error);
typedef NSURLSessionResponseDisposition (^AFURLSessionDataTaskDidReceiveResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response);
typedef void (^AFURLSessionDataTaskDidBecomeDownloadTaskBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask);
typedef void (^AFURLSessionDataTaskDidReceiveDataBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data);
typedef NSCachedURLResponse * (^AFURLSessionDataTaskWillCacheResponseBlock)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse);
typedef void (^AFURLSessionDidFinishEventsForBackgroundURLSessionBlock)(NSURLSession *session);
typedef NSURL * (^AFURLSessionDownloadTaskDidFinishDownloadingBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location);
typedef void (^AFURLSessionDownloadTaskDidWriteDataBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite);
typedef void (^AFURLSessionDownloadTaskDidResumeBlock)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes);
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
#pragma mark -
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSProgress *uploadProgress;
@property (nonatomic, strong) NSProgress *downloadProgress;
@property (nonatomic, copy) NSURL *downloadFileURL;
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
+ (instancetype)delegateForManager:(AFURLSessionManager *)manager
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
@end
@implementation AFURLSessionManagerTaskDelegate
+ (instancetype)delegateForManager:(AFURLSessionManager *)manager
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[self alloc] init];
delegate.manager = manager;
delegate.responseSerializer = manager.responseSerializer;
delegate.completionHandler = completionHandler;
return delegate;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.mutableData = [NSMutableData data];
self.uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
self.downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
return self;
}
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(__unused NSURLSessionTask *)task
didSendBodyData:(__unused int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
int64_t totalUnitCount = totalBytesExpectedToSend;
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
self.uploadProgress.totalUnitCount = totalUnitCount;
self.uploadProgress.completedUnitCount = totalBytesSent;
}
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = self.responseSerializer;
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (self.mutableData) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = [NSData dataWithData:self.mutableData];
}
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [self.responseSerializer responseObjectForResponse:task.response data:[NSData dataWithData:self.mutableData] error:&serializationError];
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
#pragma clang diagnostic pop
}
#pragma mark - NSURLSessionDataTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
[self.mutableData appendData:data];
self.downloadProgress.completedUnitCount += (int64_t)[data length];
}
#pragma mark - NSURLSessionDownloadTaskDelegate
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSError *fileManagerError = nil;
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
[[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
if (fileManagerError) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
- (void)URLSession:(__unused NSURLSession *)session
downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask
didWriteData:(__unused int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
self.downloadProgress.completedUnitCount = totalBytesWritten;
}
- (void)URLSession:(__unused NSURLSession *)session
downloadTask:(__unused NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes {
self.downloadProgress.totalUnitCount = expectedTotalBytes;
self.downloadProgress.completedUnitCount = fileOffset;
}
@end
#pragma mark -
@interface AFURLSessionManager ()
@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
@property (readwrite, nonatomic, strong) NSURLSession *session;
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
@property (readwrite, nonatomic, strong) NSLock *lock;
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
@end
@implementation AFURLSessionManager
- (instancetype)init {
return [self initWithSessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount;
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.sessionConfiguration = configuration;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, self.session, self.operationQueue];
}
#pragma mark -
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
[task addObserver:self forKeyPath:NSStringFromSelector(@selector(state)) options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:AFTaskStateChangedContext];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self.lock unlock];
}
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
[self.lock lock];
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(state)) context:AFTaskStateChangedContext];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
- (void)removeAllDelegates {
[self.lock lock];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeAllObjects];
[self.lock unlock];
}
#pragma mark -
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
- (NSArray *)tasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)dataTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)uploadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
- (NSArray *)downloadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
#pragma mark -
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks {
if (cancelPendingTasks) {
[self.session invalidateAndCancel];
} else {
[self.session finishTasksAndInvalidate];
}
}
#pragma mark -
- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer {
NSParameterAssert(responseSerializer);
_responseSerializer = responseSerializer;
}
#pragma mark -
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
AFURLSessionManagerTaskDelegate *delegate = [AFURLSessionManagerTaskDelegate delegateForManager:self completionHandler:completionHandler];
[self setDelegate:delegate forTask:dataTask];
return dataTask;
}
#pragma mark -
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
return [self uploadTaskWithTask:uploadTask progress:progress completionHandler:completionHandler];
}
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
return [self uploadTaskWithTask:uploadTask progress:progress completionHandler:completionHandler];
}
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
return [self uploadTaskWithTask:uploadTask progress:progress completionHandler:completionHandler];
}
- (NSURLSessionUploadTask *)uploadTaskWithTask:(NSURLSessionUploadTask *)uploadTask
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
NSParameterAssert(uploadTask);
AFURLSessionManagerTaskDelegate *delegate = [AFURLSessionManagerTaskDelegate delegateForManager:self completionHandler:completionHandler];
int64_t totalUnitCount = uploadTask.countOfBytesExpectedToSend;
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
NSString *contentLength = [uploadTask.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
delegate.uploadProgress = [NSProgress progressWithTotalUnitCount:totalUnitCount];
delegate.uploadProgress.pausingHandler = ^{
[uploadTask suspend];
};
delegate.uploadProgress.cancellationHandler = ^{
[uploadTask cancel];
};
if (progress) {
*progress = delegate.uploadProgress;
}
[self setDelegate:delegate forTask:uploadTask];
return uploadTask;
}
#pragma mark -
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(NSProgress * __autoreleasing *)progress
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
return [self downloadTaskWithTask:downloadTask progress:progress destination:destination completionHandler:completionHandler];
}
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(NSProgress * __autoreleasing *)progress
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithResumeData:resumeData];
return [self downloadTaskWithTask:downloadTask progress:progress destination:destination completionHandler:completionHandler];
}
- (NSURLSessionDownloadTask *)downloadTaskWithTask:(NSURLSessionDownloadTask *)downloadTask
progress:(NSProgress * __autoreleasing *)progress
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
NSParameterAssert(downloadTask);
AFURLSessionManagerTaskDelegate *delegate = [AFURLSessionManagerTaskDelegate delegateForManager:self completionHandler:completionHandler];
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
if (destination) {
return destination(location, task.response);
}
return location;
};
if (progress) {
*progress = delegate.downloadProgress;
}
[self setDelegate:delegate forTask:downloadTask];
return downloadTask;
}
#pragma mark -
- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block {
self.sessionDidBecomeInvalid = block;
}
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
self.sessionDidReceiveAuthenticationChallenge = block;
}
#pragma mark -
- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block {
self.taskNeedNewBodyStream = block;
}
- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block {
self.taskWillPerformHTTPRedirection = block;
}
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block {
self.taskDidReceiveAuthenticationChallenge = block;
}
- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block {
self.taskDidSendBodyData = block;
}
- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block {
self.taskDidComplete = block;
}
#pragma mark -
- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block {
self.dataTaskDidReceiveResponse = block;
}
- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block {
self.dataTaskDidBecomeDownloadTask = block;
}
- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block {
self.dataTaskDidReceiveData = block;
}
- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block {
self.dataTaskWillCacheResponse = block;
}
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block {
self.didFinishEventsForBackgroundURLSession = block;
}
#pragma mark -
- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block {
self.downloadTaskDidFinishDownloading = block;
}
- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block {
self.downloadTaskDidWriteData = block;
}
- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block {
self.downloadTaskDidResume = block;
}
#pragma mark - NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[self removeAllDelegates];
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
{
NSURLRequest *redirectRequest = request;
if (self.taskWillPerformHTTPRedirection) {
redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
}
if (completionHandler) {
completionHandler(redirectRequest);
}
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.taskDidReceiveAuthenticationChallenge) {
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
NSInputStream *inputStream = nil;
if (self.taskNeedNewBodyStream) {
inputStream = self.taskNeedNewBodyStream(session, task);
} else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
inputStream = [task.originalRequest.HTTPBodyStream copy];
}
if (completionHandler) {
completionHandler(inputStream);
}
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
if (self.taskDidSendBodyData) {
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend);
}
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
[self removeDelegateForTask:task];
}
}
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
if (self.dataTaskDidReceiveResponse) {
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
if (completionHandler) {
completionHandler(disposition);
}
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
if (delegate) {
[self removeDelegateForTask:dataTask];
[self setDelegate:delegate forTask:downloadTask];
}
if (self.dataTaskDidBecomeDownloadTask) {
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
{
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.dataTaskWillCacheResponse) {
cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.didFinishEventsForBackgroundURLSession) {
self.didFinishEventsForBackgroundURLSession(session);
}
}
#pragma mark - NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
} else if (self.downloadTaskDidFinishDownloading) {
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
NSError *error = nil;
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
if (error) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
}
}
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
if (self.downloadTaskDidWriteData) {
self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
if (self.downloadTaskDidResume) {
self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
}
}
#pragma mark - NSKeyValueObserving
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFTaskStateChangedContext && [keyPath isEqualToString:@"state"]) {
NSString *notificationName = nil;
switch ([(NSURLSessionTask *)object state]) {
case NSURLSessionTaskStateRunning:
notificationName = AFNetworkingTaskDidResumeNotification;
break;
case NSURLSessionTaskStateSuspended:
notificationName = AFNetworkingTaskDidSuspendNotification;
break;
case NSURLSessionTaskStateCompleted:
// AFNetworkingTaskDidFinishNotification posted by task completion handlers
default:
break;
}
if (notificationName) {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:object];
});
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)decoder {
NSURLSessionConfiguration *configuration = [decoder decodeObjectForKey:@"sessionConfiguration"];
self = [self initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
return [[[self class] allocWithZone:zone] initWithSessionConfiguration:self.session.configuration];
}
@end
#endif

View File

@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
#import "SM_AFURLRequestSerialization.h"
@interface BinaryRequestSerializer : SM_AFHTTPRequestSerializer
+ (instancetype)serializer;
@end

View File

@@ -0,0 +1,53 @@
#import "BinaryRequestSerializer.h"
@implementation BinaryRequestSerializer
+ (instancetype)serializer
{
BinaryRequestSerializer *serializer = [[self alloc] init];
return serializer;
}
#pragma mark - SM_AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/octet-stream" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody: parameters];
}
return mutableRequest;
}
#pragma mark - NSSecureCoding
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
return self;
}
@end

View File

@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
#import "SM_AFURLResponseSerialization.h"
@interface BinaryResponseSerializer : SM_AFHTTPResponseSerializer
+ (instancetype)serializer;
@end

View File

@@ -0,0 +1,126 @@
#import "BinaryResponseSerializer.h"
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
@implementation BinaryResponseSerializer
+ (instancetype)serializer {
BinaryResponseSerializer *serializer = [[self alloc] init];
return serializer;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = nil;
return self;
}
- (NSString*)decodeResponseData:(NSData*)rawResponseData withEncoding:(CFStringEncoding)cfEncoding {
NSStringEncoding nsEncoding;
NSString* decoded = nil;
if (cfEncoding != kCFStringEncodingInvalidId) {
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}
NSStringEncoding supportedEncodings[6] = {
NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding,
NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding
};
for (int i = 0; i < sizeof(supportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == supportedEncodings[i]) {
decoded = [[NSString alloc] initWithData:rawResponseData encoding:supportedEncodings[i]];
}
}
return decoded;
}
- (CFStringEncoding) getEncoding:(NSURLResponse *)response {
CFStringEncoding encoding = kCFStringEncodingInvalidId;
if (response.textEncodingName) {
encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
}
return encoding;
}
#pragma mark -
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"SM_AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey: [response URL],
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
// trying to decode error message in body
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey] = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
}
if (error) {
*error = [NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo];
}
return NO;
}
}
return YES;
}
#pragma mark - SM_AFURLResponseSerialization
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
return [data base64EncodedStringWithOptions:0];
}
@end

View File

@@ -1,17 +1,20 @@
#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
#import <Cordova/CDVJSON.h>
@interface CordovaHttpPlugin : CDVPlugin
- (void)useBasicAuth:(CDVInvokedUrlCommand*)command;
- (void)setHeader:(CDVInvokedUrlCommand*)command;
- (void)enableSSLPinning:(CDVInvokedUrlCommand*)command;
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command;
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
- (void)setClientAuthMode:(CDVInvokedUrlCommand*)command;
- (void)post:(CDVInvokedUrlCommand*)command;
- (void)put:(CDVInvokedUrlCommand*)command;
- (void)patch:(CDVInvokedUrlCommand*)command;
- (void)get:(CDVInvokedUrlCommand*)command;
- (void)uploadFile:(CDVInvokedUrlCommand*)command;
- (void)delete:(CDVInvokedUrlCommand*)command;
- (void)head:(CDVInvokedUrlCommand*)command;
- (void)options:(CDVInvokedUrlCommand*)command;
- (void)uploadFiles:(CDVInvokedUrlCommand*)command;
- (void)downloadFile:(CDVInvokedUrlCommand*)command;
- (void)abort:(CDVInvokedUrlCommand*)command;
@end
@end

View File

@@ -1,239 +1,656 @@
#import "CordovaHttpPlugin.h"
#import "CDVFile.h"
#import "BinaryRequestSerializer.h"
#import "BinaryResponseSerializer.h"
#import "TextResponseSerializer.h"
#import "HttpManager.h"
#import "TextRequestSerializer.h"
#import "SM_AFHTTPSessionManager.h"
#import "SDNetworkActivityIndicator.h"
@interface CordovaHttpPlugin()
- (void)setRequestHeaders:(NSDictionary*)headers;
- (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:(SM_AFHTTPSessionManager*)manager;
- (void)setRedirect:(bool)redirect forManager:(SM_AFHTTPSessionManager*)manager;
@end
@implementation CordovaHttpPlugin {
AFHTTPRequestSerializer *requestSerializer;
SM_AFSecurityPolicy *securityPolicy;
NSURLCredential *x509Credential;
NSMutableDictionary *reqDict;
}
- (void)pluginInitialize {
requestSerializer = [AFHTTPRequestSerializer serializer];
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
reqDict = [NSMutableDictionary dictionary];
}
- (void)setRequestHeaders:(NSDictionary*)headers {
[HttpManager sharedClient].requestSerializer = [AFHTTPRequestSerializer serializer];
[requestSerializer.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[[HttpManager sharedClient].requestSerializer setValue:obj forHTTPHeaderField:key];
}];
[headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[[HttpManager sharedClient].requestSerializer setValue:obj forHTTPHeaderField:key];
}];
- (void)addRequest:(NSNumber*)reqId forTask:(NSURLSessionDataTask*)task {
[reqDict setObject:task forKey:reqId];
}
- (void)useBasicAuth:(CDVInvokedUrlCommand*)command {
NSString *username = [command.arguments objectAtIndex:0];
NSString *password = [command.arguments objectAtIndex:1];
[requestSerializer setAuthorizationHeaderFieldWithUsername:username password:password];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
- (void)removeRequest:(NSNumber*)reqId {
[reqDict removeObjectForKey:reqId];
}
- (void)setHeader:(CDVInvokedUrlCommand*)command {
NSString *header = [command.arguments objectAtIndex:0];
NSString *value = [command.arguments objectAtIndex:1];
[requestSerializer setValue:value forHTTPHeaderField: header];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)enableSSLPinning:(CDVInvokedUrlCommand*)command {
bool enable = [[command.arguments objectAtIndex:0] boolValue];
if (enable) {
[HttpManager sharedClient].securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
[HttpManager sharedClient].securityPolicy.validatesCertificateChain = NO;
- (void)setRequestSerializer:(NSString*)serializerName forManager:(SM_AFHTTPSessionManager*)manager {
if ([serializerName isEqualToString:@"json"]) {
manager.requestSerializer = [SM_AFJSONRequestSerializer serializer];
} else if ([serializerName isEqualToString:@"utf8"]) {
manager.requestSerializer = [TextRequestSerializer serializer];
} else if ([serializerName isEqualToString:@"raw"]) {
manager.requestSerializer = [BinaryRequestSerializer serializer];
} else {
[HttpManager sharedClient].securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
manager.requestSerializer = [SM_AFHTTPRequestSerializer serializer];
}
}
- (void)setupAuthChallengeBlock:(SM_AFHTTPSessionManager*)manager {
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(
NSURLSession * _Nonnull session,
NSURLAuthenticationChallenge * _Nonnull challenge,
NSURLCredential * _Nullable __autoreleasing * _Nullable credential
) {
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodServerTrust]) {
*credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (![self->securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
return NSURLSessionAuthChallengeRejectProtectionSpace;
}
if (credential) {
return NSURLSessionAuthChallengeUseCredential;
}
}
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodClientCertificate] && self->x509Credential) {
*credential = self->x509Credential;
return NSURLSessionAuthChallengeUseCredential;
}
return NSURLSessionAuthChallengePerformDefaultHandling;
}];
}
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(SM_AFHTTPSessionManager*)manager {
[headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[manager.requestSerializer setValue:obj forHTTPHeaderField:key];
}];
}
- (void)setRedirect:(bool)followRedirect forManager:(SM_AFHTTPSessionManager*)manager {
[manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest * _Nonnull(NSURLSession * _Nonnull session,
NSURLSessionTask * _Nonnull task, NSURLResponse * _Nonnull response, NSURLRequest * _Nonnull request) {
if (followRedirect) {
return request;
} else {
return nil;
}
}];
}
- (void)setTimeout:(NSTimeInterval)timeout forManager:(SM_AFHTTPSessionManager*)manager {
[manager.requestSerializer setTimeoutInterval:timeout];
}
- (void)setResponseSerializer:(NSString*)responseType forManager:(SM_AFHTTPSessionManager*)manager {
if ([responseType isEqualToString: @"text"] || [responseType isEqualToString: @"json"]) {
manager.responseSerializer = [TextResponseSerializer serializer];
} else {
manager.responseSerializer = [BinaryResponseSerializer serializer];
}
}
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data {
if (response != nil) {
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
}
if (data != nil) {
[dictionary setObject:data forKey:@"data"];
}
}
- (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:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
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 if(!aborted) {
[dictionary setObject:[self getStatusCode:error] forKey:@"status"];
[dictionary setObject:[error localizedDescription] forKey:@"error"];
}
}
- (void)handleException:(NSException*)exception withCommand:(CDVInvokedUrlCommand*)command {
CordovaHttpPlugin* __weak weakSelf = self;
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setValue:exception.userInfo forKey:@"error"];
[dictionary setObject:[NSNumber numberWithInt:-1] forKey:@"status"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (NSNumber*)getStatusCode:(NSError*) error {
switch ([error code]) {
case -1001:
// timeout
return [NSNumber numberWithInt:-4];
case -1002:
// unsupported URL
return [NSNumber numberWithInt:-5];
case -1003:
// server not found
return [NSNumber numberWithInt:-3];
case -1009:
// no connection
return [NSNumber numberWithInt:-6];
case -1200: // secure connection failed
case -1201: // certificate has bad date
case -1202: // certificate untrusted
case -1203: // certificate has unknown root
case -1204: // certificate is not yet valid
// configuring SSL failed
return [NSNumber numberWithInt:-2];
default:
return [NSNumber numberWithInt:-1];
}
}
- (NSMutableDictionary*)copyHeaderFields:(NSDictionary *)headerFields {
NSMutableDictionary *headerFieldsCopy = [[NSMutableDictionary alloc] initWithCapacity:headerFields.count];
NSString *headerKeyCopy;
for (NSString *headerKey in headerFields.allKeys) {
headerKeyCopy = [[headerKey mutableCopy] lowercaseString];
[headerFieldsCopy setValue:[headerFields objectForKey:headerKey] forKey:headerKeyCopy];
}
return headerFieldsCopy;
}
- (void)executeRequestWithoutData:(CDVInvokedUrlCommand*)command withMethod:(NSString*) method {
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSTimeInterval connectTimeout = [[command.arguments objectAtIndex:2] doubleValue];
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:3] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
NSString *responseType = [command.arguments objectAtIndex:5];
NSNumber *reqId = [command.arguments objectAtIndex:6];
[self setRequestSerializer: @"default" forManager: manager];
[self setupAuthChallengeBlock: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:readTimeout forManager:manager];
[self setRedirect:followRedirect forManager:manager];
[self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
// no 'body' for HEAD request, omitting 'data'
if ([method isEqualToString:@"HEAD"]) {
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
} else {
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
}
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[manager invalidateSessionCancelingTasks:YES];
};
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[manager invalidateSessionCancelingTasks:YES];
};
NSURLSessionDataTask *task = [manager downloadTaskWithHTTPMethod:method URLString:url parameters:nil progress:nil success:onSuccess failure:onFailure];
[self addRequest:reqId forTask:task];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)executeRequestWithData:(CDVInvokedUrlCommand*)command withMethod:(NSString*)method {
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 connectTimeout = [[command.arguments objectAtIndex:4] doubleValue];
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:5] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:6] boolValue];
NSString *responseType = [command.arguments objectAtIndex:7];
NSNumber *reqId = [command.arguments objectAtIndex:8];
[self setRequestSerializer: serializerName forManager: manager];
[self setupAuthChallengeBlock: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:readTimeout forManager:manager];
[self setRedirect:followRedirect forManager:manager];
[self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
void (^constructBody)(id<AFMultipartFormData>) = ^(id<AFMultipartFormData> formData) {
NSArray *buffers = [data mutableArrayValueForKey:@"buffers"];
NSArray *fileNames = [data mutableArrayValueForKey:@"fileNames"];
NSArray *names = [data mutableArrayValueForKey:@"names"];
NSArray *types = [data mutableArrayValueForKey:@"types"];
NSError *error;
for (int i = 0; i < [buffers count]; ++i) {
NSData *decodedBuffer = [[NSData alloc] initWithBase64EncodedString:[buffers objectAtIndex:i] options:0];
NSString *fileName = [fileNames objectAtIndex:i];
NSString *partName = [names objectAtIndex:i];
NSString *partType = [types objectAtIndex:i];
if (![fileName isEqual:[NSNull null]]) {
[formData appendPartWithFileData:decodedBuffer name:partName fileName:fileName mimeType:partType];
} else {
[formData appendPartWithFormData:decodedBuffer name:[names objectAtIndex:i]];
}
}
if (error) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:400] forKey:@"status"];
[dictionary setObject:@"Could not add part to multipart request body." forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
return;
}
};
void (^onSuccess)(NSURLSessionTask *, id) = ^(NSURLSessionTask *task, id responseObject) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[manager invalidateSessionCancelingTasks:YES];
};
void (^onFailure)(NSURLSessionTask *, NSError *) = ^(NSURLSessionTask *task, NSError *error) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[manager invalidateSessionCancelingTasks:YES];
};
NSURLSessionDataTask *task;
if ([serializerName isEqualToString:@"multipart"]) {
task = [manager uploadTaskWithHTTPMethod:method URLString:url parameters:nil constructingBodyWithBlock:constructBody progress:nil success:onSuccess failure:onFailure];
} else {
task = [manager uploadTaskWithHTTPMethod:method URLString:url parameters:data progress:nil success:onSuccess failure:onFailure];
}
[self addRequest:reqId forTask:task];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command {
NSString *certMode = [command.arguments objectAtIndex:0];
if ([certMode isEqualToString: @"default"] || [certMode isEqualToString: @"legacy"]) {
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
} else if ([certMode isEqualToString: @"nocheck"]) {
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName = NO;
} else if ([certMode isEqualToString: @"pinned"]) {
securityPolicy = [SM_AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
}
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult = nil;
bool allow = [[command.arguments objectAtIndex:0] boolValue];
[HttpManager sharedClient].securityPolicy.allowInvalidCertificates = allow;
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
- (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);
CFRelease(options);
if (securityError != noErr) {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
} else {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
SecTrustRef trust = (SecTrustRef)CFDictionaryGetValue(identityDict, kSecImportItemTrust);
int count = (int)SecTrustGetCertificateCount(trust);
NSMutableArray* trustCertificates = nil;
if (count > 1) {
trustCertificates = [NSMutableArray arrayWithCapacity:SecTrustGetCertificateCount(trust)];
for (int i=1;i<count; ++i) {
[trustCertificates addObject:(id)SecTrustGetCertificateAtIndex(trust, i)];
}
}
self->x509Credential = [NSURLCredential credentialWithIdentity:identity certificates: trustCertificates persistence:NSURLCredentialPersistenceForSession];
CFRelease(items);
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
}
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)post:(CDVInvokedUrlCommand*)command {
HttpManager *manager = [HttpManager sharedClient];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
[self setRequestHeaders: headers];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
[dictionary setObject:responseObject forKey:@"data"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
[dictionary setObject:[error localizedDescription] forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
[self executeRequestWithData: command withMethod:@"POST"];
}
- (void)put:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"PUT"];
}
- (void)patch:(CDVInvokedUrlCommand*)command {
[self executeRequestWithData: command withMethod:@"PATCH"];
}
- (void)get:(CDVInvokedUrlCommand*)command {
HttpManager *manager = [HttpManager sharedClient];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
[self setRequestHeaders: headers];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
[dictionary setObject:responseObject forKey:@"data"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
[dictionary setObject:[error localizedDescription] forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
[self executeRequestWithoutData: command withMethod:@"GET"];
}
- (void)uploadFile:(CDVInvokedUrlCommand*)command {
HttpManager *manager = [HttpManager sharedClient];
- (void)delete:(CDVInvokedUrlCommand*)command {
[self executeRequestWithoutData: command withMethod:@"DELETE"];
}
- (void)head:(CDVInvokedUrlCommand*)command {
[self executeRequestWithoutData: command withMethod:@"HEAD"];
}
- (void)options:(CDVInvokedUrlCommand*)command {
[self executeRequestWithoutData: command withMethod:@"OPTIONS"];
}
- (void)uploadFiles:(CDVInvokedUrlCommand*)command {
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
NSString *filePath = [command.arguments objectAtIndex: 3];
NSString *name = [command.arguments objectAtIndex: 4];
NSURL *fileURL = [NSURL fileURLWithPath: filePath];
[self setRequestHeaders: headers];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSArray *filePaths = [command.arguments objectAtIndex: 2];
NSArray *names = [command.arguments objectAtIndex: 3];
NSTimeInterval connectTimeout = [[command.arguments objectAtIndex:4] doubleValue];
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:5] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:6] boolValue];
NSString *responseType = [command.arguments objectAtIndex:7];
NSNumber *reqId = [command.arguments objectAtIndex:8];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:readTimeout forManager:manager];
[self setRedirect:followRedirect forManager:manager];
[self setResponseSerializer:responseType forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSError *error;
[formData appendPartWithFileURL:fileURL name:name error:&error];
if (error) {
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
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];
NSString *uploadName = (NSString *) [names objectAtIndex:i];
NSURL *fileURL = [NSURL URLWithString: filePath];
[formData appendPartWithFileURL:fileURL name:uploadName error:&error];
}
if (error) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
[dictionary setObject:@"Could not add file to post body." forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
return;
}
} progress:nil success:^(NSURLSessionTask *task, id responseObject) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
[dictionary setObject:@"Could not add image to post body." forKey:@"error"];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
[dictionary setObject:[error localizedDescription] forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
[self addRequest:reqId forTask:task];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)downloadFile:(CDVInvokedUrlCommand*)command {
HttpManager *manager = [HttpManager sharedClient];
SM_AFHTTPSessionManager *manager = [SM_AFHTTPSessionManager manager];
manager.responseSerializer = [SM_AFHTTPResponseSerializer serializer];
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
NSString *filePath = [command.arguments objectAtIndex: 3];
[self setRequestHeaders: headers];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSString *filePath = [command.arguments objectAtIndex: 2];
NSTimeInterval connectTimeout = [[command.arguments objectAtIndex:3] doubleValue];
NSTimeInterval readTimeout = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
NSNumber *reqId = [command.arguments objectAtIndex:6];
[self setRequestHeaders: headers forManager: manager];
[self setupAuthChallengeBlock: manager];
[self setTimeout:readTimeout forManager:manager];
[self setRedirect:followRedirect forManager:manager];
if ([filePath hasPrefix:@"file://"]) {
filePath = [filePath substringFromIndex:7];
}
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Modified by Andrew Stephan for Sync OnSet
*
*/
// Download response is okay; begin streaming output to file
NSString* parentPath = [filePath stringByDeletingLastPathComponent];
// create parent directories if needed
NSError *error;
if ([[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:&error] == NO) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
if (error) {
[dictionary setObject:[NSString stringWithFormat:@"Could not create path to save downloaded file: %@", [error localizedDescription]] forKey:@"error"];
} else {
[dictionary setObject:@"Could not create path to save downloaded file" forKey:@"error"];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
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
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Modified by Andrew Stephan for Sync OnSet
*
*/
// Download response is okay; begin streaming output to file
NSString* parentPath = [filePath stringByDeletingLastPathComponent];
// create parent directories if needed
NSError *error;
if ([[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:&error] == NO) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
if (error) {
[dictionary setObject:[NSString stringWithFormat:@"Could not create path to save downloaded file: %@", [error localizedDescription]] forKey:@"error"];
} else {
[dictionary setObject:@"Could not create path to save downloaded file" forKey:@"error"];
}
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
return;
}
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}
NSData *data = (NSData *)responseObject;
if (![data writeToFile:filePath atomically:YES]) {
NSData *data = (NSData *)responseObject;
if (![data writeToFile:filePath atomically:YES]) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
[dictionary setObject:@"Could not write the data to the given filePath." forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
return;
}
id filePlugin = [self.commandDelegate getCommandInstance:@"File"];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
[dictionary setObject:@"Could not write the data to the given filePath." forKey:@"error"];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
[dictionary setObject:[filePlugin getDirectoryEntry:filePath isDirectory:NO] forKey:@"file"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
[weakSelf removeRequest:reqId];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
[dictionary setObject:@"There was an error downloading the file" forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
[self addRequest:reqId forTask:task];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)abort:(CDVInvokedUrlCommand*)command {
NSNumber *reqId = [command.arguments objectAtIndex:0];
CDVPluginResult *pluginResult;
bool removed = false;
NSURLSessionDataTask *task = [reqDict objectForKey:reqId];
if(task){
@try{
[task cancel];
removed = true;
} @catch (NSException *exception) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setValue:exception.userInfo forKey:@"error"];
[dictionary setObject:[NSNumber numberWithInt:-1] forKey:@"status"];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
}
CDVFile *file = [[CDVFile alloc] init];
}
if(!pluginResult){
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
[dictionary setObject:[file getDirectoryEntry:filePath isDirectory:NO] forKey:@"file"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"];
[dictionary setObject:[error localizedDescription] forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
[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,35 +0,0 @@
// Copyright (c) 2012 Mattt Thompson (http://mattt.me/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Modified by Andrew Stephan
#import "HttpManager.h"
@implementation HttpManager
+ (instancetype)sharedClient {
static HttpManager *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [HttpManager manager];
});
return _sharedClient;
}
@end

View File

@@ -0,0 +1,20 @@
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,52 @@
# SDNetworkActivityIndicator
Handle showing / hiding of the iOS network activity indicator to allow multiple concurrent threads to show / hide the indicator such that the indicator remains visible until all the requests have completed and requested the indicator to be hidden.
## Requirements
* iOS 5.0 or later.
* ARC memory management.
## Installation
The easiest way to install it is by copying the following files to your project:
* SDNetworkActivityIndicator.h
* SDNetworkActivityIndicator.m
## Usage
* When you start a network activity (will show the network activity indicator):
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
* When you finish a network activity (will hide the network activity indicator only if the number of calls to `stopActivity` matches the number of calls to `startActivity`):
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
* To hide the network activity indicator regardless of whether all activities have finished (without having to call `stopActivity` for each `startActivity` called):
[[SDNetworkActivityIndicator sharedActivityIndicator] stopAllActivity];
## License
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,18 @@
/*
* This file is part of the SDNetworkActivityIndicator package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import <Foundation/Foundation.h>
@interface SDNetworkActivityIndicator : NSObject
+ (id)sharedActivityIndicator;
- (void)startActivity;
- (void)stopActivity;
- (void)stopAllActivity;
@end

View File

@@ -0,0 +1,70 @@
/*
* This file is part of the SDNetworkActivityIndicator package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDNetworkActivityIndicator.h"
@interface SDNetworkActivityIndicator()
{
@private NSUInteger counter;
}
@end
@implementation SDNetworkActivityIndicator
+ (instancetype) sharedActivityIndicator
{
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (id)init
{
if ((self = [super init]))
{
counter = 0;
}
return self;
}
- (void)startActivity
{
@synchronized(self)
{
counter++;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
}
- (void)stopActivity
{
@synchronized(self)
{
if (counter > 0 && --counter == 0)
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
}
}
- (void)stopAllActivity
{
@synchronized(self)
{
counter = 0;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
}
@end

View File

@@ -0,0 +1,353 @@
// 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
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#if !TARGET_OS_WATCH
#import <SystemConfiguration/SystemConfiguration.h>
#endif
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <CoreServices/CoreServices.h>
#endif
#import "SM_AFURLSessionManager.h"
/**
`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 `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.
## Methods to Override
To change the behavior of all data task operation construction, which is also used in the `GET` / `POST` / et al. convenience methods, override `dataTaskWithRequest:completionHandler:`.
## 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 `<SM_AFURLRequestSerialization>`.
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
For HTTP convenience methods, the request serializer constructs URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`, when provided. If `baseURL` is `nil`, `path` needs to resolve to a valid `NSURL` object using `NSURL +URLWithString:`.
Below are a few examples of how `baseURL` and relative paths interact:
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz
[NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo
[NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/
[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
Also important to note is that a trailing slash will be added to any `baseURL` without one. This would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash.
@warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance.
*/
NS_ASSUME_NONNULL_BEGIN
@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.
*/
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
/**
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `SM_AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
@warning `requestSerializer` must not be `nil`.
*/
@property (nonatomic, strong) SM_AFHTTPRequestSerializer <SM_AFURLRequestSerialization> * requestSerializer;
/**
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `SM_AFJSONResponseSerializer`.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) SM_AFHTTPResponseSerializer <SM_AFURLResponseSerialization> * responseSerializer;
///---------------------
/// @name Initialization
///---------------------
/**
Creates and returns an `SM_AFHTTPSessionManager` object.
*/
+ (instancetype)manager;
/**
Initializes an `SM_AFHTTPSessionManager` object with the specified base URL.
@param url The base URL for the HTTP client.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
/**
Initializes an `SM_AFHTTPSessionManager` object with the specified base URL.
This is the designated initializer.
@param url The base URL for the HTTP client.
@param configuration The configuration used to create the managed session.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url
sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
///---------------------------
/// @name Making HTTP Requests
///---------------------------
/**
Creates and runs an `NSURLSessionDataTask` with a `GET` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
/**
Creates and runs an `NSURLSessionDataTask` with a `GET` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `HEAD` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes a single arguments: the data task.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
/**
Creates and runs an `NSURLSessionDataTask` with a `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
/**
Creates and runs an `NSURLSessionDataTask` with a multipart `POST` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `PUT` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `PATCH` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a `DELETE` request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:completionHandler:
*/
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a multipart request using given HTTP method.
@param HTTPMethod The HTTP method used to create the request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
- (nullable NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)HTTPMethod
URLString:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with given HTTP method.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param uploadProgress A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
- (nullable NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)HTTPMethod
URLString:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
/**
Creates and runs an `NSURLSessionDataTask` with a given HTTP method.
@param HTTPMethod The HTTP method used to create the request.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded according to the client request serializer.
@param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param success A block object to be executed when the task finishes successfully. This block has no return value and takes two arguments: the data task, and the response object created by the client response serializer.
@param failure A block object to be executed when the task finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a two arguments: the data task and the error describing the network or parsing error that occurred.
@see -dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:
*/
- (nullable NSURLSessionDataTask *)downloadTaskWithHTTPMethod:(NSString *)HTTPMethod
URLString:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,464 @@
// 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
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "SM_AFHTTPSessionManager.h"
#import "SM_AFURLRequestSerialization.h"
#import "SM_AFURLResponseSerialization.h"
#import <Availability.h>
#import <TargetConditionals.h>
#import <Security/Security.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
#elif TARGET_OS_WATCH
#import <WatchKit/WatchKit.h>
#endif
@interface SM_AFHTTPSessionManager ()
@property (readwrite, nonatomic, strong) NSURL *baseURL;
@end
@implementation SM_AFHTTPSessionManager
@dynamic responseSerializer;
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
- (instancetype)init {
return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [SM_AFHTTPRequestSerializer serializer];
self.responseSerializer = [SM_AFJSONResponseSerializer serializer];
return self;
}
#pragma mark -
- (void)setRequestSerializer:(SM_AFHTTPRequestSerializer <SM_AFURLRequestSerialization> *)requestSerializer {
NSParameterAssert(requestSerializer);
_requestSerializer = requestSerializer;
}
- (void)setResponseSerializer:(SM_AFHTTPResponseSerializer <SM_AFURLResponseSerialization> *)responseSerializer {
NSParameterAssert(responseSerializer);
[super setResponseSerializer:responseSerializer];
}
#pragma mark -
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
return [self GET:URLString parameters:parameters progress:nil success:success failure:failure];
}
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, __unused id responseObject) {
if (success) {
success(task);
}
} failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
return [self POST:URLString parameters:parameters progress:nil success:success failure:failure];
}
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id<AFMultipartFormData> _Nonnull))block
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
return [self POST:URLString parameters:parameters constructingBodyWithBlock:block progress:nil success:success failure:failure];
}
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
[request setHTTPShouldHandleCookies:NO];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
[request setHTTPShouldHandleCookies:NO];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
[request setHTTPShouldHandleCookies:NO];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)uploadTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:method URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)downloadTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *task = [self dataTaskWithHTTPMethod:method URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:downloadProgress success:success failure:failure];
[task resume];
return task;
}
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
[request setHTTPShouldHandleCookies:NO];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
#pragma mark - NSObject
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue];
}
#pragma mark - NSSecureCoding
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))];
NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"];
if (!configuration) {
NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"];
if (configurationIdentifier) {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100)
configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier];
#else
configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier];
#endif
}
}
self = [self initWithBaseURL:baseURL sessionConfiguration:configuration];
if (!self) {
return nil;
}
self.requestSerializer = [decoder decodeObjectOfClass:[SM_AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))];
self.responseSerializer = [decoder decodeObjectOfClass:[SM_AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))];
SM_AFSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[SM_AFSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))];
if (decodedPolicy) {
self.securityPolicy = decodedPolicy;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))];
if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) {
[coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"];
} else {
[coder encodeObject:self.session.configuration.identifier forKey:@"identifier"];
}
[coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))];
[coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))];
[coder encodeObject:self.securityPolicy forKey:NSStringFromSelector(@selector(securityPolicy))];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone {
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];
HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone];
return HTTPClient;
}
@end

View File

@@ -1,6 +1,5 @@
// AFNetworkReachabilityManager.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com)
// 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
// of this software and associated documentation files (the "Software"), to deal
@@ -8,10 +7,10 @@
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -21,13 +20,9 @@
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#if !TARGET_OS_WATCH
#import <SystemConfiguration/SystemConfiguration.h>
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
@@ -36,14 +31,18 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
NS_ASSUME_NONNULL_BEGIN
/**
`AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces.
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.
`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 `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.
@@ -74,11 +73,18 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
*/
+ (instancetype)sharedManager;
/**
Creates and returns a network reachability manager with the default socket address.
@return An initialized network reachability manager, actively monitoring the default socket address.
*/
+ (instancetype)manager;
/**
Creates and returns a network reachability manager for the specified domain.
@param domain The domain used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified domain.
*/
+ (instancetype)managerForDomain:(NSString *)domain;
@@ -86,20 +92,20 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
/**
Creates and returns a network reachability manager for the socket address.
@param address The socket address used to evaluate network reachability.
@param address The socket address (`sockaddr_in6`) used to evaluate network reachability.
@return An initialized network reachability manager, actively monitoring the specified socket address.
*/
+ (instancetype)managerForAddress:(const struct sockaddr_in *)address;
+ (instancetype)managerForAddress:(const void *)address;
/**
Initializes an instance of a network reachability manager from the specified reachability object.
@param reachability The reachability object to monitor.
@return An initialized network reachability manager, actively monitoring the specified reachability.
*/
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability;
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
///--------------------------------------------------
/// @name Starting & Stopping Reachability Monitoring
@@ -133,7 +139,7 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
@param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
*/
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block;
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
@end
@@ -144,7 +150,7 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
/**
## 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,
@@ -169,8 +175,8 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
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.
*/
@@ -180,12 +186,12 @@ typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
/**
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`).
*/
extern NSString * const AFNetworkingReachabilityDidChangeNotification;
extern NSString * const AFNetworkingReachabilityNotificationStatusItem;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingReachabilityNotificationStatusItem;
///--------------------
/// @name Functions
@@ -194,4 +200,7 @@ extern NSString * const AFNetworkingReachabilityNotificationStatusItem;
/**
Returns a localized string representation of an `AFNetworkReachabilityStatus` value.
*/
extern NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
FOUNDATION_EXPORT NSString * SM_AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
NS_ASSUME_NONNULL_END
#endif

View File

@@ -1,6 +1,5 @@
// AFNetworkReachabilityManager.m
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com)
// 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
// of this software and associated documentation files (the "Software"), to deal
@@ -8,10 +7,10 @@
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -20,30 +19,31 @@
// 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
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
NSString * const SM_AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
NSString * const SM_AFNetworkingReachabilityNotificationStatusItem = @"SM_AFNetworkingReachabilityNotificationStatusItem";
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
typedef NS_ENUM(NSUInteger, AFNetworkReachabilityAssociation) {
AFNetworkReachabilityForAddress = 1,
AFNetworkReachabilityForAddressPair = 2,
AFNetworkReachabilityForName = 3,
};
NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
NSString * SM_AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
return NSLocalizedStringFromTable(@"Not Reachable", @"SM_AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWWAN:
return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
return NSLocalizedStringFromTable(@"Reachable via WWAN", @"SM_AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWiFi:
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"SM_AFNetworking", nil);
case AFNetworkReachabilityStatusUnknown:
default:
return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
return NSLocalizedStringFromTable(@"Unknown", @"SM_AFNetworking", nil);
}
}
@@ -70,20 +70,31 @@ static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetwork
return status;
}
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
/**
* Queue a status change notification for the main thread.
*
* This is done to ensure that the notifications are received in the same order
* as they are sent. If notifications are sent directly, it is possible that
* a queued notification (for an earlier status condition) is processed after
* the later update, resulting in the listener being left in the wrong state.
*/
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
AFNetworkReachabilityStatusBlock block = (__bridge AFNetworkReachabilityStatusBlock)info;
if (block) {
block(status);
}
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block(status);
}
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }];
NSDictionary *userInfo = @{ SM_AFNetworkingReachabilityNotificationStatusItem: @(status) };
[notificationCenter postNotificationName:SM_AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
@@ -94,25 +105,19 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
}
}
@interface AFNetworkReachabilityManager ()
@property (readwrite, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
@property (readwrite, nonatomic, assign) AFNetworkReachabilityAssociation networkReachabilityAssociation;
@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, ^{
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
_sharedManager = [self managerForAddress:&address];
_sharedManager = [self manager];
});
return _sharedManager;
@@ -121,39 +126,60 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
manager.networkReachabilityAssociation = AFNetworkReachabilityForName;
SM_AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const struct sockaddr_in *)address {
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
SM_AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
manager.networkReachabilityAssociation = AFNetworkReachabilityForAddress;
CFRelease(reachability);
return manager;
}
+ (instancetype)manager
{
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address];
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
self.networkReachability = reachability;
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
- (instancetype)init NS_UNAVAILABLE
{
return nil;
}
- (void)dealloc {
[self stopMonitoring];
if (_networkReachability) {
if (_networkReachability != NULL) {
CFRelease(_networkReachability);
_networkReachability = NULL;
}
}
@@ -188,29 +214,19 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
switch (self.networkReachabilityAssociation) {
case AFNetworkReachabilityForName:
break;
case AFNetworkReachabilityForAddress:
case AFNetworkReachabilityForAddressPair:
default: {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
SCNetworkReachabilityGetFlags(self.networkReachability, &flags);
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
callback(status);
});
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
break;
}
});
}
- (void)stopMonitoring {
@@ -224,7 +240,7 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
#pragma mark -
- (NSString *)localizedNetworkReachabilityStatusString {
return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
return SM_AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}
#pragma mark -
@@ -244,3 +260,4 @@ static void AFNetworkReachabilityReleaseCallback(const void *info) {
}
@end
#endif

View File

@@ -1,4 +1,6 @@
// Copyright (c) 2012 Mattt Thompson (http://mattt.me/)
// SM_AFNetworking.h
//
// 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
@@ -17,12 +19,23 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Modified by Andrew Stephan
#import <Foundation/Foundation.h>
#import "AFHTTPRequestOperationManager.h"
#import <Availability.h>
#import <TargetConditionals.h>
@interface HttpManager : AFHTTPRequestOperationManager
#ifndef _AFNETWORKING_
#define _AFNETWORKING_
+ (instancetype)sharedClient;
#import "SM_AFURLRequestSerialization.h"
#import "SM_AFURLResponseSerialization.h"
#import "SM_AFSecurityPolicy.h"
@end
#if !TARGET_OS_WATCH
#import "SM_AFNetworkReachabilityManager.h"
#endif
#import "SM_AFURLSessionManager.h"
#import "SM_AFHTTPSessionManager.h"
#endif /* _AFNETWORKING_ */

View File

@@ -1,6 +1,5 @@
// AFSecurity.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com)
// 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
// of this software and associated documentation files (the "Software"), to deal
@@ -30,26 +29,28 @@ 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.
*/
@interface AFSecurityPolicy : NSObject
NS_ASSUME_NONNULL_BEGIN
@interface SM_AFSecurityPolicy : NSObject <NSSecureCoding, NSCopying>
/**
The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`.
*/
@property (nonatomic, assign) AFSSLPinningMode SSLPinningMode;
@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
/**
Whether to evaluate an entire SSL certificate chain, or just the leaf certificate. Defaults to `YES`.
*/
@property (nonatomic, assign) BOOL validatesCertificateChain;
The certificates used to evaluate server trust according to the SSL pinning mode.
/**
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 app bundle.
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.
*/
@property (nonatomic, strong) NSArray *pinnedCertificates;
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
/**
Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`.
@@ -57,17 +58,28 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
@property (nonatomic, assign) BOOL allowInvalidCertificates;
/**
Whether or not to validate the domain name in the certificates CN field. Defaults to `YES` for `AFSSLPinningModePublicKey` or `AFSSLPinningModeCertificate`, otherwise `NO`.
Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`.
*/
@property (nonatomic, assign) BOOL validatesDomainName;
///-----------------------------------------
/// @name Getting Certificates from the Bundle
///-----------------------------------------
/**
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.
*/
+ (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle;
///-----------------------------------------
/// @name Getting Specific Security Policies
///-----------------------------------------
/**
Returns the shared default security policy, which does not accept invalid certificates, and does not validate against pinned certificates or public keys.
Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys.
@return The default security policy.
*/
+ (instancetype)defaultPolicy;
@@ -78,13 +90,23 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
/**
Creates and returns a security policy with the specified pinning mode.
@param pinningMode The SSL pinning mode.
@return A new security policy.
*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;
/**
Creates and returns a security policy with the specified pinning mode.
@param pinningMode The SSL pinning mode.
@param pinnedCertificates The certificates to pin against.
@return A new security policy.
*/
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates;
///------------------------------
/// @name Evaluating Server Trust
///------------------------------
@@ -94,29 +116,18 @@ typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
This method should be used when responding to an authentication challenge from a server.
@param serverTrust The X.509 certificate trust of the server.
@return Whether or not to trust the server.
@warning This method has been deprecated in favor of `-evaluateServerTrust:forDomain:`.
*/
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust DEPRECATED_ATTRIBUTE;
/**
Whether or not the specified server trust should be accepted, based on the security policy.
This method should be used when responding to an authentication challenge from a server.
@param serverTrust The X.509 certificate trust of the server.
@param domain The domain of serverTrust. If `nil`, the domain will not be validated.
@return Whether or not to trust the server.
*/
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain;
forDomain:(nullable NSString *)domain;
@end
NS_ASSUME_NONNULL_END
///----------------
/// @name Constants
///----------------

View File

@@ -0,0 +1,363 @@
// 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
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "SM_AFSecurityPolicy.h"
#import <AssertMacros.h>
#if !TARGET_OS_IOS && !TARGET_OS_WATCH && !TARGET_OS_TV
static NSData * AFSecKeyGetData(SecKeyRef key) {
CFDataRef data = NULL;
__Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out);
return (__bridge_transfer NSData *)data;
_out:
if (data) {
CFRelease(data);
}
return nil;
}
#endif
static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
return [(__bridge id)key1 isEqual:(__bridge id)key2];
#else
return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)];
#endif
}
static id AFPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecCertificateRef allowedCertificates[1];
CFArrayRef tempCertificates = nil;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result;
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
__Require_Quiet(allowedCertificate != NULL, _out);
allowedCertificates[0] = allowedCertificate;
tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
policy = SecPolicyCreateBasicX509();
__Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
_out:
if (allowedTrust) {
CFRelease(allowedTrust);
}
if (policy) {
CFRelease(policy);
}
if (tempCertificates) {
CFRelease(tempCertificates);
}
if (allowedCertificate) {
CFRelease(allowedCertificate);
}
return allowedPublicKey;
}
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) {
BOOL isValid = NO;
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
_out:
return isValid;
}
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
}
return [NSArray arrayWithArray:trustChain];
}
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
SecPolicyRef policy = SecPolicyCreateBasicX509();
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
for (CFIndex i = 0; i < certificateCount; i++) {
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
SecCertificateRef someCertificates[] = {certificate};
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
SecTrustRef trust;
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
SecTrustResultType result;
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
_out:
if (trust) {
CFRelease(trust);
}
if (certificates) {
CFRelease(certificates);
}
continue;
}
CFRelease(policy);
return [NSArray arrayWithArray:trustChain];
}
#pragma mark -
@interface SM_AFSecurityPolicy()
@property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode;
@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;
@end
@implementation SM_AFSecurityPolicy
+(BOOL) isRunningOnCapacitor {
return NSClassFromString(@"CAPPlugin") != nil;
}
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
NSString* assetDir = @"www";
if([self isRunningOnCapacitor]) {
// we are running on capacitor and its assets dir is 'public'
assetDir = @"public";
}
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory: [NSString stringWithFormat:@"%@/certificates", assetDir]];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
return [NSSet setWithSet:certificates];
}
+ (NSSet *)defaultPinnedCertificates {
static NSSet *_defaultPinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [self isRunningOnCapacitor] ? [NSBundle mainBundle] : [NSBundle bundleForClass:[self class]];
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
});
return _defaultPinnedCertificates;
}
+ (instancetype)defaultPolicy {
SM_AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
SM_AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = pinningMode;
[securityPolicy setPinnedCertificates:pinnedCertificates];
return securityPolicy;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.validatesDomainName = YES;
return self;
}
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
id publicKey = AFPublicKeyForCertificate(certificate);
if (!publicKey) {
continue;
}
[mutablePinnedPublicKeys addObject:publicKey];
}
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
#pragma mark -
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
#pragma mark - NSKeyValueObserving
+ (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys {
return [NSSet setWithObject:@"pinnedCertificates"];
}
#pragma mark - NSSecureCoding
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [self init];
if (!self) {
return nil;
}
self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue];
self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))];
self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))];
[coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))];
[coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))];
[coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))];
}
#pragma mark - NSCopying
- (instancetype)copyWithZone:(NSZone *)zone {
SM_AFSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
securityPolicy.SSLPinningMode = self.SSLPinningMode;
securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
securityPolicy.validatesDomainName = self.validatesDomainName;
securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone];
return securityPolicy;
}
@end

View File

@@ -1,6 +1,5 @@
// AFSerialization.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com)
// 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
// of this software and associated documentation files (the "Software"), to deal
@@ -8,10 +7,10 @@
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -21,16 +20,47 @@
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <TargetConditionals.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
#elif TARGET_OS_WATCH
#import <WatchKit/WatchKit.h>
#endif
NS_ASSUME_NONNULL_BEGIN
/**
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.
Returns a percent-escaped string following RFC 3986 for a query string key or value.
RFC 3986 states that the following characters are "reserved" characters.
- General Delimiters: ":", "#", "[", "]", "@", "?", "/"
- Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
should be percent-escaped in the query string.
@param string The string to be percent-escaped.
@return The percent-escaped string.
*/
FOUNDATION_EXPORT NSString * SM_AFPercentEscapedStringFromString(NSString *string);
/**
A helper method to generate encoded url query parameters for appending to the end of a URL.
@param parameters A dictionary of key/values to be encoded.
@return A url encoded query string
*/
FOUNDATION_EXPORT NSString * SM_AFQueryStringFromParameters(NSDictionary *parameters);
/**
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, NSCoding, NSCopying>
@protocol SM_AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>
/**
Returns a request with the specified parameters encoded into a copy of the original request.
@@ -41,9 +71,9 @@
@return A serialized request.
*/
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError * __autoreleasing *)error;
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
@@ -59,11 +89,11 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
@protocol AFMultipartFormData;
/**
`AFHTTPRequestSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
`SM_AFHTTPRequestSerializer` conforms to the `SM_AFURLRequestSerialization` & `SM_AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPRequestSerializer` in order to ensure consistent default behavior.
Any request or response serializer dealing with HTTP is encouraged to subclass `SM_AFHTTPRequestSerializer` in order to ensure consistent default behavior.
*/
@interface AFHTTPRequestSerializer : NSObject <AFURLRequestSerialization>
@interface SM_AFHTTPRequestSerializer : NSObject <SM_AFURLRequestSerialization>
/**
The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default.
@@ -72,42 +102,42 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
/**
Whether created requests can use the devices cellular radio (if present). `YES` by default.
@see NSMutableURLRequest -setAllowsCellularAccess:
*/
@property (nonatomic, assign) BOOL allowsCellularAccess;
/**
The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default.
@see NSMutableURLRequest -setCachePolicy:
*/
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
/**
Whether created requests should use the default cookie handling. `YES` by default.
@see NSMutableURLRequest -setHTTPShouldHandleCookies:
*/
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
/**
Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default
@see NSMutableURLRequest -setHTTPShouldUsePipelining:
*/
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
/**
The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default.
@see NSMutableURLRequest -setNetworkServiceType:
*/
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
/**
The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds.
@see NSMutableURLRequest -setTimeoutInterval:
*/
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
@@ -117,9 +147,14 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
///---------------------------------------
/**
Default HTTP header field values to be applied to serialized requests.
Default HTTP header field values to be applied to serialized requests. By default, these include the following:
- `Accept-Language` with the contents of `NSLocale +preferredLanguages`
- `User-Agent` with the contents of various bundle identifiers and OS designations
@discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`.
*/
@property (readonly, nonatomic, strong) NSDictionary *HTTPRequestHeaders;
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;
/**
Creates and returns a serializer with default configuration.
@@ -132,9 +167,18 @@ typedef NS_ENUM(NSUInteger, AFHTTPRequestQueryStringSerializationStyle) {
@param field The HTTP header to set a default value for
@param value The value set as default for the specified header, or `nil`
*/
- (void)setValue:(NSString *)value
- (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field;
/**
Returns the value for the HTTP headers set in the request serializer.
@param field The HTTP header to retrieve the default value for
@return The value set as default for the specified header, or `nil`
*/
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;
/**
Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header.
@@ -144,14 +188,6 @@ forHTTPHeaderField:(NSString *)field;
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password;
/**
Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a token-based authentication value, such as an OAuth access token. This overwrites any existing value for this header.
@param token The authentication token
*/
- (void)setAuthorizationHeaderFieldWithToken:(NSString *)token;
/**
Clears any existing value for the "Authorization" HTTP header.
*/
@@ -164,7 +200,7 @@ forHTTPHeaderField:(NSString *)field;
/**
HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default.
*/
@property (nonatomic, strong) NSSet *HTTPMethodsEncodingParametersInURI;
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;
/**
Set the method of query string serialization according to one of the pre-defined styles.
@@ -180,19 +216,12 @@ forHTTPHeaderField:(NSString *)field;
@param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request.
*/
- (void)setQueryStringSerializationWithBlock:(NSString * (^)(NSURLRequest *request, NSDictionary *parameters, NSError * __autoreleasing *error))block;
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
///-------------------------------
/// @name Creating Request Objects
///-------------------------------
/**
@deprecated This method has been deprecated. Use -requestWithMethod:URLString:parameters:error: instead.
*/
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters DEPRECATED_ATTRIBUTE;
/**
Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string.
@@ -201,22 +230,14 @@ forHTTPHeaderField:(NSString *)field;
@param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`.
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body.
@param error The error that occured while constructing the request.
@param error The error that occurred while constructing the request.
@return An `NSMutableURLRequest` object.
*/
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError * __autoreleasing *)error;
/**
@deprecated This method has been deprecated. Use -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error: instead.
*/
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block DEPRECATED_ATTRIBUTE;
parameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error;
/**
Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2
@@ -227,40 +248,37 @@ forHTTPHeaderField:(NSString *)field;
@param URLString The URL string used to create the request URL.
@param parameters The parameters to be encoded and set in the request HTTP body.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
@param error The error that occured while constructing the request.
@param error The error that occurred while constructing the request.
@return An `NSMutableURLRequest` object
*/
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
error:(NSError * __autoreleasing *)error;
parameters:(nullable NSDictionary <NSString *, id> *)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
error:(NSError * _Nullable __autoreleasing *)error;
/**
Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished.
@param request The multipart form request.
@param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`.
@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
*/
- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
writingStreamContentsToFile:(NSURL *)fileURL
completionHandler:(void (^)(NSError *error))handler;
completionHandler:(nullable void (^)(NSError * _Nullable error))handler;
@end
#pragma mark -
extern NSUInteger const kAFUploadStream3GSuggestedPacketSize;
extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
/**
The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`.
The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `SM_AFHTTPRequestSerializer -multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:`.
*/
@protocol AFMultipartFormData
@@ -277,7 +295,7 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
error:(NSError * __autoreleasing *)error;
error:(NSError * _Nullable __autoreleasing *)error;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary.
@@ -294,7 +312,7 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
error:(NSError * __autoreleasing *)error;
error:(NSError * _Nullable __autoreleasing *)error;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary.
@@ -305,10 +323,10 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
@param length The length of the specified input stream in bytes.
@param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`.
*/
- (void)appendPartWithInputStream:(NSInputStream *)inputStream
- (void)appendPartWithInputStream:(nullable NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(NSUInteger)length
length:(int64_t)length
mimeType:(NSString *)mimeType;
/**
@@ -339,15 +357,15 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
Appends HTTP headers, followed by the encoded data and the multipart form boundary.
@param headers The HTTP headers to be appended to the form data.
@param body The data to be encoded and appended to the form data.
@param body The data to be encoded and appended to the form data. This parameter must not be `nil`.
*/
- (void)appendPartWithHeaders:(NSDictionary *)headers
- (void)appendPartWithHeaders:(nullable NSDictionary <NSString *, NSString *> *)headers
body:(NSData *)body;
/**
Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream.
When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth.
When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`SM_kAFUploadStream3GSuggestedPacketSize` and `SM_kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, there is no definite way to distinguish between a 3G, EDGE, or LTE connection over `NSURLConnection`. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth.
@param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 16kb.
@param delay Duration of delay each time a packet is read. By default, no delay is set.
@@ -357,25 +375,12 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
@end
///----------------
/// @name Constants
///----------------
/**
## Throttling Bandwidth for HTTP Request Input Streams
@see -throttleBandwidthWithPacketSize:delay:
`kAFUploadStream3GSuggestedPacketSize`
Maximum packet size, in number of bytes. Equal to 16kb.
`kAFUploadStream3GSuggestedDelay`
Duration of delay each time a packet is read. Equal to 0.2 seconds.
*/
#pragma mark -
@interface AFJSONRequestSerializer : AFHTTPRequestSerializer
/**
`SM_AFJSONRequestSerializer` is a subclass of `SM_AFHTTPRequestSerializer` that encodes parameters as JSON using `NSJSONSerialization`, setting the `Content-Type` of the encoded request to `application/json`.
*/
@interface SM_AFJSONRequestSerializer : SM_AFHTTPRequestSerializer
/**
Options for writing the request JSON data from Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONWritingOptions". `0` by default.
@@ -391,7 +396,12 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
@end
@interface AFPropertyListRequestSerializer : AFHTTPRequestSerializer
#pragma mark -
/**
`SM_AFPropertyListRequestSerializer` is a subclass of `SM_AFHTTPRequestSerializer` that encodes parameters as JSON using `NSPropertyListSerializer`, setting the `Content-Type` of the encoded request to `application/x-plist`.
*/
@interface SM_AFPropertyListRequestSerializer : SM_AFHTTPRequestSerializer
/**
The property list format. Possible values are described in "NSPropertyListFormat".
@@ -408,10 +418,62 @@ extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
@param format The property list format.
@param writeOptions The property list write options.
@warning The `writeOptions` property is currently unused.
*/
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
writeOptions:(NSPropertyListWriteOptions)writeOptions;
@end
#pragma mark -
///----------------
/// @name Constants
///----------------
/**
## Error Domains
The following error domain is predefined.
- `NSString * const SM_AFURLRequestSerializationErrorDomain`
### Constants
`SM_AFURLRequestSerializationErrorDomain`
AFURLRequestSerializer errors. Error codes for `SM_AFURLRequestSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
*/
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 SM_AFNetworkingOperationFailingURLRequestErrorKey`
### Constants
`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 SM_AFNetworkingOperationFailingURLRequestErrorKey;
/**
## Throttling Bandwidth for HTTP Request Input Streams
@see -throttleBandwidthWithPacketSize:delay:
### Constants
`SM_kAFUploadStream3GSuggestedPacketSize`
Maximum packet size, in number of bytes. Equal to 16kb.
`SM_kAFUploadStream3GSuggestedDelay`
Duration of delay each time a packet is read. Equal to 0.2 seconds.
*/
FOUNDATION_EXPORT NSUInteger const SM_kAFUploadStream3GSuggestedPacketSize;
FOUNDATION_EXPORT NSTimeInterval const SM_kAFUploadStream3GSuggestedDelay;
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
// AFSerialization.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com)
// 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
// of this software and associated documentation files (the "Software"), to deal
@@ -8,10 +7,10 @@
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -23,12 +22,14 @@
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
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, NSCoding, NSCopying>
@protocol SM_AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
/**
The response object decoded from the data associated with a specified response.
@@ -39,23 +40,25 @@
@return The object decoded from the specified response data.
*/
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error;
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
@end
#pragma mark -
/**
`AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
`SM_AFHTTPResponseSerializer` conforms to the `SM_AFURLRequestSerialization` & `SM_AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation.
Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior.
Any request or response serializer dealing with HTTP is encouraged to subclass `SM_AFHTTPResponseSerializer` in order to ensure consistent default behavior.
*/
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>
@interface SM_AFHTTPResponseSerializer : NSObject <SM_AFURLResponseSerialization>
- (instancetype)init;
/**
The string encoding used to serialize parameters.
The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default.
*/
@property (nonatomic, assign) NSStringEncoding stringEncoding;
@@ -73,12 +76,12 @@
See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
*/
@property (nonatomic, strong) NSIndexSet *acceptableStatusCodes;
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
/**
The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation.
*/
@property (nonatomic, strong) NSSet *acceptableContentTypes;
@property (nonatomic, copy, nullable) NSSet <NSString *> *acceptableContentTypes;
/**
Validates the specified response and data.
@@ -91,9 +94,9 @@
@return `YES` if the response is valid, otherwise `NO`.
*/
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error;
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;
@end
@@ -101,21 +104,28 @@
/**
`AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses.
`SM_AFJSONResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes JSON responses.
By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types:
By default, `SM_AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types:
- `application/json`
- `text/json`
- `text/javascript`
*/
@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
@interface SM_AFJSONResponseSerializer : SM_AFHTTPResponseSerializer
- (instancetype)init;
/**
Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default.
*/
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
/**
Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`.
*/
@property (nonatomic, assign) BOOL removesKeysWithNullValues;
/**
Creates and returns a JSON serializer with specified reading and writing options.
@@ -128,14 +138,14 @@
#pragma mark -
/**
`AFXMLParserSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects.
`SM_AFXMLParserResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects.
By default, `AFXMLParserSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
By default, `SM_AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
- `application/xml`
- `text/xml`
*/
@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer
@interface SM_AFXMLParserResponseSerializer : SM_AFHTTPResponseSerializer
@end
@@ -144,14 +154,16 @@
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
/**
`AFXMLDocumentSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
`AFXMLDocumentResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
By default, `AFXMLDocumentSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
- `application/xml`
- `text/xml`
*/
@interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer
@interface AFXMLDocumentResponseSerializer : SM_AFHTTPResponseSerializer
- (instancetype)init;
/**
Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default.
@@ -172,13 +184,15 @@
#pragma mark -
/**
`AFPropertyListSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
`SM_AFPropertyListResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects.
By default, `AFPropertyListSerializer` accepts the following MIME types:
By default, `SM_AFPropertyListResponseSerializer` accepts the following MIME types:
- `application/x-plist`
*/
@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer
@interface SM_AFPropertyListResponseSerializer : SM_AFHTTPResponseSerializer
- (instancetype)init;
/**
The property list format. Possible values are described in "NSPropertyListFormat".
@@ -204,9 +218,9 @@
#pragma mark -
/**
`AFImageSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses.
`SM_AFImageResponseSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that validates and decodes image responses.
By default, `AFImageSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage:
By default, `SM_AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage:
- `image/tiff`
- `image/jpeg`
@@ -219,9 +233,9 @@
- `image/x-xbitmap`
- `image/x-win-bitmap`
*/
@interface AFImageResponseSerializer : AFHTTPResponseSerializer
@interface SM_AFImageResponseSerializer : SM_AFHTTPResponseSerializer
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
/**
The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance.
*/
@@ -238,20 +252,67 @@
#pragma mark -
/**
`AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer.
`AFCompoundSerializer` is a subclass of `SM_AFHTTPResponseSerializer` that delegates the response serialization to the first `SM_AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `SM_AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer.
*/
@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer
@interface SM_AFCompoundResponseSerializer : SM_AFHTTPResponseSerializer
/**
The component response serializers.
*/
@property (readonly, nonatomic, strong) NSArray *responseSerializers;
@property (readonly, nonatomic, copy) NSArray <id<SM_AFURLResponseSerialization>> *responseSerializers;
/**
Creates and returns a compound serializer comprised of the specified response serializers.
@warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`.
@warning Each response serializer specified must be a subclass of `SM_AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`.
*/
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers;
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray <id<SM_AFURLResponseSerialization>> *)responseSerializers;
@end
///----------------
/// @name Constants
///----------------
/**
## Error Domains
The following error domain is predefined.
- `NSString * const SM_AFURLResponseSerializationErrorDomain`
### Constants
`SM_AFURLResponseSerializationErrorDomain`
AFURLResponseSerializer errors. Error codes for `SM_AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`.
*/
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 SM_AFNetworkingOperationFailingURLResponseErrorKey`
- `NSString * const SM_AFNetworkingOperationFailingURLResponseDataErrorKey`
### Constants
`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`.
`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 SM_AFNetworkingOperationFailingURLResponseErrorKey;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingOperationFailingURLResponseDataErrorKey;
/**
`SM_AFNetworkingOperationFailingURLResponseBodyErrorKey`
The corresponding value is an `NSString` containing the decoded error message.
*/
FOUNDATION_EXPORT NSString * const SM_AFNetworkingOperationFailingURLResponseBodyErrorKey;
NS_ASSUME_NONNULL_END

View File

@@ -1,6 +1,5 @@
// AFSerialization.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com)
// 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
// of this software and associated documentation files (the "Software"), to deal
@@ -8,10 +7,10 @@
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -20,17 +19,23 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFURLResponseSerialization.h"
#import "SM_AFURLResponseSerialization.h"
extern NSString * const AFNetworkingErrorDomain;
extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey;
#import <TargetConditionals.h>
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
#elif TARGET_OS_WATCH
#import <WatchKit/WatchKit.h>
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
#import <Cocoa/Cocoa.h>
#endif
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) {
return underlyingError;
@@ -46,17 +51,42 @@ static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingE
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
if (error.code == code) {
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCode(error.userInfo[NSUnderlyingErrorKey], code);
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
@implementation AFHTTPResponseSerializer
static id AFJSONObjectByRemovingKeysWithNullValues(id JSONObject, NSJSONReadingOptions readingOptions) {
if ([JSONObject isKindOfClass:[NSArray class]]) {
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:[(NSArray *)JSONObject count]];
for (id value in (NSArray *)JSONObject) {
[mutableArray addObject:AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions)];
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableArray : [NSArray arrayWithArray:mutableArray];
} else if ([JSONObject isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:JSONObject];
for (id <NSCopying> key in [(NSDictionary *)JSONObject allKeys]) {
id value = (NSDictionary *)JSONObject[key];
if (!value || [value isEqual:[NSNull null]]) {
[mutableDictionary removeObjectForKey:key];
} else if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
mutableDictionary[key] = AFJSONObjectByRemovingKeysWithNullValues(value, readingOptions);
}
}
return (readingOptions & NSJSONReadingMutableContainers) ? mutableDictionary : [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
return JSONObject;
}
@implementation SM_AFHTTPResponseSerializer
+ (instancetype)serializer {
return [[self alloc] init];
@@ -86,28 +116,37 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) {
if ([data length] > 0) {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response
};
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] &&
!([response MIMEType] == nil && [data length] == 0)) {
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo], validationError);
if ([data length] > 0 && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"SM_AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode]) {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%lu)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (unsigned long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response
};
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"SM_AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo], validationError);
if (data) {
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
@@ -120,7 +159,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
return responseIsValid;
}
#pragma mark - AFURLResponseSerialization
#pragma mark - SM_AFURLResponseSerialization
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
@@ -131,16 +170,20 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
return data;
}
#pragma mark - NSCoding
#pragma mark - NSSecureCoding
- (id)initWithCoder:(NSCoder *)decoder {
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [self init];
if (!self) {
return nil;
}
self.acceptableStatusCodes = [decoder decodeObjectForKey:NSStringFromSelector(@selector(acceptableStatusCodes))];
self.acceptableContentTypes = [decoder decodeObjectForKey:NSStringFromSelector(@selector(acceptableContentTypes))];
self.acceptableStatusCodes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableStatusCodes))];
self.acceptableContentTypes = [decoder decodeObjectOfClass:[NSIndexSet class] forKey:NSStringFromSelector(@selector(acceptableContentTypes))];
return self;
}
@@ -152,8 +195,8 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
- (instancetype)copyWithZone:(NSZone *)zone {
SM_AFHTTPResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
serializer.acceptableStatusCodes = [self.acceptableStatusCodes copyWithZone:zone];
serializer.acceptableContentTypes = [self.acceptableContentTypes copyWithZone:zone];
@@ -164,14 +207,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
#pragma mark -
@implementation AFJSONResponseSerializer
@implementation SM_AFJSONResponseSerializer
+ (instancetype)serializer {
return [self serializerWithReadingOptions:0];
return [self serializerWithReadingOptions:(NSJSONReadingOptions)0];
}
+ (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions {
AFJSONResponseSerializer *serializer = [[self alloc] init];
SM_AFJSONResponseSerializer *serializer = [[self alloc] init];
serializer.readingOptions = readingOptions;
return serializer;
@@ -188,70 +231,50 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
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 (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
NSStringEncoding stringEncoding = self.stringEncoding;
if (response.textEncodingName) {
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
if (encoding != kCFStringEncodingInvalidId) {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
}
}
id responseObject = nil;
NSError *serializationError = nil;
@autoreleasepool {
NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding];
if (responseString && ![responseString isEqualToString:@" "]) {
// Workaround for a bug in NSJSONSerialization when Unicode character escape codes are used instead of the actual character
// See http://stackoverflow.com/a/12843465/157142
data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
if (data) {
if ([data length] > 0) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
} else {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Data failed decoding as a UTF-8 string", nil, @"AFNetworking"),
NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Could not decode string: %@", nil, @"AFNetworking"), responseString]
};
serializationError = [NSError errorWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
}
}
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);;
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
#pragma mark - NSCoding
#pragma mark - NSSecureCoding
- (id)initWithCoder:(NSCoder *)decoder {
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
self.readingOptions = (NSJSONReadingOptions)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(readingOptions))];
self.readingOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readingOptions))] unsignedIntegerValue];
self.removesKeysWithNullValues = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))] boolValue];
return self;
}
@@ -259,14 +282,16 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeInteger:self.readingOptions forKey:NSStringFromSelector(@selector(readingOptions))];
[coder encodeObject:@(self.readingOptions) forKey:NSStringFromSelector(@selector(readingOptions))];
[coder encodeObject:@(self.removesKeysWithNullValues) forKey:NSStringFromSelector(@selector(removesKeysWithNullValues))];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
- (instancetype)copyWithZone:(NSZone *)zone {
SM_AFJSONResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
serializer.readingOptions = self.readingOptions;
serializer.removesKeysWithNullValues = self.removesKeysWithNullValues;
return serializer;
}
@@ -275,10 +300,10 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
#pragma mark -
@implementation AFXMLParserResponseSerializer
@implementation SM_AFXMLParserResponseSerializer
+ (instancetype)serializer {
AFXMLParserResponseSerializer *serializer = [[self alloc] init];
SM_AFXMLParserResponseSerializer *serializer = [[self alloc] init];
return serializer;
}
@@ -294,14 +319,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
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 (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
@@ -339,14 +364,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
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 (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
@@ -361,15 +386,15 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
return document;
}
#pragma mark - NSCoding
#pragma mark - NSSecureCoding
- (id)initWithCoder:(NSCoder *)decoder {
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
self.options = [decoder decodeIntegerForKey:NSStringFromSelector(@selector(options))];
self.options = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(options))] unsignedIntegerValue];
return self;
}
@@ -377,12 +402,12 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeInteger:self.options forKey:NSStringFromSelector(@selector(options))];
[coder encodeObject:@(self.options) forKey:NSStringFromSelector(@selector(options))];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
- (instancetype)copyWithZone:(NSZone *)zone {
AFXMLDocumentResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
serializer.options = self.options;
@@ -395,7 +420,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
#pragma mark -
@implementation AFPropertyListResponseSerializer
@implementation SM_AFPropertyListResponseSerializer
+ (instancetype)serializer {
return [self serializerWithFormat:NSPropertyListXMLFormat_v1_0 readOptions:0];
@@ -404,7 +429,7 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
+ (instancetype)serializerWithFormat:(NSPropertyListFormat)format
readOptions:(NSPropertyListReadOptions)readOptions
{
AFPropertyListResponseSerializer *serializer = [[self alloc] init];
SM_AFPropertyListResponseSerializer *serializer = [[self alloc] init];
serializer.format = format;
serializer.readOptions = readOptions;
@@ -422,14 +447,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
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 (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
@@ -448,16 +473,16 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
return responseObject;
}
#pragma mark - NSCoding
#pragma mark - NSSecureCoding
- (id)initWithCoder:(NSCoder *)decoder {
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
self.format = (NSPropertyListFormat)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(format))];
self.readOptions = (NSPropertyListReadOptions)[decoder decodeIntegerForKey:NSStringFromSelector(@selector(readOptions))];
self.format = (NSPropertyListFormat)[[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(format))] unsignedIntegerValue];
self.readOptions = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(readOptions))] unsignedIntegerValue];
return self;
}
@@ -465,14 +490,14 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeInteger:self.format forKey:NSStringFromSelector(@selector(format))];
[coder encodeInteger:(NSInteger)self.readOptions forKey:NSStringFromSelector(@selector(readOptions))];
[coder encodeObject:@(self.format) forKey:NSStringFromSelector(@selector(format))];
[coder encodeObject:@(self.readOptions) forKey:NSStringFromSelector(@selector(readOptions))];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
- (instancetype)copyWithZone:(NSZone *)zone {
SM_AFPropertyListResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
serializer.format = self.format;
serializer.readOptions = self.readOptions;
@@ -483,11 +508,38 @@ static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
#pragma mark -
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
#import <CoreGraphics/CoreGraphics.h>
#import <UIKit/UIKit.h>
@interface UIImage (SM_AFNetworkingSafeImageLoading)
+ (UIImage *)af_safeImageWithData:(NSData *)data;
@end
static NSLock* imageLock = nil;
@implementation UIImage (SM_AFNetworkingSafeImageLoading)
+ (UIImage *)af_safeImageWithData:(NSData *)data {
UIImage* image = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
imageLock = [[NSLock alloc] init];
});
[imageLock lock];
image = [UIImage imageWithData:data];
[imageLock unlock];
return image;
}
@end
static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
UIImage *image = [[UIImage alloc] initWithData:data];
UIImage *image = [UIImage af_safeImageWithData:data];
if (image.images) {
return image;
}
return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
}
@@ -505,10 +557,11 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
} else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
// CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so if so, fall back to AFImageWithDataAtScale
if (imageRef) {
CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef);
CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace);
// CGImageCreateWithJPEGDataProvider does not properly handle CMKY, so fall back to AFImageWithDataAtScale
if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
CGImageRelease(imageRef);
imageRef = NULL;
@@ -540,13 +593,16 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
return image;
}
size_t bytesPerRow = 0; // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate
// CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate
size_t bytesPerRow = 0;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
if (colorSpaceModel == kCGColorSpaceModelRGB) {
uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wassign-enum"
if (alpha == kCGImageAlphaNone) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
@@ -554,6 +610,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
#pragma clang diagnostic pop
}
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
@@ -575,13 +632,13 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
CGImageRelease(inflatedImageRef);
CGImageRelease(imageRef);
return inflatedImage;
}
#endif
@implementation AFImageResponseSerializer
@implementation SM_AFImageResponseSerializer
- (instancetype)init {
self = [super init];
@@ -591,9 +648,12 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
self.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#if TARGET_OS_IOS || TARGET_OS_TV
self.imageScale = [[UIScreen mainScreen] scale];
self.automaticallyInflatesResponseImage = YES;
#elif TARGET_OS_WATCH
self.imageScale = [[WKInterfaceDevice currentDevice] screenScale];
self.automaticallyInflatesResponseImage = YES;
#endif
return self;
@@ -606,18 +666,18 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
if (self.automaticallyInflatesResponseImage) {
return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale);
} else {
return AFImageWithDataAtScale(data, self.imageScale);
}
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
#else
// Ensure that the image is set to it's correct pixel width and height
NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data];
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])];
@@ -629,16 +689,22 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
return nil;
}
#pragma mark - NSCoding
#pragma mark - NSSecureCoding
- (id)initWithCoder:(NSCoder *)decoder {
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
self.imageScale = [[decoder decodeObjectForKey:NSStringFromSelector(@selector(imageScale))] floatValue];
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
NSNumber *imageScale = [decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(imageScale))];
#if CGFLOAT_IS_DOUBLE
self.imageScale = [imageScale doubleValue];
#else
self.imageScale = [imageScale floatValue];
#endif
self.automaticallyInflatesResponseImage = [decoder decodeBoolForKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))];
#endif
@@ -648,7 +714,7 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
[coder encodeObject:@(self.imageScale) forKey:NSStringFromSelector(@selector(imageScale))];
[coder encodeBool:self.automaticallyInflatesResponseImage forKey:NSStringFromSelector(@selector(automaticallyInflatesResponseImage))];
#endif
@@ -656,10 +722,10 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
- (instancetype)copyWithZone:(NSZone *)zone {
SM_AFImageResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
serializer.imageScale = self.imageScale;
serializer.automaticallyInflatesResponseImage = self.automaticallyInflatesResponseImage;
#endif
@@ -671,27 +737,27 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
#pragma mark -
@interface AFCompoundResponseSerializer ()
@property (readwrite, nonatomic, strong) NSArray *responseSerializers;
@interface SM_AFCompoundResponseSerializer ()
@property (readwrite, nonatomic, copy) NSArray *responseSerializers;
@end
@implementation AFCompoundResponseSerializer
@implementation SM_AFCompoundResponseSerializer
+ (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers {
AFCompoundResponseSerializer *serializer = [[self alloc] init];
SM_AFCompoundResponseSerializer *serializer = [[self alloc] init];
serializer.responseSerializers = responseSerializers;
return serializer;
}
#pragma mark - AFURLResponseSerialization
#pragma mark - SM_AFURLResponseSerialization
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
for (id <AFURLResponseSerialization> serializer in self.responseSerializers) {
if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
for (id <SM_AFURLResponseSerialization> serializer in self.responseSerializers) {
if (![serializer isKindOfClass:[SM_AFHTTPResponseSerializer class]]) {
continue;
}
@@ -705,19 +771,19 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
return responseObject;
}
}
return [super responseObjectForResponse:response data:data error:error];
}
#pragma mark - NSCoding
#pragma mark - NSSecureCoding
- (id)initWithCoder:(NSCoder *)decoder {
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
self.responseSerializers = [decoder decodeObjectForKey:NSStringFromSelector(@selector(responseSerializers))];
self.responseSerializers = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(responseSerializers))];
return self;
}
@@ -730,8 +796,8 @@ static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *r
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
- (instancetype)copyWithZone:(NSZone *)zone {
SM_AFCompoundResponseSerializer *serializer = [[[self class] allocWithZone:zone] init];
serializer.responseSerializers = self.responseSerializers;
return serializer;

View File

@@ -1,6 +1,5 @@
// AFURLSessionManager.h
//
// Copyright (c) 2013 AFNetworking (http://afnetworking.com)
// 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
// of this software and associated documentation files (the "Software"), to deal
@@ -8,10 +7,10 @@
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -20,69 +19,75 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "AFURLResponseSerialization.h"
#import "AFURLRequestSerialization.h"
#import "AFSecurityPolicy.h"
#import "AFNetworkReachabilityManager.h"
#import "SM_AFURLResponseSerialization.h"
#import "SM_AFURLRequestSerialization.h"
#import "SM_AFSecurityPolicy.h"
#if !TARGET_OS_WATCH
#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`
- `URLSession:didBecomeInvalidWithError:`
- `URLSession:didReceiveChallenge:completionHandler:`
- `URLSessionDidFinishEventsForBackgroundURLSession:`
### `NSURLSessionTaskDelegate`
- `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`
- `URLSession:task:didReceiveChallenge:completionHandler:`
- `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`
- `URLSession:task:needNewBodyStream:`
- `URLSession:task:didCompleteWithError:`
### `NSURLSessionDataDelegate`
- `URLSession:dataTask:didReceiveResponse:completionHandler:`
- `URLSession:dataTask:didBecomeDownloadTask:`
- `URLSession:dataTask:didReceiveData:`
- `URLSession:dataTask:willCacheResponse:completionHandler:`
- `URLSessionDidFinishEventsForBackgroundURLSession:`
### `NSURLSessionDownloadDelegate`
- `URLSession:downloadTask:didFinishDownloadingToURL:`
- `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`
- `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`
If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first.
## 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
- Encoded managers do not include any block properties. Be sure to set delegate callback blocks when using `-initWithCoder:` or `NSKeyedUnarchiver`.
## NSCopying Caveats
- `-copy` and `-copyWithZone:` return a new manager with a new `NSURLSession` created from the configuration of the original.
- Operation copies do not include any delegate callback blocks, as they often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ session manager when copied.
@warning Managers for background sessions must be owned for the duration of their use. This can be accomplished by creating an application-wide or shared singleton instance.
*/
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
NS_ASSUME_NONNULL_BEGIN
@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSCoding, NSCopying>
@interface SM_AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>
/**
The managed session.
@@ -95,29 +100,31 @@
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
/**
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `SM_AFJSONResponseSerializer`.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
@property (nonatomic, strong) id <SM_AFURLResponseSerialization> responseSerializer;
///-------------------------------
/// @name Managing Security Policy
///-------------------------------
/**
The security policy used by created request operations 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
///--------------------------------------
/// @name Monitoring Network Reachability
///--------------------------------------
/**
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
///----------------------------
/// @name Getting Session Tasks
@@ -126,36 +133,49 @@
/**
The data, upload, and download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray *tasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
/**
The data tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray *dataTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
/**
The upload tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray *uploadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
/**
The download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray *downloadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
///---------------------------------
///-------------------------------
/// @name Managing Callback Queues
///---------------------------------
///-------------------------------
/**
The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, strong) dispatch_queue_t completionQueue;
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
/**
The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
*/
@property (nonatomic, strong) dispatch_group_t completionGroup;
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
///---------------------------------
/// @name Working Around System Bugs
///---------------------------------
/**
Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default.
@bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again.
@see https://github.com/AFNetworking/AFNetworking/issues/1675
*/
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
///---------------------
/// @name Initialization
@@ -163,16 +183,16 @@
/**
Creates and returns a manager for a session created with the specified configuration. This is the designated initializer.
@param configuration The configuration used to create the managed session.
@return A manager for a newly-created session.
*/
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
/**
Invalidates the managed session, optionally canceling pending tasks.
@param cancelPendingTasks Whether or not to cancel pending tasks.
*/
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
@@ -188,7 +208,20 @@
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
*/
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
/**
Creates an `NSURLSessionDataTask` with the specified request.
@param request The HTTP request for the request.
@param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
*/
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
///---------------------------
/// @name Running Upload Tasks
@@ -199,37 +232,39 @@
@param request The HTTP request for the request.
@param fileURL A URL to the local file to be uploaded.
@param progress A progress object monitoring the current upload progress.
@param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
@see `attemptsToRecreateUploadTasksForBackgroundSessions`
*/
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
/**
Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body.
@param request The HTTP request for the request.
@param bodyData A data object containing the HTTP body to be uploaded.
@param progress A progress object monitoring the current upload progress.
@param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
*/
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
fromData:(nullable NSData *)bodyData
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
/**
Creates an `NSURLSessionUploadTask` with the specified streaming request.
@param request The HTTP request for the request.
@param progress A progress object monitoring the current upload progress.
@param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
*/
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
///-----------------------------
/// @name Running Download Tasks
@@ -239,27 +274,51 @@
Creates an `NSURLSessionDownloadTask` with the specified request.
@param request The HTTP request for the request.
@param progress A progress object monitoring the current download progress.
@param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
@param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
@warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method.
*/
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(NSProgress * __autoreleasing *)progress
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler;
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
/**
Creates an `NSURLSessionDownloadTask` with the specified resume data.
@param resumeData The data used to resume downloading.
@param progress A progress object monitoring the current download progress.
@param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
@param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
*/
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(NSProgress * __autoreleasing *)progress
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler;
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
///---------------------------------
/// @name Getting Progress for Tasks
///---------------------------------
/**
Returns the upload progress of the specified task.
@param task The session task. Must not be `nil`.
@return An `NSProgress` object reporting the upload progress of a task, or `nil` if the progress is unavailable.
*/
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;
/**
Returns the download progress of the specified task.
@param task The session task. Must not be `nil`.
@return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable.
*/
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
///-----------------------------------------
/// @name Setting Session Delegate Callbacks
@@ -267,17 +326,17 @@
/**
Sets a block to be executed when the managed session becomes invalid, as handled by the `NSURLSessionDelegate` method `URLSession:didBecomeInvalidWithError:`.
@param block A block object to be executed when the managed session becomes invalid. The block has no return value, and takes two arguments: the session, and the error related to the cause of invalidation.
*/
- (void)setSessionDidBecomeInvalidBlock:(void (^)(NSURLSession *session, NSError *error))block;
- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
/**
Sets a block to be executed when a connection level authentication challenge has occurred, as handled by the `NSURLSessionDelegate` method `URLSession:didReceiveChallenge:completionHandler:`.
@param block A block object to be executed when a connection level authentication challenge has occurred. The block returns the disposition of the authentication challenge, and takes three arguments: the session, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge.
*/
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block;
- (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
///--------------------------------------
/// @name Setting Task Delegate Callbacks
@@ -288,35 +347,35 @@
@param block A block object to be executed when a task requires a new request body stream.
*/
- (void)setTaskNeedNewBodyStreamBlock:(NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
- (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
/**
Sets a block to be executed when an HTTP request is attempting to perform a redirection to a different URL, as handled by the `NSURLSessionTaskDelegate` method `URLSession:willPerformHTTPRedirection:newRequest:completionHandler:`.
@param block A block object to be executed when an HTTP request is attempting to perform a redirection to a different URL. The block returns the request to be made for the redirection, and takes four arguments: the session, the task, the redirection response, and the request corresponding to the redirection response.
*/
- (void)setTaskWillPerformHTTPRedirectionBlock:(NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
- (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
/**
Sets a block to be executed when a session task has received a request specific authentication challenge, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didReceiveChallenge:completionHandler:`.
@param block A block object to be executed when a session task has received a request specific authentication challenge. The block returns the disposition of the authentication challenge, and takes four arguments: the session, the task, the authentication challenge, and a pointer to the credential that should be used to resolve the challenge.
*/
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * __autoreleasing *credential))block;
- (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
/**
Sets a block to be executed periodically to track upload progress, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:`.
@param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes five arguments: the session, the task, the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread.
*/
- (void)setTaskDidSendBodyDataBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;
- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;
/**
Sets a block to be executed as the last message related to a specific task, as handled by the `NSURLSessionTaskDelegate` method `URLSession:task:didCompleteWithError:`.
@param block A block object to be executed when a session task is completed. The block has no return value, and takes three arguments: the session, the task, and any error that occurred in the process of executing the task.
*/
- (void)setTaskDidCompleteBlock:(void (^)(NSURLSession *session, NSURLSessionTask *task, NSError *error))block;
- (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
///-------------------------------------------
/// @name Setting Data Task Delegate Callbacks
@@ -327,35 +386,35 @@
@param block A block object to be executed when a data task has received a response. The block returns the disposition of the session response, and takes three arguments: the session, the data task, and the received response.
*/
- (void)setDataTaskDidReceiveResponseBlock:(NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;
/**
Sets a block to be executed when a data task has become a download task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didBecomeDownloadTask:`.
@param block A block object to be executed when a data task has become a download task. The block has no return value, and takes three arguments: the session, the data task, and the download task it has become.
*/
- (void)setDataTaskDidBecomeDownloadTaskBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;
/**
Sets a block to be executed when a data task receives data, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:didReceiveData:`.
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the session, the data task, and the data received. This block may be called multiple times, and will execute on the session manager operation queue.
*/
- (void)setDataTaskDidReceiveDataBlock:(void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;
/**
Sets a block to be executed to determine the caching behavior of a data task, as handled by the `NSURLSessionDataDelegate` method `URLSession:dataTask:willCacheResponse:completionHandler:`.
@param block A block object to be executed to determine the caching behavior of a data task. The block returns the response to cache, and takes three arguments: the session, the data task, and the proposed cached URL response.
*/
- (void)setDataTaskWillCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
/**
Sets a block to be executed once all messages enqueued for a session have been delivered, as handled by the `NSURLSessionDataDelegate` method `URLSessionDidFinishEventsForBackgroundURLSession:`.
@param block A block object to be executed once all messages enqueued for a session have been delivered. The block has no return value and takes a single argument: the session.
*/
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(void (^)(NSURLSession *session))block;
- (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
///-----------------------------------------------
/// @name Setting Download Task Delegate Callbacks
@@ -363,128 +422,79 @@
/**
Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`.
@param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error.
@param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `SM_AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error.
*/
- (void)setDownloadTaskDidFinishDownloadingBlock:(NSURL * (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
/**
Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`.
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue.
*/
- (void)setDownloadTaskDidWriteDataBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
/**
Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
@param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded.
*/
- (void)setDownloadTaskDidResumeBlock:(void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
@end
#endif
///--------------------
/// @name Notifications
///--------------------
/**
Posted when a task begins executing.
@deprecated Use `AFNetworkingTaskDidResumeNotification` instead.
*/
extern NSString * const AFNetworkingTaskDidStartNotification DEPRECATED_ATTRIBUTE;
/**
Posted when a task resumes.
*/
extern NSString * const AFNetworkingTaskDidResumeNotification;
/**
Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task.
@deprecated Use `AFNetworkingTaskDidCompleteNotification` instead.
*/
extern NSString * const AFNetworkingTaskDidFinishNotification DEPRECATED_ATTRIBUTE;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidResumeNotification;
/**
Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task.
*/
extern NSString * const AFNetworkingTaskDidCompleteNotification;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteNotification;
/**
Posted when a task suspends its execution.
*/
extern NSString * const AFNetworkingTaskDidSuspendNotification;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidSuspendNotification;
/**
Posted when a session is invalidated.
*/
extern NSString * const AFURLSessionDidInvalidateNotification;
FOUNDATION_EXPORT NSString * const SM_AFURLSessionDidInvalidateNotification;
/**
Posted when a session download task encountered an error when moving the temporary download file to a specified destination.
*/
extern NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification;
FOUNDATION_EXPORT NSString * const SM_AFURLSessionDownloadTaskDidFailToMoveFileNotification;
/**
The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task.
@deprecated Use `AFNetworkingTaskDidCompleteResponseDataKey` instead.
The raw response data of the task. Included in the userInfo dictionary of the `SM_AFNetworkingTaskDidCompleteNotification` if response data exists for the task.
*/
extern NSString * const AFNetworkingTaskDidFinishResponseDataKey DEPRECATED_ATTRIBUTE;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteResponseDataKey;
/**
The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if response data exists for the task.
The serialized response object of the task. Included in the userInfo dictionary of the `SM_AFNetworkingTaskDidCompleteNotification` if the response was serialized.
*/
extern NSString * const AFNetworkingTaskDidCompleteResponseDataKey;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteSerializedResponseKey;
/**
The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized.
@deprecated Use `AFNetworkingTaskDidCompleteSerializedResponseKey` instead.
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.
*/
extern NSString * const AFNetworkingTaskDidFinishSerializedResponseKey DEPRECATED_ATTRIBUTE;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteResponseSerializerKey;
/**
The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the response was serialized.
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.
*/
extern NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteAssetPathKey;
/**
The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer.
@deprecated Use `AFNetworkingTaskDidCompleteResponseSerializerKey` instead.
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.
*/
extern NSString * const AFNetworkingTaskDidFinishResponseSerializerKey DEPRECATED_ATTRIBUTE;
FOUNDATION_EXPORT NSString * const SM_AFNetworkingTaskDidCompleteErrorKey;
/**
The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if the task has an associated response serializer.
*/
extern NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey;
/**
The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk.
@deprecated Use `AFNetworkingTaskDidCompleteAssetPathKey` instead.
*/
extern NSString * const AFNetworkingTaskDidFinishAssetPathKey DEPRECATED_ATTRIBUTE;
/**
The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an the response data has been stored directly to disk.
*/
extern NSString * const AFNetworkingTaskDidCompleteAssetPathKey;
/**
Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists.
@deprecated Use `AFNetworkingTaskDidCompleteErrorKey` instead.
*/
extern NSString * const AFNetworkingTaskDidFinishErrorKey DEPRECATED_ATTRIBUTE;
/**
Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidFinishNotification` if an error exists.
*/
extern NSString * const AFNetworkingTaskDidCompleteErrorKey;
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
#import "SM_AFURLRequestSerialization.h"
@interface TextRequestSerializer : SM_AFHTTPRequestSerializer
+ (instancetype)serializer;
@end

View File

@@ -0,0 +1,53 @@
#import "TextRequestSerializer.h"
@implementation TextRequestSerializer
+ (instancetype)serializer
{
TextRequestSerializer *serializer = [[self alloc] init];
return serializer;
}
#pragma mark - SM_AFURLRequestSerialization
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"text/plain; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody: [[parameters valueForKey:@"text"] dataUsingEncoding:NSUTF8StringEncoding]];
}
return mutableRequest;
}
#pragma mark - NSSecureCoding
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (!self) {
return nil;
}
return self;
}
@end

View File

@@ -1,8 +1,8 @@
#import <Foundation/Foundation.h>
#import "AFURLResponseSerialization.h"
#import "SM_AFURLResponseSerialization.h"
@interface TextResponseSerializer : AFHTTPResponseSerializer
@interface TextResponseSerializer : SM_AFHTTPResponseSerializer
+ (instancetype)serializer;
@end
@end

View File

@@ -1,58 +1,145 @@
#import "TextResponseSerializer.h"
static BOOL AFErrorOrUnderlyingErrorHasCode(NSError *error, NSInteger code) {
if (error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCode(error.userInfo[NSUnderlyingErrorKey], code);
}
static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
if (!error) {
return underlyingError;
}
return NO;
if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
return error;
}
NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
}
static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger code, NSString *domain) {
if ([error.domain isEqualToString:domain] && error.code == code) {
return YES;
} else if (error.userInfo[NSUnderlyingErrorKey]) {
return AFErrorOrUnderlyingErrorHasCodeInDomain(error.userInfo[NSUnderlyingErrorKey], code, domain);
}
return NO;
}
@implementation TextResponseSerializer
+ (instancetype)serializer {
TextResponseSerializer *serializer = [[self alloc] init];
return serializer;
TextResponseSerializer *serializer = [[self alloc] init];
return serializer;
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self = [super init];
self.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"text/html", @"text/json", @"application/json", @"text/xml", @"application/xml", @"text/css", nil];
if (!self) {
return nil;
}
return self;
self.acceptableContentTypes = nil;
return self;
}
#pragma mark - AFURLResponseSerialization
- (NSString*)decodeResponseData:(NSData*)rawResponseData withEncoding:(CFStringEncoding)cfEncoding {
NSStringEncoding nsEncoding;
NSString* decoded = nil;
if (cfEncoding != kCFStringEncodingInvalidId) {
nsEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
}
NSStringEncoding supportedEncodings[6] = {
NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding,
NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding
};
for (int i = 0; i < sizeof(supportedEncodings) / sizeof(NSStringEncoding) && !decoded; ++i) {
if (cfEncoding == kCFStringEncodingInvalidId || nsEncoding == supportedEncodings[i]) {
decoded = [[NSString alloc] initWithData:rawResponseData encoding:supportedEncodings[i]];
}
}
return decoded;
}
- (CFStringEncoding) getEncoding:(NSURLResponse *)response {
CFStringEncoding encoding = kCFStringEncodingInvalidId;
if (response.textEncodingName) {
encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
}
return encoding;
}
#pragma mark -
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
decoded:(NSString **)decoded
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (data) {
*decoded = [self decodeResponseData:data withEncoding:[self getEncoding:response]];
}
if (data && !*decoded) {
NSMutableDictionary *mutableUserInfo = [@{
NSURLErrorFailingURLErrorKey:[response URL],
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: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)", @"SM_AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey: [response URL],
SM_AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
mutableUserInfo[SM_AFNetworkingOperationFailingURLResponseBodyErrorKey] = *decoded;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:SM_AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}
#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 (AFErrorOrUnderlyingErrorHasCode(*error, NSURLErrorCannotDecodeContentData)) {
return nil;
}
NSString* decoded = nil;
if (![self validateResponse:(NSHTTPURLResponse *)response data:data decoded:&decoded error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, SM_AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
NSStringEncoding stringEncoding = self.stringEncoding;
if (response.textEncodingName) {
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
if (encoding != kCFStringEncodingInvalidId) {
stringEncoding = CFStringConvertEncodingToNSStringEncoding(encoding);
}
}
NSString *responseString = [[NSString alloc] initWithData:data encoding:stringEncoding];
return responseString;
return decoded;
}
@end
@end

View File

@@ -0,0 +1,14 @@
{
"ios": {
"debug": {
"buildFlag": [
"-UseModernBuildSystem=0"
]
},
"release": {
"buildFlag": [
"-UseModernBuildSystem=0"
]
}
}
}

View File

@@ -0,0 +1,31 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.ilkimen.http.demo" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>HttpDemo</name>
<description>
A sample Apache Cordova application that demonstrates advanced HTTP plugin.
</description>
<author email="dev@cordova.apache.org" href="http://cordova.io">
Sefa Ilkimen
</author>
<content src="index.html" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<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>
<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

@@ -0,0 +1,29 @@
{
"name": "com.ilkimen.http.demo",
"displayName": "HttpDemo",
"version": "1.0.0",
"description": "A sample Apache Cordova application that demonstrates advanced HTTP plugin.",
"scripts": {
"build": "scripts/build.sh",
"test": "npm run build && scripts/test.sh"
},
"author": "Sefa Ilkimen",
"license": "Apache-2.0",
"dependencies": {
"cordova": "12.0.0",
"cordova-android": "12.0.1",
"cordova-browser": "7.0.0",
"cordova-ios": "7.1.1",
"cordova-plugin-device": "2.0.3"
},
"cordova": {
"platforms": [
"android",
"ios",
"browser"
],
"plugins": {
"cordova-plugin-device": {}
}
}
}

View File

@@ -0,0 +1,34 @@
* {
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
}
body {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
height:100%;
margin:0px;
padding:20px 0;
width:100%;
font-family: sans-serif;
}
button, input, textarea {
display: block;
box-sizing: border-box;
width: 100%;
}
button {
height: 2.5em;
font-size: 10pt;
}
input {
text-align: center;
}
h1 {
font-size: 12pt;
text-align: center;
}

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com https://self-signed.badssl.com http://httpbin.org http://www.columbia.edu 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content: blob:;">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<link rel="stylesheet" type="text/css" href="index.css">
</head>
<body>
<h1 id="descriptionLbl">Advanced HTTP test suite</h1>
<input value="idle" id="statusInput" readonly></input>
<textarea rows="16" cols="50" id="expectedTextarea" readonly>Click next to run first test.</textarea>
<textarea rows="16" cols="50" id="resultTextarea" readonly></textarea>
<button id="nextBtn">Run next test</button>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="e2e-specs.js"></script>
<script type="text/javascript" src="index.js"></script>
</body>
</html>

View File

@@ -0,0 +1,170 @@
/* global hooks, tests */
const app = {
testIndex: -1,
lastResult: null,
testsFlaggedToRun: [],
initialize: function () {
document.getElementById('nextBtn').addEventListener('click', app.onNextBtnClick);
var onlyFlaggedTests = [];
var enabledTests = [];
tests.forEach(function (test) {
if (test.only) {
onlyFlaggedTests.push(test);
}
if (!test.disabled) {
enabledTests.push(test);
}
});
app.testsFlaggedToRun = onlyFlaggedTests.length ? onlyFlaggedTests : enabledTests;
},
printResult: function (prefix, content) {
const text = prefix + ': ' + JSON.stringify(content);
document.getElementById('resultTextarea').value += text;
},
reject: function (content) {
document.getElementById('statusInput').value = 'finished';
app.printResult('result - rejected', content);
app.lastResult = {
type: 'rejected',
data: content
};
},
resolve: function (content) {
document.getElementById('statusInput').value = 'finished';
app.printResult('result - resolved', content);
app.lastResult = {
type: 'resolved',
data: content
};
},
skip: function (content) {
document.getElementById('statusInput').value = 'finished';
app.printResult('result - skipped', content);
app.lastResult = {
type: 'skipped',
data: content
};
},
throw: function (error) {
document.getElementById('statusInput').value = 'finished';
app.printResult('result - throwed', error.message);
app.lastResult = {
type: 'throwed',
message: error.message
};
},
getResult: function (cb) {
cb(app.lastResult);
},
runTest: function (tests, index) {
const testDefinition = tests[index];
const titleText = index + ': ' + testDefinition.description;
const expectedText = 'expected - ' + testDefinition.expected;
document.getElementById('statusInput').value = 'running';
document.getElementById('expectedTextarea').value = expectedText;
document.getElementById('resultTextarea').value = '';
document.getElementById('descriptionLbl').innerText = titleText;
const onSuccessFactory = function (cbChain) {
return function () {
cbChain.shift()(cbChain);
}
};
const onFailFactory = function (prefix) {
return function (errorMessage) {
app.reject(prefix + ': ' + errorMessage);
}
};
const onThrowedHandler = function (prefix, error) {
app.throw(new Error(prefix + ': ' + error.message));
};
const execBeforeEachTest = function (cbChain) {
const prefix = 'in before each hook';
try {
if (!hooks || !hooks.onBeforeEachTest) {
return onSuccessFactory(cbChain)();
}
hooks.onBeforeEachTest(
onSuccessFactory(cbChain),
onFailFactory(prefix)
);
} catch (error) {
onThrowedHandler(prefix, error);
}
};
const execBeforeTest = function (cbChain) {
const prefix = 'in before hook';
try {
if (!testDefinition.before) {
return onSuccessFactory(cbChain)();
}
testDefinition.before(
onSuccessFactory(cbChain),
onFailFactory(prefix)
);
} catch (error) {
onThrowedHandler(prefix, error);
}
};
const execTest = function () {
try {
testDefinition.func(app.resolve, app.reject, app.skip);
} catch (error) {
app.throw(error);
}
};
onSuccessFactory([execBeforeEachTest, execBeforeTest, execTest])();
},
onFinishedAllTests: function () {
const titleText = 'No more tests';
const expectedText = 'You have run all available tests.';
document.getElementById('expectedTextarea').value = expectedText;
document.getElementById('resultTextarea').value = '';
document.getElementById('descriptionLbl').innerText = titleText;
},
onNextBtnClick: function () {
app.testIndex += 1;
if (app.testIndex < app.testsFlaggedToRun.length) {
app.runTest(app.testsFlaggedToRun, app.testIndex);
} else {
app.onFinishedAllTests();
}
}
};
app.initialize();

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

1193
test/e2e-specs.js Normal file

File diff suppressed because it is too large Load Diff

93
test/e2e-tooling/caps.js Normal file
View File

@@ -0,0 +1,93 @@
module.exports = { getCaps };
const path = require('path');
const configs = {
// testing on local machine
localIosDevice: {
platformName: 'iOS',
platformVersion: '10.3',
automationName: 'XCUITest',
deviceName: 'iPhone 8',
autoWebview: true,
app: path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app')
},
localIosEmulator: {
platformName: 'iOS',
platformVersion: '14.3',
automationName: 'XCUITest',
deviceName: 'iPhone 8',
autoWebview: true,
app: path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app')
},
localAndroidEmulator: {
platformName: 'Android',
platformVersion: '9',
deviceName: 'Android Emulator',
autoWebview: true,
fullReset: true,
app: path.resolve('temp/platforms/android/app/build/outputs/apk/debug/app-debug.apk')
},
// testing on SauceLabs
saucelabsIosDevice: {
browserName: '',
'appium-version': '1.20.1',
platformName: 'iOS',
platformVersion: '14.3',
deviceName: 'iPhone 6',
autoWebview: true,
app: 'sauce-storage:HttpDemo.app.zip'
},
saucelabsIosEmulator: {
browserName: '',
'appium-version': '1.20.1',
platformName: 'iOS',
platformVersion: '14.3',
deviceName: 'iPhone Simulator',
autoWebview: true,
app: 'sauce-storage:HttpDemo.app.zip'
},
saucelabsAndroidEmulator: {
browserName: '',
'appium-version': '1.20.1',
platformName: 'Android',
platformVersion: '8.0',
deviceName: 'Android Emulator',
autoWebview: true,
app: 'sauce-storage:HttpDemo.apk'
},
// testing on BrowserStack
browserstackIosDevice: {
'appium-version': '1.22.0',
device: 'iPhone 12',
os_version: '14',
project: 'HTTP Test App',
autoWebview: true,
app: 'HttpTestAppIos',
'browserstack.networkLogs': false
},
browserstackAndroidDevice: {
'appium-version': '1.22.0',
device: 'Samsung Galaxy S22 Ultra',
os_version: '12.0',
project: 'HTTP Test App',
autoWebview: true,
app: 'HttpTestAppAndroid',
'browserstack.networkLogs': false
}
};
function getCaps(environment, os, runtime) {
const key = environment.toLowerCase() + capitalize(os) + capitalize(runtime);
const caps = configs[key];
caps.name = `cordova-plugin-advanced-http (${os})`;
return caps;
}
function capitalize(text) {
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
}

Some files were not shown because too many files have changed in this diff Show More