Compare commits

..
36 Commits
Author SHA1 Message Date
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 IlkimenandGitHub 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 IlkimenandGitHub 35dc0205aa Merge pull request #82 from chax/master
Replaced connectionFactory with OkHttp implementation
2018-02-27 12:53:03 +01:00
Raman RasliukandGitHub 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
Johny101andGitHub 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
Johny101andGitHub 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
Roberto Capuano 5d5a859cfc added 'raw' serializer 2017-12-12 16:42:03 +01:00
29 changed files with 1018 additions and 516 deletions
+1 -3
View File
@@ -13,9 +13,7 @@ cache:
- 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
+18
View File
@@ -1,5 +1,23 @@
# Changelog
## 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
+14 -5
View File
@@ -32,11 +32,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,7 +69,7 @@ This sets up all future requests to use Basic HTTP authentication with the given
cordova.plugin.http.useBasicAuth('user', 'password');
```
### setHeader
### 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
@@ -101,11 +107,14 @@ Set the data serializer which will be used for all future PATCH, POST and PUT re
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.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-advanced-http",
"version": "1.9.1",
"version": "1.10.2",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
"testandroid": "./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
+7 -1
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.9.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.10.2">
<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,6 @@
<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.9.1"/>
</platform>
</plugin>
+5
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
+5
View File
@@ -5,6 +5,11 @@ PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
TEMP=$ROOT/temp
if [ -z $SAUCE_USERNAME ] || [ -z $SAUCE_ACCESS_KEY ]; then
echo "Skipping uploading artifact, because Saucelabs credentials are not set.";
exit 0;
fi
if [ $PLATFORM = "android" ]; then
curl -u $SAUCE_USERNAME:$SAUCE_ACCESS_KEY \
-X POST \
@@ -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
*
@@ -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);
}
}
@@ -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) {
@@ -12,6 +12,14 @@ 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;
@@ -19,6 +27,7 @@ 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;
@@ -28,7 +37,7 @@ 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);
@@ -43,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) {
@@ -140,15 +144,12 @@ abstract class CordovaHttp {
return request;
}
protected HttpRequest setupDataSerializer(HttpRequest request) throws JSONException, Exception {
if (new String("json").equals(this.getSerializerName())) {
protected void setupDataSerializer(HttpRequest request) throws JSONException, Exception {
if ("json".equals(this.getSerializerName())) {
request.contentType(request.CONTENT_TYPE_JSON, request.CHARSET_UTF8);
request.send(this.getParamsObject().toString());
} else {
request.form(this.getParamsMap());
} else if ("utf8".equals(this.getSerializerName())) {
request.contentType("text/plain", request.CHARSET_UTF8);
}
return request;
}
protected void respondWithError(int status, String msg) {
@@ -222,31 +223,85 @@ abstract class CordovaHttp {
protected void prepareRequest(HttpRequest request) throws HttpRequestException, JSONException {
this.setupRedirect(request);
this.setupSecurity(request);
request.readTimeout(this.getRequestTimeout());
request.acceptCharset(CHARSET);
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();
String body = request.body(CHARSET);
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", body);
response.put("data", decodeBody(rawOutputReference, request.charset()));
this.getCallbackContext().success(response);
} else {
response.put("error", body);
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");
}
}
@@ -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: " + e.getMessage());
}
this.handleHttpRequestException(e);
} catch (Exception e) {
this.respondWithError(-1, e.getMessage());
this.respondWithError(e.getMessage());
}
}
}
@@ -25,8 +25,9 @@ class CordovaHttpPatch extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.patch(this.getUrlString());
this.prepareRequest(request);
this.setupDataSerializer(request);
this.prepareRequest(request);
this.prepareRequestBody(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
this.handleHttpRequestException(e);
@@ -25,8 +25,9 @@ class CordovaHttpPost extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.post(this.getUrlString());
this.prepareRequest(request);
this.setupDataSerializer(request);
this.prepareRequest(request);
this.prepareRequestBody(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
this.handleHttpRequestException(e);
@@ -25,8 +25,9 @@ class CordovaHttpPut extends CordovaHttp implements Runnable {
try {
HttpRequest request = HttpRequest.put(this.getUrlString());
this.prepareRequest(request);
this.setupDataSerializer(request);
this.prepareRequest(request);
this.prepareRequestBody(request);
this.returnResponseObject(request);
} catch (HttpRequestException e) {
this.handleHttpRequestException(e);
@@ -54,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();
@@ -72,6 +70,8 @@ class CordovaHttpUpload extends CordovaHttp implements Runnable {
return;
}
}
request.part(this.name, filename, mimeType, new File(uri));
this.returnResponseObject(request);
} catch (URISyntaxException e) {
+215 -153
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];
}
@@ -52,7 +55,7 @@
- (void)handleSuccess:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response andData:(id)data {
if (response != nil) {
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
[dictionary setObject:[NSNumber numberWithInt:response.statusCode] forKey:@"status"];
[dictionary setObject:[NSNumber numberWithInt:(int)response.statusCode] forKey:@"status"];
[dictionary setObject:[self copyHeaderFields:response.allHeaderFields] forKey:@"headers"];
}
@@ -64,15 +67,28 @@
- (void)handleError:(NSMutableDictionary*)dictionary withResponse:(NSHTTPURLResponse*)response error:(NSError*)error {
if (response != nil) {
[dictionary setValue:response.URL.absoluteString forKey:@"url"];
[dictionary setObject:[NSNumber numberWithInt:response.statusCode] forKey:@"status"];
[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:
@@ -159,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 {
@@ -190,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 {
@@ -224,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 {
@@ -256,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 {
@@ -286,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 {
@@ -316,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 {
@@ -353,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: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];
}];
}];
}
@catch (NSException *exception) {
[self handleException:exception withCommand:command];
}
}
@@ -400,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
+8
View File
@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
#import "AFURLRequestSerialization.h"
@interface TextRequestSerializer : AFHTTPRequestSerializer
+ (instancetype)serializer;
@end
+53
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
+3 -1
View File
@@ -5,4 +5,6 @@
+ (instancetype)serializer;
@end
FOUNDATION_EXPORT NSString * const AFNetworkingOperationFailingURLResponseBodyKey;
@end
+115 -31
View File
@@ -1,32 +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];
self = [super init];
if (!self) {
return nil;
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 = 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
@@ -35,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
Binary file not shown.
+83 -1
View File
@@ -1,13 +1,17 @@
const hooks = {
onBeforeEachTest: function(done) {
cordova.plugin.http.clearCookies();
cordova.plugin.http.acceptAllCerts(false, done, done);
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); },
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();
@@ -382,6 +386,84 @@ const tests = [
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');
}
}
];
+19 -9
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,32 @@ 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(`${PLUGIN_ID}.messages`, require('../www/messages'));
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) #24', () => {
http.setHeader('*', 'myKey', 'myValue');
http.headers['*'].myKey.should.equal('myValue');
http.getHeaders('*').myKey.should.equal('myValue');
});
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 #24', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -59,7 +69,7 @@ describe('Advanced HTTP www interface', function() {
});
it('resolves host headers correctly (set without port number) #37', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -78,7 +88,7 @@ describe('Advanced HTTP www interface', function() {
});
it('resolves host headers correctly (set with port number) #37', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -97,7 +107,7 @@ describe('Advanced HTTP www interface', function() {
});
it('resolves request headers correctly', () => {
mock(`${PLUGIN_ID}.cookie-handler`, {
mock(`${HELPERS_ID}.cookie-handler`, {
getCookieString: () => 'fakeCookieString'
});
@@ -116,7 +126,7 @@ describe('Advanced HTTP www interface', function() {
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', () => {
+90 -214
View File
@@ -20,166 +20,36 @@
* 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
*
* Modified by Sefa Ilkimen
*/
/*
* An HTTP Plugin for 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');
var messages = require(pluginId + '.messages');
var MANDATORY_SUCCESS = 'advanced-http: missing mandatory "onSuccess" callback function';
var MANDATORY_FAIL = 'advanced-http: missing mandatory "onFail" callback function';
var ADDING_COOKIES_NOT_SUPPORTED = 'advanced-http: "setHeader" does not support adding cookies, please use "setCookie" function instead';
var HEADER_VALUE_MUST_BE_STRING = 'advanced-http: header values must be strings';
var internals = {
headers: {},
dataSerializer: 'urlencoded',
timeoutInSeconds: 60.0,
};
// 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 checkHeaders(headers) {
var keys = Object.keys(headers);
var key;
for (var i = 0; i < keys.length; i++) {
key = keys[i];
if (typeof headers[key] !== 'string') {
return false;
}
}
return true;
}
function onInvalidHeader(handler) {
handler({
status: 0,
error: HEADER_VALUE_MUST_BE_STRING,
headers: {}
});
}
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(MANDATORY_SUCCESS);
}
if (Object.prototype.toString.call(failFn) !== '[object Function]') {
throw new Error(MANDATORY_FAIL);
}
}
var http = {
headers: {},
dataSerializer: 'urlencoded',
sslPinning: false,
timeoutInSeconds: 60.0,
var publicInterface = {
getBasicAuthHeader: function (username, password) {
return {'Authorization': 'Basic ' + b64EncodeUnicode(username + ':' + password)};
return {'Authorization': 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password)};
},
useBasicAuth: function (username, password) {
this.setHeader('*', 'Authorization', 'Basic ' + b64EncodeUnicode(username + ':' + password));
this.setHeader('*', 'Authorization', 'Basic ' + helpers.b64EncodeUnicode(username + ':' + password));
},
getHeaders: function (host) {
return internals.headers[host || '*'] || null;
},
setHeader: function () {
// this one is for being backward compatible
@@ -194,18 +64,21 @@ var http = {
}
if (header.toLowerCase() === 'cookie') {
throw new Error(ADDING_COOKIES_NOT_SUPPORTED);
throw new Error(messages.ADDING_COOKIES_NOT_SUPPORTED);
}
if (typeof value !== 'string') {
throw new Error(HEADER_VALUE_MUST_BE_STRING);
if (helpers.getTypeOf(value) !== 'String') {
throw new Error(messages.HEADER_VALUE_MUST_BE_STRING);
}
this.headers[host] = this.headers[host] || {};
this.headers[host][header] = value;
internals.headers[host] = internals.headers[host] || {};
internals.headers[host][header] = value;
},
getDataSerializer: function () {
return internals.dataSerializer;
},
setDataSerializer: function (serializer) {
this.dataSerializer = checkSerializer(serializer);
internals.dataSerializer = helpers.checkSerializer(serializer);
},
setCookie: function (url, cookie, options) {
cookieHandler.setCookie(url, cookie, options);
@@ -219,8 +92,11 @@ var http = {
getCookieString: function (url) {
return cookieHandler.getCookieString(url);
},
getRequestTimeout: function () {
return internals.timeoutInSeconds;
},
setRequestTimeout: function (timeout) {
this.timeoutInSeconds = timeout;
internals.timeoutInSeconds = timeout;
},
enableSSLPinning: function (enable, success, failure) {
return exec(success, failure, 'CordovaHttpPlugin', 'enableSSLPinning', [enable]);
@@ -232,131 +108,131 @@ var http = {
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');
failure(messages.DEPRECATED_VDN);
},
post: function (url, data, headers, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
data = data || {};
headers = getMergedHeaders(url, headers, this.headers);
data = helpers.getProcessedData(data, internals.dataSerializer);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'post', [url, data, this.dataSerializer, headers, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'post', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]);
},
get: function (url, params, headers, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'get', [url, params, headers, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'get', [url, params, headers, internals.timeoutInSeconds]);
},
put: function (url, data, headers, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
data = data || {};
headers = getMergedHeaders(url, headers, this.headers);
data = helpers.getProcessedData(data, internals.dataSerializer);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'put', [url, data, this.dataSerializer, headers, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'put', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]);
},
patch: function (url, data, headers, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
data = data || {};
headers = getMergedHeaders(url, headers, this.headers);
data = helpers.getProcessedData(data, internals.dataSerializer);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'patch', [url, data, this.dataSerializer, headers, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'patch', [url, data, internals.dataSerializer, headers, internals.timeoutInSeconds]);
},
delete: function (url, params, headers, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'delete', [url, params, headers, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'delete', [url, params, headers, internals.timeoutInSeconds]);
},
head: function (url, params, headers, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'head', [url, params, headers, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'head', [url, params, headers, internals.timeoutInSeconds]);
},
uploadFile: function (url, params, headers, filePath, name, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, success);
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, success);
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, params, headers, filePath, name, internals.timeoutInSeconds]);
},
downloadFile: function (url, params, headers, filePath, success, failure) {
handleMissingCallbacks(success, failure);
helpers.handleMissingCallbacks(success, failure);
params = params || {};
headers = getMergedHeaders(url, headers, this.headers);
headers = helpers.getMergedHeaders(url, headers, internals.headers);
if (!checkHeaders(headers)) {
return onInvalidHeader(failure);
if (!helpers.checkHeaders(headers)) {
return helpers.onInvalidHeader(failure);
}
var onSuccess = injectCookieHandler(url, injectFileEntryHandler(success));
var onFail = injectCookieHandler(url, failure);
var onSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
var onFail = helpers.injectCookieHandler(url, failure);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, params, headers, filePath, this.timeoutInSeconds]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, params, headers, filePath, internals.timeoutInSeconds]);
}
};
angularIntegration.registerService(http);
module.exports = http;
angularIntegration.registerService(publicInterface);
module.exports = publicInterface;
+1 -1
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');
+196
View File
@@ -0,0 +1,196 @@
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' ];
module.exports = {
b64EncodeUnicode: b64EncodeUnicode,
getTypeOf: getTypeOf,
checkHeaders: checkHeaders,
onInvalidHeader: onInvalidHeader,
checkSerializer: checkSerializer,
injectCookieHandler: injectCookieHandler,
injectFileEntryHandler: injectFileEntryHandler,
getMergedHeaders: getMergedHeaders,
getProcessedData: getProcessedData,
handleMissingCallbacks: handleMissingCallbacks
};
// 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 checkHeaders(headers) {
var keys = Object.keys(headers);
var key;
for (var i = 0; i < keys.length; i++) {
key = keys[i];
if (getTypeOf(headers[key]) !== 'String') {
return false;
}
}
return true;
}
function onInvalidHeader(handler) {
handler({
status: 0,
error: messages.HEADER_VALUE_MUST_BE_STRING,
headers: {}
});
}
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;
}
// 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);
}
}
+1 -1
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');
+8
View File
@@ -0,0 +1,8 @@
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:',
DEPRECATED_VDN: 'advanced-http: "validateDomainName" is no more supported, please see change log for further info',
HEADER_VALUE_MUST_BE_STRING: 'advanced-http: header values must be strings',
MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function'
};