From 07e0195c91f7160d685873a4eb22e23ddcb26431 Mon Sep 17 00:00:00 2001 From: Denis Babineau Date: Wed, 7 Oct 2015 13:33:07 -0300 Subject: [PATCH] add HEAD request support and return response headers for all request types both for success and failures. --- .../synconset/CordovaHTTP/CordovaHttp.java | 15 +++++ .../CordovaHTTP/CordovaHttpDownload.java | 1 + .../synconset/CordovaHTTP/CordovaHttpGet.java | 1 + .../CordovaHTTP/CordovaHttpHead.java | 64 +++++++++++++++++++ .../CordovaHTTP/CordovaHttpPlugin.java | 8 +++ .../CordovaHTTP/CordovaHttpPost.java | 1 + .../CordovaHTTP/CordovaHttpUpload.java | 1 + src/ios/CordovaHttpPlugin.m | 55 ++++++++++++++++ www/cordovaHTTP.js | 6 ++ 9 files changed, 152 insertions(+) create mode 100644 src/android/com/synconset/CordovaHTTP/CordovaHttpHead.java diff --git a/src/android/com/synconset/CordovaHTTP/CordovaHttp.java b/src/android/com/synconset/CordovaHTTP/CordovaHttp.java index c5196c9..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; @@ -109,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 ba700e2..cd0a9d5 100644 --- a/src/android/com/synconset/CordovaHTTP/CordovaHttpDownload.java +++ b/src/android/com/synconset/CordovaHTTP/CordovaHttpDownload.java @@ -40,6 +40,7 @@ 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); 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 482f2ea..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); 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/ios/CordovaHttpPlugin.m b/src/ios/CordovaHttpPlugin.m index 5630d2b..97763fe 100644 --- a/src/ios/CordovaHttpPlugin.m +++ b/src/ios/CordovaHttpPlugin.m @@ -91,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]; @@ -116,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]; @@ -156,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]; @@ -236,12 +285,18 @@ id filePlugin = [self.commandDelegate getCommandInstance:@"File"]; 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:[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 4dc08b3..9613f3e 100644 --- a/www/cordovaHTTP.js +++ b/www/cordovaHTTP.js @@ -28,6 +28,9 @@ var http = { 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]); }, @@ -122,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); },