Compare commits

...

235 Commits

Author SHA1 Message Date
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
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
84 changed files with 17145 additions and 2046 deletions

View File

@@ -1,16 +1,13 @@
# 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 = 4
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
@@ -19,4 +16,4 @@ trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
trim_trailing_whitespace = false

9
.gitignore vendored
View File

@@ -1,2 +1,9 @@
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/**

5
.npmignore Normal file
View File

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

59
.travis.yml Normal file
View File

@@ -0,0 +1,59 @@
notifications:
slack:
secure: lXE+2AgsxZU5G5dI91LkMAIgo8MAWfdM7DB5UOtn5LpuNln+2FmJo1gOI7tkdmLOqpXTGYnpI2VyQN3H4nOF21YhuouzD1Sh8n2wtQg1iTm353kuQpqiVhSBX8ZJ7Be1e1G8OsnxoYOxbs4Zo9qI40EruwkvqLCBHWM5MRGyd4M7EFWwb9Z29VZN0y1Nt5g/c3bT76kdKmF+JCLur2OeEKxAity7sIKgZekSqeIMwEVLSxXnda6Dbjc/cg0MJ0iDArkD7iu6fz/Fcrrxgm/pUxjcgvqze7Gy5i31mjEfspnrglWV1cshMd48BTDKCJ2AMmxH8O3GPSWE2txjIvGRWUve7iViNylvmQCVz3Eyf99+4EuuVGa+5PSodQ/CqODx/65EwtcN3PE1tNz2puKOK8nrOJcFkcbG8KTHKUlQtHCkjitbykUnj/hvhLK5/oWlQYVOLWWrHwdGUh8FI8aFPVGjRjWbHbhdayjEIqxwr1ns+6mYrP1EFNXbaeZxnLNC59XpJl1ifuezqYAk7YEiU5j4rtC7YKgyQ3ueb7anOHTJoTMyDn8mpZXgwuyhoBaeEYytQVgRyMtL6Y5cP98Jn2kv0+vdne3rkk9/JEBTo32HOjvoij6rsqEvXC0LhUDJSNadOVdHht0jjoN6zBH37HIE5/3zysLlPcAcHAS83ow=
cache:
directories:
- node_modules
addons:
sauce_connect: true
matrix:
include:
- name: "iOS Build & Test"
language: objective-c
sudo: false
os: osx
osx_image: xcode10.1
before_install:
- export LANG=en_US.UTF-8
install:
- npm install
script:
- npm run testjs &&
npm run updatecert &&
scripts/build-test-app.sh --ios --emulator &&
scripts/upload-artifact.sh --ios &&
scripts/test-app.sh --ios --emulator;
- name: "Android Build & Test"
language: android
sudo : required
android:
components:
- platform-tools
- build-tools-28.0.3
- android-27
- extra-android-support
- extra-android-m2repository
- extra-google-m2repository
before_install:
- export LANG=en_US.UTF-8 &&
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - &&
sudo apt-get install -y nodejs
install:
- npm install
script:
- npm run testjs &&
npm run updatecert &&
scripts/build-test-app.sh --android --emulator &&
scripts/upload-artifact.sh --android &&
scripts/test-app.sh --android --emulator;

View File

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

82
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,82 @@
# 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
[Travis CI](https://travis-ci.org/silkimen/cordova-plugin-advanced-http/).
You can use this information to inspect failing tests in your PR.
## Attribution
This document is adapted from
[AngularJS' Contribution Guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)

View File

@@ -1,5 +1,6 @@
The MIT License (MIT)
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 +19,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.

414
README.md
View File

@@ -1,7 +1,13 @@
Cordova Advanced HTTP
=====================
[![npm version](https://badge.fury.io/js/cordova-plugin-advanced-http.svg)](https://badge.fury.io/js/cordova-plugin-advanced-http)
[![downloads/month](https://img.shields.io/npm/dm/cordova-plugin-advanced-http.svg)](https://www.npmjs.com/package/cordova-plugin-advanced-http)
[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.png)](https://opensource.org/licenses/mit-license.php)
[![Build Status](https://travis-ci.org/silkimen/cordova-plugin-advanced-http.svg?branch=master)](https://travis-ci.org/silkimen/cordova-plugin-advanced-http)
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS, Android and [Browser](#browserSupport).
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS and Android.
This is a fork of [Wymsee's Cordova-HTTP plugin](https://github.com/wymsee/cordova-HTTP).
## Advantages over Javascript requests
@@ -19,207 +25,351 @@ Please check [CHANGELOG.md](CHANGELOG.md) for details about updating to a new ve
The plugin conforms to the Cordova plugin specification, it can be installed
using the Cordova / Phonegap command line interface.
phonegap plugin add cordova-plugin-advanced-http
```shell
phonegap plugin add cordova-plugin-advanced-http
cordova plugin add cordova-plugin-advanced-http
cordova plugin add cordova-plugin-advanced-http
```
## Usage
### Without AngularJS
### Plain Cordova
This plugin registers a global object located at `cordova.plugin.http`.
### With AngularJS
This plugin creates a cordovaHTTP service inside of a cordovaHTTP module. You must load the module when you create your app's module.
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
Check the [Ionic docs](https://ionicframework.com/docs/native/http/) for how to use this plugin with Ionic-native.
## Synchronous Functions
### getBasicAuthHeader
This returns an object representing a basic HTTP Authorization header of the form `{'Authorization': 'Basic base64encodedusernameandpassword'}`
var header = cordovaHTTP.getBasicAuthHeader("user", "password");
```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");
```js
cordova.plugin.http.useBasicAuth('user', 'password');
```
### setHeader
Set a header for all future requests. Takes a header and a value.
### 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).
cordovaHTTP.setHeader("Header", "Value");
```js
cordova.plugin.http.setHeader('Hostname', 'Header', 'Value');
```
### setDataSerializer
Set the data serializer which will be used for all future POST and PUT requests. Takes a string representing the name of the serializer.
You can also define headers used for all hosts by using wildcard character "\*" or providing only two params.
cordovaHTTP.setDataSerializer("urlencoded");
```js
cordova.plugin.http.setHeader('*', 'Header', 'Value');
cordova.plugin.http.setHeader('Header', 'Value');
```
You can choose one of these two:
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 (content type "application/x-www-form-urlencoded")
* `json`: send data as JSON encoded content in body (content type "application/json")
* `utf8`: send data as plain UTF8 encoded string in body (content type "plain/text")
Caution: `urlencoded` does not support serializing deep structures whereas `json` does.
You can also override the default content type headers by specifying your own headers (see [setHeader](#setHeader)).
__Caution__: `urlencoded` does not support serializing deep structures whereas `json` does.
### setRequestTimeout
Set how long to wait for a request to respond, in seconds.
```js
cordova.plugin.http.setRequestTimeout(5.0);
```
### setFollowRedirect<a name="setFollowRedirect"></a>
Configure if it should follow redirects automatically. This defaults to true.
```js
cordova.plugin.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.
```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.
### enableSSLPinning
Enable or disable SSL pinning. This defaults to false.
### setServerTrustMode<a name="setServerTrustMode"></a>
Set server trust mode, being one of the following values:
To use SSL pinning you must include at least one .cer SSL certificate in your app project. You can pin to your server certificate or to one of the issuing CA certificates. For ios include your certificate in the root level of your bundle (just add the .cer file to your project/target at the root level). For android include your certificate in your project's platforms/android/assets folder. In both cases all .cer files found will be loaded automatically. If you only have a .pem certificate see this [stackoverflow answer](http://stackoverflow.com/a/16583429/3182729). You want to convert it to a DER encoded certificate with a .cer extension.
* `default`: default SSL trustship and hostname verification handling using system's CA certs
* `legacy`: use legacy default behavior (< 2.0.3), excluding user installed CA certs (only for Android)
* `nocheck`: disable SSL certificate checking and hostname verification, trusting all certs (meant to be used only for testing purposes)
* `pinned`: trust only provided certificates
As an alternative, you can store your .cer files in the www/certificates folder.
To use SSL pinning you must include at least one `.cer` SSL certificate in your app project. You can pin to your server certificate or to one of the issuing CA certificates. Include your certificate in the `www/certificates` folder. All `.cer` files found there will be loaded automatically.
cordovaHTTP.enableSSLPinning(true, function() {
console.log('success!');
}, function() {
console.log('error :(');
});
: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.
### acceptAllCerts
Accept all SSL certificates. Or disable accepting all certificates. This defaults to false.
```js
// enable SSL pinning
cordova.plugin.http.setServerTrustMode('pinned', function() {
console.log('success!');
}, function() {
console.log('error :(');
});
cordovaHTTP.acceptAllCerts(true, 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 :(');
});
### validateDomainName
Whether or not to validate the domain name in the certificate. This defaults to true.
// 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 :(');
});
```
cordovaHTTP.validateDomainName(false, function() {
console.log('success!');
}, function() {
console.log('error :(');
});
### disableRedirect (deprecated)
This function was deprecated in 2.0.9. Use ["setFollowRedirect"](#setFollowRedirect) instead.
### setSSLCertMode (deprecated)
This function was deprecated in 2.0.8. Use ["setServerTrustMode"](#setServerTrustMode) instead.
### enableSSLPinning (obsolete)
This function was removed in 2.0.0. Use ["setServerTrustMode"](#setServerTrustMode) to enable SSL pinning (mode "pinned").
### acceptAllCerts (obsolete)
This function was removed in 2.0.0. Use ["setServerTrustMode"](#setServerTrustMode) to disable checking certs (mode "nocheck").
### validateDomainName (obsolete)
This function was removed in v1.6.2. Domain name validation is disabled automatically when you set server trust mode to "nocheck".
### removeCookies
Remove all cookies associated with a given URL.
```js
cordova.plugin.http.removeCookies(url, callback);
```
### sendRequest
Execute a HTTP request. Takes a URL and an options object. This is the internally used implementation of the following shorthand functions ([post](#post), [get](#get), [put](#put), [patch](#patch), [delete](#delete), [head](#head), [uploadFile](#uploadFile) and [downloadFile](#downloadFile)). You can use this function, if you want to override global settings for each single request.
The options object contains following keys:
* `method`: HTTP method to be used, defaults to `get`, needs to be one of the following values:
* `get`, `post`, `put`, `patch`, `head`, `delete`, `upload`, `download`
* `data`: payload to be send to the server (only applicable on `post`, `put` or `patch` methods)
* `params`: query params to be appended to the URL (only applicable on `get`, `head`, `delete`, `upload` or `download` methods)
* `serializer`: data serializer to be used (only applicable on `post`, `put` or `patch` methods), defaults to global serializer value, see [setDataSerializer](#setDataSerializer) for supported values
* `timeout`: timeout value for the request in seconds, defaults to global timeout value
* `followRedirect`: enable or disable automatically following redirects
* `headers`: headers object (key value pair), will be merged with global values
* `filePath`: filePath to be used during upload and download see [uploadFile](#uploadFile) and [downloadFile](#downloadFile) for detailed information
* `name`: name to be used during upload see [uploadFile](#uploadFile) for detailed information
Here's a quick example:
```js
const options = {
method: 'post',
data: { id: 12, message: 'test' },
headers: { Authorization: 'OAuth2: token' }
};
cordova.plugin.http.sendRequest('https://google.com/', options, function(response) {
// prints 200
console.log(response.status);
}, function(response) {
// prints 403
console.log(response.status);
//prints Permission denied
console.log(response.error);
});
```
### post<a name="post"></a>
Execute a POST request. Takes a URL, data, and headers.
#### success
The success function receives a response object with 3 properties: status, data, and headers. Status is the HTTP response code. Data is the response from the server as a string. Headers is an object with the headers. 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.
{
status: 200,
data: "{'id': 12, 'message': 'test'}",
headers: {
"Content-Length": "247"
}
}
Here's a quick example:
```js
{
status: 200,
data: '{"id": 12, "message": "test"}',
url: 'http://example.net/rest'
headers: {
'content-length': '247'
}
}
```
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);
});
```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);
//prints Permission denied
console.log(response.error);
});
```
#### failure
The error function receives a response object with 3 properties: status, error and headers. Status is the HTTP response code. Error is the error response from the server as a string. Headers is an object with the headers. Here's a quick example:
The error function receives a response object with 4 properties: status, error, url, and headers (url and headers being optional). **status** is the HTTP response code as numeric value. **error** is the error response from the server as a string. **url** is the final URL obtained after any redirects as a string. **headers** is an object with the headers. The keys of the returned object are the header names and the values are the respective header values. All header names are lowercase.
{
status: 403,
error: "Permission denied",
headers: {
"Content-Length": "247"
}
}
Here's a quick example:
### get
```js
{
status: 403,
error: 'Permission denied',
url: 'http://example.net/noperm'
headers: {
'content-length': '247'
}
}
```
### 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);
});
```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);
});
```
### put
### put<a name="put"></a>
Execute a PUT request. Takes a URL, data, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### delete
### 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.
### uploadFile
### head<a name="head"></a>
Execute a HEAD request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### uploadFile<a name="uploadFile"></a>
Uploads a file saved on the device. Takes a URL, parameters, headers, filePath, and the name of the parameter to pass the file along as. See the [post](#post) documentation for details on what is returned on success and failure.
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);
});
```js
cordova.plugin.http.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
### 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).
cordovaHTTP.downloadFile("https://google.com/", {
id: 12,
message: "test"
}, { Authorization: "OAuth2: token" }, "file:///somepicture.jpg", function(entry) {
// prints the filename
console.log(entry.name);
```js
cordova.plugin.http.downloadFile("https://google.com/", {
id: '12',
message: 'test'
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry) {
// prints the filename
console.log(entry.name);
// prints the filePath
console.log(entry.fullPath);
}, function(response) {
console.error(response.error);
});
// prints the filePath
console.log(entry.fullPath);
}, 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)
## 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.
## Cookies
## Contribute & Develop
- a cookie set by a request isn't sent in subsequent requests
Take this into account when using this plugin in your application.
## License
The MIT License
Copyright (c) 2014 Wymsee, Inc
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.
We've set up a separate document for our [contribution guidelines](CONTRIBUTING.md).

6345
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,17 @@
{
"name": "cordova-plugin-advanced-http",
"version": "1.4.0",
"version": "2.0.9",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
"buildbrowser": "./scripts/build-test-app.sh --browser",
"testandroid": "npm run updatecert && ./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
"testios": "npm run updatecert && ./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator",
"testapp": "npm run testandroid && npm run testios",
"testjs": "mocha ./test/js-specs.js",
"test": "npm run testjs && npm run testapp",
"release": "npm run test && ./scripts/release.sh"
},
"cordova": {
"id": "cordova-plugin-advanced-http",
"platforms": [
@@ -25,7 +35,7 @@
"engines": [
{
"name": "cordova",
"version": ">=3.0.0"
"version": ">=4.0.0"
}
],
"author": "Wymsee",
@@ -44,5 +54,17 @@
"bugs": {
"url": "https://github.com/silkimen/cordova-plugin-advanced-http/issues"
},
"homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme"
"homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme",
"devDependencies": {
"chai": "4.1.2",
"chai-as-promised": "7.1.1",
"colors": "1.1.2",
"cordova": "8.1.2",
"mocha": "4.0.0",
"mock-require": "2.0.2",
"mz": "2.7.0",
"umd-tough-cookie": "2.4.3",
"wd": "1.4.1",
"xml2js": "0.4.19"
}
}

View File

@@ -1,85 +1,90 @@
<?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="cordova-plugin-advanced-http"
version="1.4.0">
<name>Advanced HTTP plugin</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="2.0.9">
<name>Advanced HTTP plugin</name>
<description>
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
</description>
<engines>
<engine name="cordova" version=">=3.5.0" />
</engines>
<dependency id="cordova-plugin-file" version=">=2.0.0" />
<js-module src="www/cordovaHttp.js" name="http">
<clobbers target="cordova.plugin.http" />
<engines>
<engine name="cordova" version=">=4.0.0"/>
</engines>
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
<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/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/TextResponseSerializer.h"/>
<header-file src="src/ios/TextRequestSerializer.h"/>
<header-file src="src/ios/AFNetworking/AFHTTPSessionManager.h"/>
<header-file src="src/ios/AFNetworking/AFNetworking.h"/>
<header-file src="src/ios/AFNetworking/AFNetworkReachabilityManager.h"/>
<header-file src="src/ios/AFNetworking/AFSecurityPolicy.h"/>
<header-file src="src/ios/AFNetworking/AFURLRequestSerialization.h"/>
<header-file src="src/ios/AFNetworking/AFURLResponseSerialization.h"/>
<header-file src="src/ios/AFNetworking/AFURLSessionManager.h"/>
<header-file src="src/ios/SDNetworkActivityIndicator/SDNetworkActivityIndicator.h"/>
<source-file src="src/ios/CordovaHttpPlugin.m"/>
<source-file src="src/ios/TextResponseSerializer.m"/>
<source-file src="src/ios/TextRequestSerializer.m"/>
<source-file src="src/ios/AFNetworking/AFHTTPSessionManager.m"/>
<source-file src="src/ios/AFNetworking/AFNetworkReachabilityManager.m"/>
<source-file src="src/ios/AFNetworking/AFSecurityPolicy.m"/>
<source-file src="src/ios/AFNetworking/AFURLRequestSerialization.m"/>
<source-file src="src/ios/AFNetworking/AFURLResponseSerialization.m"/>
<source-file src="src/ios/AFNetworking/AFURLSessionManager.m"/>
<source-file src="src/ios/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"/>
</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/CordovaServerTrust.java" target-dir="src/com/silkimen/cordovahttp"/>
<source-file src="src/android/com/silkimen/http/HttpBodyDecoder.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/HttpRequest.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/JsonUtils.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/KeyChainKeyManager.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/OkConnectionFactory.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/TLSConfiguration.java" target-dir="src/com/silkimen/http"/>
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
<framework src="com.squareup.okhttp3:okhttp-urlconnection:3.10.0"/>
</platform>
<platform name="browser">
<config-file target="config.xml" parent="/*">
<feature name="CordovaHttpPlugin">
<param name="browser-package" value="CordovaHttpPlugin"/>
</feature>
</config-file>
<js-module src="src/browser/cordova-http-plugin.js" name="http-proxy">
<runs/>
</js-module>
<!-- 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/TextResponseSerializer.h" />
<source-file src="src/ios/TextResponseSerializer.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/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" />
<framework src="SystemConfiguration.framework" />
</platform>
<!--android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="CordovaHttpPlugin">
<param name="android-package" value="com.synconset.cordovahttp.CordovaHttpPlugin"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.INTERNET" />
</config-file>
<source-file src="src/android/com/github/kevinsawicki/http/HttpRequest.java" target-dir="src/com/github/kevinsawicki/http" />
<source-file src="src/android/com/github/kevinsawicki/http/TLSSocketFactory.java" target-dir="src/com/github/kevinsawicki/http" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttp.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpDelete.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpDownload.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpGet.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpHead.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPlugin.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPost.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPut.java" target-dir="src/com/synconset/cordovahttp" />
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpUpload.java" target-dir="src/com/synconset/cordovahttp" />
</platform>
</plugin>
</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 ]); then
echo "Skipping CI tests, because Saucelabs credentials are not set.";
exit 0;
fi
printf 'Running e2e tests\n'
pushd $ROOT
./node_modules/.bin/mocha ./test/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,31 @@
const args = process.argv.slice(2);
const fs = require('mz/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);
};
fs.readFile(xmlPath, 'utf-8')
.then(xml => parse(xml))
.then(parsed => {
parsed.plugin.$.version = args[0];
return fs.writeFile(xmlPath, stringify(parsed));
});

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

30
scripts/upload-artifact.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

@@ -1,61 +0,0 @@
package com.github.kevinsawicki.http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory(SSLContext context) {
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"});
}
return socket;
}
}

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,174 @@
package com.silkimen.cordovahttp;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLException;
import com.silkimen.http.HttpBodyDecoder;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.HttpRequest.HttpRequestException;
import com.silkimen.http.JsonUtils;
import com.silkimen.http.OkConnectionFactory;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
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 Object data;
protected JSONObject headers;
protected int timeout;
protected boolean followRedirects;
protected TLSConfiguration tlsConfiguration;
protected CallbackContext callbackContext;
public CordovaHttpBase(String method, String url, String serializer, Object data, JSONObject headers, int timeout,
boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
this.method = method;
this.url = url;
this.serializer = serializer;
this.data = data;
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
public CordovaHttpBase(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
this.method = method;
this.url = url;
this.headers = headers;
this.timeout = timeout;
this.followRedirects = followRedirects;
this.tlsConfiguration = tlsConfiguration;
this.callbackContext = callbackContext;
}
@Override
public void run() {
CordovaHttpResponse response = new CordovaHttpResponse();
try {
HttpRequest request = this.createRequest();
this.prepareRequest(request);
this.sendBody(request);
this.processResponse(request, response);
} catch (HttpRequestException e) {
if (e.getCause() instanceof SSLException) {
response.setStatus(-2);
response.setErrorMessage("TLS connection could not be established: " + e.getMessage());
Log.w(TAG, "TLS connection could not be established", e);
} else if (e.getCause() instanceof UnknownHostException) {
response.setStatus(-3);
response.setErrorMessage("Host could not be resolved: " + e.getMessage());
Log.w(TAG, "Host could not be resolved", e);
} else if (e.getCause() instanceof SocketTimeoutException) {
response.setStatus(-4);
response.setErrorMessage("Request timed out: " + e.getMessage());
Log.w(TAG, "Request timed out", e);
} else {
response.setStatus(-1);
response.setErrorMessage("There was an error with the request: " + e.getCause().getMessage());
Log.w(TAG, "Generic request error", e);
}
} catch (Exception e) {
response.setStatus(-1);
response.setErrorMessage(e.getMessage());
Log.e(TAG, "An unexpected error occured", e);
}
try {
if (response.hasFailed()) {
this.callbackContext.error(response.toJSON());
} else {
this.callbackContext.success(response.toJSON());
}
} catch (JSONException e) {
Log.e(TAG, "An unexpected error occured while creating HTTP response object", e);
}
}
protected HttpRequest createRequest() throws JSONException {
return new HttpRequest(this.url, this.method);
}
protected void prepareRequest(HttpRequest request) throws JSONException, IOException {
request.followRedirects(this.followRedirects);
request.readTimeout(this.timeout);
request.acceptCharset("UTF-8");
request.uncompress(true);
request.setConnectionFactory(new OkConnectionFactory());
if (this.tlsConfiguration.getHostnameVerifier() != null) {
request.setHostnameVerifier(this.tlsConfiguration.getHostnameVerifier());
}
request.setSSLSocketFactory(this.tlsConfiguration.getTLSSocketFactory());
// setup content type before applying headers, so user can override it
this.setContentType(request);
request.headers(JsonUtils.getStringMap(this.headers));
}
protected void setContentType(HttpRequest request) {
if ("json".equals(this.serializer)) {
request.contentType("application/json", "UTF-8");
} else if ("utf8".equals(this.serializer)) {
request.contentType("text/plain", "UTF-8");
} else if ("urlencoded".equals(this.serializer)) {
// intentionally left blank, because content type is set in HttpRequest.form()
}
}
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 ("urlencoded".equals(this.serializer)) {
request.form(JsonUtils.getObjectMap((JSONObject) this.data));
}
}
protected void processResponse(HttpRequest request, CordovaHttpResponse response) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
request.receive(outputStream);
ByteBuffer rawOutput = ByteBuffer.wrap(outputStream.toByteArray());
String decodedBody = HttpBodyDecoder.decodeBody(rawOutput, request.charset());
response.setStatus(request.code());
response.setUrl(request.url().toString());
response.setHeaders(request.headers());
if (request.code() >= 200 && request.code() < 300) {
response.setBody(decodedBody);
} else {
response.setErrorMessage(decodedBody);
}
}
}

View File

@@ -0,0 +1,42 @@
package com.silkimen.cordovahttp;
import java.io.File;
import java.net.URI;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.HttpRequest;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.file.FileUtils;
import org.json.JSONObject;
class CordovaHttpDownload extends CordovaHttpBase {
private String filePath;
public CordovaHttpDownload(String url, JSONObject headers, String filePath, int timeout, boolean followRedirects,
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
super("GET", url, headers, timeout, followRedirects, 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,23 @@
package com.silkimen.cordovahttp;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.json.JSONObject;
class CordovaHttpOperation extends CordovaHttpBase {
public CordovaHttpOperation(String method, String url, String serializer, Object data, JSONObject headers,
int timeout, boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
super(method, url, serializer, data, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
}
public CordovaHttpOperation(String method, String url, JSONObject headers, int timeout, boolean followRedirects,
TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
super(method, url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
}
}

View File

@@ -0,0 +1,164 @@
package com.silkimen.cordovahttp;
import java.security.KeyStore;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import android.util.Base64;
import javax.net.ssl.TrustManagerFactory;
public class CordovaHttpPlugin extends CordovaPlugin {
private static final String TAG = "Cordova-Plugin-HTTP";
private TLSConfiguration tlsConfiguration;
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
this.tlsConfiguration = new TLSConfiguration();
try {
KeyStore store = KeyStore.getInstance("AndroidCAStore");
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
store.load(null);
tmf.init(store);
this.tlsConfiguration.setHostnameVerifier(null);
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
} catch (Exception e) {
Log.e(TAG, "An error occured while loading system's CA certificates", e);
}
}
@Override
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext)
throws JSONException {
if (action == null) {
return false;
}
if ("get".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("head".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("delete".equals(action)) {
return this.executeHttpRequestWithoutData(action, args, callbackContext);
} else if ("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 ("uploadFile".equals(action)) {
return this.uploadFile(args, callbackContext);
} else if ("downloadFile".equals(action)) {
return this.downloadFile(args, callbackContext);
} else if ("setServerTrustMode".equals(action)) {
return this.setServerTrustMode(args, callbackContext);
} else if ("setClientAuthMode".equals(action)) {
return this.setClientAuthMode(args, callbackContext);
} else {
return false;
}
}
private boolean executeHttpRequestWithoutData(final String method, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
int timeout = args.getInt(2) * 1000;
boolean followRedirect = args.getBoolean(3);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, headers, timeout, followRedirect,
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(request);
return true;
}
private boolean executeHttpRequestWithData(final String method, final JSONArray args,
final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
Object data = args.get(1);
String serializer = args.getString(2);
JSONObject headers = args.getJSONObject(3);
int timeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
CordovaHttpOperation request = new CordovaHttpOperation(method.toUpperCase(), url, serializer, data, headers,
timeout, followRedirect, this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(request);
return true;
}
private boolean uploadFile(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
String filePath = args.getString(2);
String uploadName = args.getString(3);
int timeout = args.getInt(4) * 1000;
boolean followRedirect = args.getBoolean(5);
CordovaHttpUpload upload = new CordovaHttpUpload(url, headers, filePath, uploadName, timeout, followRedirect,
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(upload);
return true;
}
private boolean downloadFile(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
String url = args.getString(0);
JSONObject headers = args.getJSONObject(1);
String filePath = args.getString(2);
int timeout = args.getInt(3) * 1000;
boolean followRedirect = args.getBoolean(4);
CordovaHttpDownload download = new CordovaHttpDownload(url, headers, filePath, timeout, followRedirect,
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(download);
return true;
}
private boolean setServerTrustMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
CordovaServerTrust runnable = new CordovaServerTrust(args.getString(0), this.cordova.getActivity(),
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(runnable);
return true;
}
private boolean setClientAuthMode(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
byte[] pkcs = args.isNull(2) ? null : Base64.decode(args.getString(2), Base64.DEFAULT);
CordovaClientAuth runnable = new CordovaClientAuth(args.getString(0), args.isNull(1) ? null : args.getString(1),
pkcs, args.getString(3), this.cordova.getActivity(), this.cordova.getActivity().getApplicationContext(),
this.tlsConfiguration, callbackContext);
cordova.getThreadPool().execute(runnable);
return true;
}
}

View File

@@ -0,0 +1,90 @@
package com.silkimen.cordovahttp;
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;
class CordovaHttpResponse {
private int status;
private String url;
private Map<String, List<String>> headers;
private String body;
private JSONObject fileEntry;
private boolean hasFailed;
private boolean isFileOperation;
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 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.hasFailed) {
json.put("error", this.error);
} else if (this.isFileOperation) {
json.put("headers", new JSONObject(getFilteredHeaders()));
json.put("file", this.fileEntry);
} else {
json.put("headers", new JSONObject(getFilteredHeaders()));
json.put("data", this.body);
}
return json;
}
private Map<String, String> getFilteredHeaders() throws JSONException {
Map<String, String> filteredHeaders = new HashMap<String, String>();
if (this.headers == null || this.headers.isEmpty()) {
return filteredHeaders;
}
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,43 @@
package com.silkimen.cordovahttp;
import android.webkit.MimeTypeMap;
import com.silkimen.http.HttpRequest;
import java.io.File;
import java.net.URI;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.silkimen.http.TLSConfiguration;
import org.apache.cordova.CallbackContext;
import org.json.JSONObject;
class CordovaHttpUpload extends CordovaHttpBase {
private String filePath;
private String uploadName;
public CordovaHttpUpload(String url, JSONObject headers, String filePath, String uploadName, int timeout,
boolean followRedirects, TLSConfiguration tlsConfiguration, CallbackContext callbackContext) {
super("POST", url, headers, timeout, followRedirects, tlsConfiguration, callbackContext);
this.filePath = filePath;
this.uploadName = uploadName;
}
@Override
protected void sendBody(HttpRequest request) throws Exception {
int filenameIndex = this.filePath.lastIndexOf('/');
String filename = this.filePath.substring(filenameIndex + 1);
int extIndex = this.filePath.lastIndexOf('.');
String ext = this.filePath.substring(extIndex + 1);
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeType = mimeTypeMap.getMimeTypeFromExtension(ext);
request.part(this.uploadName, filename, mimeType, new File(new URI(this.filePath)));
}
}

View File

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

View File

@@ -0,0 +1,48 @@
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(ByteBuffer rawOutput, String charsetName)
throws CharacterCodingException, MalformedInputException {
if (charsetName == null) {
return tryDecodeByteBuffer(rawOutput);
}
return decodeByteBuffer(rawOutput, charsetName);
}
private static String tryDecodeByteBuffer(ByteBuffer rawOutput) throws CharacterCodingException, MalformedInputException {
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
try {
return decodeByteBuffer(rawOutput, ACCEPTED_CHARSETS[i]);
} catch (MalformedInputException e) {
continue;
} catch (CharacterCodingException e) {
continue;
}
}
return decodeBody(rawOutput, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
}
private static String decodeByteBuffer(ByteBuffer rawOutput, String charsetName)
throws CharacterCodingException, MalformedInputException {
return createCharsetDecoder(charsetName).decode(rawOutput).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,26 @@
package com.silkimen.http;
import okhttp3.OkHttpClient;
import okhttp3.OkUrlFactory;
import java.net.URL;
import java.net.HttpURLConnection;
import java.net.URLStreamHandler;
import java.net.Proxy;
public class OkConnectionFactory implements HttpRequest.ConnectionFactory {
private final OkHttpClient client = new OkHttpClient();
public HttpURLConnection create(URL url) {
OkUrlFactory urlFactory = new OkUrlFactory(this.client);
return (HttpURLConnection) urlFactory.open(url);
}
public HttpURLConnection create(URL url, Proxy proxy) {
OkHttpClient clientWithProxy = new OkHttpClient.Builder().proxy(proxy).build();
OkUrlFactory urlFactory = new OkUrlFactory(clientWithProxy);
return (HttpURLConnection) urlFactory.open(url);
}
}

View File

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

View File

@@ -0,0 +1,63 @@
package com.silkimen.http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory delegate;
public TLSSocketFactory(SSLContext context) {
delegate = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(delegate.createSocket(socket, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if (socket != null && (socket instanceof SSLSocket)) {
((SSLSocket) socket).setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
}
return socket;
}
}

View File

@@ -1,58 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import java.net.UnknownHostException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
class CordovaHttpDelete extends CordovaHttp implements Runnable {
public CordovaHttpDelete(String urlString, JSONObject data, JSONObject headers, CallbackContext callbackContext) {
super(urlString, data, headers, callbackContext);
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.delete(this.getUrlString(), this.getParamsMap(), false);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
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 if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,64 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import java.net.UnknownHostException;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import javax.net.ssl.SSLHandshakeException;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
class CordovaHttpPut extends CordovaHttp implements Runnable {
public CordovaHttpPut(String urlString, JSONObject data, String serializerName, JSONObject headers, CallbackContext callbackContext) {
super(urlString, data, serializerName, headers, callbackContext);
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.put(this.getUrlString());
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
if (new String("json").equals(this.getSerializerName())) {
request.contentType(request.CONTENT_TYPE_JSON, request.CHARSET_UTF8);
request.send(this.getParamsObject().toString());
} else {
request.form(this.getParamsMap());
}
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
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 if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,157 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.Iterator;
import com.github.kevinsawicki.http.HttpRequest;
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 static AtomicBoolean validateDomainName = new AtomicBoolean(true);
private String urlString;
private JSONObject params;
private String serializerName;
private JSONObject headers;
private CallbackContext callbackContext;
public CordovaHttp(String urlString, JSONObject params, JSONObject headers, CallbackContext callbackContext) {
this.urlString = urlString;
this.params = params;
this.serializerName = "default";
this.headers = headers;
this.callbackContext = callbackContext;
}
public CordovaHttp(String urlString, JSONObject params, String serializerName, JSONObject headers, CallbackContext callbackContext) {
this.urlString = urlString;
this.params = params;
this.serializerName = serializerName;
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);
}
}
public static void validateDomainName(boolean accept) {
validateDomainName.set(accept);
}
protected String getUrlString() {
return this.urlString;
}
protected JSONObject getParamsObject() {
return this.params;
}
protected String getSerializerName() {
return this.serializerName;
}
protected HashMap<String, Object> getParamsMap() throws JSONException {
return this.getMapFromJSONObject(this.params);
}
protected JSONObject getHeadersObject() {
return this.headers;
}
protected HashMap<String, String> getHeadersMap() throws JSONException {
return this.getStringMapFromJSONObject(this.headers);
}
protected CallbackContext getCallbackContext() {
return this.callbackContext;
}
protected HttpRequest setupSecurity(HttpRequest request) {
if (acceptAllCerts.get()) {
request.trustAllCerts();
}
if (!validateDomainName.get()) {
request.trustAllHosts();
}
if (sslPinning.get()) {
request.pinToCerts();
}
return request;
}
protected 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);
}
protected void addResponseHeaders(HttpRequest request, JSONObject response) throws JSONException {
Map<String, List<String>> headers = request.headers();
Map<String, String> parsed_headers = new HashMap<String, String>();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
String key = entry.getKey();
List<String> value = entry.getValue();
if ((key != null) && (!value.isEmpty())) {
parsed_headers.put(key, value.get(0));
}
}
response.put("headers", new JSONObject(parsed_headers));
}
protected HashMap<String, String> getStringMapFromJSONObject(JSONObject object) throws JSONException {
HashMap<String, String> map = new HashMap<String, String>();
Iterator<?> i = object.keys();
while (i.hasNext()) {
String key = (String)i.next();
map.put(key, object.getString(key));
}
return map;
}
protected 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,67 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
import java.io.File;
import java.net.UnknownHostException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.file.FileUtils;
import org.json.JSONException;
import org.json.JSONObject;
class CordovaHttpDownload extends CordovaHttp implements Runnable {
private String filePath;
public CordovaHttpDownload(String urlString, JSONObject params, JSONObject 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.getParamsMap(), true);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
int code = request.code();
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
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.getFilePlugin().getEntryForFile(file);
response.put("file", fileEntry);
this.getCallbackContext().success(response);
} else {
response.put("error", "There was an error downloading the file");
this.getCallbackContext().error(response);
}
} catch(URISyntaxException e) {
this.respondWithError("There was an error with the given filePath");
} catch (JSONException e) {
this.respondWithError("There was an error generating the response");
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,58 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import java.net.UnknownHostException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
class CordovaHttpGet extends CordovaHttp implements Runnable {
public CordovaHttpGet(String urlString, JSONObject params, JSONObject headers, CallbackContext callbackContext) {
super(urlString, params, headers, callbackContext);
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.get(this.getUrlString(), this.getParamsMap(), false);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
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 if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,57 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import java.net.UnknownHostException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
class CordovaHttpHead extends CordovaHttp implements Runnable {
public CordovaHttpHead(String urlString, JSONObject params, JSONObject headers, CallbackContext callbackContext) {
super(urlString, params, headers, callbackContext);
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.head(this.getUrlString(), this.getParamsMap(), true);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
int code = request.code();
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
response.put("status", code);
if (code >= 200 && code < 300) {
// no 'body' to return for HEAD request
this.getCallbackContext().success(response);
} else {
String body = request.body(CHARSET);
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 if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,152 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.res.AssetManager;
import com.github.kevinsawicki.http.HttpRequest;
public class CordovaHttpPlugin extends CordovaPlugin {
private static final String TAG = "CordovaHTTP";
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
}
@Override
public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals("post")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
String serializerName = args.getString(2);
JSONObject headers = args.getJSONObject(3);
CordovaHttpPost post = new CordovaHttpPost(urlString, params, serializerName, headers, callbackContext);
cordova.getThreadPool().execute(post);
} else if (action.equals("get")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
CordovaHttpGet get = new CordovaHttpGet(urlString, params, headers, callbackContext);
cordova.getThreadPool().execute(get);
} else if (action.equals("put")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
String serializerName = args.getString(2);
JSONObject headers = args.getJSONObject(3);
CordovaHttpPut put = new CordovaHttpPut(urlString, params, serializerName, headers, callbackContext);
cordova.getThreadPool().execute(put);
} else if (action.equals("delete")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
CordovaHttpDelete delete = new CordovaHttpDelete(urlString, params, headers, callbackContext);
cordova.getThreadPool().execute(delete);
} else if (action.equals("head")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
CordovaHttpHead head = new CordovaHttpHead(urlString, params, headers, callbackContext);
cordova.getThreadPool().execute(head);
} else if (action.equals("enableSSLPinning")) {
try {
boolean enable = args.getBoolean(0);
this.enableSSLPinning(enable);
callbackContext.success();
} catch(Exception e) {
e.printStackTrace();
callbackContext.error("There was an error setting up ssl pinning");
}
} else if (action.equals("acceptAllCerts")) {
boolean accept = args.getBoolean(0);
CordovaHttp.acceptAllCerts(accept);
callbackContext.success();
} else if (action.equals("validateDomainName")) {
boolean accept = args.getBoolean(0);
CordovaHttp.validateDomainName(accept);
callbackContext.success();
} else if (action.equals("uploadFile")) {
String urlString = args.getString(0);
JSONObject params = args.getJSONObject(1);
JSONObject headers = args.getJSONObject(2);
String filePath = args.getString(3);
String name = args.getString(4);
CordovaHttpUpload upload = new CordovaHttpUpload(urlString, params, headers, 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);
String filePath = args.getString(3);
CordovaHttpDownload download = new CordovaHttpDownload(urlString, params, headers, callbackContext, filePath);
cordova.getThreadPool().execute(download);
} else {
return false;
}
return true;
}
private void enableSSLPinning(boolean enable) throws GeneralSecurityException, IOException {
if (enable) {
AssetManager assetManager = cordova.getActivity().getAssets();
String[] files = assetManager.list("");
int index;
ArrayList<String> cerFiles = new ArrayList<String>();
for (int i = 0; i < files.length; i++) {
index = files[i].lastIndexOf('.');
if (index != -1) {
if (files[i].substring(index).equals(".cer")) {
cerFiles.add(files[i]);
}
}
}
// scan the www/certificates folder for .cer files as well
files = assetManager.list("www/certificates");
for (int i = 0; i < files.length; i++) {
index = files[i].lastIndexOf('.');
if (index != -1) {
if (files[i].substring(index).equals(".cer")) {
cerFiles.add("www/certificates/" + files[i]);
}
}
}
for (int i = 0; i < cerFiles.size(); i++) {
InputStream in = cordova.getActivity().getAssets().open(cerFiles.get(i));
InputStream caInput = new BufferedInputStream(in);
HttpRequest.addCert(caInput);
}
CordovaHttp.enableSSLPinning(true);
} else {
CordovaHttp.enableSSLPinning(false);
}
}
}

View File

@@ -1,64 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import java.net.UnknownHostException;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import javax.net.ssl.SSLHandshakeException;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
class CordovaHttpPost extends CordovaHttp implements Runnable {
public CordovaHttpPost(String urlString, JSONObject params, String serializerName, JSONObject headers, CallbackContext callbackContext) {
super(urlString, params, serializerName, headers, callbackContext);
}
@Override
public void run() {
try {
HttpRequest request = HttpRequest.post(this.getUrlString());
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
if (new String("json").equals(this.getSerializerName())) {
request.contentType(request.CONTENT_TYPE_JSON, request.CHARSET_UTF8);
request.send(this.getParamsObject().toString());
} else {
request.form(this.getParamsMap());
}
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
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 if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

View File

@@ -1,106 +0,0 @@
/**
* A HTTP plugin for Cordova / Phonegap
*/
package com.synconset.cordovahttp;
import java.io.File;
import java.net.UnknownHostException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONObject;
import javax.net.ssl.SSLHandshakeException;
import android.webkit.MimeTypeMap;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
class CordovaHttpUpload extends CordovaHttp implements Runnable {
private String filePath;
private String name;
public CordovaHttpUpload(String urlString, JSONObject params, JSONObject 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.getHeadersMap());
URI uri = new URI(filePath);
int filenameIndex = filePath.lastIndexOf('/');
String filename = filePath.substring(filenameIndex + 1);
int extIndex = filePath.lastIndexOf('.');
String ext = filePath.substring(extIndex + 1);
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeType = mimeTypeMap.getMimeTypeFromExtension(ext);
request.part(this.name, filename, mimeType, new File(uri));
Set<?> set = (Set<?>)this.getParamsMap().entrySet();
Iterator<?> i = set.iterator();
while (i.hasNext()) {
Entry<?, ?> e = (Entry<?, ?>)i.next();
String key = (String)e.getKey();
Object value = e.getValue();
if (value instanceof Number) {
request.part(key, (Number)value);
} else if (value instanceof String) {
request.part(key, (String)value);
} else {
this.respondWithError("All parameters must be Numbers or Strings");
return;
}
}
int code = request.code();
String body = request.body(CHARSET);
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
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 if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
}
}
}

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

@@ -0,0 +1,199 @@
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var cordovaProxy = require('cordova/exec/proxy');
var jsUtil = require(pluginId + '.js-util');
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 deserializeResponseHeaders(headers) {
var headerMap = {};
var arr = headers.trim().split(/[\r\n]+/);
arr.forEach(function (line) {
var parts = line.split(': ');
var header = parts.shift().toLowerCase();
var value = parts.join(': ');
headerMap[header] = value;
});
return headerMap;
}
function createXhrSuccessObject(xhr) {
return {
url: xhr.responseURL,
status: xhr.status,
data: jsUtil.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response,
headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
};
}
function createXhrFailureObject(xhr) {
var obj = {};
obj.headers = xhr.getAllResponseHeaders();
obj.error = jsUtil.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response;
obj.error = obj.error || 'advanced-http: please check browser console for error messages';
if (xhr.responseURL) obj.url = xhr.responseURL;
if (xhr.status) obj.status = xhr.status;
return obj;
}
function 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 = withData ? opts[1] : null;
var params = withData ? null : serializeParams(opts[1]);
var serializer = withData ? opts[2] : null;
var headers = withData ? opts[3] : opts[2];
var timeout = withData ? opts[4] : opts[3];
var url = params ? opts[0] + '?' + params : opts[0];
var processedData = null;
var xhr = new XMLHttpRequest();
xhr.open(method, url);
if (headers.Cookie && headers.Cookie.length > 0) {
return failure('advanced-http: custom cookies not supported on browser platform');
}
switch (serializer) {
case 'json':
setDefaultContentType(headers, 'application/json; charset=utf8');
processedData = serializeJsonData(data);
if (processedData === null) {
return failure('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;
}
xhr.timeout = timeout * 1000;
setHeaders(xhr, headers);
xhr.onerror = xhr.ontimeout = function () {
return failure(createXhrFailureObject(xhr));
};
xhr.onload = function () {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status < 200 || xhr.status > 299) {
return failure(createXhrFailureObject(xhr));
}
return success(createXhrSuccessObject(xhr));
};
xhr.send(processedData);
}
var browserInterface = {
post: function (success, failure, opts) {
return sendRequest('post', true, opts, success, failure);
},
get: function (success, failure, opts) {
return sendRequest('get', false, opts, success, failure);
},
put: function (success, failure, opts) {
return sendRequest('put', true, opts, success, failure);
},
patch: function (success, failure, opts) {
return sendRequest('patch', true, opts, success, failure);
},
delete: function (success, failure, opts) {
return sendRequest('delete', false, opts, success, failure);
},
head: function (success, failure, opts) {
return sendRequest('head', false, opts, success, failure);
},
uploadFile: function (success, failure, opts) {
return failure('advanced-http: function "uploadFile" not supported on browser platform');
},
downloadFile: function (success, failure, opts) {
return failure('advanced-http: function "downloadFile" not supported on browser platform');
},
setServerTrustMode: function (success, failure, opts) {
return failure('advanced-http: function "setServerTrustMode" not supported on browser platform');
},
setClientAuthMode: function (success, failure, opts) {
return failure('advanced-http: function "setClientAuthMode" not supported on browser platform');
},
disableRedirect: function (success, failure, opts) {
return failure('advanced-http: function "disableRedirect" not supported on browser platform');
}
};
module.exports = browserInterface;
cordovaProxy.add('CordovaHttpPlugin', browserInterface);

View File

@@ -156,16 +156,9 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
@implementation AFSecurityPolicy
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"www/certificates"];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
// also add certs from www/certificates
paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"www/certificates"];
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
@@ -284,13 +277,13 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
@@ -307,7 +300,7 @@ static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
return trustedPublicKeyCount > 0;
}
}
return NO;
}

View File

@@ -392,11 +392,11 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
- On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled.
- On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled.
- Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there.
Some Assumptions:
- No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
- No background task classes override `resume` or `suspend`
The current solution:
1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
2) Grab a pointer to the original implementation of `af_resume`
@@ -415,7 +415,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
#pragma clang diagnostic pop
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
while (class_getInstanceMethod(currentClass, @selector(resume))) {
Class superClass = [currentClass superclass];
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
@@ -426,7 +426,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
}
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
@@ -454,7 +454,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_resume];
if (state != NSURLSessionTaskStateRunning) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
@@ -464,7 +464,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_suspend];
if (state != NSURLSessionTaskStateSuspended) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
}
@@ -978,7 +978,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
@@ -1025,7 +1025,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;

View File

@@ -4,12 +4,11 @@
@interface CordovaHttpPlugin : CDVPlugin
- (void)enableSSLPinning:(CDVInvokedUrlCommand*)command;
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command;
- (void)validateDomainName:(CDVInvokedUrlCommand*)command;
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
- (void)post:(CDVInvokedUrlCommand*)command;
- (void)get:(CDVInvokedUrlCommand*)command;
- (void)put:(CDVInvokedUrlCommand*)command;
- (void)patch:(CDVInvokedUrlCommand*)command;
- (void)delete:(CDVInvokedUrlCommand*)command;
- (void)uploadFile:(CDVInvokedUrlCommand*)command;
- (void)downloadFile:(CDVInvokedUrlCommand*)command;

View File

@@ -1,16 +1,22 @@
#import "CordovaHttpPlugin.h"
#import "CDVFile.h"
#import "TextResponseSerializer.h"
#import "TextRequestSerializer.h"
#import "AFHTTPSessionManager.h"
#import "SDNetworkActivityIndicator.h"
@interface CordovaHttpPlugin()
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(AFHTTPSessionManager*)manager;
- (void)setResults:(NSMutableDictionary*)dictionary withTask:(NSURLSessionTask*)task;
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data;
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error;
- (NSNumber*)getStatusCode:(NSError*) error;
- (NSMutableDictionary*)copyHeaderFields:(NSDictionary*)headerFields;
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager;
- (void)setRedirect:(bool)redirect forManager:(AFHTTPSessionManager*)manager;
@end
@implementation CordovaHttpPlugin {
AFSecurityPolicy *securityPolicy;
}
@@ -20,11 +26,13 @@
}
- (void)setRequestSerializer:(NSString*)serializerName forManager:(AFHTTPSessionManager*)manager {
if ([serializerName isEqualToString:@"json"]) {
manager.requestSerializer = [AFJSONRequestSerializer serializer];
} else {
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
}
if ([serializerName isEqualToString:@"json"]) {
manager.requestSerializer = [AFJSONRequestSerializer serializer];
} else if ([serializerName isEqualToString:@"utf8"]) {
manager.requestSerializer = [TextRequestSerializer serializer];
} else {
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
}
}
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(AFHTTPSessionManager*)manager {
@@ -33,236 +41,439 @@
}];
}
- (void)setResults:(NSMutableDictionary*)dictionary withTask:(NSURLSessionTask*)task {
if (task.response != nil) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
[dictionary setObject:[NSNumber numberWithInt:response.statusCode] forKey:@"status"];
[dictionary setObject:response.allHeaderFields forKey:@"headers"];
- (void)setRedirect:(bool)followRedirect forManager:(AFHTTPSessionManager*)manager {
[manager setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest * _Nonnull(NSURLSession * _Nonnull session,
NSURLSessionTask * _Nonnull task, NSURLResponse * _Nonnull response, NSURLRequest * _Nonnull request) {
if (followRedirect) {
return request;
} else {
return nil;
}
}];
}
- (void)setTimeout:(NSTimeInterval)timeout forManager:(AFHTTPSessionManager*)manager {
[manager.requestSerializer setTimeoutInterval:timeout];
}
- (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)enableSSLPinning:(CDVInvokedUrlCommand*)command {
bool enable = [[command.arguments objectAtIndex:0] boolValue];
if (enable) {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error {
if (response != nil) {
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
if (error.userInfo[AFNetworkingOperationFailingURLResponseBodyKey]) {
[dictionary setObject:error.userInfo[AFNetworkingOperationFailingURLResponseBodyKey] forKey:@"error"];
}
} else {
[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)setServerTrustMode:(CDVInvokedUrlCommand*)command {
NSString *certMode = [command.arguments objectAtIndex:0];
if ([certMode isEqualToString: @"default"] || [certMode isEqualToString: @"legacy"]) {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
} else if ([certMode isEqualToString: @"nocheck"]) {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName = NO;
} else if ([certMode isEqualToString: @"pinned"]) {
securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
}
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)acceptAllCerts:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult = nil;
bool allow = [[command.arguments objectAtIndex:0] boolValue];
securityPolicy.allowInvalidCertificates = allow;
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)validateDomainName:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult = nil;
bool validate = [[command.arguments objectAtIndex:0] boolValue];
securityPolicy.validatesDomainName = validate;
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)post:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSString *serializerName = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:3];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager POST:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
[dictionary setObject:responseObject forKey:@"data"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
[dictionary setObject:errResponse forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
- (void)get:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
[self setRequestSerializer: @"default" forManager: manager];
[self setRequestHeaders: headers forManager: manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager GET:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
[dictionary setObject:responseObject forKey:@"data"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
[dictionary setObject:errResponse forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
- (void)put:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSString *serializerName = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:3];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager PUT:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
[dictionary setObject:responseObject forKey:@"data"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
[dictionary setObject:errResponse forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
- (void)delete:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
[self setRequestSerializer: @"default" forManager: manager];
[self setRequestHeaders: headers forManager: manager];
@try {
[manager GET:url parameters:nil success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CordovaHttpPlugin* __weak weakSelf = self;
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
manager.responseSerializer = [TextResponseSerializer serializer];
[manager DELETE:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
[dictionary setObject:responseObject forKey:@"data"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
[dictionary setObject:errResponse forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)head:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager HEAD:url parameters:parameters success:^(NSURLSessionTask *task) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
// no 'body' for HEAD request, omitting 'data'
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
[dictionary setObject:errResponse forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
[manager HEAD:url parameters:nil success:^(NSURLSessionTask *task) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
// no 'body' for HEAD request, omitting 'data'
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)delete:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:2] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:3] boolValue];
[self setRequestSerializer: @"default" forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
[manager DELETE:url parameters:nil success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)post:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *data = [command.arguments objectAtIndex:1];
NSString *serializerName = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
[manager POST:url parameters:data success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)put:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *data = [command.arguments objectAtIndex:1];
NSString *serializerName = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
[manager PUT:url parameters:data success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)patch:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *data = [command.arguments objectAtIndex:1];
NSString *serializerName = [command.arguments objectAtIndex:2];
NSDictionary *headers = [command.arguments objectAtIndex:3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
[self setRequestSerializer: serializerName forManager: manager];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
@try {
[manager PATCH:url parameters:data success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)uploadFile:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
NSString *filePath = [command.arguments objectAtIndex: 3];
NSString *name = [command.arguments objectAtIndex: 4];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSString *filePath = [command.arguments objectAtIndex: 2];
NSString *name = [command.arguments objectAtIndex: 3];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:4] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:5] boolValue];
NSURL *fileURL = [NSURL URLWithString: filePath];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect 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 {
[manager POST:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSError *error;
[formData appendPartWithFileURL:fileURL name:name error:&error];
if (error) {
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) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
[dictionary setObject:@"Could not add file 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) {
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;
}
} progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
[dictionary setObject:errResponse forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
- (void)downloadFile:(CDVInvokedUrlCommand*)command {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.securityPolicy = securityPolicy;
NSString *url = [command.arguments objectAtIndex:0];
NSDictionary *parameters = [command.arguments objectAtIndex:1];
NSDictionary *headers = [command.arguments objectAtIndex:2];
NSString *filePath = [command.arguments objectAtIndex: 3];
NSDictionary *headers = [command.arguments objectAtIndex:1];
NSString *filePath = [command.arguments objectAtIndex: 2];
NSTimeInterval timeoutInSeconds = [[command.arguments objectAtIndex:3] doubleValue];
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
[self setRequestHeaders: headers forManager: manager];
[self setTimeout:timeoutInSeconds forManager:manager];
[self setRedirect:followRedirect forManager:manager];
if ([filePath hasPrefix:@"file://"]) {
filePath = [filePath substringFromIndex:7];
@@ -270,70 +481,83 @@
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager GET:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, 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];
[[SDNetworkActivityIndicator sharedActivityIndicator] startActivity];
// 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"];
@try {
[manager GET:url parameters:nil success:^(NSURLSessionTask *task, 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"];
}
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
return;
}
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;
}
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]) {
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];
return;
}
id filePlugin = [self.commandDelegate getCommandInstance:@"File"];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
[dictionary setObject:[filePlugin getDirectoryEntry:filePath isDirectory:NO] forKey:@"file"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self setResults: dictionary withTask: task];
NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
[dictionary setObject:errResponse forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
id filePlugin = [self.commandDelegate getCommandInstance:@"File"];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[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) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
[dictionary setObject:@"There was an error downloading the file" forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
}];
}
@catch (NSException *exception) {
[[SDNetworkActivityIndicator sharedActivityIndicator] stopActivity];
[self handleException:exception withCommand:command];
}
}
@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,8 @@
#import <Foundation/Foundation.h>
#import "AFURLRequestSerialization.h"
@interface TextRequestSerializer : 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 - 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

@@ -5,4 +5,6 @@
+ (instancetype)serializer;
@end
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyKey;
@end

View File

@@ -1,31 +1,126 @@
#import "TextResponseSerializer.h"
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);
}
NSString * const AFNetworkingOperationFailingURLResponseBodyKey = @"com.alamofire.serialization.response.error.body";
NSStringEncoding const SupportedEncodings[6] = { NSUTF8StringEncoding, NSWindowsCP1252StringEncoding, NSISOLatin1StringEncoding, NSISOLatin2StringEncoding, NSASCIIStringEncoding, NSUnicodeStringEncoding };
return NO;
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 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];
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);
}
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]];
}
self.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"text/html", @"text/json", @"application/json", @"text/xml", @"application/xml", @"text/css", nil];
if (data && !*decoded) {
NSMutableDictionary *mutableUserInfo = [@{
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
AFNetworkingOperationFailingURLResponseDataErrorKey: data,
AFNetworkingOperationFailingURLResponseBodyKey: @"Could not decode response data due to invalid or unknown charset encoding",
} mutableCopy];
return self;
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
} else if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey: [response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
mutableUserInfo[AFNetworkingOperationFailingURLResponseBodyKey] = *decoded;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}
#pragma mark - AFURLResponseSerialization
@@ -34,25 +129,15 @@ static BOOL AFErrorOrUnderlyingErrorHasCodeInDomain(NSError *error, NSInteger co
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
NSString* decoded = nil;
if (![self validateResponse:(NSHTTPURLResponse *)response data:data decoded:&decoded error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, 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,30 @@
<?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">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
<engine name="android" spec="7.1.0" />
<engine name="browser" spec="5.0.0" />
<engine name="ios" spec="4.4.0" />
<plugin name="cordova-plugin-file" spec="6.0.1" />
<preference name="AndroidPersistentFileLocation" value="Internal" />
</widget>

View File

@@ -0,0 +1,26 @@
{
"name": "com.ilkimen.http.demo",
"displayName": "HttpDemo",
"version": "1.0.0",
"description": "A sample Apache Cordova application that demonstrates advanced HTTP plugin.",
"main": "index.js",
"scripts": {
"build": "scripts/build.sh",
"test": "npm run build && scripts/test.sh"
},
"author": "Sefa Ilkimen",
"license": "Apache-2.0",
"dependencies": {
"cordova": "7.0.1",
"cordova-android": "7.1.0",
"cordova-browser": "5.0.0",
"cordova-ios": "4.4.0"
},
"cordova": {
"platforms": [
"android",
"ios"
]
},
"devDependencies": {}
}

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

642
test/e2e-specs.js Normal file
View File

@@ -0,0 +1,642 @@
const hooks = {
onBeforeEachTest: function (resolve, reject) {
cordova.plugin.http.clearCookies();
helpers.enableFollowingRedirect(function() {
helpers.setDefaultServerTrustMode(function () {
// @TODO: not ready yet
// helpers.setNoneClientAuthMode(resolve, reject);
resolve();
}, reject);
});
}
};
const helpers = {
setDefaultServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('default', resolve, reject); },
setNoCheckServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('nocheck', resolve, reject); },
setPinnedServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('pinned', resolve, reject); },
setNoneClientAuthMode: function (resolve, reject) { cordova.plugin.http.setClientAuthMode('none', resolve, reject); },
setBufferClientAuthMode: function (resolve, reject) {
helpers.getWithXhr(function(pkcs) {
cordova.plugin.http.setClientAuthMode('buffer', {
rawPkcs: pkcs,
pkcsPassword: 'badssl.com'
}, resolve, reject);
}, './certificates/badssl-client-cert.pkcs', 'arraybuffer');
},
setJsonSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('json')); },
setUtf8StringSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('utf8')); },
setUrlEncodedSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('urlencoded')); },
disableFollowingRedirect: function (resolve) { resolve(cordova.plugin.http.setFollowRedirect(false)); },
enableFollowingRedirect: function(resolve) { resolve(cordova.plugin.http.setFollowRedirect(true)); },
getWithXhr: function (done, url, type) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function () {
if (!type || type === 'text') {
done(this.responseText);
} else {
done(this.response);
}
});
xhr.responseType = type;
xhr.open('GET', url);
xhr.send();
},
writeToFile: function (done, fileName, content) {
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function (directoryEntry) {
directoryEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
fileEntry.createWriter(function (fileWriter) {
var blob = new Blob([content], { type: 'text/plain' });
fileWriter.onwriteend = done;
fileWriter.onerror = done;
fileWriter.write(blob);
}, done);
}, done);
}, done);
}
};
const messageFactory = {
sslTrustAnchor: function () { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
invalidCertificate: function (domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
}
const tests = [
{
description: 'should reject self signed cert (GET)',
expected: 'rejected: {"status":-2, ...',
func: function (resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function (driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},
{
description: 'should reject self signed cert (PUT)',
expected: 'rejected: {"status":-2, ...',
func: function (resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},
{
description: 'should reject self signed cert (POST)',
expected: 'rejected: {"status":-2, ...',
func: function (resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},
{
description: 'should reject self signed cert (PATCH)',
expected: 'rejected: {"status":-2, ...',
func: function (resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},
{
description: 'should reject self signed cert (DELETE)',
expected: 'rejected: {"status":-2, ...',
func: function (resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function (driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},
{
description: 'should accept bad cert (GET)',
expected: 'resolved: {"status":200, ...',
before: helpers.setNoCheckServerTrustMode,
func: function (resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.should.include({ status: 200 });
}
},
{
description: 'should accept bad cert (PUT)',
expected: 'rejected: {"status":405, ... // will be rejected because PUT is not allowed',
before: helpers.setNoCheckServerTrustMode,
func: function (resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},
{
description: 'should accept bad cert (POST)',
expected: 'rejected: {"status":405, ... // will be rejected because POST is not allowed',
before: helpers.setNoCheckServerTrustMode,
func: function (resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},
{
description: 'should accept bad cert (PATCH)',
expected: 'rejected: {"status":405, ... // will be rejected because PATCH is not allowed',
before: helpers.setNoCheckServerTrustMode,
func: function (resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},
{
description: 'should accept bad cert (DELETE)',
expected: 'rejected: {"status":405, ... // will be rejected because DELETE is not allowed',
before: helpers.setNoCheckServerTrustMode,
func: function (resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.should.include({ status: 405 });
}
},
{
description: 'should fetch data from http://httpbin.org/ (GET)',
expected: 'resolved: {"status":200, ...',
before: helpers.setNoCheckServerTrustMode,
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.should.include({ status: 200 });
}
},
{
description: 'should send JSON object correctly (POST)',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
}
},
{
description: 'should send JSON object correctly (PUT)',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
}
},
{
description: 'should send JSON object correctly (PATCH)',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
}
},
{
description: 'should send JSON array correctly (POST) #26',
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
}
},
{
description: 'should send JSON array correctly (PUT) #26',
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
}
},
{
description: 'should send JSON array correctly (PATCH) #26',
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', [1, 2, 3], {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON.parse(result.data.data).json.should.eql([1, 2, 3]);
}
},
{
description: 'should send url encoded data correctly (POST) #41',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},
{
description: 'should send url encoded data correctly (PUT)',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
func: function (resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},
{
description: 'should send url encoded data correctly (PATCH)',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
func: function (resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},
{
description: 'should resolve correct URL after redirect (GET) #33',
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.url.should.be.equal('http://httpbin.org/anything');
}
},
{
description: 'should not follow 302 redirect when following redirects is disabled',
expected: 'rejected: {"status": 302, ...',
before: function(resolve, reject) { cordova.plugin.http.disableRedirect(true, resolve, reject)},
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.status.should.be.equal(302);
}
},
{
description: 'should download a file from given URL to given path in local filesystem',
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
func: function (resolve, reject) {
var sourceUrl = 'http://httpbin.org/xml';
var targetPath = cordova.file.cacheDirectory + 'test.xml';
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
helpers.getWithXhr(function (content) {
resolve({
sourceUrl: sourceUrl,
targetPath: targetPath,
fullPath: entry.fullPath,
name: entry.name,
content: content
});
}, targetPath);
}, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.name.should.be.equal('test.xml');
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
}
},
{
description: 'should upload a file from given path in local filesystem to given URL #27',
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
func: function (resolve, reject) {
var fileName = 'test-file.txt';
var fileContent = 'I am a dummy file. I am used for testing purposes!';
var sourcePath = cordova.file.cacheDirectory + fileName;
var targetUrl = 'http://httpbin.org/post';
helpers.writeToFile(function () {
cordova.plugin.http.uploadFile(targetUrl, {}, {}, sourcePath, fileName, resolve, reject);
}, fileName, fileContent);
},
validationFunc: function (driver, result) {
var fileName = 'test-file.txt';
var fileContent = 'I am a dummy file. I am used for testing purposes!';
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.files[fileName]
.should.be.equal(fileContent);
}
},
{
description: 'should encode HTTP array params correctly (GET) #45',
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3\\"}\" ...',
func: function (resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/get', { myArray: ['val1', 'val2', 'val3'], myString: 'testString' }, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.url
.should.include('httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
}
},
{
description: 'should throw on non-string values in local header object #54',
expected: 'throwed: {"message": "advanced-http: header values must be strings"}',
func: function (resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/get', {}, { myTestHeader: 1 }, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('throwed');
result.message.should.be.equal('advanced-http: header values must be strings');
}
},
{
description: 'should throw an error while setting non-string value as global header #54',
expected: 'throwed: "advanced-http: header values must be strings"',
func: function (resolve, reject) {
cordova.plugin.http.setHeader('myTestHeader', 2);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('throwed');
result.message.should.be.equal('advanced-http: header values must be strings');
}
},
{
description: 'should accept content-type "application/xml" #58',
expected: 'resolved: {"status": 200, ...',
func: function (resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/xml', {}, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.status.should.be.equal(200);
}
},
{
description: 'should send programmatically set cookies correctly (GET)',
expected: 'resolved: {"status": 200, ...',
func: function (resolve, reject) {
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.headers
.Cookie
.should.be.equal('myCookie=myValue; mySecondCookie=mySecondValue');
}
},
{
description: 'should not send any cookies after running "clearCookies" (GET) #59',
expected: 'resolved: {"status": 200, "data": "{\"headers\": {\"Cookie\": \"\"...',
func: function (resolve, reject) {
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
cordova.plugin.http.clearCookies();
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.headers
.should.not.have.property('Cookie');
}
},
{
description: 'should send programmatically set cookies correctly (DOWNLOAD) #57',
expected: 'resolved: {"content":{"cookies":{"myCookie":"myValue ...',
func: function (resolve, reject) {
var sourceUrl = 'http://httpbin.org/cookies';
var targetPath = cordova.file.cacheDirectory + 'cookies.json';
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
helpers.getWithXhr(function (content) {
resolve({
sourceUrl: sourceUrl,
targetPath: targetPath,
fullPath: entry.fullPath,
name: entry.name,
content: content
});
}, targetPath);
}, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.name.should.be.equal('cookies.json');
result.data.content.should.be.a('string');
var cookies = JSON.parse(result.data.content).cookies;
cookies.myCookie.should.be.equal('myValue');
cookies.mySecondCookie.should.be.equal('mySecondValue');
}
},
{
description: 'should send UTF-8 encoded raw string correctly (POST) #34',
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"this is a test string\\"...',
before: helpers.setUtf8StringSerializer,
func: function (resolve, reject) {
cordova.plugin.http.post('http://httpbin.org/anything', 'this is a test string', {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).data.should.be.equal('this is a test string');
}
},
{
description: 'should encode spaces in query string (params object) correctly (GET) #71',
expected: 'resolved: {"status": 200, "data": "{\\"args\\": \\"query param\\": \\"and value with spaces\\"...',
func: function (resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/get', { 'query param': 'and value with spaces' }, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).args['query param'].should.be.equal('and value with spaces');
}
},
{
description: 'should decode latin1 (iso-8859-1) encoded body correctly (GET) #72',
expected: 'resolved: {"status": 200, "data": "<!DOCTYPE HTML PUBLIC \\"-//W3C//DTD HTML 4.01 Transitional//EN\\"> ...',
func: function (resolve, reject) {
cordova.plugin.http.get('http://www.columbia.edu/kermit/latin1.html', {}, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.include('[¡] 161 10/01 241 A1 INVERTED EXCLAMATION MARK\n[¢] 162 10/02 242 A2 CENT SIGN');
}
},
{
description: 'should return empty body string correctly (GET)',
expected: 'resolved: {"status": 200, "data": "" ...',
func: function (resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/stream/0', {}, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.equal('');
}
},
{
description: 'should pin SSL cert correctly (GET)',
expected: 'resolved: {"status": 200 ...',
before: helpers.setPinnedServerTrustMode,
func: function (resolve, reject) {
cordova.plugin.http.get('https://httpbin.org', {}, {}, resolve, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.status.should.be.equal(200);
}
},
{
description: 'should reject when pinned cert does not match received server cert (GET)',
expected: 'rejected: {"status": -2 ...',
before: helpers.setPinnedServerTrustMode,
func: function (resolve, reject) {
cordova.plugin.http.get('https://sha512.badssl.com/', {}, {}, resolve, reject);
},
validationFunc: function (driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('sha512.badssl.com') });
}
},
{
description: 'should send deeply structured JSON object correctly (POST) #65',
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"{\\\\"outerObj\\\\":{\\\\"innerStr\\\\":\\\\"testString\\\\",\\\\"innerArr\\\\":[1,2,3]}}\\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] } }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] } });
}
},
{
description: 'should override header "content-type" correctly (POST) #78',
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Type\\": \\"text/plain\\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', {}, { 'Content-Type': 'text/plain' }, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).headers['Content-Type'].should.be.equal('text/plain');
}
},
{
description: 'should handle error during file download correctly (DOWNLOAD) #83',
expected: 'rejected: {"status": 403, "error": "There was an error downloading the file" ...',
func: function (resolve, reject) { cordova.plugin.http.downloadFile('http://httpbin.org/status/403', {}, {}, cordova.file.tempDirectory + 'testfile.txt', resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('rejected');
result.data.status.should.be.equal(403);
result.data.error.should.be.equal('There was an error downloading the file');
}
},
{
description: 'should handle gzip encoded response correctly',
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Encoding\\": \\"gzip\\" ...',
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/gzip', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.status.should.be.equal(200);
JSON.parse(result.data.data).gzipped.should.be.equal(true);
}
},
{
description: 'should send empty string correctly',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"\\" ...',
before: helpers.setUtf8StringSerializer,
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', '', {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).data.should.be.equal('');
}
},
{
description: 'shouldn\'t escape forward slashes #184',
expected: 'resolved: {"status": 200, "data": "{\\"json\\":\\"/\\" ...',
before: helpers.setJsonSerializer,
func: function (resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { testString: '/' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.testString.should.be.equal('/');
}
},
{
description: 'should not double encode spaces in url path #195',
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"https://httpbin.org/anything/containing spaces in url\\" ...',
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything/containing%20spaces%20in%20url', {}, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything/containing spaces in url');
}
},
{
description: 'should encode spaces in url query correctly',
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"https://httpbin.org/anything?query key=very long query value with spaces\\" ...',
func: function (resolve, reject) { cordova.plugin.http.get('https://httpbin.org/anything', { 'query key': 'very long query value with spaces' }, {}, resolve, reject); },
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).url.should.be.equal('https://httpbin.org/anything?query key=very long query value with spaces');
}
},
{
description: 'should download a file from given HTTPS URL to given path in local filesystem #197',
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
func: function (resolve, reject) {
var sourceUrl = 'https://httpbin.org/xml';
var targetPath = cordova.file.cacheDirectory + 'test.xml';
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
helpers.getWithXhr(function (content) {
resolve({
sourceUrl: sourceUrl,
targetPath: targetPath,
fullPath: entry.fullPath,
name: entry.name,
content: content
});
}, targetPath);
}, reject);
},
validationFunc: function (driver, result) {
result.type.should.be.equal('resolved');
result.data.name.should.be.equal('test.xml');
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
}
},
// @TODO: not ready yet
// {
// description: 'should authenticate correctly when client cert auth is configured with a PKCS12 container',
// expected: 'resolved: {"status": 200, ...',
// before: helpers.setBufferClientAuthMode,
// func: function (resolve, reject) { cordova.plugin.http.get('https://client.badssl.com/', {}, {}, resolve, reject); },
// validationFunc: function (driver, result) {
// result.type.should.be.equal('resolved');
// result.data.data.should.include('TLS handshake');
// }
// }
];
if (typeof module !== 'undefined' && module.exports) {
module.exports = { tests: tests, hooks: hooks };
}

View File

@@ -0,0 +1,10 @@
const path = require('path');
if (process.env.SAUCE_USERNAME) {
exports.iosTestApp = 'sauce-storage:HttpDemo.app.zip';
exports.androidTestApp = 'sauce-storage:HttpDemo.apk';
} else {
// these paths are relative to working directory
exports.iosTestApp = path.resolve('temp/platforms/ios/build/emulator/HttpDemo.app');
exports.androidTestApp = path.resolve('temp/platforms/android/app/build/outputs/apk/debug/app-debug.apk');
}

View File

@@ -0,0 +1,60 @@
const local = {
iosDevice: {
platformName: 'iOS',
platformVersion: '10.3',
deviceName: 'iPhone 6',
autoWebview: true,
app: undefined // will be set later
},
iosEmulator: {
platformName: 'iOS',
platformVersion: '11.0',
deviceName: 'iPhone Simulator',
autoWebview: true,
app: undefined // will be set later
},
androidEmulator: {
platformName: 'Android',
platformVersion: '5.1',
deviceName: 'Android Emulator',
autoWebview: true,
fullReset: true,
app: undefined // will be set later
}
};
const sauce = {
iosDevice: {
browserName: '',
'appium-version': '1.7.1',
platformName: 'iOS',
platformVersion: '10.3',
deviceName: 'iPhone 6',
autoWebview: true,
app: undefined // will be set later
},
iosEmulator: {
browserName: '',
'appium-version': '1.7.1',
platformName: 'iOS',
platformVersion: '10.3',
deviceName: 'iPhone Simulator',
autoWebview: true,
app: undefined // will be set later
},
androidEmulator: {
browserName: '',
'appium-version': '1.7.1',
platformName: 'Android',
platformVersion: '5.1',
deviceName: 'Android Emulator',
autoWebview: true,
app: undefined // will be set later
}
};
if (process.env.SAUCE_USERNAME) {
module.exports = sauce;
} else {
module.exports = local;
}

View File

@@ -0,0 +1,13 @@
exports.configure = driver => {
driver.on('status', info => {
console.log(info.cyan);
});
driver.on('command', (meth, path, data) => {
console.log(' > ' + meth.yellow, path.grey, data || '');
});
driver.on('http', (meth, path, data) => {
console.log(' > ' + meth.magenta, path, (data || '').grey);
});
};

View File

@@ -0,0 +1,16 @@
const local = {
host: 'localhost',
port: 4723
};
const sauce = {
host: 'ondemand.saucelabs.com',
port: 80,
auth: process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY
};
if (process.env.SAUCE_USERNAME) {
module.exports = sauce;
} else {
module.exports = local;
}

View File

@@ -0,0 +1,12 @@
const wd = require("wd");
require('colors');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const should = chai.should();
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
exports.should = should;

94
test/e2e-tooling/test.js Normal file
View File

@@ -0,0 +1,94 @@
require('./helpers/setup');
const wd = require('wd');
const apps = require('./helpers/apps');
const caps = Object.assign({}, require('./helpers/caps'));
const serverConfig = require('./helpers/server');
const testDefinitions = require('../e2e-specs');
const pkgjson = require('../../package.json');
describe('Advanced HTTP', function() {
const isDevice = process.argv.includes('--device');
const isAndroid = process.argv.includes('--android');
const targetInfo = { isDevice, isAndroid };
let driver = null;
let allPassed = true;
this.timeout(900000);
const getCaps = appName => {
const desiredOs = isAndroid ? 'android' : 'ios';
const desiredCaps = caps[desiredOs + (isDevice ? 'Device' : 'Emulator')];
const desiredApp = apps[desiredOs + appName];
desiredCaps.name = pkgjson.name + ` (${desiredOs})`;
desiredCaps.app = desiredApp;
return desiredCaps;
};
const validateTestIndex = number => driver
.elementById('descriptionLbl')
.text()
.then(text => parseInt(text.match(/(\d+):/)[1], 10))
.should.eventually.become(number, 'Test index is not matching!');
const validateTestTitle = testTitle => driver
.elementById('descriptionLbl')
.text()
.then(text => text.match(/\d+:\ (.*)/)[1])
.should.eventually.become(testTitle, 'Test description is not matching!');
const waitToBeFinished = timeout => new Promise((resolve, reject) => {
const timeoutTimestamp = Date.now() + timeout;
const checkIfFinished = () => driver
.elementById('statusInput')
.getValue()
.then(value => {
if (value === 'finished') {
resolve();
} else if (Date.now() > timeoutTimestamp) {
reject('Test function timed out!');
} else {
setTimeout(checkIfFinished, 500);
}
});
checkIfFinished();
});
const validateResult = testDefinition => driver
.safeExecute('app.lastResult')
.then(result => testDefinition.validationFunc(driver, result, targetInfo));
const clickNext = () => driver
.elementById('nextBtn')
.click()
.sleep(1000);
before(() => {
driver = wd.promiseChainRemote(serverConfig);
require('./helpers/logging').configure(driver);
return driver.init(getCaps('TestApp'));
});
after(() => driver
.quit()
.finally(function () {
if (process.env.SAUCE_USERNAME) {
return driver.sauceJobStatus(allPassed);
}
}));
testDefinitions.tests.forEach((definition, index) => {
it(index + ': ' + definition.description, function() {
return clickNext()
.then(() => validateTestIndex(index))
.then(() => validateTestTitle(definition.description))
.then(() => waitToBeFinished(definition.timeout || 10000))
.then(() => validateResult(definition))
});
});
});

376
test/js-specs.js Normal file
View File

@@ -0,0 +1,376 @@
const chai = require('chai');
const mock = require('mock-require');
const should = chai.should();
describe('Advanced HTTP public interface', function () {
const messages = require('../www/messages');
let http = {};
const noop = () => { /* intentionally doing nothing */ };
const getDependenciesBlueprint = () => {
const globalConfigs = require('../www/global-configs');
const jsUtil = require('../www/js-util');
const ToughCookie = require('../www/umd-tough-cookie');
const lodash = require('../www/lodash');
const WebStorageCookieStore = require('../www/local-storage-store')(ToughCookie, lodash);
const cookieHandler = require('../www/cookie-handler')(null, ToughCookie, WebStorageCookieStore);
const helpers = require('../www/helpers')(jsUtil, cookieHandler, messages);
const urlUtil = require('../www/url-util')(jsUtil);
return { exec: noop, cookieHandler, urlUtil: urlUtil, helpers, globalConfigs };
};
const loadHttp = (deps) => {
http = require('../www/public-interface')(deps.exec, deps.cookieHandler, deps.urlUtil, deps.helpers, deps.globalConfigs);
};
beforeEach(() => {
// mocked btoa function (base 64 encoding strings)
global.btoa = decoded => new Buffer(decoded).toString('base64');
loadHttp(getDependenciesBlueprint());
});
it('sets global headers correctly with two args (old interface)', () => {
http.setHeader('myKey', 'myValue');
http.getHeaders('*').myKey.should.equal('myValue');
});
it('sets global headers correctly with three args (new interface) #24', () => {
http.setHeader('*', 'myKey', 'myValue');
http.getHeaders('*').myKey.should.equal('myValue');
});
it('sets host headers correctly #24', () => {
http.setHeader('www.google.de', 'myKey', 'myValue');
http.getHeaders('www.google.de').myKey.should.equal('myValue');
});
it('resolves global headers correctly #24', () => {
const deps = getDependenciesBlueprint();
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
};
loadHttp(deps);
http.setHeader('*', 'myKey', 'myValue');
http.get('url', {}, {}, noop, noop);
});
it('resolves host headers correctly (set without port number) #37', () => {
const deps = getDependenciesBlueprint();
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
};
loadHttp(deps);
http.setHeader('www.google.de', 'myKey', 'myValue');
http.get('https://www.google.de/?gws_rd=ssl', {}, {}, noop, noop);
});
it('resolves host headers correctly (set with port number) #37', () => {
const deps = getDependenciesBlueprint();
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
};
loadHttp(deps);
http.setHeader('www.google.de:8080', 'myKey', 'myValue');
http.get('https://www.google.de:8080/?gws_rd=ssl', {}, {}, noop, noop);
});
it('resolves request headers correctly', () => {
const deps = getDependenciesBlueprint();
deps.cookieHandler.getCookieString = () => 'fakeCookieString';
deps.exec = (onSuccess, onFail, namespace, method, params) => {
const headers = params[1];
headers.should.eql({
Cookie: 'fakeCookieString',
myKey: 'myValue'
});
};
loadHttp(deps);
http.get('https://www.google.de/?gws_rd=ssl', {}, { myKey: 'myValue' }, noop, noop);
});
it('sets basic authentication header correctly #36', () => {
http.useBasicAuth('name', 'pass');
http.getHeaders('*').Authorization.should.equal('Basic bmFtZTpwYXNz');
});
it('throws an Error when you try to add a cookie by using "setHeader" #46', () => {
(function () { http.setHeader('*', 'cookie', 'value'); }).should.throw();
});
it('configures global timeout value correctly with given valid value', () => {
http.setRequestTimeout(10);
http.getRequestTimeout().should.equal(10);
});
it('throws an Error when you try to configure global timeout with a string', () => {
(function () { http.setRequestTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
});
it('sets global option for following redirects correctly', () => {
http.setFollowRedirect(false);
http.getFollowRedirect().should.equal(false);
});
it('throws an Error when you try to configure global option for following redirects with a string', () => {
(function () { http.setFollowRedirect('myString'); }).should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
});
});
describe('URL util', function () {
const jsUtil = require('../www/js-util');
const util = require('../www/url-util')(jsUtil);
it('parses URL with protocol, hostname and path correctly', () => {
util.parseUrl('http://ilkimen.net/test').should.include({
protocol: 'http:',
host: 'ilkimen.net',
hostname: 'ilkimen.net',
pathname: '/test',
port: '',
search: '',
hash: ''
});
});
it('parses URL with protocol, hostname, port and path correctly', () => {
util.parseUrl('http://ilkimen.net:8080/test').should.include({
protocol: 'http:',
host: 'ilkimen.net:8080',
hostname: 'ilkimen.net',
pathname: '/test',
port: '8080',
search: '',
hash: ''
});
});
it('parses URL with protocol, hostname, port, path and query string correctly', () => {
util.parseUrl('http://ilkimen.net:8080/test?param=value').should.include({
protocol: 'http:',
host: 'ilkimen.net:8080',
hostname: 'ilkimen.net',
pathname: '/test',
port: '8080',
search: '?param=value',
hash: ''
});
});
it('parses URL with protocol, hostname, port, path, query string and hash param correctly', () => {
util.parseUrl('http://ilkimen.net:8080/test?param=value#myHash').should.include({
protocol: 'http:',
host: 'ilkimen.net:8080',
hostname: 'ilkimen.net',
pathname: '/test',
port: '8080',
search: '?param=value',
hash: '#myHash'
});
});
it('serializes query params correctly', () => {
util.serializeQueryParams({
strParam1: 'value with spaces',
strParam2: 'value with special character äöü%',
boolParam: true,
numberParam: 1,
nullParam: null,
}, false).should.equal('strParam1=value with spaces&strParam2=value with special character äöü%&boolParam=true&numberParam=1&nullParam=null');
});
it('serializes query params correctly with URL encoding enabled', () => {
util.serializeQueryParams({
'param 1': 'value with spaces',
'param 2': 'value with special character äöü%&'
}, true).should.equal('param%201=value%20with%20spaces&param%202=value%20with%20special%20character%20%C3%A4%C3%B6%C3%BC%25%26');
});
it('serializes array of query params correctly', () => {
util.serializeQueryParams({
myArray: ['val1', 'val2', 'val3'],
myString: 'testString'
}, false).should.equal('myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
});
it('serializes deeply structured array of query params correctly', () => {
util.serializeQueryParams({
myArray: [['val1.1', 'val1.2', 'val1.3'], 'val2', 'val3'],
myString: 'testString'
}, false).should.equal('myArray[][]=val1.1&myArray[][]=val1.2&myArray[][]=val1.3&myArray[]=val2&myArray[]=val3&myString=testString');
});
it('serializes deeply structured object of query params correctly', () => {
util.serializeQueryParams({
myObject: { obj1: { 'param1.1': 'val1.1', 'param1.2': 'val1.2' }, param2: 'val2' }
}, false).should.equal('myObject[obj1][param1.1]=val1.1&myObject[obj1][param1.2]=val1.2&myObject[param2]=val2');
});
it('appends query params string correctly to given URL without query parameters', () => {
util.appendQueryParamsString('http://ilkimen.net/', 'param1=value1')
.should.equal('http://ilkimen.net/?param1=value1');
});
it('appends query params string correctly to given URL with existing query parameters', () => {
util.appendQueryParamsString('http://ilkimen.net/?myParam=myValue', 'param1=value1')
.should.equal('http://ilkimen.net/?myParam=myValue&param1=value1');
});
it('appends query params string correctly to given URL with existing query parameters and hash value', () => {
util.appendQueryParamsString('http://ilkimen.net/?myParam=myValue#myHash', 'param1=value1')
.should.equal('http://ilkimen.net/?myParam=myValue&param1=value1#myHash');
});
});
describe('Common helpers', function () {
describe('mergeHeaders(globalHeaders, localHeaders)', function () {
const init = require('../www/helpers');
init.debug = true;
const helpers = init(null, null, null);
it('merges empty header sets correctly', () => {
helpers.mergeHeaders({}, {}).should.eql({});
});
it('merges ssimple header sets without collision correctly', () => {
helpers.mergeHeaders({ a: 1 }, { b: 2 }).should.eql({ a: 1, b: 2 });
});
it('merges header sets with collision correctly', () => {
helpers.mergeHeaders({ a: 1 }, { a: 2 }).should.eql({ a: 2 });
});
});
describe('getCookieHeader(url)', function () {
it('resolves cookie header correctly when no cookie is set #198', () => {
const helpers = require('../www/helpers')(null, { getCookieString: () => '' }, null);
helpers.getCookieHeader('http://ilkimen.net').should.eql({});
});
it('resolves cookie header correctly when a cookie is set', () => {
const helpers = require('../www/helpers')(null, { getCookieString: () => 'cookie=value' }, null);
helpers.getCookieHeader('http://ilkimen.net').should.eql({ Cookie: 'cookie=value' });
});
});
describe('checkClientAuthOptions()', function () {
const jsUtil = require('../www/js-util');
const messages = require('../www/messages');
const helpers = require('../www/helpers')(jsUtil, null, messages);
it('returns options object with empty values when mode is "none" and no options are given', () => {
helpers.checkClientAuthOptions('none').should.eql({
alias: null,
rawPkcs: null,
pkcsPassword: ''
});
});
it('returns options object with empty values when mode is "none" and random options are given', () => {
helpers.checkClientAuthOptions('none', {
alias: 'myAlias',
pkcsPath: 'myPath'
}).should.eql({
alias: null,
rawPkcs: null,
pkcsPassword: ''
});
});
it('throws an error when mode is "systemstore" and alias is not a string or undefined', () => {
(() => helpers.checkClientAuthOptions('systemstore', { alias: 1 }))
.should.throw(messages.INVALID_CLIENT_AUTH_ALIAS);
(() => helpers.checkClientAuthOptions('systemstore', { alias: undefined }))
.should.not.throw();
});
it('returns an object with null alias when mode is "systemstore" and no options object is given', () => {
helpers.checkClientAuthOptions('systemstore').should.eql({
alias: null,
rawPkcs: null,
pkcsPassword: ''
});
});
it('throws an error when mode is "buffer" and rawPkcs is not an array buffer', () => {
(() => helpers.checkClientAuthOptions('buffer', {
rawPkcs: undefined,
pkcsPassword: 'password'
})).should.throw(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
(() => helpers.checkClientAuthOptions('buffer', {
pkcsPath: 1,
pkcsPassword: 'password'
})).should.throw(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
});
it('throws an error when mode is "buffer" and pkcsPassword is not a string', () => {
(() => helpers.checkClientAuthOptions('buffer', {
rawPkcs: new ArrayBuffer(),
pkcsPassword: undefined
})).should.throw(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
(() => helpers.checkClientAuthOptions('buffer', {
rawPkcs: new ArrayBuffer(),
pkcsPassword: 1
})).should.throw(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
});
});
describe('handleMissingOptions()', function () {
const jsUtil = require('../www/js-util');
const messages = require('../www/messages');
const helpers = require('../www/helpers')(jsUtil, null, messages);
const mockGlobals = {
headers: {},
serializer: 'urlencoded',
followRedirect: true,
timeout: 60.0,
}
it('adds missing "followRedirect" option correctly', () => {
helpers.handleMissingOptions({}, mockGlobals).should.include({ followRedirect: true });
});
it('throws an error when "followRedirect" option is not a boolean', () => {
(() => helpers.handleMissingOptions({ followRedirect: 1 }, mockGlobals))
.should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
});
});
})

19
www/advanced-http.js Normal file
View File

@@ -0,0 +1,19 @@
/*
* A native HTTP Plugin for Cordova / PhoneGap.
*/
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var exec = require('cordova/exec');
var messages = require(pluginId + '.messages');
var globalConfigs = require(pluginId + '.global-configs');
var jsUtil = require(pluginId + '.js-util');
var ToughCookie = require(pluginId + '.tough-cookie');
var lodash = require(pluginId + '.lodash');
var WebStorageCookieStore = require(pluginId + '.local-storage-store')(ToughCookie, lodash);
var cookieHandler = require(pluginId + '.cookie-handler')(window.localStorage, ToughCookie, WebStorageCookieStore);
var helpers = require(pluginId + '.helpers')(jsUtil, cookieHandler, messages);
var urlUtil = require(pluginId + '.url-util')(jsUtil);
var publicInterface = require(pluginId + '.public-interface')(exec, cookieHandler, urlUtil, helpers, globalConfigs);
module.exports = publicInterface;

70
www/cookie-handler.js Normal file
View File

@@ -0,0 +1,70 @@
module.exports = function init(storage, ToughCookie, WebStorageCookieStore) {
var storeKey = '__advancedHttpCookieStore__';
var store = new WebStorageCookieStore(storage, storeKey);
var cookieJar = new ToughCookie.CookieJar(store);
return {
setCookieFromString: setCookieFromString,
setCookie: setCookie,
getCookieString: getCookieString,
clearCookies: clearCookies,
removeCookies: removeCookies
};
function splitCookieString(cookieStr) {
var cookieParts = cookieStr.split(',');
var splitCookies = [];
var processedCookie = null;
for (var i = 0; i < cookieParts.length; ++i) {
if (cookieParts[i].substr(-11, 8).toLowerCase() === 'expires=') {
processedCookie = cookieParts[i] + ',' + cookieParts[i + 1];
i++;
} else {
processedCookie = cookieParts[i];
}
processedCookie = processedCookie.trim();
splitCookies.push(processedCookie);
}
return splitCookies;
}
function setCookieFromString(url, cookieStr) {
if (!cookieStr) return;
var cookies = splitCookieString(cookieStr);
for (var i = 0; i < cookies.length; ++i) {
cookieJar.setCookieSync(cookies[i], url, { ignoreError: true });
}
}
function setCookie(url, cookie, options) {
options = options || {};
options.ignoreError = false;
cookieJar.setCookieSync(cookie, url, options);
}
function getCookieString(url) {
return cookieJar.getCookieStringSync(url);
}
function clearCookies() {
window.localStorage.removeItem(storeKey);
}
function removeCookies(url, cb) {
cookieJar.getCookies(url, function (error, cookies) {
if (!cookies || cookies.length === 0) {
return cb(null, []);
}
var domain = cookies[0].domain;
cookieJar.store.removeCookies(domain, null, cb);
});
}
};

217
www/cordovaHttp.js vendored
View File

@@ -1,217 +0,0 @@
/*global angular*/
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Modified by Andrew Stephan for Sync OnSet
* Modified by Sefa Ilkimen:
* - added configurable params serializer
*
*/
/*
* An HTTP Plugin for PhoneGap.
*/
var exec = require('cordova/exec');
var validSerializers = ['urlencoded', 'json'];
// Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function mergeHeaders(globalHeaders, localHeaders) {
var globalKeys = Object.keys(globalHeaders);
var key;
for (var i = 0; i < globalKeys.length; i++) {
key = globalKeys[i];
if (!localHeaders.hasOwnProperty(key)) {
localHeaders[key] = globalHeaders[key];
}
}
return localHeaders;
}
function checkSerializer(serializer) {
serializer = serializer || '';
serializer = serializer.trim().toLowerCase();
if (validSerializers.indexOf(serializer) > -1) {
return serializer;
}
return serializer[0];
}
var http = {
headers: {},
dataSerializer: 'urlencoded',
sslPinning: false,
getBasicAuthHeader: function (username, password) {
return {'Authorization': 'Basic ' + b64EncodeUnicode(username + ':' + password)};
},
useBasicAuth: function (username, password) {
this.headers.Authorization = 'Basic ' + b64EncodeUnicode(username + ':' + password);
},
setHeader: function (header, value) {
this.headers[header] = value;
},
setDataSerializer: function (serializer) {
this.dataSerializer = checkSerializer(serializer);
},
enableSSLPinning: function (enable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'enableSSLPinning', [enable]);
},
acceptAllCerts: function (allow, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'acceptAllCerts', [allow]);
},
validateDomainName: function (validate, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'validateDomainName', [validate]);
},
post: function (url, data, headers, success, failure) {
data = data || {};
headers = headers || {};
headers = mergeHeaders(this.headers, headers);
return exec(success, failure, 'CordovaHttpPlugin', 'post', [url, data, this.dataSerializer, headers]);
},
get: function (url, params, headers, success, failure) {
params = params || {};
headers = headers || {};
headers = mergeHeaders(this.headers, headers);
return exec(success, failure, 'CordovaHttpPlugin', 'get', [url, params, headers]);
},
put: function (url, data, headers, success, failure) {
data = data || {};
headers = headers || {};
headers = mergeHeaders(this.headers, headers);
return exec(success, failure, 'CordovaHttpPlugin', 'put', [url, data, this.dataSerializer, headers]);
},
delete: function (url, params, headers, success, failure) {
params = params || {};
headers = headers || {};
headers = mergeHeaders(this.headers, headers);
return exec(success, failure, 'CordovaHttpPlugin', 'delete', [url, params, headers]);
},
head: function (url, params, headers, success, failure) {
headers = mergeHeaders(this.headers, headers);
return exec(success, failure, 'CordovaHttpPlugin', 'head', [url, params, headers]);
},
uploadFile: function (url, params, headers, filePath, name, success, failure) {
headers = mergeHeaders(this.headers, headers);
return exec(success, failure, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name]);
},
downloadFile: function (url, params, headers, filePath, success, failure) {
headers = mergeHeaders(this.headers, headers);
var win = function (result) {
var entry = new (require('cordova-plugin-file.FileEntry'))();
entry.isDirectory = false;
entry.isFile = true;
entry.name = result.file.name;
entry.fullPath = result.file.fullPath;
entry.filesystem = new FileSystem(result.file.filesystemName || (result.file.filesystem == window.PERSISTENT ? 'persistent' : 'temporary'));
entry.nativeURL = result.file.nativeURL;
success(entry);
};
return exec(win, failure, 'CordovaHttpPlugin', 'downloadFile', [url, params, headers, filePath]);
}
};
if (typeof angular !== 'undefined') {
angular.module('cordovaHTTP', []).factory('cordovaHTTP', function ($timeout, $q) {
function makePromise(fn, args, async) {
var deferred = $q.defer();
var success = function (response) {
if (async) {
$timeout(function () {
deferred.resolve(response);
});
} else {
deferred.resolve(response);
}
};
var fail = function (response) {
if (async) {
$timeout(function () {
deferred.reject(response);
});
} else {
deferred.reject(response);
}
};
args.push(success);
args.push(fail);
fn.apply(http, args);
return deferred.promise;
}
var cordovaHTTP = {
getBasicAuthHeader: http.getBasicAuthHeader,
useBasicAuth: function (username, password) {
return http.useBasicAuth(username, password);
},
setHeader: function (header, value) {
return http.setHeader(header, value);
},
setDataSerializer: function (serializer) {
return http.setParamSerializer(serializer);
},
enableSSLPinning: function (enable) {
return makePromise(http.enableSSLPinning, [enable]);
},
acceptAllCerts: function (allow) {
return makePromise(http.acceptAllCerts, [allow]);
},
validateDomainName: function (validate) {
return makePromise(http.validateDomainName, [validate]);
},
post: function (url, data, headers) {
return makePromise(http.post, [url, data, headers], true);
},
get: function (url, params, headers) {
return makePromise(http.get, [url, params, headers], true);
},
put: function (url, data, headers) {
return makePromise(http.put, [url, data, headers], true);
},
delete: function (url, params, headers) {
return makePromise(http.delete, [url, params, headers], true);
},
head: function (url, params, headers) {
return makePromise(http.head, [url, params, headers], true);
},
uploadFile: function (url, params, headers, filePath, name) {
return makePromise(http.uploadFile, [url, params, headers, filePath, name], true);
},
downloadFile: function (url, params, headers, filePath) {
return makePromise(http.downloadFile, [url, params, headers, filePath], true);
}
};
return cordovaHTTP;
});
}
module.exports = http;

8
www/global-configs.js Normal file
View File

@@ -0,0 +1,8 @@
var globalConfigs = {
headers: {},
serializer: 'urlencoded',
followRedirect: true,
timeout: 60.0,
};
module.exports = globalConfigs;

315
www/helpers.js Normal file
View File

@@ -0,0 +1,315 @@
module.exports = function init(jsUtil, cookieHandler, messages) {
var validSerializers = ['urlencoded', 'json', 'utf8'];
var validCertModes = ['default', 'nocheck', 'pinned', 'legacy'];
var validClientAuthModes = ['none', 'systemstore', 'buffer'];
var validHttpMethods = ['get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download'];
var interface = {
b64EncodeUnicode: b64EncodeUnicode,
checkSerializer: checkSerializer,
checkSSLCertMode: checkSSLCertMode,
checkClientAuthMode: checkClientAuthMode,
checkClientAuthOptions: checkClientAuthOptions,
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
checkTimeoutValue: checkTimeoutValue,
checkFollowRedirectValue: checkFollowRedirectValue,
injectCookieHandler: injectCookieHandler,
injectFileEntryHandler: injectFileEntryHandler,
getMergedHeaders: getMergedHeaders,
getProcessedData: getProcessedData,
handleMissingCallbacks: handleMissingCallbacks,
handleMissingOptions: handleMissingOptions
};
// expose all functions for testing purposes
if (init.debug) {
interface.mergeHeaders = mergeHeaders;
interface.checkForValidStringValue = checkForValidStringValue;
interface.checkKeyValuePairObject = checkKeyValuePairObject;
interface.checkHttpMethod = checkHttpMethod;
interface.checkHeadersObject = checkHeadersObject;
interface.checkParamsObject = checkParamsObject;
interface.resolveCookieString = resolveCookieString;
interface.createFileEntry = createFileEntry;
interface.getCookieHeader = getCookieHeader;
interface.getMatchingHostHeaders = getMatchingHostHeaders;
interface.getAllowedDataTypes = getAllowedDataTypes;
}
return interface;
// Thanks Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function mergeHeaders(globalHeaders, localHeaders) {
var globalKeys = Object.keys(globalHeaders);
var key;
for (var i = 0; i < globalKeys.length; i++) {
key = globalKeys[i];
if (!localHeaders.hasOwnProperty(key)) {
localHeaders[key] = globalHeaders[key];
}
}
return localHeaders;
}
function checkForValidStringValue(list, value, onInvalidValueMessage) {
if (jsUtil.getTypeOf(value) !== 'String') {
throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
}
value = value.trim().toLowerCase();
if (list.indexOf(value) === -1) {
throw new Error(onInvalidValueMessage + ' ' + list.join(', '));
}
return value;
}
function checkKeyValuePairObject(obj, allowedChildren, onInvalidValueMessage) {
if (jsUtil.getTypeOf(obj) !== 'Object') {
throw new Error(onInvalidValueMessage);
}
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
if (allowedChildren.indexOf(jsUtil.getTypeOf(obj[keys[i]])) === -1) {
throw new Error(onInvalidValueMessage);
}
}
return obj;
}
function checkHttpMethod(method) {
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
}
function checkSerializer(serializer) {
return checkForValidStringValue(validSerializers, serializer, messages.INVALID_DATA_SERIALIZER);
}
function checkSSLCertMode(mode) {
return checkForValidStringValue(validCertModes, mode, messages.INVALID_SSL_CERT_MODE);
}
function checkClientAuthMode(mode) {
return checkForValidStringValue(validClientAuthModes, mode, messages.INVALID_CLIENT_AUTH_MODE);
}
function checkClientAuthOptions(mode, options) {
options = options || {};
// none
if (mode === validClientAuthModes[0]) {
return {
alias: null,
rawPkcs: null,
pkcsPassword: ''
};
}
if (jsUtil.getTypeOf(options) !== 'Object') {
throw new Error(messages.INVALID_CLIENT_AUTH_OPTIONS);
}
// systemstore
if (mode === validClientAuthModes[1]) {
if (jsUtil.getTypeOf(options.alias) !== 'String'
&& jsUtil.getTypeOf(options.alias) !== 'Undefined') {
throw new Error(messages.INVALID_CLIENT_AUTH_ALIAS);
}
return {
alias: jsUtil.getTypeOf(options.alias) === 'Undefined' ? null : options.alias,
rawPkcs: null,
pkcsPassword: ''
};
}
// buffer
if (mode === validClientAuthModes[2]) {
if (jsUtil.getTypeOf(options.rawPkcs) !== 'ArrayBuffer') {
throw new Error(messages.INVALID_CLIENT_AUTH_RAW_PKCS);
}
if (jsUtil.getTypeOf(options.pkcsPassword) !== 'String') {
throw new Error(messages.INVALID_CLIENT_AUTH_PKCS_PASSWORD);
}
return {
alias: null,
rawPkcs: options.rawPkcs,
pkcsPassword: options.pkcsPassword
}
}
}
function checkForBlacklistedHeaderKey(key) {
if (key.toLowerCase() === 'cookie') {
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
}
return key;
}
function checkForInvalidHeaderValue(value) {
if (jsUtil.getTypeOf(value) !== 'String') {
throw new Error(messages.INVALID_HEADERS_VALUE);
}
return value;
}
function checkTimeoutValue(timeout) {
if (jsUtil.getTypeOf(timeout) !== 'Number' || timeout < 0) {
throw new Error(messages.INVALID_TIMEOUT_VALUE);
}
return timeout;
}
function checkFollowRedirectValue(follow) {
if (jsUtil.getTypeOf(follow) !== 'Boolean') {
throw new Error(messages.INVALID_FOLLOW_REDIRECT_VALUE);
}
return follow;
}
function checkHeadersObject(headers) {
return checkKeyValuePairObject(headers, ['String'], messages.INVALID_HEADERS_VALUE);
}
function checkParamsObject(params) {
return checkKeyValuePairObject(params, ['String', 'Array'], messages.INVALID_PARAMS_VALUE);
}
function resolveCookieString(headers) {
var keys = Object.keys(headers || {});
for (var i = 0; i < keys.length; ++i) {
if (keys[i].match(/^set-cookie$/i)) {
return headers[keys[i]];
}
}
return null;
}
function createFileEntry(rawEntry) {
var entry = new (require('cordova-plugin-file.FileEntry'))();
entry.isDirectory = rawEntry.isDirectory;
entry.isFile = rawEntry.isFile;
entry.name = rawEntry.name;
entry.fullPath = rawEntry.fullPath;
entry.filesystem = new FileSystem(rawEntry.filesystemName || (rawEntry.filesystem == window.PERSISTENT ? 'persistent' : 'temporary'));
entry.nativeURL = rawEntry.nativeURL;
return entry;
}
function injectCookieHandler(url, cb) {
return function (response) {
cookieHandler.setCookieFromString(url, resolveCookieString(response.headers));
cb(response);
}
}
function injectFileEntryHandler(cb) {
return function (response) {
cb(createFileEntry(response.file));
}
}
function getCookieHeader(url) {
var cookieString = cookieHandler.getCookieString(url);
if (cookieString.length) {
return { Cookie: cookieHandler.getCookieString(url) };
}
return {};
}
function getMatchingHostHeaders(url, headersList) {
var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
var domain = matches && matches[1];
return headersList[domain] || null;
}
function getMergedHeaders(url, requestHeaders, predefinedHeaders) {
var globalHeaders = predefinedHeaders['*'] || {};
var hostHeaders = getMatchingHostHeaders(url, predefinedHeaders) || {};
var mergedHeaders = mergeHeaders(globalHeaders, hostHeaders);
mergedHeaders = mergeHeaders(mergedHeaders, requestHeaders);
mergedHeaders = mergeHeaders(mergedHeaders, getCookieHeader(url));
return mergedHeaders;
}
function getAllowedDataTypes(dataSerializer) {
switch (dataSerializer) {
case 'utf8':
return ['String'];
case 'urlencoded':
return ['Object'];
default:
return ['Array', 'Object'];
}
}
function getProcessedData(data, dataSerializer) {
var currentDataType = jsUtil.getTypeOf(data);
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
if (allowedDataTypes.indexOf(currentDataType) === -1) {
throw new Error(messages.DATA_TYPE_MISMATCH + ' ' + allowedDataTypes.join(', '));
}
if (dataSerializer === 'utf8') {
data = { text: data };
}
return data;
}
function handleMissingCallbacks(successFn, failFn) {
if (jsUtil.getTypeOf(successFn) !== 'Function') {
throw new Error(messages.MANDATORY_SUCCESS);
}
if (jsUtil.getTypeOf(failFn) !== 'Function') {
throw new Error(messages.MANDATORY_FAIL);
}
}
function handleMissingOptions(options, globals) {
options = options || {};
return {
method: checkHttpMethod(options.method || validHttpMethods[0]),
serializer: checkSerializer(options.serializer || globals.serializer),
timeout: checkTimeoutValue(options.timeout || globals.timeout),
followRedirect: checkFollowRedirectValue(options.followRedirect || globals.followRedirect),
headers: checkHeadersObject(options.headers || {}),
params: checkParamsObject(options.params || {}),
data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data,
filePath: options.filePath || '',
name: options.name || ''
};
}
};

28
www/js-util.js Normal file
View File

@@ -0,0 +1,28 @@
module.exports = {
// typeof is not working reliably in JS
getTypeOf: function (object) {
switch (Object.prototype.toString.call(object)) {
case '[object Array]':
return 'Array';
case '[object ArrayBuffer]':
return 'ArrayBuffer';
case '[object Boolean]':
return 'Boolean';
case '[object Function]':
return 'Function';
case '[object Null]':
return 'Null';
case '[object Number]':
return 'Number';
case '[object Object]':
return 'Object';
case '[object String]':
return 'String';
case '[object Undefined]':
return 'Undefined';
default:
return 'Unknown';
}
}
}

181
www/local-storage-store.js Normal file
View File

@@ -0,0 +1,181 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Exponent
*
* 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.
*
* Based on "tough-cookie-web-storage-store" v1.0.0
* Thanks James Ide: https://github.com/exponentjs/tough-cookie-web-storage-store
*
* Modified by Sefa Ilkimen for cordova plugin integration
*
*/
'use strict';
module.exports = function init(ToughCookie, _) {
function WebStorageCookieStore(storage, storeKey) {
ToughCookie.Store.call(this);
this._storage = storage;
this._storeKey = storeKey || '__cookieStore__';
this.synchronous = true;
}
WebStorageCookieStore.prototype = Object.create(ToughCookie.Store);
WebStorageCookieStore.prototype.findCookie = function (domain, path, key, callback) {
var store = this._readStore();
var cookie = _.get(store, [domain, path, key], null);
callback(null, ToughCookie.Cookie.fromJSON(cookie));
};
WebStorageCookieStore.prototype.findCookies = function (domain, path, callback) {
if (!domain) {
callback(null, []);
return;
}
var that = this;
var cookies = [];
var store = this._readStore();
var domains = ToughCookie.permuteDomain(domain) || [domain];
domains.forEach(function (domain) {
if (!store[domain]) {
return;
}
var matchingPaths = Object.keys(store[domain]);
if (path != null) {
matchingPaths = matchingPaths.filter(function (cookiePath) {
return that._isOnPath(cookiePath, path);
});
}
matchingPaths.forEach(function (path) {
Array.prototype.push.apply(cookies, _.values(store[domain][path]));
});
});
cookies = cookies.map(function (cookie) {
return ToughCookie.Cookie.fromJSON(cookie);
});
callback(null, cookies);
};
/**
* Returns whether `cookiePath` is on the given `urlPath`
*/
WebStorageCookieStore.prototype._isOnPath = function (cookiePath, urlPath) {
if (!cookiePath) {
return false;
}
if (cookiePath === urlPath) {
return true;
}
if (urlPath.indexOf(cookiePath) !== 0) {
return false;
}
if (cookiePath[cookiePath.length - 1] !== '/' && urlPath[cookiePath.length] !== '/') {
return false;
}
return true;
};
WebStorageCookieStore.prototype.putCookie = function (cookie, callback) {
var store = this._readStore();
_.set(store, [cookie.domain, cookie.path, cookie.key], cookie);
this._writeStore(store);
callback(null);
};
WebStorageCookieStore.prototype.updateCookie = function (oldCookie, newCookie, callback) {
this.putCookie(newCookie, callback);
};
WebStorageCookieStore.prototype.removeCookie = function (domain, path, key, callback) {
var store = this._readStore();
_.unset(store, [domain, path, key]);
this._writeStore(store);
callback(null);
};
WebStorageCookieStore.prototype.removeCookies = function (domain, path, callback) {
var store = this._readStore();
if (path == null) {
_.unset(store, [domain]);
} else {
_.unset(store, [domain, path]);
}
this._writeStore(store);
callback(null);
};
WebStorageCookieStore.prototype.getAllCookies = function (callback) {
var cookies = [];
var store = this._readStore();
Object.keys(store).forEach(function (domain) {
Object.keys(store[domain]).forEach(function (path) {
Array.protype.push.apply(cookies, _.values(store[domain][path]));
});
});
cookies = cookies.map(function (cookie) {
return ToughCookie.Cookie.fromJSON(cookie);
});
cookies.sort(function (c1, c2) {
return (c1.creationIndex || 0) - (c2.creationIndex || 0);
});
callback(null, cookies);
};
WebStorageCookieStore.prototype._readStore = function () {
var json = this._storage.getItem(this._storeKey);
if (json !== null) {
try {
return JSON.parse(json);
} catch (e) { }
}
return {};
};
WebStorageCookieStore.prototype._writeStore = function (store) {
this._storage.setItem(this._storeKey, JSON.stringify(store));
};
return WebStorageCookieStore;
};

20
www/lodash.js Normal file
View File

@@ -0,0 +1,20 @@
/**
* @license
* Lodash (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
* Build: `lodash include="get,set,unset,values" exports="node"`
*/
;(function(){function t(t,e){for(var r=-1,n=null==t?0:t.length,o=Array(n);++r<n;)o[r]=e(t[r],r,t);return o}function e(t){return function(e){return t(e)}}function r(e,r){return t(r,function(t){return e[t]})}function n(){}function o(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}function u(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}function i(t){var e=-1,r=null==t?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1]);
}}function c(t,e){for(var r=t.length;r--;)if(m(t[r][0],e))return r;return-1}function a(t,e){e=h(e,t);for(var r=0,n=e.length;null!=t&&r<n;)t=t[j(e[r++])];return r&&r==n?t:E}function l(t){if(null==t)return t===E?"[object Undefined]":"[object Null]";t=Object(t);var e;if(nt&&nt in t){var r=Q.call(t,nt),n=t[nt];try{t[nt]=E,e=true}catch(t){}var o=Y.call(t);e&&(r?t[nt]=n:delete t[nt]),e=o}else e=Y.call(t);return e}function s(t){return w(t)&&"[object Arguments]"==l(t)}function f(t){return w(t)&&z(t.length)&&!!M[l(t)];
}function p(e){if(typeof e=="string")return e;if(ft(e))return t(e,p)+"";if(x(e))return at?at.call(e):"";var r=e+"";return"0"==r&&1/e==-T?"-0":r}function h(t,e){var r;return ft(t)?r=t:(ft(t)?r=false:(r=typeof t,r=!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=t&&!x(t))||(B.test(t)||!I.test(t)||null!=e&&t in Object(e))),r=r?[t]:lt(F(t))),r}function y(t,e){var r=t.__data__,n=typeof e;return("string"==n||"number"==n||"symbol"==n||"boolean"==n?"__proto__"!==e:null===e)?r[typeof e=="string"?"string":"hash"]:r.map;
}function b(t,e){var r=null==t?E:t[e];return(!S(r)||X&&X in r?0:(O(r)?Z:L).test(g(r)))?r:E}function _(t,e){return e=null==e?9007199254740991:e,!!e&&(typeof t=="number"||R.test(t))&&-1<t&&0==t%1&&t<e}function j(t){if(typeof t=="string"||x(t))return t;var e=t+"";return"0"==e&&1/t==-T?"-0":e}function g(t){if(null!=t){try{return K.call(t)}catch(t){}return t+""}return""}function v(t){var e=null==t?0:t.length;return e?t[e-1]:E}function d(t,e){function r(){var n=arguments,o=e?e.apply(this,n):n[0],u=r.cache;
return u.has(o)?u.get(o):(n=t.apply(this,n),r.cache=u.set(o,n)||u,n)}if(typeof t!="function"||null!=e&&typeof e!="function")throw new TypeError("Expected a function");return r.cache=new(d.Cache||i),r}function m(t,e){return t===e||t!==t&&e!==e}function A(t){return null!=t&&z(t.length)&&!O(t)}function O(t){return!!S(t)&&(t=l(t),"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t)}function z(t){return typeof t=="number"&&-1<t&&0==t%1&&9007199254740991>=t;
}function S(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function w(t){return null!=t&&typeof t=="object"}function x(t){return typeof t=="symbol"||w(t)&&"[object Symbol]"==l(t)}function F(t){return null==t?"":p(t)}function $(t){if(A(t)){var e=ft(t),r=!e&&st(t),n=!e&&!r&&pt(t),o=!e&&!r&&!n&&ht(t);if(e=e||r||n||o){for(var r=t.length,u=String,i=-1,c=Array(r);++i<r;)c[i]=u(i);r=c}else r=[];var a,u=r.length;for(a in t)!Q.call(t,a)||e&&("length"==a||n&&("offset"==a||"parent"==a)||o&&("buffer"==a||"byteLength"==a||"byteOffset"==a)||_(a,u))||r.push(a);
t=r}else if(a=t&&t.constructor,t===(typeof a=="function"&&a.prototype||H)){a=[];for(n in Object(t))Q.call(t,n)&&"constructor"!=n&&a.push(n);t=a}else t=ut(t);return t}function k(){return false}var E,T=1/0,I=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,B=/^\w*$/,P=/^\./,U=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,C=/\\(\\)?/g,L=/^\[object .+?Constructor\]$/,R=/^(?:0|[1-9]\d*)$/,M={};M["[object Float32Array]"]=M["[object Float64Array]"]=M["[object Int8Array]"]=M["[object Int16Array]"]=M["[object Int32Array]"]=M["[object Uint8Array]"]=M["[object Uint8ClampedArray]"]=M["[object Uint16Array]"]=M["[object Uint32Array]"]=true,
M["[object Arguments]"]=M["[object Array]"]=M["[object ArrayBuffer]"]=M["[object Boolean]"]=M["[object DataView]"]=M["[object Date]"]=M["[object Error]"]=M["[object Function]"]=M["[object Map]"]=M["[object Number]"]=M["[object Object]"]=M["[object RegExp]"]=M["[object Set]"]=M["[object String]"]=M["[object WeakMap]"]=false;var N,D=typeof global=="object"&&global&&global.Object===Object&&global,V=typeof self=="object"&&self&&self.Object===Object&&self,q=D||V||Function("return this")(),G=(V=typeof exports=="object"&&exports&&!exports.nodeType&&exports)&&typeof module=="object"&&module&&!module.nodeType&&module,W=G&&G.exports===V,D=W&&D.process;
t:{try{N=D&&D.binding&&D.binding("util");break t}catch(t){}N=void 0}N=N&&N.isTypedArray;var D=Array.prototype,H=Object.prototype,J=q["__core-js_shared__"],K=Function.prototype.toString,Q=H.hasOwnProperty,X=function(){var t=/[^.]+$/.exec(J&&J.keys&&J.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}(),Y=H.toString,Z=RegExp("^"+K.call(Q).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),tt=W?q.Buffer:E,W=q.Symbol,et=H.propertyIsEnumerable,rt=D.splice,nt=W?W.toStringTag:E,ot=function(){
try{var t=b(Object,"defineProperty");return t({},"",{}),t}catch(t){}}(),D=tt?tt.isBuffer:E,ut=function(t,e){return function(r){return t(e(r))}}(Object.keys,Object),it=b(q,"Map"),ct=b(Object,"create"),at=(q=W?W.prototype:E)?q.toString:E;o.prototype.clear=function(){this.__data__=ct?ct(null):{},this.size=0},o.prototype.delete=function(t){return t=this.has(t)&&delete this.__data__[t],this.size-=t?1:0,t},o.prototype.get=function(t){var e=this.__data__;return ct?(t=e[t],"__lodash_hash_undefined__"===t?E:t):Q.call(e,t)?e[t]:E;
},o.prototype.has=function(t){var e=this.__data__;return ct?e[t]!==E:Q.call(e,t)},o.prototype.set=function(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=ct&&e===E?"__lodash_hash_undefined__":e,this},u.prototype.clear=function(){this.__data__=[],this.size=0},u.prototype.delete=function(t){var e=this.__data__;return t=c(e,t),!(0>t)&&(t==e.length-1?e.pop():rt.call(e,t,1),--this.size,true)},u.prototype.get=function(t){var e=this.__data__;return t=c(e,t),0>t?E:e[t][1]},u.prototype.has=function(t){
return-1<c(this.__data__,t)},u.prototype.set=function(t,e){var r=this.__data__,n=c(r,t);return 0>n?(++this.size,r.push([t,e])):r[n][1]=e,this},i.prototype.clear=function(){this.size=0,this.__data__={hash:new o,map:new(it||u),string:new o}},i.prototype.delete=function(t){return t=y(this,t).delete(t),this.size-=t?1:0,t},i.prototype.get=function(t){return y(this,t).get(t)},i.prototype.has=function(t){return y(this,t).has(t)},i.prototype.set=function(t,e){var r=y(this,t),n=r.size;return r.set(t,e),this.size+=r.size==n?0:1,
this};var lt=function(t){t=d(t,function(t){return 500===e.size&&e.clear(),t});var e=t.cache;return t}(function(t){var e=[];return P.test(t)&&e.push(""),t.replace(U,function(t,r,n,o){e.push(n?o.replace(C,"$1"):r||t)}),e});d.Cache=i;var st=s(function(){return arguments}())?s:function(t){return w(t)&&Q.call(t,"callee")&&!et.call(t,"callee")},ft=Array.isArray,pt=D||k,ht=N?e(N):f;n.keys=$,n.memoize=d,n.set=function(t,e,r){if(null!=t&&S(t)){e=h(e,t);for(var n=-1,o=e.length,u=o-1,i=t;null!=i&&++n<o;){var c=j(e[n]),a=r;
if(n!=u){var l=i[c],a=E;a===E&&(a=S(l)?l:_(e[n+1])?[]:{})}var s=i,l=c,f=s[l];Q.call(s,l)&&m(f,a)&&(a!==E||l in s)||("__proto__"==l&&ot?ot(s,l,{configurable:true,enumerable:true,value:a,writable:true}):s[l]=a),i=i[c]}}return t},n.unset=function(t,e){var r;if(null==t)r=true;else{var n=t,o=r=h(e,n);if(!(2>o.length)){var u=0,i=-1,c=-1,l=o.length;for(0>u&&(u=-u>l?0:l+u),i=i>l?l:i,0>i&&(i+=l),l=u>i?0:i-u>>>0,u>>>=0,i=Array(l);++c<l;)i[c]=o[c+u];n=a(n,i)}r=j(v(r)),r=!(null!=n&&Q.call(n,r))||delete n[r]}return r;
},n.values=function(t){return null==t?[]:r(t,$(t))},n.eq=m,n.get=function(t,e,r){return t=null==t?E:a(t,e),t===E?r:t},n.isArguments=st,n.isArray=ft,n.isArrayLike=A,n.isBuffer=pt,n.isFunction=O,n.isLength=z,n.isObject=S,n.isObjectLike=w,n.isSymbol=x,n.isTypedArray=ht,n.last=v,n.stubFalse=k,n.toString=F,n.VERSION="4.17.1",G&&((G.exports=n)._=n,V._=n)}).call(this);

18
www/messages.js Normal file
View File

@@ -0,0 +1,18 @@
module.exports = {
ADDING_COOKIES_NOT_SUPPORTED: 'advanced-http: "setHeader" does not support adding cookies, please use "setCookie" function instead',
DATA_TYPE_MISMATCH: 'advanced-http: "data" argument supports only following data types:',
MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
INVALID_CLIENT_AUTH_MODE: 'advanced-http: invalid client certificate authentication mode, supported modes are:',
INVALID_CLIENT_AUTH_OPTIONS: 'advanced-http: invalid client certificate authentication options, needs to be an object',
INVALID_CLIENT_AUTH_ALIAS: 'advanced-http: invalid client certificate alias, needs to be a string or undefined',
INVALID_CLIENT_AUTH_RAW_PKCS: 'advanced-http: invalid PKCS12 container, needs to be an array buffer',
INVALID_CLIENT_AUTH_PKCS_PASSWORD: 'advanced-http: invalid PKCS12 container password, needs to be a string',
INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings',
INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value',
INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings'
};

198
www/public-interface.js Normal file
View File

@@ -0,0 +1,198 @@
module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConfigs) {
const publicInterface = {
getBasicAuthHeader: getBasicAuthHeader,
useBasicAuth: useBasicAuth,
getHeaders: getHeaders,
setHeader: setHeader,
getDataSerializer: getDataSerializer,
setDataSerializer: setDataSerializer,
setCookie: setCookie,
clearCookies: clearCookies,
removeCookies: removeCookies,
getCookieString: getCookieString,
getRequestTimeout: getRequestTimeout,
setRequestTimeout: setRequestTimeout,
getFollowRedirect: getFollowRedirect,
setFollowRedirect: setFollowRedirect,
// @DEPRECATED
disableRedirect: disableRedirect,
// @DEPRECATED
setSSLCertMode: setServerTrustMode,
setServerTrustMode: setServerTrustMode,
setClientAuthMode: setClientAuthMode,
sendRequest: sendRequest,
post: post,
get: get,
put: put,
patch: patch,
delete: del,
head: head,
uploadFile: uploadFile,
downloadFile: downloadFile
};
function getBasicAuthHeader(username, password) {
return { 'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password) };
}
function useBasicAuth(username, password) {
this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password));
}
function getHeaders(host) {
return globalConfigs.headers[host || '*'] || null;
}
function setHeader() {
// this one is for being backward compatible
var host = '*';
var header = arguments[0];
var value = arguments[1];
if (arguments.length === 3) {
host = arguments[0];
header = arguments[1];
value = arguments[2];
}
helpers.checkForBlacklistedHeaderKey(header);
helpers.checkForInvalidHeaderValue(value);
globalConfigs.headers[host] = globalConfigs.headers[host] || {};
globalConfigs.headers[host][header] = value;
}
function getDataSerializer() {
return globalConfigs.serializer;
}
function setDataSerializer(serializer) {
globalConfigs.serializer = helpers.checkSerializer(serializer);
}
function setCookie(url, cookie, options) {
cookieHandler.setCookie(url, cookie, options);
}
function clearCookies() {
cookieHandler.clearCookies();
}
function removeCookies(url, callback) {
cookieHandler.removeCookies(url, callback);
}
function getCookieString(url) {
return cookieHandler.getCookieString(url);
}
function getRequestTimeout() {
return globalConfigs.timeout;
}
function setRequestTimeout(timeout) {
globalConfigs.timeout = helpers.checkTimeoutValue(timeout);
}
function getFollowRedirect() {
return globalConfigs.followRedirect;
}
function setFollowRedirect(follow) {
globalConfigs.followRedirect = helpers.checkFollowRedirectValue(follow);
}
// @DEPRECATED
function disableRedirect(disable, success, failure) {
helpers.handleMissingCallbacks(success, failure);
setFollowRedirect(!disable);
success();
}
function setServerTrustMode(mode, success, failure) {
helpers.handleMissingCallbacks(success, failure);
return exec(success, failure, 'CordovaHttpPlugin', 'setServerTrustMode', [helpers.checkSSLCertMode(mode)]);
}
function setClientAuthMode() {
var mode = arguments[0];
var options = null;
var success = arguments[1];
var failure = arguments[2];
if (arguments.length === 4) {
options = arguments[1];
success = arguments[2];
failure = arguments[3];
}
mode = helpers.checkClientAuthMode(mode);
options = helpers.checkClientAuthOptions(mode, options);
helpers.handleMissingCallbacks(success, failure);
return exec(success, failure, 'CordovaHttpPlugin', 'setClientAuthMode', [mode, options.alias, options.rawPkcs, options.pkcsPassword]);
}
function sendRequest(url, options, success, failure) {
helpers.handleMissingCallbacks(success, failure);
options = helpers.handleMissingOptions(options, globalConfigs);
url = urlUtil.appendQueryParamsString(url, urlUtil.serializeQueryParams(options.params, true));
var headers = helpers.getMergedHeaders(url, options.headers, globalConfigs.headers);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
switch (options.method) {
case 'post':
case 'put':
case 'patch':
var data = helpers.getProcessedData(options.data, options.serializer);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect]);
case 'upload':
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, headers, options.filePath, options.name, options.timeout, options.followRedirect]);
case 'download':
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, options.filePath, options.timeout, options.followRedirect]);
default:
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, headers, options.timeout, options.followRedirect]);
}
}
function post(url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'post', data: data, headers: headers }, success, failure);
};
function get(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure);
};
function put(url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'put', data: data, headers: headers }, success, failure);
}
function patch(url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'patch', data: data, headers: headers }, success, failure);
}
function del(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'delete', params: params, headers: headers }, success, failure);
}
function head(url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'head', params: params, headers: headers }, success, failure);
}
function uploadFile(url, params, headers, filePath, name, success, failure) {
return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure);
}
function downloadFile(url, params, headers, filePath, success, failure) {
return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
}
return publicInterface;
}

5089
www/umd-tough-cookie.js Normal file

File diff suppressed because one or more lines are too long

103
www/url-util.js Normal file
View File

@@ -0,0 +1,103 @@
module.exports = function init(jsUtil) {
return {
parseUrl: parseUrl,
appendQueryParamsString: appendQueryParamsString,
serializeQueryParams: serializeQueryParams
}
function parseUrl(url) {
var match = url.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
return match && {
protocol: match[1],
host: match[2],
hostname: match[3],
port: match[4] || '',
pathname: match[5],
search: match[6],
hash: match[7]
}
}
function appendQueryParamsString(url, params) {
if (!url.length || !params.length) {
return url;
}
var parsed = parseUrl(url);
return parsed.protocol
+ '//'
+ parsed.host
+ parsed.pathname
+ (parsed.search.length ? parsed.search + '&' + params : '?' + params)
+ parsed.hash;
}
function serializeQueryParams(params, encode) {
return serializeObject('', params, encode);
}
function serializeObject(parentKey, object, encode) {
var parts = [];
for (var key in object) {
if (!object.hasOwnProperty(key)) {
continue;
}
var identifier = parentKey.length ? parentKey + '[' + key + ']' : key;
if (jsUtil.getTypeOf(object[key]) === 'Array') {
parts.push(serializeArray(identifier, object[key], encode));
continue;
} else if (jsUtil.getTypeOf(object[key]) === 'Object') {
parts.push(serializeObject(identifier, object[key], encode));
continue;
}
parts.push(serializeIdentifier(parentKey, key, encode) + '=' + serializeValue(object[key], encode));
}
return parts.join('&');
}
function serializeArray(parentKey, array, encode) {
var parts = [];
for (var i = 0; i < array.length; ++i) {
if (jsUtil.getTypeOf(array[i]) === 'Array') {
parts.push(serializeArray(parentKey + '[]', array[i], encode));
continue;
} else if (jsUtil.getTypeOf(array[i]) === 'Object') {
parts.push(serializeObject(parentKey + '[]' + array[i], encode));
continue;
}
parts.push(serializeIdentifier(parentKey, '', encode) + '=' + serializeValue(array[i], encode));
}
return parts.join('&');
}
function serializeIdentifier(parentKey, key, encode) {
if (!parentKey.length) {
return encode ? encodeURIComponent(key) : key;
}
if (encode) {
return encodeURIComponent(parentKey) + '[' + encodeURIComponent(key) + ']';
} else {
return parentKey + '[' + key + ']';
}
}
function serializeValue(value, encode) {
if (encode) {
return encodeURIComponent(value);
} else {
return value;
}
}
};