Compare commits

...

78 Commits

Author SHA1 Message Date
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
41 changed files with 1892 additions and 892 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

2
.gitignore vendored
View File

@@ -3,3 +3,5 @@ tags
.zedstate
npm-debug.log
/temp
/android-sdk-macosx.zip
/android-sdk-macosx/**

View File

@@ -4,38 +4,35 @@ language: objective-c
os: osx
osx_image: xcode9.1
env:
- TARGET_PLATFORM=android
- TARGET_PLATFORM=ios
cache:
directories:
- node_modules
addons:
sauce_connect:
username: silkimen
jwt: RZHC8x5ggrXdu1xzhhHmQDwege9s3putJXY1ZG8+MNMaBTLfLBWzFsHuR6Lvz+XnrAT/CTGG957kw6ikSaV6VEkGvtO91irZoJyShjPq2hBbn2CaEbo91Eq8iNYaRG+4FVMxoHa6xpF/HtvOLQdIkURc/lzLj/BOFMmtV/d3adLDcryQZuYB211iN4d6exGHMQe8uvJdkCy4ac85guhF8KmAELyaVa0rx0m6gNaszWU9BJ8Rn+4HJbJtfpQcU1bJN43Dx8forRU/Com53K+KFtDKPUdBHZnuENrZrQ+8RU59vdLrHwsCvP9elJFkcX9xk/r45oUGXhqVV3vMyaqTa9YUO772rhgKuAi4TqiaR51H58KWqynp7wOZwL5FqJs0KzL5rNDDIoKKrklnOidS4RAUKXCi1CUNXwDdOo4Kd96cYOIHqpwa7Beci8CtVlzd/49yE9/AXnZYVwp10uKZvJJdliAOHhVEAtCIp8PzHOOPYJsXvUcEU4ORWWWpusBAz6wFiLOL4d/CAHNj6wDFliRexBrv/Wunu5ifva4oVJWrtAtyGDtsX8kBkf8OcIdy/aPUIIBrbW1sapjPFymMGeL/KkSZRjEy7KjFZKZh9L7sJE56sq+IUremFbcyaX1E3rho6Cs3ongLaKhL9f9vz8EFs7//w8pNlNS9EUdOU68=
sauce_connect: true
before_install:
- export LANG=en_US.UTF-8
- brew update
install:
- npm install
- brew install gradle
- wget http://dl.google.com/android/android-sdk_r24.4-macosx.zip
- tar -xvf android-sdk_r24.4-macosx.zip
- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter platform-tools
- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter build-tools-25.0.0
- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter android-25
- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-support
- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-m2repository
- echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-google-m2repository
- export ANDROID_HOME=./android-sdk-macosx
- export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/23.0.2
- if [ $TARGET_PLATFORM = "android" ]; then
brew update &&
brew install gradle &&
scripts/setup-android-sdk.sh &&
export ANDROID_HOME=$(pwd)/android-sdk-macosx &&
export PATH=${PATH}:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools/23.0.2;
fi
script:
- travis_wait scripts/build-test-app.sh
- scripts/upload-artifact.sh
- npm run testjs
- scripts/test-app.sh --ios --emulator
- travis_wait scripts/build-test-app.sh --$TARGET_PLATFORM --emulator &&
scripts/upload-artifact.sh --$TARGET_PLATFORM &&
scripts/test-app.sh --$TARGET_PLATFORM --emulator;
after_success:

View File

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

131
README.md
View File

@@ -6,7 +6,8 @@ Cordova Advanced HTTP
[![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 and Android.
Cordova / Phonegap plugin for communicating with HTTP servers. Supports iOS, Android and [Browser](#browserSupport).
This is a fork of [Wymsee's Cordova-HTTP plugin](https://github.com/wymsee/cordova-HTTP).
## Advantages over Javascript requests
@@ -32,11 +33,17 @@ 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
### With Ionic-native wrapper
Check the [Ionic docs](https://ionicframework.com/docs/native/http/) for how to use this plugin with Ionic-native.
### With AngularJS (Deprecated)
:warning: *This feature is deprecated and will be removed anytime soon.* :warning:
This plugin creates a cordovaHTTP service inside of a cordovaHTTP module. You must load the module when you create your app's module.
@@ -63,8 +70,8 @@ This sets up all future requests to use Basic HTTP authentication with the given
cordova.plugin.http.useBasicAuth('user', 'password');
```
### setHeader
Set a header for all future requests to a specified host. Takes a hostname, 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).
```js
cordova.plugin.http.setHeader('Hostname', 'Header', 'Value');
@@ -87,25 +94,21 @@ cordova.plugin.http.setHeader('www.example.com', 'Header', 'Value');
cordova.plugin.http.setHeader('www.example.com:8080', 'Header', 'Value');
```
### disableRedirect
If set to `true`, it won't follow redirects automatically. This is a global setting.
```js
cordova.plugin.http.disableRedirect(true);
```
### setDataSerializer
### 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 two:
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.
@@ -114,6 +117,20 @@ Set how long to wait for a request to respond, in seconds.
cordova.plugin.http.setRequestTimeout(5.0);
```
### 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.
@@ -150,6 +167,17 @@ cordova.plugin.http.acceptAllCerts(true, function() {
});
```
### disableRedirect
If set to `true`, it won't follow redirects automatically. This defaults to false.
```js
cordova.plugin.http.disableRedirect(true, function() {
console.log('success!');
}, function() {
console.log('error :(');
});
```
### validateDomainName
This function was removed in v1.6.2. Domain name validation is disabled automatically when you enable "acceptAllCerts".
@@ -157,7 +185,43 @@ This function was removed in v1.6.2. Domain name validation is disabled automati
Remove all cookies associated with a given URL.
```js
cordova.plugin.http.removeCookies(url);
cordova.plugin.http.removeCookies(url, callback);
```
### sendRequest
Execute a HTTP request. Takes a URL and an options object. This is the internally used implementation of the following shorthand functions ([post](#post), [get](#get), [put](#put), [patch](#patch), [delete](#delete), [head](#head), [uploadFile](#uploadFile) and [downloadFile](#downloadFile)). You can use this function, if you want to override global settings for each single request.
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
* `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>
@@ -218,7 +282,7 @@ Here's a quick example:
}
```
### get
### get<a name="get"></a>
Execute a GET request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
```js
@@ -232,19 +296,19 @@ cordova.plugin.http.get('https://google.com/', {
});
```
### 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.
### patch
### 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
### delete<a name="delete"></a>
Execute a DELETE request. Takes a URL, parameters, and headers. See the [post](#post) documentation for details on what is returned on success and failure.
### head
### 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
### 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.
```js
@@ -258,7 +322,7 @@ cordova.plugin.http.uploadFile("https://google.com/", {
});
```
### 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).
```js
@@ -276,15 +340,28 @@ cordova.plugin.http.downloadFile("https://google.com/", {
});
```
## 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 both of them.
We made a few modifications to the networking libraries.
## Contribute & Develop

View File

@@ -1,9 +1,12 @@
{
"name": "cordova-plugin-advanced-http",
"version": "1.7.1",
"version": "1.11.1",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
"testapp": "./scripts/build-test-app.sh && ./scripts/test-app.sh --ios --emulator",
"buildbrowser": "./scripts/build-test-app.sh --browser",
"testandroid": "./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
"testios": "./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator",
"testapp": "npm run testandroid && npm run testios",
"testjs": "mocha ./test/js-mocha-specs.js",
"test": "npm run testjs && npm run testapp",
"release": "npm run test && ./scripts/release.sh"
@@ -55,7 +58,7 @@
"chai": "4.1.2",
"chai-as-promised": "7.1.1",
"colors": "1.1.2",
"cordova": "7.0.1",
"cordova": "7.1.0",
"mocha": "4.0.0",
"mock-require": "2.0.2",
"mz": "2.7.0",

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="1.7.1">
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="1.11.1">
<name>Advanced HTTP plugin</name>
<description>
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
@@ -10,9 +10,11 @@
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
<js-module src="www/lodash.js" name="lodash"/>
<js-module src="www/umd-tough-cookie.js" name="tough-cookie"/>
<js-module src="www/messages.js" name="messages"/>
<js-module src="www/local-storage-store.js" name="local-storage-store"/>
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
<js-module src="www/angular-integration.js" name="angular-integration"/>
<js-module src="www/helpers.js" name="helpers"/>
<js-module src="www/advanced-http.js" name="http">
<clobbers target="cordova.plugin.http"/>
</js-module>
@@ -24,6 +26,7 @@
</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"/>
@@ -33,6 +36,7 @@
<header-file src="src/ios/AFNetworking/AFURLSessionManager.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"/>
@@ -52,6 +56,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
</config-file>
<source-file src="src/android/com/github/kevinsawicki/http/HttpRequest.java" target-dir="src/com/github/kevinsawicki/http"/>
<source-file src="src/android/com/github/kevinsawicki/http/OkConnectionFactory.java" target-dir="src/com/github/kevinsawicki/http"/>
<source-file src="src/android/com/github/kevinsawicki/http/TLSSocketFactory.java" target-dir="src/com/github/kevinsawicki/http"/>
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttp.java" target-dir="src/com/synconset/cordovahttp"/>
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpDelete.java" target-dir="src/com/synconset/cordovahttp"/>
@@ -63,5 +68,16 @@
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPut.java" target-dir="src/com/synconset/cordovahttp"/>
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpPatch.java" target-dir="src/com/synconset/cordovahttp"/>
<source-file src="src/android/com/synconset/cordovahttp/CordovaHttpUpload.java" target-dir="src/com/synconset/cordovahttp"/>
<framework src="com.squareup.okhttp3:okhttp-urlconnection:3.10.0"/>
</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>
</platform>
</plugin>

View File

@@ -1,11 +1,39 @@
#!/usr/bin/env bash
set -e
PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
TARGET=$([[ "${@#--device}" = "$@" ]] && echo "emulator" || echo "device")
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
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
rm -rf $ROOT/temp
mkdir $ROOT/temp
cp -r $ROOT/test/app-template/ $ROOT/temp/

12
scripts/setup-android-sdk.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
curl http://dl.google.com/android/android-sdk_r24.4-macosx.zip -o android-sdk-macosx.zip
tar -xvf android-sdk-macosx.zip
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter platform-tools
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter build-tools-25.0.0
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter android-25
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-support
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-android-m2repository
echo y | ./android-sdk-macosx/tools/android update sdk --no-ui --all --filter extra-google-m2repository

View File

@@ -3,6 +3,11 @@ set -e
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && 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
pushd $ROOT
./node_modules/.bin/mocha ./test/app-mocha-specs/test.js "$@"
popd

View File

@@ -1,16 +1,30 @@
#!/usr/bin/env bash
set -e
PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
TEMP=$ROOT/temp
rm -rf $TEMP/HttpDemo.app.zip
pushd $TEMP/platforms/ios/build/emulator
zip -r $TEMP/HttpDemo.app.zip ./HttpDemo.app
popd
if [ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]; then
echo "Skipping uploading artifact, because Saucelabs credentials are not set.";
exit 0;
fi
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
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/build/outputs/apk/android-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

@@ -92,6 +92,8 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import android.text.TextUtils;
/**
* A fluid interface for making HTTP requests using an underlying
* {@link HttpURLConnection} (or sub-class).
@@ -106,6 +108,11 @@ public class HttpRequest {
*/
public static final String CHARSET_UTF8 = "UTF-8";
/**
* 'ISO-8859-1' charset name
*/
public static final String CHARSET_LATIN1 = "ISO-8859-1";
/**
* 'application/x-www-form-urlencoded' content type header value
*/
@@ -363,26 +370,35 @@ public class HttpRequest {
}
private static StringBuilder addParam(final Object key, Object value,
final StringBuilder result) {
final StringBuilder result) throws HttpRequestException {
return addParam(key, value, result, CHARSET_UTF8);
}
private static StringBuilder addParam(final Object key, Object value,
final StringBuilder result, String charset) throws HttpRequestException {
if (value != null && value.getClass().isArray())
value = arrayToList(value);
if (value instanceof Iterable<?>) {
Iterator<?> iterator = ((Iterable<?>) value).iterator();
while (iterator.hasNext()) {
result.append(key);
result.append("[]=");
Object element = iterator.next();
if (element != null)
result.append(element);
if (iterator.hasNext())
result.append("&");
try {
if (value instanceof Iterable<?>) {
Iterator<?> iterator = ((Iterable<?>) value).iterator();
while (iterator.hasNext()) {
result.append(URLEncoder.encode(key.toString(), charset));
result.append("[]=");
Object element = iterator.next();
if (element != null)
result.append(URLEncoder.encode(element.toString(), charset));
if (iterator.hasNext())
result.append("&");
}
} else {
result.append(URLEncoder.encode(key.toString(), charset));
result.append("=");
if (value != null)
result.append(URLEncoder.encode(value.toString(), charset));
}
} else {
result.append(key);
result.append("=");
if (value != null)
result.append(value);
} catch (UnsupportedEncodingException e) {
throw new HttpRequestException(e);
}
return result;
@@ -412,15 +428,7 @@ public class HttpRequest {
* A {@link ConnectionFactory} which uses the built-in
* {@link URL#openConnection()}
*/
ConnectionFactory DEFAULT = new ConnectionFactory() {
public HttpURLConnection create(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
public HttpURLConnection create(URL url, Proxy proxy) throws IOException {
return (HttpURLConnection) url.openConnection(proxy);
}
};
ConnectionFactory DEFAULT = new OkConnectionFactory();
}
private static ConnectionFactory CONNECTION_FACTORY = ConnectionFactory.DEFAULT;
@@ -1929,16 +1937,24 @@ public class HttpRequest {
}
/**
* Get the response body as a {@link String} and set it as the value of the
* Get the response body as ByteBuffer and set it as the value of the
* given reference.
*
* @param output
* @return this request
* @throws HttpRequestException
*/
public HttpRequest body(final AtomicReference<String> output) throws HttpRequestException {
output.set(body());
return this;
public HttpRequest body(final AtomicReference<ByteBuffer> output) throws HttpRequestException {
final ByteArrayOutputStream outputStream = byteStream();
try {
copy(buffer(), outputStream);
output.set(ByteBuffer.wrap(outputStream.toByteArray()));
return this;
} catch (IOException e) {
throw new HttpRequestException(e);
}
}
/**
@@ -1955,7 +1971,6 @@ public class HttpRequest {
return this;
}
/**
* Is the response body empty?
*
@@ -2529,6 +2544,16 @@ public class HttpRequest {
return header(HEADER_ACCEPT_CHARSET, acceptCharset);
}
/**
* Set the 'Accept-Charset' header to given values
*
* @param acceptCharsets
* @return this request
*/
public HttpRequest acceptCharset(final String[] acceptCharsets) {
return header(HEADER_ACCEPT_CHARSET, TextUtils.join(", ", acceptCharsets));
}
/**
* Get the 'Content-Encoding' header from the response
*

View File

@@ -0,0 +1,26 @@
package com.github.kevinsawicki.http;
import okhttp3.OkUrlFactory;
import okhttp3.OkHttpClient;
import java.net.URL;
import java.net.HttpURLConnection;
import java.net.URLStreamHandler;
import java.net.Proxy;
public class OkConnectionFactory implements HttpRequest.ConnectionFactory {
protected OkHttpClient okHttpClient = new OkHttpClient();
public HttpURLConnection create(URL url) {
OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClient);
return (HttpURLConnection) okUrlFactory.open(url);
}
public HttpURLConnection create(URL url, Proxy proxy) {
OkHttpClient okHttpClientWithProxy = okHttpClient.newBuilder().proxy(proxy).build();
OkUrlFactory okUrlFactory = new OkUrlFactory(okHttpClientWithProxy);
return (HttpURLConnection) okUrlFactory.open(url);
}
}

View File

@@ -11,45 +11,45 @@ import javax.net.ssl.SSLSocketFactory;
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
private SSLSocketFactory delegate;
public TLSSocketFactory(SSLContext context) {
internalSSLSocketFactory = context.getSocketFactory();
delegate = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
return enableTLSOnSocket(delegate.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));
return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
return enableTLSOnSocket(delegate.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));
return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {

View File

@@ -6,21 +6,38 @@ package com.synconset.cordovahttp;
import org.apache.cordova.CallbackContext;
import org.json.JSONException;
import org.json.JSONArray;
import org.json.JSONObject;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.MalformedInputException;
import javax.net.ssl.SSLHandshakeException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Iterator;
import android.text.TextUtils;
import com.github.kevinsawicki.http.HttpRequest;
import com.github.kevinsawicki.http.HttpRequest.HttpRequestException;
abstract class CordovaHttp {
protected static final String TAG = "CordovaHTTP";
protected static final String CHARSET = "UTF-8";
protected static final String[] ACCEPTED_CHARSETS = new String[] { HttpRequest.CHARSET_UTF8, HttpRequest.CHARSET_LATIN1 };
private static AtomicBoolean sslPinning = new AtomicBoolean(false);
private static AtomicBoolean acceptAllCerts = new AtomicBoolean(false);
@@ -35,12 +52,7 @@ abstract class CordovaHttp {
private CallbackContext callbackContext;
public CordovaHttp(String urlString, Object params, JSONObject headers, int timeout, CallbackContext callbackContext) {
this.urlString = urlString;
this.params = params;
this.serializerName = "default";
this.headers = headers;
this.timeoutInMilliseconds = timeout;
this.callbackContext = callbackContext;
this(urlString, params, "default", headers, timeout, callbackContext);
}
public CordovaHttp(String urlString, Object params, String serializerName, JSONObject headers, int timeout, CallbackContext callbackContext) {
@@ -120,6 +132,7 @@ abstract class CordovaHttp {
if (sslPinning.get()) {
request.pinToCerts();
}
return request;
}
@@ -127,9 +140,18 @@ abstract class CordovaHttp {
if (disableRedirect.get()) {
request.followRedirects(false);
}
return request;
}
protected void setupDataSerializer(HttpRequest request) throws JSONException, Exception {
if ("json".equals(this.getSerializerName())) {
request.contentType(request.CONTENT_TYPE_JSON, request.CHARSET_UTF8);
} else if ("utf8".equals(this.getSerializerName())) {
request.contentType("text/plain", request.CHARSET_UTF8);
}
}
protected void respondWithError(int status, String msg) {
try {
JSONObject response = new JSONObject();
@@ -142,7 +164,7 @@ abstract class CordovaHttp {
}
protected void respondWithError(String msg) {
this.respondWithError(500, msg);
this.respondWithError(-1, msg);
}
protected void addResponseHeaders(HttpRequest request, JSONObject response) throws JSONException {
@@ -172,14 +194,126 @@ abstract class CordovaHttp {
return map;
}
protected ArrayList<Object> getListFromJSONArray(JSONArray array) throws JSONException {
ArrayList<Object> list = new ArrayList<Object>();
for (int i = 0; i < array.length(); i++) {
list.add(array.get(i));
}
return list;
}
protected HashMap<String, Object> getMapFromJSONObject(JSONObject object) throws JSONException {
HashMap<String, Object> map = new HashMap<String, Object>();
Iterator<?> i = object.keys();
while(i.hasNext()) {
String key = (String)i.next();
map.put(key, object.get(key));
Object value = object.get(key);
if (value instanceof JSONArray) {
map.put(key, getListFromJSONArray((JSONArray)value));
} else {
map.put(key, object.get(key));
}
}
return map;
}
protected void prepareRequest(HttpRequest request) throws HttpRequestException, JSONException {
this.setupRedirect(request);
this.setupSecurity(request);
request.readTimeout(this.getRequestTimeout());
request.acceptCharset(ACCEPTED_CHARSETS);
request.headers(this.getHeadersMap());
request.uncompress(true);
}
protected void prepareRequestBody(HttpRequest request) throws JSONException, Exception {
if ("json".equals(this.getSerializerName())) {
request.send(this.getParamsObject().toString());
} else if ("utf8".equals(this.getSerializerName())) {
request.send(this.getParamsMap().get("text").toString());
} else {
request.form(this.getParamsMap());
}
}
private CharsetDecoder createCharsetDecoder(final String charsetName) {
return Charset.forName(charsetName).newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
private String decodeBody(AtomicReference<ByteBuffer> rawOutput, String charsetName)
throws CharacterCodingException, MalformedInputException {
if (charsetName == null) {
return tryDecodeByteBuffer(rawOutput);
}
return decodeByteBuffer(rawOutput, charsetName);
}
private String tryDecodeByteBuffer(AtomicReference<ByteBuffer> rawOutput)
throws CharacterCodingException, MalformedInputException {
for (int i = 0; i < ACCEPTED_CHARSETS.length - 1; i++) {
try {
return decodeByteBuffer(rawOutput, ACCEPTED_CHARSETS[i]);
} catch (MalformedInputException e) {
continue;
} catch (CharacterCodingException e) {
continue;
}
}
return decodeBody(rawOutput, ACCEPTED_CHARSETS[ACCEPTED_CHARSETS.length - 1]);
}
private String decodeByteBuffer(AtomicReference<ByteBuffer> rawOutput, String charsetName)
throws CharacterCodingException, MalformedInputException {
return createCharsetDecoder(charsetName).decode(rawOutput.get()).toString();
}
protected void returnResponseObject(HttpRequest request) throws HttpRequestException {
try {
JSONObject response = new JSONObject();
int code = request.code();
AtomicReference<ByteBuffer> rawOutputReference = new AtomicReference<ByteBuffer>();
request.body(rawOutputReference);
response.put("status", code);
response.put("url", request.url().toString());
this.addResponseHeaders(request, response);
if (code >= 200 && code < 300) {
response.put("data", decodeBody(rawOutputReference, request.charset()));
this.getCallbackContext().success(response);
} else {
response.put("error", decodeBody(rawOutputReference, request.charset()));
this.getCallbackContext().error(response);
}
} catch(JSONException e) {
this.respondWithError("There was an error generating the response");
} catch(MalformedInputException e) {
this.respondWithError("Could not decode response data due to malformed data");
} catch(CharacterCodingException e) {
this.respondWithError("Could not decode response data due to invalid or unknown charset encoding");
}
}
protected void handleHttpRequestException(HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request: " + e.getMessage());
}
}
}

View File

@@ -26,41 +26,12 @@ class CordovaHttpDelete extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.delete(this.getUrlString(), this.getParamsMap(), false);
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
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");
this.prepareRequest(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

View File

@@ -33,17 +33,15 @@ class CordovaHttpDownload extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.get(this.getUrlString(), this.getParamsMap(), true);
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
int code = request.code();
this.prepareRequest(request);
JSONObject response = new JSONObject();
this.addResponseHeaders(request, response);
int code = request.code();
response.put("status", code);
response.put("url", request.url().toString());
this.addResponseHeaders(request, response);
if (code >= 200 && code < 300) {
URI uri = new URI(filePath);
File file = new File(uri);
@@ -60,17 +58,9 @@ class CordovaHttpDownload extends CordovaHttp implements Runnable {
} 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 SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

View File

@@ -26,41 +26,12 @@ class CordovaHttpGet extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.get(this.getUrlString(), this.getParamsMap(), false);
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
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");
this.prepareRequest(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

View File

@@ -25,41 +25,12 @@ class CordovaHttpHead extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.head(this.getUrlString(), this.getParamsMap(), true);
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
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");
this.prepareRequest(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

View File

@@ -25,48 +25,14 @@ class CordovaHttpPatch extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.patch(this.getUrlString());
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
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");
this.setupDataSerializer(request);
this.prepareRequest(request);
this.prepareRequestBody(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

View File

@@ -25,48 +25,14 @@ class CordovaHttpPost extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.post(this.getUrlString());
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
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");
this.setupDataSerializer(request);
this.prepareRequest(request);
this.prepareRequestBody(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

View File

@@ -25,48 +25,14 @@ class CordovaHttpPut extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.put(this.getUrlString());
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
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");
this.setupDataSerializer(request);
this.prepareRequest(request);
this.prepareRequestBody(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

View File

@@ -41,12 +41,7 @@ class CordovaHttpUpload extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.post(this.getUrlString());
request.readTimeout(this.getRequestTimeout());
this.setupRedirect(request);
this.setupSecurity(request);
request.acceptCharset(CHARSET);
request.headers(this.getHeadersMap());
request.uncompress(true);
this.prepareRequest(request);
URI uri = new URI(filePath);
@@ -59,8 +54,6 @@ class CordovaHttpUpload extends CordovaHttp implements Runnable {
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();
@@ -77,38 +70,18 @@ class CordovaHttpUpload extends CordovaHttp implements Runnable {
return;
}
}
request.part(this.name, filename, mimeType, new File(uri));
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);
}
this.returnResponseObject(request);
} catch (URISyntaxException e) {
this.respondWithError("There was an error loading the file");
} catch (JSONException e) {
this.respondWithError("There was an error generating the response");
} catch (HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
} else {
this.respondWithError("There was an error with the request");
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}

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

@@ -0,0 +1,181 @@
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var cordovaProxy = require('cordova/exec/proxy');
var helpers = require(pluginId + '.helpers');
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 (helpers.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: helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response,
headers: deserializeResponseHeaders(xhr.getAllResponseHeaders())
};
}
function createXhrFailureObject(xhr) {
var obj = {};
obj.headers = xhr.getAllResponseHeaders();
obj.error = helpers.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 setHeaders(xhr, headers) {
Object.keys(headers).forEach(function(key) {
if (key === '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':
xhr.setRequestHeader('Content-Type', 'application/json; charset=utf8');
processedData = serializeJsonData(data);
if (processedData === null) {
return failure('advanced-http: failed serializing data');
}
break;
case 'utf8':
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf8');
processedData = data.text;
break;
case 'urlencoded':
xhr.setRequestHeader('Content-Type', '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');
},
enableSSLPinning: function (success, failure, opts) {
return failure('advanced-http: function "enableSSLPinning" not supported on browser platform');
},
acceptAllCerts: function (success, failure, opts) {
return failure('advanced-http: function "acceptAllCerts" not supported on browser platform');
},
disableRedirect: function (success, failure, opts) {
return failure('advanced-http: function "disableRedirect" not supported on browser platform');
}
};
module.exports = browserInterface;
cordovaProxy.add('CordovaHttpPlugin', browserInterface);

View File

@@ -1,6 +1,7 @@
#import "CordovaHttpPlugin.h"
#import "CDVFile.h"
#import "TextResponseSerializer.h"
#import "TextRequestSerializer.h"
#import "AFHTTPSessionManager.h"
@interface CordovaHttpPlugin()
@@ -28,6 +29,8 @@
- (void)setRequestSerializer:(NSString*)serializerName forManager:(AFHTTPSessionManager*)manager {
if ([serializerName isEqualToString:@"json"]) {
manager.requestSerializer = [AFJSONRequestSerializer serializer];
} else if ([serializerName isEqualToString:@"utf8"]) {
manager.requestSerializer = [TextRequestSerializer serializer];
} else {
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
}
@@ -51,7 +54,8 @@
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data {
if (response != nil) {
[dictionary setObject:[NSNumber numberWithInt:response.statusCode] forKey:@"status"];
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
}
@@ -62,15 +66,29 @@
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error {
if (response != nil) {
[dictionary setObject:[NSNumber numberWithInt:response.statusCode] forKey:@"status"];
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
[dictionary setObject:[[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding] forKey:@"error"];
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:
@@ -157,19 +175,25 @@
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 handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
@try {
[manager POST:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
- (void)get:(CDVInvokedUrlCommand*)command {
@@ -188,21 +212,26 @@
[self setRedirect: 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 handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
@try {
[manager GET:url parameters:parameters progress:nil success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
- (void)put:(CDVInvokedUrlCommand*)command {
@@ -222,19 +251,25 @@
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager PUT:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
@try {
[manager PUT:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
- (void)patch:(CDVInvokedUrlCommand*)command {
@@ -254,19 +289,25 @@
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager PATCH:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
@try {
[manager PATCH:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
- (void)delete:(CDVInvokedUrlCommand*)command {
@@ -284,21 +325,26 @@
[self setRedirect: manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [TextResponseSerializer serializer];
[manager DELETE:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
@try {
[manager DELETE:url parameters:parameters success:^(NSURLSessionTask *task, id responseObject) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:responseObject];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
- (void)head:(CDVInvokedUrlCommand*)command {
@@ -314,22 +360,27 @@
[self setRedirect: manager];
CordovaHttpPlugin* __weak weakSelf = self;
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager HEAD:url parameters:parameters success:^(NSURLSessionTask *task) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
// no 'body' for HEAD request, omitting 'data'
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
@try {
[manager HEAD:url parameters:parameters success:^(NSURLSessionTask *task) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
// no 'body' for HEAD request, omitting 'data'
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
- (void)uploadFile:(CDVInvokedUrlCommand*)command {
@@ -351,30 +402,36 @@
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) {
@try {
[manager POST:url parameters:parameters 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];
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];
} 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 handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
@@ -398,70 +455,77 @@
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];
// 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: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];
// 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];
return;
}
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}
NSData *data = (NSData *)responseObject;
if (![data writeToFile:filePath atomically:YES]) {
NSData *data = (NSData *)responseObject;
if (![data writeToFile:filePath atomically:YES]) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
[dictionary setObject:@"Could not write the data to the given filePath." forKey:@"error"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}
id filePlugin = [self.commandDelegate getCommandInstance:@"File"];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:[NSNumber numberWithInt:500] forKey:@"status"];
[dictionary setObject:@"Could not write the data to the given filePath." forKey:@"error"];
[self handleSuccess:dictionary withResponse:(NSHTTPURLResponse*)task.response andData:nil];
[dictionary setObject:[filePlugin getDirectoryEntry:filePath isDirectory:NO] forKey:@"file"];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
} 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];
return;
}
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];
} failure:^(NSURLSessionTask *task, NSError *error) {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self handleError:dictionary withResponse:(NSHTTPURLResponse*)task.response error:error];
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
[weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
@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/hal+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

View File

@@ -8,18 +8,21 @@ const testDefinitions = require('../app-test-definitions');
const pkgjson = require('../../package.json');
describe('Advanced HTTP', function() {
const isDevice = process.argv.includes('--device');
const isAndroid = process.argv.includes('--android');
const targetInfo = { isDevice, isAndroid };
let driver = null;
let allPassed = true;
this.timeout(900000);
const getCaps = appName => {
const isDevice = process.argv.includes('--device');
const isAndroid = process.argv.includes('--android');
const desiredCaps = caps[(isAndroid ? 'android' : 'ios') + (isDevice ? 'Device' : 'Emulator')];
const desiredApp = apps[(isAndroid ? 'android' : 'ios') + appName];
const desiredOs = isAndroid ? 'android' : 'ios';
const desiredCaps = caps[desiredOs + (isDevice ? 'Device' : 'Emulator')];
const desiredApp = apps[desiredOs + appName];
desiredCaps.name = pkgjson.name;
desiredCaps.name = pkgjson.name + ` (${desiredOs})`;
desiredCaps.app = desiredApp;
return desiredCaps;
@@ -57,7 +60,7 @@ describe('Advanced HTTP', function() {
const validateResult = testDefinition => driver
.safeExecute('app.lastResult')
.then(result => testDefinition.validationFunc(driver, result));
.then(result => testDefinition.validationFunc(driver, result, targetInfo));
const clickNext = () => driver
.elementById('nextBtn')

View File

@@ -23,5 +23,7 @@
<allow-intent href="itms-apps:*" />
</platform>
<engine name="android" spec="6.2.3" />
<engine name="browser" spec="5.0.0" />
<engine name="ios" spec="4.4.0" />
<plugin name="cordova-plugin-file" spec="4.3.3" />
</widget>

Binary file not shown.

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com https://self-signed.badssl.com http://httpbin.org http://www.columbia.edu 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
<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">

View File

@@ -33,6 +33,16 @@ const app = {
};
},
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);
},
@@ -46,7 +56,12 @@ const app = {
document.getElementById('expectedTextarea').value = expectedText;
document.getElementById('resultTextarea').value = '';
document.getElementById('descriptionLbl').innerText = titleText;
testDefinition.func(app.resolve, app.reject);
try {
testDefinition.func(app.resolve, app.reject);
} catch (error) {
app.throw(error);
}
},
onBeforeTest: function(testIndex, cb) {

View File

@@ -1,12 +1,41 @@
const hooks = {
onBeforeEachTest: function(done) {
cordova.plugin.http.acceptAllCerts(false, done, done);
cordova.plugin.http.clearCookies();
cordova.plugin.http.acceptAllCerts(false, function() {
cordova.plugin.http.enableSSLPinning(false, done, done);
}, done);
}
};
const helpers = {
acceptAllCerts: function(done) { cordova.plugin.http.acceptAllCerts(true, done, done); },
setJsonSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('json')); }
enableSSLPinning: function(done) { cordova.plugin.http.enableSSLPinning(true, done, done); },
setJsonSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('json')); },
setUtf8StringSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('utf8')); },
setUrlEncodedSerializer: function(done) { done(cordova.plugin.http.setDataSerializer('urlencoded')); },
getWithXhr: function(done, url) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
done(this.responseText);
});
xhr.open('GET', url);
xhr.send();
},
writeToFile: function (done, fileName, content) {
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function (directoryEntry) {
directoryEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
fileEntry.createWriter(function (fileWriter) {
var blob = new Blob([ content ], { type: 'text/plain' });
fileWriter.onwriteend = done;
fileWriter.onerror = done;
fileWriter.write(blob);
}, done);
}, done);
}, done);
}
};
const tests = [
@@ -14,41 +43,41 @@ const tests = [
description: 'should reject self signed cert (GET)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
func: function(resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function(driver, result) {
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error:'cancelled' });
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
}
},{
description: 'should reject self signed cert (PUT)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
func: function(resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error:'cancelled' });
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
}
},{
description: 'should reject self signed cert (POST)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
func: function(resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error:'cancelled' });
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
}
},{
description: 'should reject self signed cert (PATCH)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
func: function(resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error:'cancelled' });
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
}
},{
description: 'should reject self signed cert (DELETE)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
func: function(resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function(driver, result) {
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error:'cancelled' });
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
}
},{
description: 'should accept bad cert (GET)',
@@ -106,7 +135,7 @@ const tests = [
}
},{
description: 'should send JSON object correctly (POST)',
expected: 'resolved: {"status": 200, data: "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
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) {
@@ -115,7 +144,7 @@ const tests = [
}
},{
description: 'should send JSON object correctly (PUT)',
expected: 'resolved: {"status": 200, data: "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
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) {
@@ -124,7 +153,7 @@ const tests = [
}
},{
description: 'should send JSON object correctly (PATCH)',
expected: 'resolved: {"status": 200, data: "{\\"json\\":\\"test\\": \\"testString\\"}\" ...',
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) {
@@ -132,8 +161,8 @@ const tests = [
JSON.parse(result.data.data).json.should.eql({ test: 'testString' });
}
},{
description: 'should send JSON array correctly (POST)',
expected: 'resolved: {"status": 200, data: "[ 1, 2, 3 ]\" ...',
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) {
@@ -141,8 +170,8 @@ const tests = [
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
}
},{
description: 'should send JSON array correctly (PUT)',
expected: 'resolved: {"status": 200, data: "[ 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) {
@@ -150,14 +179,291 @@ const tests = [
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
}
},{
description: 'should send JSON array correctly (PATCH)',
expected: 'resolved: {"status": 200, data: "[ 1, 2, 3 ]\" ...',
description: 'should send JSON array correctly (PATCH) #26',
expected: 'resolved: {"status": 200, "data": "[ 1, 2, 3 ]\" ...',
before: helpers.setJsonSerializer,
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', [ 1, 2, 3 ], {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON.parse(result.data.data).json.should.eql([ 1, 2, 3 ]);
}
},{
description: 'should send url encoded data correctly (POST) #41',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},{
description: 'should send url encoded data correctly (PUT)',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
func: function(resolve, reject) { cordova.plugin.http.put('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},{
description: 'should send url encoded data correctly (PATCH)',
expected: 'resolved: {"status": 200, "data": "{\\"form\\":\\"test\\": \\"testString\\"}\" ...',
before: helpers.setUrlEncodedSerializer,
func: function(resolve, reject) { cordova.plugin.http.patch('http://httpbin.org/anything', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
}
},{
description: 'should resolve correct URL after redirect (GET) #33',
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
func: function(resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.url.should.be.equal('http://httpbin.org/anything');
}
},{
description: 'should download a file from given URL to given path in local filesystem',
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
func: function(resolve, reject) {
var sourceUrl = 'http://httpbin.org/xml';
var targetPath = cordova.file.cacheDirectory + 'test.xml';
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function(entry) {
helpers.getWithXhr(function(content) {
resolve({
sourceUrl: sourceUrl,
targetPath: targetPath,
fullPath: entry.fullPath,
name: entry.name,
content: content
});
}, targetPath);
}, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.name.should.be.equal('test.xml');
result.data.content.should.be.equal("<?xml version='1.0' encoding='us-ascii'?>\n\n<!-- A SAMPLE set of slides -->\n\n<slideshow \n title=\"Sample Slide Show\"\n date=\"Date of publication\"\n author=\"Yours Truly\"\n >\n\n <!-- TITLE SLIDE -->\n <slide type=\"all\">\n <title>Wake up to WonderWidgets!</title>\n </slide>\n\n <!-- OVERVIEW -->\n <slide type=\"all\">\n <title>Overview</title>\n <item>Why <em>WonderWidgets</em> are great</item>\n <item/>\n <item>Who <em>buys</em> WonderWidgets</item>\n </slide>\n\n</slideshow>");
}
},{
description: 'should upload a file from given path in local filesystem to given URL #27',
expected: 'resolved: {"status": 200, "data": "files": {"test-file.txt": "I am a dummy file. I am used ...',
func: function(resolve, reject) {
var fileName = 'test-file.txt';
var fileContent = 'I am a dummy file. I am used for testing purposes!';
var sourcePath = cordova.file.cacheDirectory + fileName;
var targetUrl = 'http://httpbin.org/post';
helpers.writeToFile(function() {
cordova.plugin.http.uploadFile(targetUrl, {}, {}, sourcePath, fileName, resolve, reject);
}, fileName, fileContent);
},
validationFunc: function(driver, result) {
var fileName = 'test-file.txt';
var fileContent = 'I am a dummy file. I am used for testing purposes!';
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.files[fileName]
.should.be.equal(fileContent);
}
},{
description: 'should encode HTTP array params correctly (GET) #45',
expected: 'resolved: {"status": 200, "data": "{\\"url\\":\\"http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3\\"}\" ...',
func: function(resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/get', { myArray: [ 'val1', 'val2', 'val3' ], myString: 'testString' }, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.url
.should.be.equal('http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
}
},{
description: 'should throw on non-string values in local header object #54',
expected: 'throwed: {"message": "advanced-http: header values must be strings"}',
func: function(resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/get', {}, { myTestHeader: 1 }, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('throwed');
result.message.should.be.equal('advanced-http: header values must be strings');
}
},{
description: 'should throw an error while setting non-string value as global header #54',
expected: 'throwed: "advanced-http: header values must be strings"',
func: function(resolve, reject) {
cordova.plugin.http.setHeader('myTestHeader', 2);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('throwed');
result.message.should.be.equal('advanced-http: header values must be strings');
}
},{
description: 'should accept content-type "application/xml" #58',
expected: 'resolved: {"status": 200, ...',
func: function(resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/xml', {}, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.status.should.be.equal(200);
}
},{
description: 'should send programmatically set cookies correctly (GET)',
expected: 'resolved: {"status": 200, ...',
func: function(resolve, reject) {
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.headers
.Cookie
.should.be.equal('myCookie=myValue; mySecondCookie=mySecondValue');
}
},{
description: 'should not send any cookies after running "clearCookies" (GET) #59',
expected: 'resolved: {"status": 200, "data": "{\"headers\": {\"Cookie\": \"\"...',
func: function(resolve, reject) {
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
cordova.plugin.http.clearCookies();
cordova.plugin.http.get('http://httpbin.org/get', {}, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.a('string');
JSON
.parse(result.data.data)
.headers
.Cookie
.should.be.equal('');
}
},{
description: 'should send programmatically set cookies correctly (DOWNLOAD) #57',
expected: 'resolved: {"content":{"cookies":{"myCookie":"myValue ...',
func: function(resolve, reject) {
var sourceUrl = 'http://httpbin.org/cookies';
var targetPath = cordova.file.cacheDirectory + 'cookies.json';
cordova.plugin.http.setCookie('http://httpbin.org/get', 'myCookie=myValue');
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function(entry) {
helpers.getWithXhr(function(content) {
resolve({
sourceUrl: sourceUrl,
targetPath: targetPath,
fullPath: entry.fullPath,
name: entry.name,
content: content
});
}, targetPath);
}, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.name.should.be.equal('cookies.json');
result.data.content.should.be.a('string');
var cookies = JSON.parse(result.data.content).cookies;
cookies.myCookie.should.be.equal('myValue');
cookies.mySecondCookie.should.be.equal('mySecondValue');
}
},{
description: 'should send UTF-8 encoded raw string correctly (POST) #34',
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"this is a test string\\"...',
before: helpers.setUtf8StringSerializer,
func: function(resolve, reject) {
cordova.plugin.http.post('http://httpbin.org/anything', 'this is a test string', {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).data.should.be.equal('this is a test string');
}
},{
description: 'should encode spaces in query string (params object) correctly (GET) #71',
expected: 'resolved: {"status": 200, "data": "{\\"args\\": \\"query param\\": \\"and value with spaces\\"...',
func: function(resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/get', { 'query param': 'and value with spaces' }, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).args['query param'].should.be.equal('and value with spaces');
}
},{
description: 'should decode latin1 (iso-8859-1) encoded body correctly (GET) #72',
expected: 'resolved: {"status": 200, "data": "<!DOCTYPE HTML PUBLIC \\"-//W3C//DTD HTML 4.01 Transitional//EN\\"> ...',
func: function(resolve, reject) {
cordova.plugin.http.get('http://www.columbia.edu/kermit/latin1.html', {}, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.include('[¡] 161 10/01 241 A1 INVERTED EXCLAMATION MARK\n[¢] 162 10/02 242 A2 CENT SIGN');
}
},{
description: 'should return empty body string correctly (GET)',
expected: 'resolved: {"status": 200, "data": "" ...',
func: function(resolve, reject) {
cordova.plugin.http.get('http://httpbin.org/stream/0', {}, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
result.data.data.should.be.equal('');
}
},{
description: 'should pin SSL cert correctly (GET)',
expected: 'resolved: {"status": 200 ...',
before: helpers.enableSSLPinning,
func: function(resolve, reject) {
cordova.plugin.http.get('https://httpbin.org', {}, {}, resolve, reject);
},
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
}
},{
description: 'should send deeply structured JSON object correctly (POST) #65',
expected: 'resolved: {"status": 200, "data": "{\\"data\\": \\"{\\\\"outerObj\\\\":{\\\\"innerStr\\\\":\\\\"testString\\\\",\\\\"innerArr\\\\":[1,2,3]}}\\" ...',
before: helpers.setJsonSerializer,
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', { outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] }}, {}, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).json.should.eql({ outerObj: { innerStr: 'testString', innerArr: [1, 2, 3] }});
}
},{
description: 'should override header "content-type" correctly (POST) #78',
expected: 'resolved: {"status": 200, "headers": "{\\"Content-Type\\": \\"text/plain\\" ...',
before: helpers.setJsonSerializer,
func: function(resolve, reject) { cordova.plugin.http.post('http://httpbin.org/anything', {}, { 'Content-Type': 'text/plain' }, resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('resolved');
JSON.parse(result.data.data).headers['Content-Type'].should.be.equal('text/plain');
}
},{
description: 'should handle error during file download correctly (DOWNLOAD) #83',
expected: 'rejected: {"status": 403, "error": "There was an error downloading the file" ...',
func: function(resolve, reject) { cordova.plugin.http.downloadFile('http://httpbin.org/status/403', {}, {}, cordova.file.tempDirectory + 'testfile.txt', resolve, reject); },
validationFunc: function(driver, result) {
result.type.should.be.equal('rejected');
result.data.status.should.be.equal(403);
result.data.error.should.be.equal('There was an error downloading the file');
}
}
];

View File

@@ -3,12 +3,18 @@ const mock = require('mock-require');
const path = require('path');
const should = chai.should();
const HELPERS_ID = path.resolve(__dirname, '..', 'www', 'helpers');
const PLUGIN_ID = path.resolve(__dirname, '..', 'www', 'advanced-http');
describe('Advanced HTTP www interface', function() {
let http = {};
let helpers = {};
const noop = () => { /* intentionally doing nothing */ };
const loadHttp = () => {
mock(`${PLUGIN_ID}.helpers`, mock.reRequire('../www/helpers'));
http = mock.reRequire('../www/advanced-http');
};
@@ -19,28 +25,31 @@ describe('Advanced HTTP www interface', function() {
global.btoa = decoded => new Buffer(decoded).toString('base64');
mock('cordova/exec', noop);
mock(`${PLUGIN_ID}.angular-integration`, { registerService: noop });
mock(`${PLUGIN_ID}.cookie-handler`, {});
mock(`${HELPERS_ID}.cookie-handler`, {});
mock(`${HELPERS_ID}.messages`, require('../www/messages'));
mock(`${PLUGIN_ID}.angular-integration`, { registerService: noop });
loadHttp();
});
it('sets global headers correctly with two args (old interface)', () => {
http.setHeader('myKey', 'myValue');
http.headers['*'].myKey.should.equal('myValue');
http.getHeaders('*').myKey.should.equal('myValue');
});
it('sets global headers correctly with three args (new interface)', () => {
it('sets global headers correctly with three args (new interface) #24', () => {
http.setHeader('*', 'myKey', 'myValue');
http.headers['*'].myKey.should.equal('myValue');
http.getHeaders('*').myKey.should.equal('myValue');
});
it('sets host headers correctly', () => {
it('sets host headers correctly #24', () => {
http.setHeader('www.google.de', 'myKey', 'myValue');
http.headers['www.google.de'].myKey.should.equal('myValue');
http.getHeaders('www.google.de').myKey.should.equal('myValue');
});
it('resolves global headers correctly', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
it('resolves global headers correctly #24', () => {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -58,8 +67,8 @@ describe('Advanced HTTP www interface', function() {
http.get('url', {}, {}, noop, noop);
});
it('resolves host headers correctly (set without port number)', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
it('resolves host headers correctly (set without port number) #37', () => {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -77,8 +86,8 @@ describe('Advanced HTTP www interface', function() {
http.get('https://www.google.de/?gws_rd=ssl', {}, {}, noop, noop);
});
it('resolves host headers correctly (set with port number)', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
it('resolves host headers correctly (set with port number) #37', () => {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -97,7 +106,7 @@ describe('Advanced HTTP www interface', function() {
});
it('resolves request headers correctly', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -114,8 +123,12 @@ describe('Advanced HTTP www interface', function() {
http.get('https://www.google.de/?gws_rd=ssl', {}, { myKey: 'myValue' }, noop, noop);
});
it('sets basic authentication header correctly', () => {
it('sets basic authentication header correctly #36', () => {
http.useBasicAuth('name', 'pass');
http.headers['*'].Authorization.should.equal('Basic bmFtZTpwYXNz');
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();
});
});

View File

@@ -1,288 +1,131 @@
/*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
* - added put and delete methods
* - using cordova www module pattern
* - some minor improvements
*
*/
/*
* An HTTP Plugin for PhoneGap.
* A native HTTP Plugin for Cordova / PhoneGap.
*/
var pluginId = module.id.slice(0, module.id.indexOf('.'));
var validSerializers = ['urlencoded', 'json'];
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var exec = require('cordova/exec');
var angularIntegration = require(pluginId +'.angular-integration');
var cookieHandler = require(pluginId + '.cookie-handler');
var helpers = require(pluginId + '.helpers');
// 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];
}
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) {
return { Cookie: cookieHandler.getCookieString(url) };
}
function getMatchingHostHeaders(url, headersList) {
var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
var domain = matches && matches[1];
return headersList[domain] || null;
}
function getMergedHeaders(url, requestHeaders, predefinedHeaders) {
var globalHeaders = predefinedHeaders['*'] || {};
var hostHeaders = getMatchingHostHeaders(url, predefinedHeaders) || {};
var mergedHeaders = mergeHeaders(globalHeaders, hostHeaders);
mergedHeaders = mergeHeaders(mergedHeaders, requestHeaders);
mergedHeaders = mergeHeaders(mergedHeaders, getCookieHeader(url));
return mergedHeaders;
}
function handleMissingCallbacks(successFn, failFn) {
if (Object.prototype.toString.call(successFn) !== '[object Function]') {
throw new Error('advanced-http: missing mandatory "onSuccess" callback function');
}
if (Object.prototype.toString.call(failFn) !== '[object Function]') {
throw new Error('advanced-http: missing mandatory "onFail" callback function');
}
}
var http = {
headers: {},
dataSerializer: 'urlencoded',
sslPinning: false,
timeoutInSeconds: 60.0,
getBasicAuthHeader: function (username, password) {
return {'Authorization': 'Basic ' + b64EncodeUnicode(username + ':' + password)};
},
useBasicAuth: function (username, password) {
this.setHeader('*', 'Authorization', 'Basic ' + b64EncodeUnicode(username + ':' + password));
},
setHeader: function () {
// this one is for being backward compatible
var host = '*';
var header = arguments[0];
var value = arguments[1];
if (arguments.length === 3) {
host = arguments[0];
header = arguments[1];
value = arguments[2];
}
this.headers[host] = this.headers[host] || {};
this.headers[host][header] = value;
},
setDataSerializer: function (serializer) {
this.dataSerializer = checkSerializer(serializer);
},
clearCookies: function () {
return cookieHandler.clearCookies();
},
removeCookies: function (url, callback) {
cookieHandler.removeCookies(url, callback);
},
setRequestTimeout: function(timeout) {
this.timeoutInSeconds = timeout;
},
enableSSLPinning: function (enable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'enableSSLPinning', [enable]);
},
acceptAllCerts: function (allow, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'acceptAllCerts', [allow]);
},
disableRedirect: function(disable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [disable]);
},
validateDomainName: function (validate, success, failure) {
failure('advanced-http: "validateDomainName" is no more supported, please see change log for further info');
},
post: function (url, data, headers, success, failure) {
handleMissingCallbacks(success, failure);
data = data || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'post', [url, data, this.dataSerializer, headers, this.timeoutInSeconds]);
},
get: function (url, params, headers, success, failure) {
handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'get', [url, params, headers, this.timeoutInSeconds]);
},
put: function (url, data, headers, success, failure) {
handleMissingCallbacks(success, failure);
data = data || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'put', [url, data, this.dataSerializer, headers, this.timeoutInSeconds]);
},
patch: function (url, data, headers, success, failure) {
handleMissingCallbacks(success, failure);
data = data || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'patch', [url, data, this.dataSerializer, headers, this.timeoutInSeconds]);
},
delete: function (url, params, headers, success, failure) {
handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'delete', [url, params, headers, this.timeoutInSeconds]);
},
head: function (url, params, headers, success, failure) {
handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'head', [url, params, headers, this.timeoutInSeconds]);
},
uploadFile: function (url, params, headers, filePath, name, success, failure) {
handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name, this.timeoutInSeconds]);
},
downloadFile: function (url, params, headers, filePath, success, failure) {
handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
var onSuccess = injectCookieHandler(url, injectFileEntryHandler(success));
var onFail = injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, params, headers, filePath, this.timeoutInSeconds]);
}
var globalConfigs = {
headers: {},
serializer: 'urlencoded',
timeout: 60.0,
};
angularIntegration.registerService(http);
module.exports = http;
var publicInterface = {
getBasicAuthHeader: function (username, password) {
return {'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)};
},
useBasicAuth: function (username, password) {
this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password));
},
getHeaders: function (host) {
return globalConfigs.headers[host || '*'] || null;
},
setHeader: function () {
// this one is for being backward compatible
var host = '*';
var header = arguments[0];
var value = arguments[1];
if (arguments.length === 3) {
host = arguments[0];
header = arguments[1];
value = arguments[2];
}
helpers.checkForBlacklistedHeaderKey(header);
helpers.checkForInvalidHeaderValue(value);
globalConfigs.headers[host] = globalConfigs.headers[host] || {};
globalConfigs.headers[host][header] = value;
},
getDataSerializer: function () {
return globalConfigs.serializer;
},
setDataSerializer: function (serializer) {
globalConfigs.serializer = helpers.checkSerializer(serializer);
},
setCookie: function (url, cookie, options) {
cookieHandler.setCookie(url, cookie, options);
},
clearCookies: function () {
cookieHandler.clearCookies();
},
removeCookies: function (url, callback) {
cookieHandler.removeCookies(url, callback);
},
getCookieString: function (url) {
return cookieHandler.getCookieString(url);
},
getRequestTimeout: function () {
return globalConfigs.timeout;
},
setRequestTimeout: function (timeout) {
globalConfigs.timeout = timeout;
},
enableSSLPinning: function (enable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'enableSSLPinning', [ enable ]);
},
acceptAllCerts: function (allow, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'acceptAllCerts', [ allow ]);
},
disableRedirect: function (disable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'disableRedirect', [ disable ]);
},
sendRequest: function (url, options, success, failure) {
helpers.handleMissingCallbacks(success, failure);
options = helpers.handleMissingOptions(options, globalConfigs);
var headers = helpers.getMergedHeaders(url, options.headers, globalConfigs.headers);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
switch(options.method) {
case 'post':
case 'put':
case 'patch':
var data = helpers.getProcessedData(options.data, options.serializer);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [ url, data, options.serializer, headers, options.timeout ]);
case 'upload':
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [ url, options.params, headers, options.filePath, options.name, options.timeout ]);
case 'download':
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [ url, options.params, headers, options.filePath, options.timeout ]);
default:
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [ url, options.params, headers, options.timeout ]);
}
},
post: function (url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'post', data: data, headers: headers }, success, failure);
},
get: function (url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'get', params: params, headers: headers }, success, failure);
},
put: function (url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'put', data: data, headers: headers }, success, failure);
},
patch: function (url, data, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'patch', data: data, headers: headers }, success, failure);
},
delete: function (url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'delete', params: params, headers: headers }, success, failure);
},
head: function (url, params, headers, success, failure) {
return publicInterface.sendRequest(url, { method: 'head', params: params, headers: headers }, success, failure);
},
uploadFile: function (url, params, headers, filePath, name, success, failure) {
return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure);
},
downloadFile: function (url, params, headers, filePath, success, failure) {
return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
}
};
// angular service is deprecated and will be removed anytime soon
angularIntegration.registerService(publicInterface);
module.exports = publicInterface;

View File

@@ -1,4 +1,4 @@
var pluginId = module.id.slice(0, module.id.indexOf('.'));
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var ToughCookie = require(pluginId + '.tough-cookie');
var WebStorageCookieStore = require(pluginId + '.local-storage-store');
@@ -10,6 +10,7 @@ var cookieJar = new ToughCookie.CookieJar(store);
module.exports = {
setCookieFromString: setCookieFromString,
setCookie: setCookie,
getCookieString: getCookieString,
clearCookies: clearCookies,
removeCookies: removeCookies
@@ -45,6 +46,12 @@ function setCookieFromString(url, cookieStr) {
}
}
function setCookie(url, cookie, options) {
options = options || {};
options.ignoreError = false;
cookieJar.setCookieSync(cookie, url, options);
}
function getCookieString(url) {
return cookieJar.getCookieStringSync(url);
}

249
www/helpers.js Normal file
View File

@@ -0,0 +1,249 @@
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var cookieHandler = require(pluginId + '.cookie-handler');
var messages = require(pluginId + '.messages');
var validSerializers = [ 'urlencoded', 'json', 'utf8' ];
var validHttpMethods = [ 'get', 'put', 'post', 'patch', 'head', 'delete', 'upload', 'download' ];
module.exports = {
b64EncodeUnicode: b64EncodeUnicode,
getTypeOf: getTypeOf,
checkSerializer: checkSerializer,
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
injectCookieHandler: injectCookieHandler,
injectFileEntryHandler: injectFileEntryHandler,
getMergedHeaders: getMergedHeaders,
getProcessedData: getProcessedData,
handleMissingCallbacks: handleMissingCallbacks,
handleMissingOptions: handleMissingOptions
};
// 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 (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 (getTypeOf(obj) !== 'Object') {
throw new Error(onInvalidValueMessage);
}
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
if (allowedChildren.indexOf(getTypeOf(obj[keys[i]])) === -1) {
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 checkForBlacklistedHeaderKey(key) {
if (key.toLowerCase() === 'cookie') {
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
}
return key;
}
function checkForInvalidHeaderValue(value) {
if (getTypeOf(value) !== 'String') {
throw new Error(messages.INVALID_HEADERS_VALUE);
}
return value;
}
function checkTimeoutValue(timeout) {
if (getTypeOf(timeout) !== 'Number' || timeout < 0) {
throw new Error(messages.INVALID_TIMEOUT_VALUE);
}
return timeout;
}
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) {
return { Cookie: cookieHandler.getCookieString(url) };
}
function getMatchingHostHeaders(url, headersList) {
var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
var domain = matches && matches[1];
return headersList[domain] || null;
}
function getMergedHeaders(url, requestHeaders, predefinedHeaders) {
var globalHeaders = predefinedHeaders['*'] || {};
var hostHeaders = getMatchingHostHeaders(url, predefinedHeaders) || {};
var mergedHeaders = mergeHeaders(globalHeaders, hostHeaders);
mergedHeaders = mergeHeaders(mergedHeaders, requestHeaders);
mergedHeaders = mergeHeaders(mergedHeaders, getCookieHeader(url));
return mergedHeaders;
}
// typeof is not working reliably in JS
function getTypeOf(object) {
switch (Object.prototype.toString.call(object)) {
case '[object Array]':
return 'Array';
case '[object Boolean]':
return 'Boolean';
case '[object Function]':
return 'Function';
case '[object Null]':
return 'Null';
case '[object Number]':
return 'Number';
case '[object Object]':
return 'Object';
case '[object String]':
return 'String';
case '[object Undefined]':
return 'Undefined';
default:
return 'Unknown';
}
}
function getAllowedDataTypes(dataSerializer) {
switch (dataSerializer) {
case 'utf8':
return ['String'];
case 'urlencoded':
return ['Object'];
default:
return ['Array', 'Object'];
}
}
function getProcessedData(data, dataSerializer) {
data = data || {};
var currentDataType = getTypeOf(data);
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
if (allowedDataTypes.indexOf(currentDataType) === -1) {
throw new Error(messages.DATA_TYPE_MISMATCH + ' ' + allowedDataTypes.join(', '));
}
if (dataSerializer === 'utf8') {
data = { text: data };
}
return data;
}
function handleMissingCallbacks(successFn, failFn) {
if (getTypeOf(successFn) !== 'Function') {
throw new Error(messages.MANDATORY_SUCCESS);
}
if (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),
headers: checkHeadersObject(options.headers || {}),
params: checkParamsObject(options.params || {}),
data: options.data || null,
filePath: options.filePath || '',
name: options.name || ''
};
}

View File

@@ -30,7 +30,7 @@
'use strict';
var pluginId = module.id.slice(0, module.id.indexOf('.'));
var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
var ToughCookie = require(pluginId + '.tough-cookie');
var _ = require(pluginId + '.lodash');

11
www/messages.js Normal file
View File

@@ -0,0 +1,11 @@
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_HEADERS_VALUE: 'advanced-http: header values must be strings',
INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings'
};