diff --git a/plugin.xml b/plugin.xml index ef01aca..481df5d 100644 --- a/plugin.xml +++ b/plugin.xml @@ -14,7 +14,7 @@ - + @@ -78,12 +78,17 @@ + + + + + - \ No newline at end of file + diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttp.java b/src/android/com/synconset/CordovaHTTP/CordovaHttp.java index e90ad6f..e6c7626 100644 --- a/src/android/com/synconset/CordovaHTTP/CordovaHttp.java +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttp.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -35,7 +37,8 @@ public abstract class CordovaHttp { private static AtomicBoolean sslPinning = new AtomicBoolean(false); private static AtomicBoolean acceptAllCerts = new AtomicBoolean(false); - + private static AtomicBoolean acceptAllHosts = new AtomicBoolean(false); + private String urlString; private Map params; private Map headers; @@ -61,7 +64,11 @@ public abstract class CordovaHttp { sslPinning.set(false); } } - + + public static void acceptAllHosts(boolean accept) { + acceptAllHosts.set(accept); + } + protected String getUrlString() { return this.urlString; } @@ -81,10 +88,11 @@ public abstract class CordovaHttp { protected HttpRequest setupSecurity(HttpRequest request) { if (acceptAllCerts.get()) { request.trustAllCerts(); - request.trustAllHosts(); + request.trustAllHosts(true); } if (sslPinning.get()) { request.pinToCerts(); + request.trustAllHosts(acceptAllHosts.get()); } return request; } @@ -103,4 +111,17 @@ public abstract class CordovaHttp { protected void respondWithError(String msg) { this.respondWithError(500, msg); } + + protected void addResponseHeaders(HttpRequest request, JSONObject response) throws JSONException { + Map> headers = request.headers(); + Map parsed_headers = new HashMap(); + for (Map.Entry> entry : headers.entrySet()) { + String key = entry.getKey(); + List value = entry.getValue(); + if ((key != null) && (!value.isEmpty())) { + parsed_headers.put(key, value.get(0)); + } + } + response.put("headers", new JSONObject(parsed_headers)); + } } diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttpDownload.java b/src/android/com/synconset/CordovaHTTP/CordovaHttpDownload.java index e046186..cd0a9d5 100644 --- a/src/android/com/synconset/CordovaHTTP/CordovaHttpDownload.java +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttpDownload.java @@ -40,12 +40,13 @@ public class CordovaHttpDownload extends CordovaHttp implements Runnable { int code = request.code(); JSONObject response = new JSONObject(); + this.addResponseHeaders(request, response); response.put("status", code); if (code >= 200 && code < 300) { URI uri = new URI(filePath); File file = new File(uri); request.receive(file); - JSONObject fileEntry = FileUtils.getEntry(file); + JSONObject fileEntry = FileUtils.getFilePlugin().getEntryForFile(file); response.put("file", fileEntry); this.getCallbackContext().success(response); } else { diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttpGet.java b/src/android/com/synconset/CordovaHTTP/CordovaHttpGet.java index 490597b..4c3ed73 100644 --- a/src/android/com/synconset/CordovaHTTP/CordovaHttpGet.java +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttpGet.java @@ -40,6 +40,7 @@ public class CordovaHttpGet extends CordovaHttp implements Runnable { 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); diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttpHead.java b/src/android/com/synconset/CordovaHTTP/CordovaHttpHead.java new file mode 100644 index 0000000..9be50b7 --- /dev/null +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttpHead.java @@ -0,0 +1,64 @@ +/** + * A HTTP plugin for Cordova / Phonegap + */ +package com.synconset; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.HostnameVerifier; + +import javax.net.ssl.SSLHandshakeException; + +import org.apache.cordova.CallbackContext; +import org.json.JSONException; +import org.json.JSONObject; + +import android.util.Log; + +import com.github.kevinsawicki.http.HttpRequest; +import com.github.kevinsawicki.http.HttpRequest.HttpRequestException; + +public class CordovaHttpHead extends CordovaHttp implements Runnable { + public CordovaHttpHead(String urlString, Map params, Map headers, CallbackContext callbackContext) { + super(urlString, params, headers, callbackContext); + } + + @Override + public void run() { + try { + HttpRequest request = HttpRequest.head(this.getUrlString(), this.getParams(), true); + this.setupSecurity(request); + request.acceptCharset(CHARSET); + request.headers(this.getHeaders()); + int code = request.code(); + JSONObject response = new JSONObject(); + this.addResponseHeaders(request, response); + response.put("status", code); + if (code >= 200 && code < 300) { + // no 'body' to return for HEAD request + this.getCallbackContext().success(response); + } else { + String body = request.body(CHARSET); + response.put("error", body); + this.getCallbackContext().error(response); + } + } catch (JSONException e) { + this.respondWithError("There was an error generating the response"); + } catch (HttpRequestException e) { + if (e.getCause() instanceof UnknownHostException) { + this.respondWithError(0, "The host could not be resolved"); + } else if (e.getCause() instanceof SSLHandshakeException) { + this.respondWithError("SSL handshake failed"); + } else { + this.respondWithError("There was an error with the request"); + } + } + } +} \ No newline at end of file diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttpPlugin.java b/src/android/com/synconset/CordovaHTTP/CordovaHttpPlugin.java index b2cf398..bdb76c6 100644 --- a/src/android/com/synconset/CordovaHTTP/CordovaHttpPlugin.java +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttpPlugin.java @@ -58,6 +58,14 @@ public class CordovaHttpPlugin extends CordovaPlugin { HashMap headersMap = this.addToMap(this.globalHeaders, headers); CordovaHttpGet get = new CordovaHttpGet(urlString, paramsMap, headersMap, callbackContext); cordova.getThreadPool().execute(get); + } else if (action.equals("head")) { + String urlString = args.getString(0); + JSONObject params = args.getJSONObject(1); + JSONObject headers = args.getJSONObject(2); + HashMap paramsMap = this.getMapFromJSONObject(params); + HashMap headersMap = this.addToMap(this.globalHeaders, headers); + CordovaHttpHead head = new CordovaHttpHead(urlString, paramsMap, headersMap, callbackContext); + cordova.getThreadPool().execute(head); } else if (action.equals("post")) { String urlString = args.getString(0); JSONObject params = args.getJSONObject(1); @@ -83,6 +91,11 @@ public class CordovaHttpPlugin extends CordovaPlugin { } else if (action.equals("acceptAllCerts")) { boolean accept = args.getBoolean(0); CordovaHttp.acceptAllCerts(accept); + callbackContext.success(); + } else if (action.equals("acceptAllHosts")) { + boolean accept = args.getBoolean(0); + CordovaHttp.acceptAllHosts(accept); + callbackContext.success(); } else if (action.equals("setHeader")) { String header = args.getString(0); String value = args.getString(1); diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttpPost.java b/src/android/com/synconset/CordovaHTTP/CordovaHttpPost.java index 7e31b75..2b8e227 100644 --- a/src/android/com/synconset/CordovaHTTP/CordovaHttpPost.java +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttpPost.java @@ -33,6 +33,7 @@ public class CordovaHttpPost extends CordovaHttp implements Runnable { 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); diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttpUpload.java b/src/android/com/synconset/CordovaHTTP/CordovaHttpUpload.java index 086e7ac..2e89558 100644 --- a/src/android/com/synconset/CordovaHTTP/CordovaHttpUpload.java +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttpUpload.java @@ -70,6 +70,7 @@ public class CordovaHttpUpload extends CordovaHttp implements Runnable { 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); diff --git a/src/android/com/synconset/CordovaHTTP/HttpRequest.java b/src/android/com/synconset/CordovaHTTP/HttpRequest.java index a6e39a5..42cf568 100644 --- a/src/android/com/synconset/CordovaHTTP/HttpRequest.java +++ b/src/android/com/synconset/CordovaHTTP/HttpRequest.java @@ -3252,11 +3252,17 @@ public class HttpRequest { * * @return this request */ - public HttpRequest trustAllHosts() { + public HttpRequest trustAllHosts(boolean enable) { final HttpURLConnection connection = getConnection(); - if (connection instanceof HttpsURLConnection) - ((HttpsURLConnection) connection) - .setHostnameVerifier(getTrustedVerifier()); + if (connection instanceof HttpsURLConnection) { + if (enable) { + ((HttpsURLConnection) connection) + .setHostnameVerifier(getTrustedVerifier()); + } else { + ((HttpsURLConnection) connection) + .setHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()); + } + } return this; } diff --git a/src/ios/CordovaHttpPlugin.h b/src/ios/CordovaHttpPlugin.h index 445bc8a..af5aa1f 100644 --- a/src/ios/CordovaHttpPlugin.h +++ b/src/ios/CordovaHttpPlugin.h @@ -1,7 +1,6 @@ #import #import -#import @interface CordovaHttpPlugin : CDVPlugin @@ -9,6 +8,7 @@ - (void)setHeader:(CDVInvokedUrlCommand*)command; - (void)enableSSLPinning:(CDVInvokedUrlCommand*)command; - (void)acceptAllCerts:(CDVInvokedUrlCommand*)command; +- (void)acceptAllHosts:(CDVInvokedUrlCommand*)command; - (void)post:(CDVInvokedUrlCommand*)command; - (void)get:(CDVInvokedUrlCommand*)command; - (void)uploadFile:(CDVInvokedUrlCommand*)command; diff --git a/src/ios/CordovaHttpPlugin.m b/src/ios/CordovaHttpPlugin.m index 54de660..97763fe 100644 --- a/src/ios/CordovaHttpPlugin.m +++ b/src/ios/CordovaHttpPlugin.m @@ -71,6 +71,14 @@ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } +- (void)acceptAllHosts:(CDVInvokedUrlCommand*)command { + CDVPluginResult* pluginResult = nil; + bool allow = [[command.arguments objectAtIndex:0] boolValue]; + [HttpManager sharedClient].securityPolicy.validatesDomainName = !allow; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + - (void)post:(CDVInvokedUrlCommand*)command { HttpManager *manager = [HttpManager sharedClient]; NSString *url = [command.arguments objectAtIndex:0]; @@ -83,12 +91,18 @@ [manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } [dictionary setObject:responseObject forKey:@"data"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } [dictionary setObject:[error localizedDescription] forKey:@"error"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; @@ -108,18 +122,55 @@ [manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } [dictionary setObject:responseObject forKey:@"data"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } [dictionary setObject:[error localizedDescription] forKey:@"error"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } +- (void)head:(CDVInvokedUrlCommand*)command { + HttpManager *manager = [HttpManager sharedClient]; + NSString *url = [command.arguments objectAtIndex:0]; + NSDictionary *parameters = [command.arguments objectAtIndex:1]; + NSDictionary *headers = [command.arguments objectAtIndex:2]; + [self setRequestHeaders: headers]; + + CordovaHttpPlugin* __weak weakSelf = self; + + manager.responseSerializer = [AFHTTPResponseSerializer serializer]; + [manager HEAD:url parameters:parameters success:^(AFHTTPRequestOperation *operation) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } + // no 'body' for HEAD request, omitting 'data' + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } + [dictionary setObject:[error localizedDescription] forKey:@"error"]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; + [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + - (void)uploadFile:(CDVInvokedUrlCommand*)command { HttpManager *manager = [HttpManager sharedClient]; NSString *url = [command.arguments objectAtIndex:0]; @@ -148,11 +199,17 @@ } success:^(AFHTTPRequestOperation *operation, id responseObject) { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } [dictionary setObject:[error localizedDescription] forKey:@"error"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; @@ -169,6 +226,10 @@ [self setRequestHeaders: headers]; + if ([filePath hasPrefix:@"file://"]) { + filePath = [filePath substringFromIndex:7]; + } + CordovaHttpPlugin* __weak weakSelf = self; manager.responseSerializer = [AFHTTPResponseSerializer serializer]; [manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { @@ -221,15 +282,21 @@ return; } - CDVFile *file = [[CDVFile alloc] init]; + id filePlugin = [self.commandDelegate getCommandInstance:@"File"]; NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; - [dictionary setObject:[file getDirectoryEntry:filePath isDirectory:NO] forKey:@"file"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } + [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:^(AFHTTPRequestOperation *operation, NSError *error) { NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; [dictionary setObject:[NSNumber numberWithInt:operation.response.statusCode] forKey:@"status"]; + if (operation.response != nil) { + [dictionary setObject:operation.response.allHeaderFields forKey:@"headers"]; + } [dictionary setObject:[error localizedDescription] forKey:@"error"]; CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary]; [weakSelf.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; diff --git a/www/cordovaHTTP.js b/www/cordovaHTTP.js index a879e20..9613f3e 100644 --- a/www/cordovaHTTP.js +++ b/www/cordovaHTTP.js @@ -19,12 +19,18 @@ var http = { acceptAllCerts: function(allow, success, failure) { return exec(success, failure, "CordovaHttpPlugin", "acceptAllCerts", [allow]); }, + acceptAllHosts: function(allow, success, failure) { + return exec(success, failure, "CordovaHttpPlugin", "acceptAllHosts", [allow]); + }, post: function(url, params, headers, success, failure) { return exec(success, failure, "CordovaHttpPlugin", "post", [url, params, headers]); }, get: function(url, params, headers, success, failure) { return exec(success, failure, "CordovaHttpPlugin", "get", [url, params, headers]); }, + head: function(url, params, headers, success, failure) { + return exec(success, failure, "CordovaHttpPlugin", "head", [url, params, headers]); + }, uploadFile: function(url, params, headers, filePath, name, success, failure) { return exec(success, failure, "CordovaHttpPlugin", "uploadFile", [url, params, headers, filePath, name]); }, @@ -52,11 +58,13 @@ var http = { * */ var win = function(result) { - var entry = new (require('org.apache.cordova.file.FileEntry'))(); + var entry = new (require('cordova-plugin-file.FileEntry'))(); entry.isDirectory = false; entry.isFile = true; entry.name = result.file.name; entry.fullPath = result.file.fullPath; + entry.filesystem = new FileSystem(result.file.filesystemName || (result.file.filesystem == window.PERSISTENT ? 'persistent' : 'temporary')); + entry.nativeURL = result.file.nativeURL; success(entry); }; return exec(win, failure, "CordovaHttpPlugin", "downloadFile", [url, params, headers, filePath]); @@ -117,6 +125,9 @@ if (typeof angular !== "undefined") { get: function(url, params, headers) { return makePromise(http.get, [url, params, headers], true); }, + head: function(url, params, headers) { + return makePromise(http.head, [url, params, headers], true); + }, uploadFile: function(url, params, headers, filePath, name) { return makePromise(http.uploadFile, [url, params, headers, filePath, name], true); },