From 645b726dd86707d3da707fea0e42a23ba4035e3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20AD=C4=82SC=C4=82LI=C8=9AEI?= Date: Wed, 2 Jul 2014 21:46:05 +0200 Subject: [PATCH 1/3] Added a synchronous "send" method to the HttpRequest interface and to the "HttpUrlonnectionRequest" implementation. Transformed the "ResponseHandler" into an interface so that it becomes easily to write ". [...] .handler(this).send()" which makes the code more readable. --- src/com/kodart/httpzoid/HttpRequest.java | 8 +- .../httpzoid/HttpUrlConnectionRequest.java | 570 ++++++++++-------- .../kodart/httpzoid/NetworkFailureAction.java | 4 +- src/com/kodart/httpzoid/ResponseHandler.java | 12 +- 4 files changed, 331 insertions(+), 263 deletions(-) diff --git a/src/com/kodart/httpzoid/HttpRequest.java b/src/com/kodart/httpzoid/HttpRequest.java index 145c255..c12a7bc 100644 --- a/src/com/kodart/httpzoid/HttpRequest.java +++ b/src/com/kodart/httpzoid/HttpRequest.java @@ -54,5 +54,11 @@ public interface HttpRequest { /** * Execute request in background. */ - public Cancellable send(); + public Cancellable asyncSend(); + + /** + * Execute request on the same thread. + * @return weather or not the request succesfully finished + */ + public boolean send(); } diff --git a/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java b/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java index ef38ec5..c16effe 100644 --- a/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java +++ b/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java @@ -2,7 +2,6 @@ import android.os.AsyncTask; import android.util.Log; -import com.google.gson.JsonParseException; import com.kodart.httpzoid.serializers.HttpSerializer; import java.io.*; @@ -10,262 +9,323 @@ import java.net.*; import java.util.*; - /** * (c) Artur Sharipov */ public class HttpUrlConnectionRequest implements HttpRequest { - private static final String TAG = "Httpzoid"; - private static final int DEFAULT_TIMEOUT = 60000; - private Proxy proxy = Proxy.NO_PROXY; - private int timeout = DEFAULT_TIMEOUT; - private String contentType; - - private ResponseHandler handler = new ResponseHandler(); - - private Map headers = new HashMap(); - private Class type; - private Object data; - - private URL url; - private String method; - private HttpSerializer serializer; - private Network network; - - public HttpUrlConnectionRequest(URL url, String method, HttpSerializer serializer, Network network) { - this.url = url; - this.method = method; - this.serializer = serializer; - this.network = network; - } - - @Override - public HttpRequest data(Object data) { - this.data = data; - return this; - } - - @Override - public HttpRequest header(String key, String value) { - headers.put(key, value); - return this; - } - - @Override - public HttpRequest contentType(String value) { - contentType = value; - return this; - } - - @Override - public HttpRequest handler(ResponseHandler handler) { - this.handler = handler; - type = findType(handler); - return this; - } - - @Override - public HttpRequest timeout(int timeout) { - this.timeout = timeout; - return this; - } - - @Override - public HttpRequest proxy(Proxy proxy) { - this.proxy = proxy; - return this; - } - - @Override - public Cancellable send() { - if (network.isOffline()) { - handler.failure(NetworkError.Offline); - handler.complete(); - return Cancellable.Empty; - } - - return new AsyncTaskCancellable(new AsyncTask() { - @Override - protected Action doInBackground(Void... params) { - HttpURLConnection connection = null; - try { - connection = (HttpURLConnection) url.openConnection(proxy); - init(connection); - sendData(connection); - final HttpDataResponse response = readData(connection); - return new Action() { - @Override - public void call() { - if (response.getCode() < 400) - handler.success(response.getData(), response); - else { - handler.error((String) response.getData(), response); - } - } - }; - - } catch (HttpzoidException e) { - Log.e(TAG, e.getMessage()); - return new NetworkFailureAction(handler, e.getNetworkError()); - } catch (SocketTimeoutException e) { - Log.e(TAG, e.getMessage()); - return new NetworkFailureAction(handler, NetworkError.Timeout); - } catch (ProtocolException e) { - Log.wtf(TAG, e.getMessage()); - return new NetworkFailureAction(handler, NetworkError.UnsupportedMethod); - } catch (Throwable e) { - Log.wtf(TAG, e); - return new NetworkFailureAction(handler, NetworkError.Unknown); - } finally { - if (connection != null) - connection.disconnect(); - } - } - - @Override - protected void onPostExecute(Action action) { - action.call(); - handler.complete(); - } - - }.execute()); - } - - private HttpDataResponse readData(HttpURLConnection connection) throws NetworkAuthenticationException, IOException { - int responseCode = getResponseCode(connection); - if (responseCode >= 500) { - String response = getString(connection.getErrorStream()); - Log.e(TAG, response); - return new HttpDataResponse(response, responseCode, connection.getHeaderFields()); - } - - if (responseCode >= 400) { - return new HttpDataResponse(getString(connection.getErrorStream()), responseCode, connection.getHeaderFields()); - } - - InputStream input = new BufferedInputStream(connection.getInputStream()); - validate(connection); - - if (type.equals(Void.class)) - return new HttpDataResponse(null, responseCode, connection.getHeaderFields()); - - if (type.equals(InputStream.class)) { - ByteArrayOutputStream memory = new ByteArrayOutputStream(); - copyStream(input, memory); - return new HttpDataResponse(new ByteArrayInputStream(memory.toByteArray()), responseCode, connection.getHeaderFields()); - } - - if (type.equals(String.class)) { - return new HttpDataResponse(getString(input), responseCode, connection.getHeaderFields()); - } - - String value = getString(input); - Log.d(TAG, "RECEIVED: " + value); - return new HttpDataResponse(serializer.deserialize(value, type), responseCode, connection.getHeaderFields()); - } - - private int getResponseCode(HttpURLConnection connection) throws IOException { - try { - return connection.getResponseCode(); - } catch (IOException e) { - if (e.getMessage().equals("Received authentication challenge is null")) - return 401; - throw e; - } - } - - private String getString(InputStream input) throws IOException { - if (input == null) - return null; - - StringBuilder builder = new StringBuilder(); - // todo need to find content encoding / getContentEncoding doesn't work - InputStreamReader reader = new InputStreamReader(input, "UTF-8"); - int bytes; - char[] buffer = new char[64 * 1024]; - while ((bytes = reader.read(buffer)) != -1) { - builder.append(buffer, 0, bytes); - } - return builder.toString(); - } - - private void copyStream(InputStream input, OutputStream output) throws IOException { - byte[] buffer = new byte[64 * 1024]; - int bytes; - while ((bytes = input.read(buffer)) != -1) { - output.write(buffer, 0, bytes); - } - } - - private void sendData(HttpURLConnection connection) throws IOException { - if (data == null) - return; - - connection.setDoOutput(true); - OutputStream outputStream = new BufferedOutputStream(connection.getOutputStream()); - try { - if (data instanceof InputStream) { - copyStream((InputStream)data, outputStream); - } - else if (data instanceof String) { - OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); - Log.d(TAG, "SENT: " + data); - writer.write((String)data); - writer.flush(); - } - else { - OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); - String output = serializer.serialize(data); - Log.d(TAG, "SENT: " + output); - writer.write(output); - writer.flush(); - } - } - finally { - outputStream.flush(); - outputStream.close(); - } - } - - private void setContentType(Object data, HttpURLConnection connection) { - if (headers.containsKey("Content-Type")) - return; - if (contentType != null) { - connection.setRequestProperty("Content-Type", contentType); - return; - } - - if (data instanceof InputStream) - connection.setRequestProperty("Content-Type", "application/octet-stream"); - else - connection.setRequestProperty("Content-Type", serializer.getContentType()); - } - - private void init(HttpURLConnection connection) throws ProtocolException { - connection.setRequestMethod(method); - connection.setConnectTimeout(timeout); - connection.setReadTimeout(timeout); - for (Map.Entry entry : headers.entrySet()) { - connection.setRequestProperty(entry.getKey(), entry.getValue()); - } - setContentType(data, connection); - } - - private void validate(HttpURLConnection connection) throws NetworkAuthenticationException { - if (!url.getHost().equals(connection.getURL().getHost())) { - throw new NetworkAuthenticationException(); - } - } - - private Class findType(ResponseHandler handler) { - Method[] methods = handler.getClass().getMethods(); - for(Method method : methods) { - if (method.getName().equals("success")) { - Class param = method.getParameterTypes()[0]; - if (!param.equals(Object.class)) - return param; - } - } - return Object.class; - } + private static final String TAG = "Httpzoid"; + private static final int DEFAULT_TIMEOUT = 60000; + private Proxy proxy = Proxy.NO_PROXY; + private int timeout = DEFAULT_TIMEOUT; + private String contentType; + + private ResponseHandler handler = null; + + private Map headers = new HashMap(); + private Class type; + private Object data; + + private URL url; + private String method; + private HttpSerializer serializer; + private Network network; + + public HttpUrlConnectionRequest(URL url, String method, HttpSerializer serializer, Network network) { + this.url = url; + this.method = method; + this.serializer = serializer; + this.network = network; + } + + @Override + public HttpRequest data(Object data) { + this.data = data; + return this; + } + + @Override + public HttpRequest header(String key, String value) { + headers.put(key, value); + return this; + } + + @Override + public HttpRequest contentType(String value) { + contentType = value; + return this; + } + + @Override + public HttpRequest handler(ResponseHandler handler) { + this.handler = handler; + type = findType(handler); + return this; + } + + @Override + public HttpRequest timeout(int timeout) { + this.timeout = timeout; + return this; + } + + @Override + public HttpRequest proxy(Proxy proxy) { + this.proxy = proxy; + return this; + } + + @Override + public Cancellable asyncSend() { + if (network.isOffline()) { + if (handler != null) { + handler.failure(NetworkError.Offline); + handler.complete(); + } + return Cancellable.Empty; + } + + return new AsyncTaskCancellable(new AsyncTask() { + @Override + protected Action doInBackground(Void... params) { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) url.openConnection(proxy); + init(connection); + sendData(connection); + final HttpDataResponse response = readData(connection); + return new Action() { + @Override + public void call() { + if (handler != null) { + if (response.getCode() < 400) + handler.success(response.getData(), response); + else { + handler.error((String) response.getData(), response); + } + } + } + }; + + } catch (HttpzoidException e) { + Log.e(TAG, e.getMessage()); + return new NetworkFailureAction(handler, e.getNetworkError()); + } catch (SocketTimeoutException e) { + Log.e(TAG, e.getMessage()); + return new NetworkFailureAction(handler, NetworkError.Timeout); + } catch (ProtocolException e) { + Log.wtf(TAG, e.getMessage()); + return new NetworkFailureAction(handler, NetworkError.UnsupportedMethod); + } catch (Throwable e) { + Log.wtf(TAG, e); + return new NetworkFailureAction(handler, NetworkError.Unknown); + } finally { + if (connection != null) + connection.disconnect(); + } + } + + @Override + protected void onPostExecute(Action action) { + action.call(); + if (handler != null) { + handler.complete(); + } + } + + }.execute()); + } + + private HttpDataResponse readData(HttpURLConnection connection) throws NetworkAuthenticationException, IOException { + int responseCode = getResponseCode(connection); + if (responseCode >= 500) { + String response = getString(connection.getErrorStream()); + Log.e(TAG, response); + return new HttpDataResponse(response, responseCode, connection.getHeaderFields()); + } + + if (responseCode >= 400) { + return new HttpDataResponse(getString(connection.getErrorStream()), responseCode, connection.getHeaderFields()); + } + + InputStream input = new BufferedInputStream(connection.getInputStream()); + validate(connection); + + if (type.equals(Void.class)) + return new HttpDataResponse(null, responseCode, connection.getHeaderFields()); + + if (type.equals(InputStream.class)) { + ByteArrayOutputStream memory = new ByteArrayOutputStream(); + copyStream(input, memory); + return new HttpDataResponse(new ByteArrayInputStream(memory.toByteArray()), responseCode, connection.getHeaderFields()); + } + + if (type.equals(String.class)) { + return new HttpDataResponse(getString(input), responseCode, connection.getHeaderFields()); + } + + String value = getString(input); + Log.d(TAG, "RECEIVED: " + value); + return new HttpDataResponse(serializer.deserialize(value, type), responseCode, connection.getHeaderFields()); + } + + private int getResponseCode(HttpURLConnection connection) throws IOException { + try { + return connection.getResponseCode(); + } catch (IOException e) { + if (e.getMessage().equals("Received authentication challenge is null")) + return 401; + throw e; + } + } + + private String getString(InputStream input) throws IOException { + if (input == null) + return null; + + StringBuilder builder = new StringBuilder(); + // todo need to find content encoding / getContentEncoding doesn't work + InputStreamReader reader = new InputStreamReader(input, "UTF-8"); + int bytes; + char[] buffer = new char[64 * 1024]; + while ((bytes = reader.read(buffer)) != -1) { + builder.append(buffer, 0, bytes); + } + return builder.toString(); + } + + private void copyStream(InputStream input, OutputStream output) throws IOException { + byte[] buffer = new byte[64 * 1024]; + int bytes; + while ((bytes = input.read(buffer)) != -1) { + output.write(buffer, 0, bytes); + } + } + + private void sendData(HttpURLConnection connection) throws IOException { + if (data == null) + return; + + connection.setDoOutput(true); + OutputStream outputStream = new BufferedOutputStream(connection.getOutputStream()); + try { + if (data instanceof InputStream) { + copyStream((InputStream) data, outputStream); + } else if (data instanceof String) { + OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); + Log.d(TAG, "SENT: " + data); + writer.write((String) data); + writer.flush(); + } else { + OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); + String output = serializer.serialize(data); + Log.d(TAG, "SENT: " + output); + writer.write(output); + writer.flush(); + } + } finally { + outputStream.flush(); + outputStream.close(); + } + } + + private void setContentType(Object data, HttpURLConnection connection) { + if (headers.containsKey("Content-Type")) + return; + if (contentType != null) { + connection.setRequestProperty("Content-Type", contentType); + return; + } + + if (data instanceof InputStream) + connection.setRequestProperty("Content-Type", "application/octet-stream"); + else + connection.setRequestProperty("Content-Type", serializer.getContentType()); + } + + private void init(HttpURLConnection connection) throws ProtocolException { + connection.setRequestMethod(method); + connection.setConnectTimeout(timeout); + connection.setReadTimeout(timeout); + for (Map.Entry entry : headers.entrySet()) { + connection.setRequestProperty(entry.getKey(), entry.getValue()); + } + setContentType(data, connection); + } + + private void validate(HttpURLConnection connection) throws NetworkAuthenticationException { + if (!url.getHost().equals(connection.getURL().getHost())) { + throw new NetworkAuthenticationException(); + } + } + + private Class findType(ResponseHandler handler) { + if (handler != null) { + Method[] methods = handler.getClass().getMethods(); + for (Method method : methods) { + if (method.getName().equals("success")) { + Class param = method.getParameterTypes()[0]; + if (!param.equals(Object.class)) + return param; + } + } + } + return Object.class; + } + + @Override + public boolean send() { + final boolean hasRequestBeenSuccesful[] = {false}; + + if (network.isOffline()) { + if (handler != null) { + handler.failure(NetworkError.Offline); + handler.complete(); + } + } else { + HttpURLConnection connection = null; + Action resultedAction = null; + + try { + connection = (HttpURLConnection) url.openConnection(proxy); + init(connection); + sendData(connection); + final HttpDataResponse response = readData(connection); + resultedAction = new Action() { + @Override + public void call() { + if (handler != null) { + if (response.getCode() < 400) { + hasRequestBeenSuccesful[0] = true; + handler.success(response.getData(), response); + } else { + handler.error((String) response.getData(), response); + } + } + } + }; + } catch (HttpzoidException e) { + Log.e(TAG, e.getMessage()); + resultedAction = new NetworkFailureAction(handler, e.getNetworkError()); + } catch (SocketTimeoutException e) { + Log.e(TAG, e.getMessage()); + resultedAction = new NetworkFailureAction(handler, NetworkError.Timeout); + } catch (ProtocolException e) { + Log.wtf(TAG, e.getMessage()); + resultedAction = new NetworkFailureAction(handler, NetworkError.UnsupportedMethod); + } catch (Throwable e) { + Log.wtf(TAG, e); + resultedAction = new NetworkFailureAction(handler, NetworkError.Unknown); + } finally { + if (connection != null) + connection.disconnect(); + } + + resultedAction.call(); + if (handler != null) { + handler.complete(); + } + } + + return hasRequestBeenSuccesful[0]; + } } \ No newline at end of file diff --git a/src/com/kodart/httpzoid/NetworkFailureAction.java b/src/com/kodart/httpzoid/NetworkFailureAction.java index 4c99a76..1f552da 100644 --- a/src/com/kodart/httpzoid/NetworkFailureAction.java +++ b/src/com/kodart/httpzoid/NetworkFailureAction.java @@ -14,6 +14,8 @@ public NetworkFailureAction(ResponseHandler handler, NetworkError error) { @Override public void call() { - handler.failure(error); + if (handler != null) { + handler.failure(error); + } } } diff --git a/src/com/kodart/httpzoid/ResponseHandler.java b/src/com/kodart/httpzoid/ResponseHandler.java index d11ecad..0aaa635 100644 --- a/src/com/kodart/httpzoid/ResponseHandler.java +++ b/src/com/kodart/httpzoid/ResponseHandler.java @@ -1,32 +1,32 @@ package com.kodart.httpzoid; /** - * Response callback handler + * Response callback handler interface * (c) Artur Sharipov */ -public class ResponseHandler { +public interface ResponseHandler { /** * Notifies about success * @param data returned data * @param response http response object */ - public void success(T data, HttpResponse response){} + void success(T data, HttpResponse response); /** * Notifies about error (http response code >= 400) * @param message error message * @param response http response object */ - public void error(String message, HttpResponse response){} + void error(String message, HttpResponse response); /** * Notifies about network failure (offline, authentication error, etc.) * @param error */ - public void failure(NetworkError error){} + void failure(NetworkError error); /** * Notifies about request complete (happens after success/error/failure) */ - public void complete(){} + void complete(); } \ No newline at end of file From c74eb1d7e3f4044709daa2370a722659e0648681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20AD=C4=82SC=C4=82=C2=82LI=C8=9AEI?= Date: Wed, 2 Jul 2014 21:46:05 +0200 Subject: [PATCH 2/3] Added a synchronous "send" method to the HttpRequest interface and to the "HttpUrlConnectionRequest" implementation. Transformed the "ResponseHandler" into an interface so that it becomes easily to write ". [...] .handler(this).send()" which makes the code more readable. --- src/com/kodart/httpzoid/HttpRequest.java | 8 +- .../httpzoid/HttpUrlConnectionRequest.java | 570 ++++++++++-------- .../kodart/httpzoid/NetworkFailureAction.java | 4 +- src/com/kodart/httpzoid/ResponseHandler.java | 12 +- 4 files changed, 331 insertions(+), 263 deletions(-) diff --git a/src/com/kodart/httpzoid/HttpRequest.java b/src/com/kodart/httpzoid/HttpRequest.java index 145c255..c12a7bc 100644 --- a/src/com/kodart/httpzoid/HttpRequest.java +++ b/src/com/kodart/httpzoid/HttpRequest.java @@ -54,5 +54,11 @@ public interface HttpRequest { /** * Execute request in background. */ - public Cancellable send(); + public Cancellable asyncSend(); + + /** + * Execute request on the same thread. + * @return weather or not the request succesfully finished + */ + public boolean send(); } diff --git a/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java b/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java index ef38ec5..c16effe 100644 --- a/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java +++ b/src/com/kodart/httpzoid/HttpUrlConnectionRequest.java @@ -2,7 +2,6 @@ import android.os.AsyncTask; import android.util.Log; -import com.google.gson.JsonParseException; import com.kodart.httpzoid.serializers.HttpSerializer; import java.io.*; @@ -10,262 +9,323 @@ import java.net.*; import java.util.*; - /** * (c) Artur Sharipov */ public class HttpUrlConnectionRequest implements HttpRequest { - private static final String TAG = "Httpzoid"; - private static final int DEFAULT_TIMEOUT = 60000; - private Proxy proxy = Proxy.NO_PROXY; - private int timeout = DEFAULT_TIMEOUT; - private String contentType; - - private ResponseHandler handler = new ResponseHandler(); - - private Map headers = new HashMap(); - private Class type; - private Object data; - - private URL url; - private String method; - private HttpSerializer serializer; - private Network network; - - public HttpUrlConnectionRequest(URL url, String method, HttpSerializer serializer, Network network) { - this.url = url; - this.method = method; - this.serializer = serializer; - this.network = network; - } - - @Override - public HttpRequest data(Object data) { - this.data = data; - return this; - } - - @Override - public HttpRequest header(String key, String value) { - headers.put(key, value); - return this; - } - - @Override - public HttpRequest contentType(String value) { - contentType = value; - return this; - } - - @Override - public HttpRequest handler(ResponseHandler handler) { - this.handler = handler; - type = findType(handler); - return this; - } - - @Override - public HttpRequest timeout(int timeout) { - this.timeout = timeout; - return this; - } - - @Override - public HttpRequest proxy(Proxy proxy) { - this.proxy = proxy; - return this; - } - - @Override - public Cancellable send() { - if (network.isOffline()) { - handler.failure(NetworkError.Offline); - handler.complete(); - return Cancellable.Empty; - } - - return new AsyncTaskCancellable(new AsyncTask() { - @Override - protected Action doInBackground(Void... params) { - HttpURLConnection connection = null; - try { - connection = (HttpURLConnection) url.openConnection(proxy); - init(connection); - sendData(connection); - final HttpDataResponse response = readData(connection); - return new Action() { - @Override - public void call() { - if (response.getCode() < 400) - handler.success(response.getData(), response); - else { - handler.error((String) response.getData(), response); - } - } - }; - - } catch (HttpzoidException e) { - Log.e(TAG, e.getMessage()); - return new NetworkFailureAction(handler, e.getNetworkError()); - } catch (SocketTimeoutException e) { - Log.e(TAG, e.getMessage()); - return new NetworkFailureAction(handler, NetworkError.Timeout); - } catch (ProtocolException e) { - Log.wtf(TAG, e.getMessage()); - return new NetworkFailureAction(handler, NetworkError.UnsupportedMethod); - } catch (Throwable e) { - Log.wtf(TAG, e); - return new NetworkFailureAction(handler, NetworkError.Unknown); - } finally { - if (connection != null) - connection.disconnect(); - } - } - - @Override - protected void onPostExecute(Action action) { - action.call(); - handler.complete(); - } - - }.execute()); - } - - private HttpDataResponse readData(HttpURLConnection connection) throws NetworkAuthenticationException, IOException { - int responseCode = getResponseCode(connection); - if (responseCode >= 500) { - String response = getString(connection.getErrorStream()); - Log.e(TAG, response); - return new HttpDataResponse(response, responseCode, connection.getHeaderFields()); - } - - if (responseCode >= 400) { - return new HttpDataResponse(getString(connection.getErrorStream()), responseCode, connection.getHeaderFields()); - } - - InputStream input = new BufferedInputStream(connection.getInputStream()); - validate(connection); - - if (type.equals(Void.class)) - return new HttpDataResponse(null, responseCode, connection.getHeaderFields()); - - if (type.equals(InputStream.class)) { - ByteArrayOutputStream memory = new ByteArrayOutputStream(); - copyStream(input, memory); - return new HttpDataResponse(new ByteArrayInputStream(memory.toByteArray()), responseCode, connection.getHeaderFields()); - } - - if (type.equals(String.class)) { - return new HttpDataResponse(getString(input), responseCode, connection.getHeaderFields()); - } - - String value = getString(input); - Log.d(TAG, "RECEIVED: " + value); - return new HttpDataResponse(serializer.deserialize(value, type), responseCode, connection.getHeaderFields()); - } - - private int getResponseCode(HttpURLConnection connection) throws IOException { - try { - return connection.getResponseCode(); - } catch (IOException e) { - if (e.getMessage().equals("Received authentication challenge is null")) - return 401; - throw e; - } - } - - private String getString(InputStream input) throws IOException { - if (input == null) - return null; - - StringBuilder builder = new StringBuilder(); - // todo need to find content encoding / getContentEncoding doesn't work - InputStreamReader reader = new InputStreamReader(input, "UTF-8"); - int bytes; - char[] buffer = new char[64 * 1024]; - while ((bytes = reader.read(buffer)) != -1) { - builder.append(buffer, 0, bytes); - } - return builder.toString(); - } - - private void copyStream(InputStream input, OutputStream output) throws IOException { - byte[] buffer = new byte[64 * 1024]; - int bytes; - while ((bytes = input.read(buffer)) != -1) { - output.write(buffer, 0, bytes); - } - } - - private void sendData(HttpURLConnection connection) throws IOException { - if (data == null) - return; - - connection.setDoOutput(true); - OutputStream outputStream = new BufferedOutputStream(connection.getOutputStream()); - try { - if (data instanceof InputStream) { - copyStream((InputStream)data, outputStream); - } - else if (data instanceof String) { - OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); - Log.d(TAG, "SENT: " + data); - writer.write((String)data); - writer.flush(); - } - else { - OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); - String output = serializer.serialize(data); - Log.d(TAG, "SENT: " + output); - writer.write(output); - writer.flush(); - } - } - finally { - outputStream.flush(); - outputStream.close(); - } - } - - private void setContentType(Object data, HttpURLConnection connection) { - if (headers.containsKey("Content-Type")) - return; - if (contentType != null) { - connection.setRequestProperty("Content-Type", contentType); - return; - } - - if (data instanceof InputStream) - connection.setRequestProperty("Content-Type", "application/octet-stream"); - else - connection.setRequestProperty("Content-Type", serializer.getContentType()); - } - - private void init(HttpURLConnection connection) throws ProtocolException { - connection.setRequestMethod(method); - connection.setConnectTimeout(timeout); - connection.setReadTimeout(timeout); - for (Map.Entry entry : headers.entrySet()) { - connection.setRequestProperty(entry.getKey(), entry.getValue()); - } - setContentType(data, connection); - } - - private void validate(HttpURLConnection connection) throws NetworkAuthenticationException { - if (!url.getHost().equals(connection.getURL().getHost())) { - throw new NetworkAuthenticationException(); - } - } - - private Class findType(ResponseHandler handler) { - Method[] methods = handler.getClass().getMethods(); - for(Method method : methods) { - if (method.getName().equals("success")) { - Class param = method.getParameterTypes()[0]; - if (!param.equals(Object.class)) - return param; - } - } - return Object.class; - } + private static final String TAG = "Httpzoid"; + private static final int DEFAULT_TIMEOUT = 60000; + private Proxy proxy = Proxy.NO_PROXY; + private int timeout = DEFAULT_TIMEOUT; + private String contentType; + + private ResponseHandler handler = null; + + private Map headers = new HashMap(); + private Class type; + private Object data; + + private URL url; + private String method; + private HttpSerializer serializer; + private Network network; + + public HttpUrlConnectionRequest(URL url, String method, HttpSerializer serializer, Network network) { + this.url = url; + this.method = method; + this.serializer = serializer; + this.network = network; + } + + @Override + public HttpRequest data(Object data) { + this.data = data; + return this; + } + + @Override + public HttpRequest header(String key, String value) { + headers.put(key, value); + return this; + } + + @Override + public HttpRequest contentType(String value) { + contentType = value; + return this; + } + + @Override + public HttpRequest handler(ResponseHandler handler) { + this.handler = handler; + type = findType(handler); + return this; + } + + @Override + public HttpRequest timeout(int timeout) { + this.timeout = timeout; + return this; + } + + @Override + public HttpRequest proxy(Proxy proxy) { + this.proxy = proxy; + return this; + } + + @Override + public Cancellable asyncSend() { + if (network.isOffline()) { + if (handler != null) { + handler.failure(NetworkError.Offline); + handler.complete(); + } + return Cancellable.Empty; + } + + return new AsyncTaskCancellable(new AsyncTask() { + @Override + protected Action doInBackground(Void... params) { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) url.openConnection(proxy); + init(connection); + sendData(connection); + final HttpDataResponse response = readData(connection); + return new Action() { + @Override + public void call() { + if (handler != null) { + if (response.getCode() < 400) + handler.success(response.getData(), response); + else { + handler.error((String) response.getData(), response); + } + } + } + }; + + } catch (HttpzoidException e) { + Log.e(TAG, e.getMessage()); + return new NetworkFailureAction(handler, e.getNetworkError()); + } catch (SocketTimeoutException e) { + Log.e(TAG, e.getMessage()); + return new NetworkFailureAction(handler, NetworkError.Timeout); + } catch (ProtocolException e) { + Log.wtf(TAG, e.getMessage()); + return new NetworkFailureAction(handler, NetworkError.UnsupportedMethod); + } catch (Throwable e) { + Log.wtf(TAG, e); + return new NetworkFailureAction(handler, NetworkError.Unknown); + } finally { + if (connection != null) + connection.disconnect(); + } + } + + @Override + protected void onPostExecute(Action action) { + action.call(); + if (handler != null) { + handler.complete(); + } + } + + }.execute()); + } + + private HttpDataResponse readData(HttpURLConnection connection) throws NetworkAuthenticationException, IOException { + int responseCode = getResponseCode(connection); + if (responseCode >= 500) { + String response = getString(connection.getErrorStream()); + Log.e(TAG, response); + return new HttpDataResponse(response, responseCode, connection.getHeaderFields()); + } + + if (responseCode >= 400) { + return new HttpDataResponse(getString(connection.getErrorStream()), responseCode, connection.getHeaderFields()); + } + + InputStream input = new BufferedInputStream(connection.getInputStream()); + validate(connection); + + if (type.equals(Void.class)) + return new HttpDataResponse(null, responseCode, connection.getHeaderFields()); + + if (type.equals(InputStream.class)) { + ByteArrayOutputStream memory = new ByteArrayOutputStream(); + copyStream(input, memory); + return new HttpDataResponse(new ByteArrayInputStream(memory.toByteArray()), responseCode, connection.getHeaderFields()); + } + + if (type.equals(String.class)) { + return new HttpDataResponse(getString(input), responseCode, connection.getHeaderFields()); + } + + String value = getString(input); + Log.d(TAG, "RECEIVED: " + value); + return new HttpDataResponse(serializer.deserialize(value, type), responseCode, connection.getHeaderFields()); + } + + private int getResponseCode(HttpURLConnection connection) throws IOException { + try { + return connection.getResponseCode(); + } catch (IOException e) { + if (e.getMessage().equals("Received authentication challenge is null")) + return 401; + throw e; + } + } + + private String getString(InputStream input) throws IOException { + if (input == null) + return null; + + StringBuilder builder = new StringBuilder(); + // todo need to find content encoding / getContentEncoding doesn't work + InputStreamReader reader = new InputStreamReader(input, "UTF-8"); + int bytes; + char[] buffer = new char[64 * 1024]; + while ((bytes = reader.read(buffer)) != -1) { + builder.append(buffer, 0, bytes); + } + return builder.toString(); + } + + private void copyStream(InputStream input, OutputStream output) throws IOException { + byte[] buffer = new byte[64 * 1024]; + int bytes; + while ((bytes = input.read(buffer)) != -1) { + output.write(buffer, 0, bytes); + } + } + + private void sendData(HttpURLConnection connection) throws IOException { + if (data == null) + return; + + connection.setDoOutput(true); + OutputStream outputStream = new BufferedOutputStream(connection.getOutputStream()); + try { + if (data instanceof InputStream) { + copyStream((InputStream) data, outputStream); + } else if (data instanceof String) { + OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); + Log.d(TAG, "SENT: " + data); + writer.write((String) data); + writer.flush(); + } else { + OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8"); + String output = serializer.serialize(data); + Log.d(TAG, "SENT: " + output); + writer.write(output); + writer.flush(); + } + } finally { + outputStream.flush(); + outputStream.close(); + } + } + + private void setContentType(Object data, HttpURLConnection connection) { + if (headers.containsKey("Content-Type")) + return; + if (contentType != null) { + connection.setRequestProperty("Content-Type", contentType); + return; + } + + if (data instanceof InputStream) + connection.setRequestProperty("Content-Type", "application/octet-stream"); + else + connection.setRequestProperty("Content-Type", serializer.getContentType()); + } + + private void init(HttpURLConnection connection) throws ProtocolException { + connection.setRequestMethod(method); + connection.setConnectTimeout(timeout); + connection.setReadTimeout(timeout); + for (Map.Entry entry : headers.entrySet()) { + connection.setRequestProperty(entry.getKey(), entry.getValue()); + } + setContentType(data, connection); + } + + private void validate(HttpURLConnection connection) throws NetworkAuthenticationException { + if (!url.getHost().equals(connection.getURL().getHost())) { + throw new NetworkAuthenticationException(); + } + } + + private Class findType(ResponseHandler handler) { + if (handler != null) { + Method[] methods = handler.getClass().getMethods(); + for (Method method : methods) { + if (method.getName().equals("success")) { + Class param = method.getParameterTypes()[0]; + if (!param.equals(Object.class)) + return param; + } + } + } + return Object.class; + } + + @Override + public boolean send() { + final boolean hasRequestBeenSuccesful[] = {false}; + + if (network.isOffline()) { + if (handler != null) { + handler.failure(NetworkError.Offline); + handler.complete(); + } + } else { + HttpURLConnection connection = null; + Action resultedAction = null; + + try { + connection = (HttpURLConnection) url.openConnection(proxy); + init(connection); + sendData(connection); + final HttpDataResponse response = readData(connection); + resultedAction = new Action() { + @Override + public void call() { + if (handler != null) { + if (response.getCode() < 400) { + hasRequestBeenSuccesful[0] = true; + handler.success(response.getData(), response); + } else { + handler.error((String) response.getData(), response); + } + } + } + }; + } catch (HttpzoidException e) { + Log.e(TAG, e.getMessage()); + resultedAction = new NetworkFailureAction(handler, e.getNetworkError()); + } catch (SocketTimeoutException e) { + Log.e(TAG, e.getMessage()); + resultedAction = new NetworkFailureAction(handler, NetworkError.Timeout); + } catch (ProtocolException e) { + Log.wtf(TAG, e.getMessage()); + resultedAction = new NetworkFailureAction(handler, NetworkError.UnsupportedMethod); + } catch (Throwable e) { + Log.wtf(TAG, e); + resultedAction = new NetworkFailureAction(handler, NetworkError.Unknown); + } finally { + if (connection != null) + connection.disconnect(); + } + + resultedAction.call(); + if (handler != null) { + handler.complete(); + } + } + + return hasRequestBeenSuccesful[0]; + } } \ No newline at end of file diff --git a/src/com/kodart/httpzoid/NetworkFailureAction.java b/src/com/kodart/httpzoid/NetworkFailureAction.java index 4c99a76..1f552da 100644 --- a/src/com/kodart/httpzoid/NetworkFailureAction.java +++ b/src/com/kodart/httpzoid/NetworkFailureAction.java @@ -14,6 +14,8 @@ public NetworkFailureAction(ResponseHandler handler, NetworkError error) { @Override public void call() { - handler.failure(error); + if (handler != null) { + handler.failure(error); + } } } diff --git a/src/com/kodart/httpzoid/ResponseHandler.java b/src/com/kodart/httpzoid/ResponseHandler.java index d11ecad..0aaa635 100644 --- a/src/com/kodart/httpzoid/ResponseHandler.java +++ b/src/com/kodart/httpzoid/ResponseHandler.java @@ -1,32 +1,32 @@ package com.kodart.httpzoid; /** - * Response callback handler + * Response callback handler interface * (c) Artur Sharipov */ -public class ResponseHandler { +public interface ResponseHandler { /** * Notifies about success * @param data returned data * @param response http response object */ - public void success(T data, HttpResponse response){} + void success(T data, HttpResponse response); /** * Notifies about error (http response code >= 400) * @param message error message * @param response http response object */ - public void error(String message, HttpResponse response){} + void error(String message, HttpResponse response); /** * Notifies about network failure (offline, authentication error, etc.) * @param error */ - public void failure(NetworkError error){} + void failure(NetworkError error); /** * Notifies about request complete (happens after success/error/failure) */ - public void complete(){} + void complete(); } \ No newline at end of file From 4c6a7bfc16fa4bfd8ea157dc8f1ab71ee9a2fac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20AD=C4=82SC=C4=82LI=C8=9AEI?= Date: Wed, 30 Jul 2014 14:41:27 +0200 Subject: [PATCH 3/3] Applied suggested modifications by Ale. --- src/com/kodart/httpzoid/serializers/HttpSerializer.java | 2 +- src/com/kodart/httpzoid/serializers/JsonHttpSerializer.java | 2 +- src/com/kodart/httpzoid/serializers/XmlHttpSerializer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/com/kodart/httpzoid/serializers/HttpSerializer.java b/src/com/kodart/httpzoid/serializers/HttpSerializer.java index 1041327..ac3c8ba 100644 --- a/src/com/kodart/httpzoid/serializers/HttpSerializer.java +++ b/src/com/kodart/httpzoid/serializers/HttpSerializer.java @@ -6,5 +6,5 @@ public interface HttpSerializer { public String getContentType(); public String serialize(Object object); - public Object deserialize(String value, Class type); + public Object deserialize(String value, Class type); } diff --git a/src/com/kodart/httpzoid/serializers/JsonHttpSerializer.java b/src/com/kodart/httpzoid/serializers/JsonHttpSerializer.java index b8c1464..1c1c2c0 100644 --- a/src/com/kodart/httpzoid/serializers/JsonHttpSerializer.java +++ b/src/com/kodart/httpzoid/serializers/JsonHttpSerializer.java @@ -19,7 +19,7 @@ public String serialize(Object object) { } @Override - public Object deserialize(String value, Class type) { + public Object deserialize(String value, Class type) { return mapper.fromJson(value, type); } } diff --git a/src/com/kodart/httpzoid/serializers/XmlHttpSerializer.java b/src/com/kodart/httpzoid/serializers/XmlHttpSerializer.java index fcfd037..80c2b74 100644 --- a/src/com/kodart/httpzoid/serializers/XmlHttpSerializer.java +++ b/src/com/kodart/httpzoid/serializers/XmlHttpSerializer.java @@ -13,7 +13,7 @@ public String serialize(Object object) { } @Override - public Object deserialize(String value, Class type) { + public Object deserialize(String value, Class type) { return null; //To change body of implemented methods use File | Settings | File Templates. } }