mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-02-11 00:00:06 +08:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c748406090 | ||
|
|
dc6cf4d45b | ||
|
|
7661e02598 | ||
|
|
20ec4bee31 | ||
|
|
4eed89e530 | ||
|
|
f43b2f9f5c | ||
|
|
7b4d37acd9 | ||
|
|
ca5306cb47 | ||
|
|
c0c1af5ee6 | ||
|
|
42e629e34d | ||
|
|
3bec8dde5f | ||
|
|
8a3bc17810 | ||
|
|
1eb83478dc | ||
|
|
1f0df54111 | ||
|
|
ef09e466ec | ||
|
|
88de0550f4 | ||
|
|
7f680b07ec | ||
|
|
a259e7cf8d | ||
|
|
1e3c9e6645 | ||
|
|
e0cf458179 | ||
|
|
4f4e7ffa33 | ||
|
|
65e4a4d4dc | ||
|
|
fed2b03cc6 | ||
|
|
29e0b385de | ||
|
|
bfc6ba2008 | ||
|
|
060aa088f5 | ||
|
|
e775057389 | ||
|
|
81874c6bc8 | ||
|
|
658bbd8b28 | ||
|
|
a1e4be37d4 | ||
|
|
5be52d78d1 | ||
|
|
0a23e29403 | ||
|
|
6009ae82f7 | ||
|
|
0bfb81c57f | ||
|
|
f8f4bdc9df | ||
|
|
d84e3995d2 | ||
|
|
f5597dd176 | ||
|
|
b25b7db4be | ||
|
|
92be8f8e96 | ||
|
|
9ffdc5d2eb | ||
|
|
23a98ae491 | ||
|
|
ab9dad8f46 | ||
|
|
69344b5357 | ||
|
|
c88d1b6cc7 | ||
|
|
b6f369b868 | ||
|
|
39fa17e4ed | ||
|
|
5a19c6ad06 | ||
|
|
78db1dc516 |
@@ -36,17 +36,19 @@ matrix:
|
||||
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-28.0.3
|
||||
- android-27
|
||||
- android-28
|
||||
- extra-android-support
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - &&
|
||||
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - &&
|
||||
sudo apt-get install -y nodejs
|
||||
- yes | sdkmanager --update
|
||||
|
||||
install:
|
||||
- npm install
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## 3.0.0
|
||||
|
||||
- Feature #158: support removing headers which were previously set via "setHeader"
|
||||
|
||||
- Fixed #345: empty file names are not handled correctly (thanks ikosta)
|
||||
|
||||
- :warning: **Breaking Change**: Dropped support for Android < 5.1
|
||||
- :warning: **Breaking Change**: Removed "disableRedirect", use "setFollowRedirect" instead
|
||||
- :warning: **Breaking Change**: Removed "setSSLCertMode", use "setServerTrustMode" instead
|
||||
|
||||
## 2.5.1
|
||||
|
||||
- Fixed #334: empty JSON response triggers error even though request is successful (thanks antikalk)
|
||||
- Fixed #248: clearCookies() does not work on iOS
|
||||
|
||||
## 2.5.0
|
||||
|
||||
- Feature #56: add support for X.509 client certificate based authentication
|
||||
|
||||
## 2.4.1
|
||||
|
||||
- Fixed #296: multipart requests are not serialized on browser platform
|
||||
- Fixed #301: data is not decoded correctly when responseType is "json" (thanks antikalk)
|
||||
- Fixed #300: FormData object containing null or undefined value is not serialized correctly
|
||||
|
||||
## 2.4.0
|
||||
|
||||
- Feature #291: add support for sending 'raw' requests (thanks to jachstet-sea and chuchuva)
|
||||
|
||||
36
README.md
36
README.md
@@ -16,6 +16,7 @@ This is a fork of [Wymsee's Cordova-HTTP plugin](https://github.com/wymsee/cordo
|
||||
|
||||
- SSL / TLS Pinning
|
||||
- CORS restrictions do not apply
|
||||
- X.509 client certificate based authentication
|
||||
- Handling of HTTP code 401 - read more at [Issue CB-2415](https://issues.apache.org/jira/browse/CB-2415)
|
||||
|
||||
## Updates
|
||||
@@ -60,7 +61,7 @@ cordova.plugin.http.useBasicAuth('user', 'password');
|
||||
```
|
||||
|
||||
### setHeader<a name="setHeader"></a>
|
||||
Set a header for all future requests to a specified host. Takes a hostname, a header and a value (must be a string value).
|
||||
Set a header for all future requests to a specified host. Takes a hostname, a header and a value (must be a string value or null).
|
||||
|
||||
```js
|
||||
cordova.plugin.http.setHeader('Hostname', 'Header', 'Value');
|
||||
@@ -114,7 +115,7 @@ This defaults to `urlencoded`. You can also override the default content type he
|
||||
:warning: `multipart` depends on several Web API standards which need to be supported in your web view. Check out https://github.com/silkimen/cordova-plugin-advanced-http/wiki/Web-APIs-required-for-Multipart-requests for more info.
|
||||
|
||||
### setRequestTimeout
|
||||
Set how long to wait for a request to respond, in seconds.
|
||||
Set the "read" timeout in seconds. This is the timeout interval to use when waiting for additional data.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.setRequestTimeout(5.0);
|
||||
@@ -186,11 +187,28 @@ cordova.plugin.http.setServerTrustMode('nocheck', function() {
|
||||
});
|
||||
```
|
||||
|
||||
### disableRedirect (deprecated)
|
||||
This function was deprecated in 2.0.9. Use ["setFollowRedirect"](#setFollowRedirect) instead.
|
||||
### setClientAuthMode<a name="setClientAuthMode"></a>
|
||||
Configure X.509 client certificate authentication. Takes mode and options. `mode` being one of following values:
|
||||
|
||||
### setSSLCertMode (deprecated)
|
||||
This function was deprecated in 2.0.8. Use ["setServerTrustMode"](#setServerTrustMode) instead.
|
||||
* `none`: disable client certificate authentication
|
||||
* `systemstore` (only on Android): use client certificate installed in the Android system store; user will be presented with a list of all installed certificates
|
||||
* `buffer`: use given client certificate; you will need to provide an options object:
|
||||
* `rawPkcs`: ArrayBuffer containing raw PKCS12 container with client certificate and private key
|
||||
* `pkcsPassword`: password of the PKCS container
|
||||
|
||||
```js
|
||||
// enable client auth using PKCS12 container given in ArrayBuffer `myPkcs12ArrayBuffer`
|
||||
cordova.plugin.http.setClientAuthMode('buffer', {
|
||||
rawPkcs: myPkcs12ArrayBuffer,
|
||||
pkcsPassword: 'mySecretPassword'
|
||||
}, success, fail);
|
||||
|
||||
// enable client auth using certificate in system store (only on Android)
|
||||
cordova.plugin.http.setClientAuthMode('systemstore', {}, success, fail);
|
||||
|
||||
// disable client auth
|
||||
cordova.plugin.http.setClientAuthMode('none', {}, success, fail);
|
||||
```
|
||||
|
||||
### removeCookies
|
||||
Remove all cookies associated with a given URL.
|
||||
@@ -213,9 +231,9 @@ The options object contains following keys:
|
||||
* `serializer`: data serializer to be used (only applicable on `post`, `put` or `patch` methods), defaults to global serializer value, see [setDataSerializer](#setDataSerializer) for supported values
|
||||
* `responseType`: expected response type, defaults to `text`, needs to be one of the following values:
|
||||
* `text`: data is returned as decoded string, use this for all kinds of string responses (e.g. XML, HTML, plain text, etc.)
|
||||
* `json` data is treated as JSON and returned as parsed object
|
||||
* `arraybuffer`: data is returned as [ArrayBuffer instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
|
||||
* `blob`: data is returned as [Blob instance](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
|
||||
* `json` data is treated as JSON and returned as parsed object, returns `undefined` when response body is empty
|
||||
* `arraybuffer`: data is returned as [ArrayBuffer instance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), returns `null` when response body is empty
|
||||
* `blob`: data is returned as [Blob instance](https://developer.mozilla.org/en-US/docs/Web/API/Blob), returns `null` when response body is empty
|
||||
* `timeout`: timeout value for the request in seconds, defaults to global timeout value
|
||||
* `followRedirect`: enable or disable automatically following redirects
|
||||
* `headers`: headers object (key value pair), will be merged with global values
|
||||
|
||||
10096
package-lock.json
generated
10096
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "2.4.0",
|
||||
"version": "3.0.0",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
@@ -58,15 +58,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/silkimen/cordova-plugin-advanced-http#readme",
|
||||
"devDependencies": {
|
||||
"chai": "4.1.2",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"colors": "1.1.2",
|
||||
"cordova": "8.1.2",
|
||||
"mocha": "4.0.0",
|
||||
"mock-require": "2.0.2",
|
||||
"mz": "2.7.0",
|
||||
"chai": "4.2.0",
|
||||
"colors": "1.4.0",
|
||||
"cordova": "9.0.0",
|
||||
"mocha": "8.0.1",
|
||||
"umd-tough-cookie": "2.4.3",
|
||||
"wd": "1.4.1",
|
||||
"xml2js": "0.4.19"
|
||||
"wd": "1.12.1",
|
||||
"xml2js": "0.4.23"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.4.0">
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="3.0.0">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
@@ -79,11 +79,8 @@
|
||||
<source-file src="src/android/com/silkimen/http/HttpRequest.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/JsonUtils.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/KeyChainKeyManager.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/OkConnectionFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSConfiguration.java" target-dir="src/com/silkimen/http"/>
|
||||
<source-file src="src/android/com/silkimen/http/TLSSocketFactory.java" target-dir="src/com/silkimen/http"/>
|
||||
<preference name="OKHTTP_VERSION" default="3.10.0"/>
|
||||
<framework src="com.squareup.okhttp3:okhttp-urlconnection:$OKHTTP_VERSION"/>
|
||||
</platform>
|
||||
<platform name="browser">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
|
||||
@@ -3,8 +3,8 @@ set -e
|
||||
|
||||
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd ..; pwd )"
|
||||
|
||||
if [ $CI == "true" ] && ([ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]); then
|
||||
echo "Skipping CI tests, because Saucelabs credentials are not set.";
|
||||
if [ $CI == "true" ] && ([ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]) && ([ -z $BROWSERSTACK_USERNAME ] || [ -z $BROWSERSTACK_ACCESS_KEY ]); then
|
||||
echo "Skipping CI tests, because Saucelabs and BrowserStack credentials are not set.";
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const args = process.argv.slice(2);
|
||||
const fs = require('mz/fs');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const xml2js = require('xml2js');
|
||||
const xmlPath = path.join(__dirname, '..', 'plugin.xml');
|
||||
@@ -22,10 +22,12 @@ const stringify = obj => {
|
||||
return builder.buildObject(obj);
|
||||
};
|
||||
|
||||
fs.readFile(xmlPath, 'utf-8')
|
||||
.then(xml => parse(xml))
|
||||
.then(parsed => {
|
||||
parsed.plugin.$.version = args[0];
|
||||
const update = async (version) => {
|
||||
const xml = fs.readFileSync(xmlPath, 'utf-8');
|
||||
const parsed = await parse(xml);
|
||||
|
||||
return fs.writeFile(xmlPath, stringify(parsed));
|
||||
});
|
||||
parsed.plugin.$.version = version;
|
||||
fs.writeFileSync(xmlPath, stringify(parsed));
|
||||
};
|
||||
|
||||
return update(args[0]);
|
||||
|
||||
@@ -16,7 +16,6 @@ import com.silkimen.http.HttpBodyDecoder;
|
||||
import com.silkimen.http.HttpRequest;
|
||||
import com.silkimen.http.HttpRequest.HttpRequestException;
|
||||
import com.silkimen.http.JsonUtils;
|
||||
import com.silkimen.http.OkConnectionFactory;
|
||||
import com.silkimen.http.TLSConfiguration;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
@@ -124,7 +123,6 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
request.readTimeout(this.timeout);
|
||||
request.acceptCharset("UTF-8");
|
||||
request.uncompress(true);
|
||||
HttpRequest.setConnectionFactory(new OkConnectionFactory());
|
||||
|
||||
if (this.tlsConfiguration.getHostnameVerifier() != null) {
|
||||
request.setHostnameVerifier(this.tlsConfiguration.getHostnameVerifier());
|
||||
@@ -193,7 +191,7 @@ abstract class CordovaHttpBase implements Runnable {
|
||||
response.setHeaders(request.headers());
|
||||
|
||||
if (request.code() >= 200 && request.code() < 300) {
|
||||
if ("text".equals(this.responseType)) {
|
||||
if ("text".equals(this.responseType) || "json".equals(this.responseType)) {
|
||||
String decoded = HttpBodyDecoder.decodeBody(outputStream.toByteArray(), request.charset());
|
||||
response.setBody(decoded);
|
||||
} else {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.silkimen.http;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.OkUrlFactory;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.net.Proxy;
|
||||
|
||||
public class OkConnectionFactory implements HttpRequest.ConnectionFactory {
|
||||
private final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
public HttpURLConnection create(URL url) {
|
||||
OkUrlFactory urlFactory = new OkUrlFactory(this.client);
|
||||
|
||||
return (HttpURLConnection) urlFactory.open(url);
|
||||
}
|
||||
|
||||
public HttpURLConnection create(URL url, Proxy proxy) {
|
||||
OkHttpClient clientWithProxy = new OkHttpClient.Builder().proxy(proxy).build();
|
||||
OkUrlFactory urlFactory = new OkUrlFactory(clientWithProxy);
|
||||
|
||||
return (HttpURLConnection) urlFactory.open(url);
|
||||
}
|
||||
}
|
||||
45
src/browser/cordova-http-plugin.js
vendored
45
src/browser/cordova-http-plugin.js
vendored
@@ -37,6 +37,39 @@ function serializeParams(params) {
|
||||
}).join('&');
|
||||
}
|
||||
|
||||
function decodeB64(dataString) {
|
||||
var binarString = atob(dataString);
|
||||
var bytes = new Uint8Array(binarString.length);
|
||||
|
||||
for (var i = 0; i < binarString.length; ++i) {
|
||||
bytes[i] = binarString.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
function processMultipartData(data) {
|
||||
if (!data) return null;
|
||||
|
||||
var fd = new FormData();
|
||||
|
||||
for (var i = 0; i < data.buffers.length; ++i) {
|
||||
var buffer = data.buffers[i];
|
||||
var name = data.names[i];
|
||||
var fileName = data.fileNames[i];
|
||||
var type = data.types[i];
|
||||
|
||||
if (fileName) {
|
||||
fd.append(name, new Blob([decodeB64(buffer)], {type: type}), fileName);
|
||||
} else {
|
||||
// we assume it's plain text if no filename was given
|
||||
fd.append(name, atob(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
function deserializeResponseHeaders(headers) {
|
||||
var headerMap = {};
|
||||
var arr = headers.trim().split(/[\r\n]+/);
|
||||
@@ -161,6 +194,18 @@ function sendRequest(method, withData, opts, success, failure) {
|
||||
processedData = serializeParams(data);
|
||||
break;
|
||||
|
||||
case 'multipart':
|
||||
const contentType = getHeaderValue(headers, 'Content-Type');
|
||||
|
||||
// intentionally don't set a default content type
|
||||
// it's set by the browser together with the content disposition string
|
||||
if (contentType) {
|
||||
headers['Content-Type'] = contentType;
|
||||
}
|
||||
|
||||
processedData = processMultipartData(data);
|
||||
break;
|
||||
|
||||
case 'raw':
|
||||
setDefaultContentType(headers, 'application/octet-stream');
|
||||
processedData = data;
|
||||
|
||||
@@ -184,6 +184,8 @@
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
@@ -232,6 +234,8 @@
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
@@ -293,6 +297,8 @@
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
@@ -358,6 +364,8 @@
|
||||
{
|
||||
NSError *serializationError = nil;
|
||||
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
|
||||
[request setHTTPShouldHandleCookies:NO];
|
||||
|
||||
if (serializationError) {
|
||||
if (failure) {
|
||||
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@interface CordovaHttpPlugin : CDVPlugin
|
||||
|
||||
- (void)setServerTrustMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)setClientAuthMode:(CDVInvokedUrlCommand*)command;
|
||||
- (void)post:(CDVInvokedUrlCommand*)command;
|
||||
- (void)put:(CDVInvokedUrlCommand*)command;
|
||||
- (void)patch:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
@implementation CordovaHttpPlugin {
|
||||
AFSecurityPolicy *securityPolicy;
|
||||
NSURLCredential *x509Credential;
|
||||
}
|
||||
|
||||
- (void)pluginInitialize {
|
||||
@@ -39,6 +40,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupAuthChallengeBlock:(AFHTTPSessionManager*)manager {
|
||||
[manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(
|
||||
NSURLSession * _Nonnull session,
|
||||
NSURLAuthenticationChallenge * _Nonnull challenge,
|
||||
NSURLCredential * _Nullable __autoreleasing * _Nullable credential
|
||||
) {
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodServerTrust]) {
|
||||
*credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
|
||||
if (![self->securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
|
||||
return NSURLSessionAuthChallengeRejectProtectionSpace;
|
||||
}
|
||||
|
||||
if (credential) {
|
||||
return NSURLSessionAuthChallengeUseCredential;
|
||||
}
|
||||
}
|
||||
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString: NSURLAuthenticationMethodClientCertificate] && self->x509Credential) {
|
||||
*credential = self->x509Credential;
|
||||
return NSURLSessionAuthChallengeUseCredential;
|
||||
}
|
||||
|
||||
return NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setRequestHeaders:(NSDictionary*)headers forManager:(AFHTTPSessionManager*)manager {
|
||||
[headers enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
[manager.requestSerializer setValue:obj forHTTPHeaderField:key];
|
||||
@@ -62,7 +90,7 @@
|
||||
}
|
||||
|
||||
- (void)setResponseSerializer:(NSString*)responseType forManager:(AFHTTPSessionManager*)manager {
|
||||
if ([responseType isEqualToString: @"text"]) {
|
||||
if ([responseType isEqualToString: @"text"] || [responseType isEqualToString: @"json"]) {
|
||||
manager.responseSerializer = [TextResponseSerializer serializer];
|
||||
} else {
|
||||
manager.responseSerializer = [BinaryResponseSerializer serializer];
|
||||
@@ -147,7 +175,6 @@
|
||||
|
||||
- (void)executeRequestWithoutData:(CDVInvokedUrlCommand*)command withMethod:(NSString*) method {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:1];
|
||||
@@ -156,6 +183,7 @@
|
||||
NSString *responseType = [command.arguments objectAtIndex:4];
|
||||
|
||||
[self setRequestSerializer: @"default" forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
@@ -199,7 +227,6 @@
|
||||
|
||||
- (void)executeRequestWithData:(CDVInvokedUrlCommand*)command withMethod:(NSString*)method {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *data = [command.arguments objectAtIndex:1];
|
||||
@@ -210,6 +237,7 @@
|
||||
NSString *responseType = [command.arguments objectAtIndex:6];
|
||||
|
||||
[self setRequestSerializer: serializerName forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
@@ -302,6 +330,51 @@
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)setClientAuthMode:(CDVInvokedUrlCommand*)command {
|
||||
CDVPluginResult* pluginResult;
|
||||
NSString *mode = [command.arguments objectAtIndex:0];
|
||||
|
||||
if ([mode isEqualToString:@"none"]) {
|
||||
x509Credential = nil;
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
|
||||
if ([mode isEqualToString:@"systemstore"]) {
|
||||
NSString *alias = [command.arguments objectAtIndex:1];
|
||||
|
||||
// TODO
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"mode 'systemstore' is not supported on iOS"];
|
||||
}
|
||||
|
||||
if ([mode isEqualToString:@"buffer"]) {
|
||||
CFDataRef container = (__bridge CFDataRef) [command.arguments objectAtIndex:2];
|
||||
CFStringRef password = (__bridge CFStringRef) [command.arguments objectAtIndex:3];
|
||||
|
||||
const void *keys[] = { kSecImportExportPassphrase };
|
||||
const void *values[] = { password };
|
||||
|
||||
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
|
||||
CFArrayRef items;
|
||||
OSStatus securityError = SecPKCS12Import(container, options, &items);
|
||||
CFRelease(options);
|
||||
|
||||
if (securityError != noErr) {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
|
||||
} else {
|
||||
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
|
||||
SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
|
||||
|
||||
self->x509Credential = [NSURLCredential credentialWithIdentity:identity certificates: nil persistence:NSURLCredentialPersistenceForSession];
|
||||
CFRelease(items);
|
||||
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)post:(CDVInvokedUrlCommand*)command {
|
||||
[self executeRequestWithData: command withMethod:@"POST"];
|
||||
}
|
||||
@@ -332,7 +405,6 @@
|
||||
|
||||
- (void)uploadFiles:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
NSDictionary *headers = [command.arguments objectAtIndex:1];
|
||||
@@ -343,6 +415,7 @@
|
||||
NSString *responseType = [command.arguments objectAtIndex:6];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
[self setResponseSerializer:responseType forManager:manager];
|
||||
@@ -392,7 +465,6 @@
|
||||
|
||||
- (void)downloadFile:(CDVInvokedUrlCommand*)command {
|
||||
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
|
||||
manager.securityPolicy = securityPolicy;
|
||||
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
|
||||
|
||||
NSString *url = [command.arguments objectAtIndex:0];
|
||||
@@ -402,6 +474,7 @@
|
||||
bool followRedirect = [[command.arguments objectAtIndex:4] boolValue];
|
||||
|
||||
[self setRequestHeaders: headers forManager: manager];
|
||||
[self setupAuthChallengeBlock: manager];
|
||||
[self setTimeout:timeoutInSeconds forManager:manager];
|
||||
[self setRedirect:followRedirect forManager:manager];
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
</platform>
|
||||
<engine name="android" spec="7.1.0" />
|
||||
<engine name="browser" spec="5.0.0" />
|
||||
<engine name="ios" spec="5.0.1" />
|
||||
<plugin name="cordova-plugin-file" spec="6.0.1" />
|
||||
<engine name="android" spec="8.1.0" />
|
||||
<engine name="browser" spec="6.0.0" />
|
||||
<engine name="ios" spec="5.1.0" />
|
||||
<plugin name="cordova-plugin-file" spec="6.0.2" />
|
||||
<preference name="AndroidPersistentFileLocation" value="Internal" />
|
||||
</widget>
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"name": "com.ilkimen.http.demo",
|
||||
"displayName": "HttpDemo",
|
||||
"version": "1.0.0",
|
||||
"description": "A sample Apache Cordova application that demonstrates advanced HTTP plugin.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "scripts/build.sh",
|
||||
"test": "npm run build && scripts/test.sh"
|
||||
},
|
||||
"author": "Sefa Ilkimen",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cordova": "7.0.1",
|
||||
"cordova-android": "7.1.0",
|
||||
"cordova-browser": "5.0.0",
|
||||
"cordova-ios": "5.0.1"
|
||||
},
|
||||
"cordova": {
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
"name": "com.ilkimen.http.demo",
|
||||
"displayName": "HttpDemo",
|
||||
"version": "1.0.0",
|
||||
"description": "A sample Apache Cordova application that demonstrates advanced HTTP plugin.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "scripts/build.sh",
|
||||
"test": "npm run build && scripts/test.sh"
|
||||
},
|
||||
"author": "Sefa Ilkimen",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cordova": "9.0.0",
|
||||
"cordova-android": "8.1.0",
|
||||
"cordova-browser": "6.0.0",
|
||||
"cordova-ios": "5.1.0"
|
||||
},
|
||||
"cordova": {
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ const hooks = {
|
||||
onBeforeEachTest: function (resolve, reject) {
|
||||
cordova.plugin.http.clearCookies();
|
||||
|
||||
helpers.enableFollowingRedirect(function() {
|
||||
// server trust mode is not supported on brpwser platform
|
||||
helpers.enableFollowingRedirect(function () {
|
||||
// server trust mode is not supported on browser platform
|
||||
if (cordova.platformId === 'browser') {
|
||||
return resolve();
|
||||
}
|
||||
@@ -23,7 +23,7 @@ const helpers = {
|
||||
setPinnedServerTrustMode: function (resolve, reject) { cordova.plugin.http.setServerTrustMode('pinned', resolve, reject); },
|
||||
setNoneClientAuthMode: function (resolve, reject) { cordova.plugin.http.setClientAuthMode('none', resolve, reject); },
|
||||
setBufferClientAuthMode: function (resolve, reject) {
|
||||
helpers.getWithXhr(function(pkcs) {
|
||||
helpers.getWithXhr(function (pkcs) {
|
||||
cordova.plugin.http.setClientAuthMode('buffer', {
|
||||
rawPkcs: pkcs,
|
||||
pkcsPassword: 'badssl.com'
|
||||
@@ -34,9 +34,9 @@ const helpers = {
|
||||
setUtf8StringSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('utf8')); },
|
||||
setUrlEncodedSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('urlencoded')); },
|
||||
setMultipartSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('multipart')); },
|
||||
setRawSerializer: function(resolve) { resolve(cordova.plugin.http.setDataSerializer('raw')); },
|
||||
setRawSerializer: function (resolve) { resolve(cordova.plugin.http.setDataSerializer('raw')); },
|
||||
disableFollowingRedirect: function (resolve) { resolve(cordova.plugin.http.setFollowRedirect(false)); },
|
||||
enableFollowingRedirect: function(resolve) { resolve(cordova.plugin.http.setFollowRedirect(true)); },
|
||||
enableFollowingRedirect: function (resolve) { resolve(cordova.plugin.http.setFollowRedirect(true)); },
|
||||
getWithXhr: function (done, url, type) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
@@ -71,7 +71,7 @@ const helpers = {
|
||||
var byteArray = new Uint8Array(buffer);
|
||||
|
||||
for (var i = 0; i < byteArray.length; i++) {
|
||||
hash = ((hash << 5) - hash) + byteArray[i];
|
||||
hash = ((hash << 5) - hash) + byteArray[i];
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
|
||||
@@ -288,25 +288,25 @@ const tests = [
|
||||
JSON.parse(result.data.data).form.should.eql({ test: 'testString' });
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should resolve correct URL after redirect (GET) #33',
|
||||
expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.url.should.be.equal('http://httpbin.org/anything');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not follow 302 redirect when following redirects is disabled',
|
||||
expected: 'rejected: {"status": 302, ...',
|
||||
before: function(resolve, reject) { cordova.plugin.http.disableRedirect(true, resolve, reject)},
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('rejected');
|
||||
result.data.status.should.be.equal(302);
|
||||
}
|
||||
},
|
||||
// {
|
||||
// description: 'should resolve correct URL after redirect (GET) #33',
|
||||
// expected: 'resolved: {"status": 200, url: "http://httpbin.org/anything", ...',
|
||||
// func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
// validationFunc: function (driver, result) {
|
||||
// result.type.should.be.equal('resolved');
|
||||
// result.data.url.should.be.equal('http://httpbin.org/anything');
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// description: 'should not follow 302 redirect when following redirects is disabled',
|
||||
// expected: 'rejected: {"status": 302, ...',
|
||||
// before: function (resolve, reject) { cordova.plugin.http.disableRedirect(true, resolve, reject) },
|
||||
// func: function (resolve, reject) { cordova.plugin.http.get('http://httpbin.org/redirect-to?url=http://httpbin.org/anything', {}, {}, resolve, reject); },
|
||||
// validationFunc: function (driver, result) {
|
||||
// result.type.should.be.equal('rejected');
|
||||
// result.data.status.should.be.equal(302);
|
||||
// }
|
||||
// },
|
||||
{
|
||||
description: 'should download a file from given URL to given path in local filesystem',
|
||||
expected: 'resolved: {"content": "<?xml version=\'1.0\' encoding=\'us-ascii\'?>\\n\\n<!-- A SAMPLE set of slides -->" ...',
|
||||
@@ -374,7 +374,7 @@ const tests = [
|
||||
var targetUrl = 'http://httpbin.org/post';
|
||||
|
||||
helpers.writeToFile(function () {
|
||||
helpers.writeToFile(function() {
|
||||
helpers.writeToFile(function () {
|
||||
cordova.plugin.http.uploadFile(targetUrl, {}, {}, [sourcePath, sourcePath2], [fileName, fileName2], resolve, reject);
|
||||
}, fileName2, fileContent2);
|
||||
}, fileName, fileContent);
|
||||
@@ -464,7 +464,7 @@ const tests = [
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not send any cookies after running "clearCookies" (GET) #59',
|
||||
description: 'should not send programmatically set 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');
|
||||
@@ -787,7 +787,7 @@ const tests = [
|
||||
},
|
||||
{
|
||||
description: 'should decode error body even if response type is "arraybuffer"',
|
||||
expected: 'rejected: {"status": 418, ...',
|
||||
expected: 'rejected: {"status":418, ...',
|
||||
func: function (resolve, reject) {
|
||||
var url = 'https://httpbin.org/status/418';
|
||||
var options = { method: 'get', responseType: 'arraybuffer' };
|
||||
@@ -801,7 +801,7 @@ const tests = [
|
||||
},
|
||||
{
|
||||
description: 'should serialize FormData instance correctly when it contains string value',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setMultipartSerializer,
|
||||
func: function (resolve, reject) {
|
||||
var ponyfills = cordova.plugin.http.ponyfills;
|
||||
@@ -820,11 +820,11 @@ const tests = [
|
||||
},
|
||||
{
|
||||
description: 'should serialize FormData instance correctly when it contains blob value',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setMultipartSerializer,
|
||||
func: function (resolve, reject) {
|
||||
var ponyfills = cordova.plugin.http.ponyfills;
|
||||
helpers.getWithXhr(function(blob) {
|
||||
helpers.getWithXhr(function (blob) {
|
||||
var formData = new ponyfills.FormData();
|
||||
formData.append('CordovaLogo', blob);
|
||||
|
||||
@@ -850,7 +850,7 @@ const tests = [
|
||||
expected: 'resolved: {"status":200,"data:application/octet-stream;base64,iVBORw0KGgoAAAANSUhEUg ...',
|
||||
before: helpers.setRawSerializer,
|
||||
func: function (resolve, reject) {
|
||||
helpers.getWithXhr(function(buffer) {
|
||||
helpers.getWithXhr(function (buffer) {
|
||||
cordova.plugin.http.post('http://httpbin.org/anything', buffer, {}, resolve, reject);
|
||||
}, './res/cordova_logo.png', 'arraybuffer');
|
||||
},
|
||||
@@ -890,18 +890,104 @@ const tests = [
|
||||
result.data.headers['access-control-allow-origin'].should.be.equal('*');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should allow empty response body even though responseType is set #334',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
func: function (resolve, reject) {
|
||||
var url = 'https://httpbin.org/status/200';
|
||||
var options = { method: 'get', responseType: 'json' };
|
||||
cordova.plugin.http.sendRequest(url, options, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
should.equal(null, result.data.data);
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should decode JSON data correctly when response type is "json" #301',
|
||||
expected: 'resolved: {"status":200,"data":{"slideshow": ... ',
|
||||
func: function (resolve, reject) {
|
||||
var url = 'https://httpbin.org/json';
|
||||
var options = { method: 'get', responseType: 'json' };
|
||||
cordova.plugin.http.sendRequest(url, options, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
result.data.data.should.be.an('object');
|
||||
result.data.data.slideshow.should.be.eql({
|
||||
author: 'Yours Truly',
|
||||
date: 'date of publication',
|
||||
slides: [
|
||||
{
|
||||
title: 'Wake up to WonderWidgets!',
|
||||
type: 'all'
|
||||
},
|
||||
{
|
||||
items: [
|
||||
'Why <em>WonderWidgets</em> are great',
|
||||
'Who <em>buys</em> WonderWidgets'
|
||||
],
|
||||
title: 'Overview',
|
||||
type: 'all'
|
||||
}
|
||||
],
|
||||
title: 'Sample Slide Show'
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should serialize FormData instance correctly when it contains null or undefined value #300',
|
||||
expected: 'resolved: {"status":200, ...',
|
||||
before: helpers.setMultipartSerializer,
|
||||
func: function (resolve, reject) {
|
||||
var ponyfills = cordova.plugin.http.ponyfills;
|
||||
var formData = new ponyfills.FormData();
|
||||
formData.append('myNullValue', null);
|
||||
formData.append('myUndefinedValue', undefined);
|
||||
|
||||
// TODO: not ready yet
|
||||
// {
|
||||
// description: 'should authenticate correctly when client cert auth is configured with a PKCS12 container',
|
||||
// expected: 'resolved: {"status": 200, ...',
|
||||
// before: helpers.setBufferClientAuthMode,
|
||||
// func: function (resolve, reject) { cordova.plugin.http.get('https://client.badssl.com/', {}, {}, resolve, reject); },
|
||||
// validationFunc: function (driver, result) {
|
||||
// result.type.should.be.equal('resolved');
|
||||
// result.data.data.should.include('TLS handshake');
|
||||
// }
|
||||
// }
|
||||
var url = 'https://httpbin.org/anything';
|
||||
var options = { method: 'post', data: formData };
|
||||
cordova.plugin.http.sendRequest(url, options, resolve, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
helpers.checkResult(result, 'resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
JSON.parse(result.data.data).form.should.be.eql({
|
||||
myNullValue: 'null',
|
||||
myUndefinedValue: 'undefined'
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should authenticate correctly when client cert auth is configured with a PKCS12 container',
|
||||
expected: 'resolved: {"status": 200, ...',
|
||||
before: helpers.setBufferClientAuthMode,
|
||||
func: function (resolve, reject) { cordova.plugin.http.get('https://client.badssl.com/', {}, {}, resolve, reject); },
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.data.should.include('TLS handshake');
|
||||
}
|
||||
},
|
||||
{
|
||||
description: 'should not send any cookies after running "clearCookies" (GET) #248',
|
||||
expected: 'resolved: {"status": 200, "data": "{\"cookies\":{}} ...',
|
||||
before: helpers.disableFollowingRedirect,
|
||||
func: function (resolve, reject) {
|
||||
cordova.plugin.http.get('https://httpbin.org/cookies/set?myCookieKey=myCookieValue', {}, {}, function () {
|
||||
cordova.plugin.http.clearCookies();
|
||||
cordova.plugin.http.get('https://httpbin.org/cookies', {}, {}, resolve, reject);
|
||||
}, function () {
|
||||
cordova.plugin.http.clearCookies();
|
||||
cordova.plugin.http.get('https://httpbin.org/cookies', {}, {}, resolve, reject);
|
||||
});
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
result.type.should.be.equal('resolved');
|
||||
result.data.status.should.be.equal(200);
|
||||
JSON.parse(result.data.data).cookies.should.be.eql({});
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
|
||||
@@ -67,8 +67,8 @@ const configs = {
|
||||
app: 'HttpTestAppAndroid'
|
||||
},
|
||||
browserstackAndroidDevice: {
|
||||
device: 'Google Nexus 9',
|
||||
os_version: '5.1',
|
||||
device: 'Google Nexus 6',
|
||||
os_version: '5.0',
|
||||
project: 'HTTP Test App',
|
||||
autoWebview: true,
|
||||
app: 'HttpTestAppAndroid'
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
const wd = require('wd');
|
||||
const chai = require('chai');
|
||||
const chaiAsPromised = require('chai-as-promised');
|
||||
const logging = require('./logging');
|
||||
const capsConfig = require('./caps');
|
||||
const serverConfig = require('./server');
|
||||
const testDefinitions = require('../e2e-specs');
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
|
||||
global.should = chai.should();
|
||||
|
||||
describe('Advanced HTTP e2e test suite', function () {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const chai = require('chai');
|
||||
const mock = require('mock-require');
|
||||
const util = require('util');
|
||||
const should = chai.should();
|
||||
|
||||
@@ -50,6 +49,13 @@ describe('Advanced HTTP public interface', function () {
|
||||
http.getHeaders('*').myKey.should.equal('myValue');
|
||||
});
|
||||
|
||||
it('clears global headers correctly when value is undefined', () => {
|
||||
http.setHeader('*', 'myKey', 'myValue');
|
||||
http.setHeader('*', 'myKey', null);
|
||||
should.equal(undefined, http.getHeaders('*').myKey);
|
||||
Object.keys(http.getHeaders('*')).length.should.be.equal(0);
|
||||
});
|
||||
|
||||
it('sets host headers correctly #24', () => {
|
||||
http.setHeader('www.google.de', 'myKey', 'myValue');
|
||||
http.getHeaders('www.google.de').myKey.should.equal('myValue');
|
||||
@@ -419,7 +425,7 @@ describe('Common helpers', function () {
|
||||
response => response.data.should.be.equal('fakeData')
|
||||
);
|
||||
|
||||
handler({ data: 'fakeData' });
|
||||
handler({ data: 'fakeData' });
|
||||
});
|
||||
|
||||
it('does not change response data if response type is "text"', () => {
|
||||
@@ -444,6 +450,17 @@ describe('Common helpers', function () {
|
||||
handler({ data: JSON.stringify(fakeData) });
|
||||
});
|
||||
|
||||
it('handles empty "json" response correctly', () => {
|
||||
const emptyData = "";
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, null, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'json',
|
||||
response => should.equal(undefined, response.data)
|
||||
);
|
||||
|
||||
handler({ data: emptyData });
|
||||
});
|
||||
|
||||
it('handles response type "arraybuffer" correctly', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, fakeBase64, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
@@ -454,6 +471,16 @@ describe('Common helpers', function () {
|
||||
handler({ data: 'myString' });
|
||||
});
|
||||
|
||||
it('handles empty "arraybuffer" response correctly', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, fakeBase64, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'arraybuffer',
|
||||
response => should.equal(null, response.data)
|
||||
);
|
||||
|
||||
handler({ data: '' });
|
||||
});
|
||||
|
||||
it('handles response type "blob" correctly', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, fakeBase64, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
@@ -465,7 +492,19 @@ describe('Common helpers', function () {
|
||||
}
|
||||
);
|
||||
|
||||
handler({ data: 'myString', headers: { 'content-type': 'fakeType'} });
|
||||
handler({ data: 'myString', headers: { 'content-type': 'fakeType' } });
|
||||
});
|
||||
|
||||
it('handles empty "blob" response correctly', () => {
|
||||
const helpers = require('../www/helpers')(null, jsUtil, null, messages, fakeBase64, errorCodes);
|
||||
const handler = helpers.injectRawResponseHandler(
|
||||
'blob',
|
||||
(response) => {
|
||||
should.equal(null, response.data)
|
||||
}
|
||||
);
|
||||
|
||||
handler({ data: '', headers: { 'content-type': 'fakeType' } });
|
||||
});
|
||||
|
||||
it('calls failure callback when post-processing fails', () => {
|
||||
@@ -546,7 +585,7 @@ describe('Common helpers', function () {
|
||||
|
||||
it('processes data correctly when serializer "utf8" is configured', (cb) => {
|
||||
helpers.processData('myString', 'utf8', (data) => {
|
||||
data.should.be.eql({text: 'myString'});
|
||||
data.should.be.eql({ text: 'myString' });
|
||||
cb();
|
||||
})
|
||||
});
|
||||
@@ -590,8 +629,46 @@ describe('Common helpers', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('processes data correctly when serializer "multipart" is configured and form data contains file value (filename set)', (cb) => {
|
||||
const formData = new FormDataMock();
|
||||
formData.append('myFile', new BlobMock([testString], { type: 'application/octet-stream' }), 'file.name');
|
||||
|
||||
helpers.processData(formData, 'multipart', (data) => {
|
||||
data.buffers.length.should.be.equal(1);
|
||||
data.names.length.should.be.equal(1);
|
||||
data.fileNames.length.should.be.equal(1);
|
||||
data.types.length.should.be.equal(1);
|
||||
|
||||
data.buffers[0].should.be.eql(testStringBase64);
|
||||
data.names[0].should.be.equal('myFile');
|
||||
data.fileNames[0].should.be.equal('file.name');
|
||||
data.types[0].should.be.equal('application/octet-stream');
|
||||
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
it('processes data correctly when serializer "multipart" is configured and form data contains file value (filename empty)', (cb) => {
|
||||
const formData = new FormDataMock();
|
||||
formData.append('myFile', new BlobMock([testString], { type: 'application/octet-stream' }), '');
|
||||
|
||||
helpers.processData(formData, 'multipart', (data) => {
|
||||
data.buffers.length.should.be.equal(1);
|
||||
data.names.length.should.be.equal(1);
|
||||
data.fileNames.length.should.be.equal(1);
|
||||
data.types.length.should.be.equal(1);
|
||||
|
||||
data.buffers[0].should.be.eql(testStringBase64);
|
||||
data.names[0].should.be.equal('myFile');
|
||||
data.fileNames[0].should.be.equal('');
|
||||
data.types[0].should.be.equal('application/octet-stream');
|
||||
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
it('processes data correctly when serializer "raw" is configured', (cb) => {
|
||||
const byteArray = new Uint8Array([1,2,3]);
|
||||
const byteArray = new Uint8Array([1, 2, 3]);
|
||||
helpers.processData(byteArray, 'raw', (data) => {
|
||||
data.should.be.a('ArrayBuffer');
|
||||
data.should.be.equal(byteArray.buffer);
|
||||
@@ -647,7 +724,7 @@ describe('Dependency Validator', function () {
|
||||
describe('checkFormDataInstance()', function () {
|
||||
it('throws an error if FormData.entries() is not supported on given instance', function () {
|
||||
const console = new ConsoleMock();
|
||||
const validator = require('../www/dependency-validator')({ FormData: {}}, console, messages);
|
||||
const validator = require('../www/dependency-validator')({ FormData: {} }, console, messages);
|
||||
|
||||
(() => validator.checkFormDataInstance({})).should.throw(messages.MISSING_FORMDATA_ENTRIES_API);
|
||||
});
|
||||
@@ -684,12 +761,12 @@ describe('Ponyfills', function () {
|
||||
const iterator = new ponyfills.Iterator([]);
|
||||
iterator.next().should.be.eql({ done: true, value: undefined });
|
||||
});
|
||||
|
||||
|
||||
it('returns iteration object correctly when end posititon of list is not reached yet', () => {
|
||||
const iterator = new ponyfills.Iterator([['first', 'this is the first item']]);
|
||||
iterator.next().should.be.eql({ done: false, value: ['first', 'this is the first item'] });
|
||||
});
|
||||
|
||||
|
||||
it('returns iteration object correctly when end posititon of list is already reached', () => {
|
||||
const iterator = new ponyfills.Iterator([['first', 'this is the first item']]);
|
||||
iterator.next();
|
||||
|
||||
@@ -3,7 +3,7 @@ const BlobMock = require('./Blob.mock');
|
||||
module.exports = class FileMock extends BlobMock {
|
||||
constructor(blob, fileName) {
|
||||
super(blob, { type: blob.type });
|
||||
this._fileName = fileName || '';
|
||||
this._fileName = fileName !== undefined ? fileName : 'blob';
|
||||
this.__lastModifiedDate = new Date();
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,9 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
|
||||
}
|
||||
|
||||
function checkForInvalidHeaderValue(value) {
|
||||
if (jsUtil.getTypeOf(value) !== 'String') {
|
||||
var type = jsUtil.getTypeOf(value);
|
||||
|
||||
if (type !== 'String' && type !== 'Null') {
|
||||
throw new Error(messages.INVALID_HEADER_VALUE);
|
||||
}
|
||||
|
||||
@@ -295,20 +297,28 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
|
||||
try {
|
||||
// json
|
||||
if (responseType === validResponseTypes[1]) {
|
||||
response.data = JSON.parse(response.data);
|
||||
response.data = response.data === ''
|
||||
? undefined
|
||||
: JSON.parse(response.data);
|
||||
}
|
||||
|
||||
// arraybuffer
|
||||
else if (responseType === validResponseTypes[2]) {
|
||||
response.data = base64.toArrayBuffer(response.data);
|
||||
response.data = response.data === ''
|
||||
? null
|
||||
: base64.toArrayBuffer(response.data);
|
||||
}
|
||||
|
||||
// blob
|
||||
else if (responseType === validResponseTypes[3]) {
|
||||
var buffer = base64.toArrayBuffer(response.data);
|
||||
var type = response.headers['content-type'] || '';
|
||||
var blob = new Blob([ buffer ], { type: type });
|
||||
response.data = blob;
|
||||
if (response.data === '') {
|
||||
response.data = null;
|
||||
} else {
|
||||
var buffer = base64.toArrayBuffer(response.data);
|
||||
var type = response.headers['content-type'] || '';
|
||||
var blob = new Blob([buffer], { type: type });
|
||||
response.data = blob;
|
||||
}
|
||||
}
|
||||
|
||||
success(response);
|
||||
@@ -384,7 +394,7 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
|
||||
if (allowedInstanceTypes) {
|
||||
var isCorrectInstanceType = false;
|
||||
|
||||
allowedInstanceTypes.forEach(function(type) {
|
||||
allowedInstanceTypes.forEach(function (type) {
|
||||
if ((global[type] && data instanceof global[type]) || (ponyfills[type] && data instanceof ponyfills[type])) {
|
||||
isCorrectInstanceType = true;
|
||||
}
|
||||
@@ -440,10 +450,10 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
|
||||
if (entry.value[1] instanceof global.Blob || entry.value[1] instanceof global.File) {
|
||||
var reader = new global.FileReader();
|
||||
|
||||
reader.onload = function() {
|
||||
reader.onload = function () {
|
||||
result.buffers.push(base64.fromArrayBuffer(reader.result));
|
||||
result.names.push(entry.value[0]);
|
||||
result.fileNames.push(entry.value[1].name || 'blob');
|
||||
result.fileNames.push(entry.value[1].name !== undefined ? entry.value[1].name : 'blob');
|
||||
result.types.push(entry.value[1].type || '');
|
||||
processFormDataIterator(iterator, textEncoder, result, onFinished);
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ module.exports = {
|
||||
INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
|
||||
INVALID_DOWNLOAD_FILE_PATH: 'advanced-http: invalid "filePath" value, needs to be a string, <filePath: string>',
|
||||
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value, <followRedirect: boolean>',
|
||||
INVALID_HEADER_VALUE: 'advanced-http: invalid header value, needs to be a string, <header: string>',
|
||||
INVALID_HEADER_VALUE: 'advanced-http: invalid header value, needs to be a string or null, <header: string | null>',
|
||||
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
|
||||
INVALID_RESPONSE_TYPE: 'advanced-http: invalid response type, supported types are:',
|
||||
INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
|
||||
|
||||
@@ -16,9 +16,9 @@ module.exports = function init(global) {
|
||||
} else if (global.Blob && value instanceof global.Blob) {
|
||||
// mimic File instance by adding missing properties
|
||||
value.lastModifiedDate = new Date();
|
||||
value.name = filename || '';
|
||||
value.name = filename !== undefined ? filename : 'blob';
|
||||
} else {
|
||||
value = value.toString ? value.toString() : value;
|
||||
value = String(value);
|
||||
}
|
||||
|
||||
this.__items.push([ name, value ]);
|
||||
@@ -44,4 +44,4 @@ module.exports = function init(global) {
|
||||
}
|
||||
|
||||
return interface;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,10 +14,6 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
setRequestTimeout: setRequestTimeout,
|
||||
getFollowRedirect: getFollowRedirect,
|
||||
setFollowRedirect: setFollowRedirect,
|
||||
// @DEPRECATED
|
||||
disableRedirect: disableRedirect,
|
||||
// @DEPRECATED
|
||||
setSSLCertMode: setServerTrustMode,
|
||||
setServerTrustMode: setServerTrustMode,
|
||||
setClientAuthMode: setClientAuthMode,
|
||||
sendRequest: sendRequest,
|
||||
@@ -62,7 +58,12 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
helpers.checkForInvalidHeaderValue(value);
|
||||
|
||||
globalConfigs.headers[host] = globalConfigs.headers[host] || {};
|
||||
globalConfigs.headers[host][header] = value;
|
||||
|
||||
if (value === null) {
|
||||
delete globalConfigs.headers[host][header];
|
||||
} else {
|
||||
globalConfigs.headers[host][header] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function getDataSerializer() {
|
||||
@@ -105,14 +106,6 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
globalConfigs.followRedirect = helpers.checkFollowRedirectValue(follow);
|
||||
}
|
||||
|
||||
// @DEPRECATED
|
||||
function disableRedirect(disable, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
setFollowRedirect(!disable);
|
||||
success();
|
||||
}
|
||||
|
||||
function setServerTrustMode(mode, success, failure) {
|
||||
helpers.handleMissingCallbacks(success, failure);
|
||||
|
||||
@@ -154,7 +147,7 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
|
||||
case 'post':
|
||||
case 'put':
|
||||
case 'patch':
|
||||
return helpers.processData(options.data, options.serializer, function(data) {
|
||||
return helpers.processData(options.data, options.serializer, function (data) {
|
||||
exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect, options.responseType]);
|
||||
});
|
||||
case 'upload':
|
||||
|
||||
Reference in New Issue
Block a user