package net.callumtaylor.asynchttp; import android.net.Uri; import net.callumtaylor.asynchttp.obj.ClientTaskImpl; import net.callumtaylor.asynchttp.obj.CountingRequestBody; import net.callumtaylor.asynchttp.obj.Packet; import net.callumtaylor.asynchttp.obj.RequestMode; import net.callumtaylor.asynchttp.response.ResponseHandler; import java.io.BufferedInputStream; import java.io.InputStream; import java.net.SocketTimeoutException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.GZIPInputStream; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import okhttp3.Cache; import okhttp3.Call; import okhttp3.Headers; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; /** * Main request task used for performing http requests. */ public class ClientExecutorTask<F> implements ClientTaskImpl<F> { private static final int BUFFER_SIZE = 1024 * 8; protected ResponseHandler response; protected Uri requestUri; protected Headers requestHeaders; protected RequestBody postData; protected RequestMode requestMode; protected boolean allowRedirect = true; protected boolean allowAllSsl = false; protected long requestTimeout = 0L; protected Cache cache; protected AtomicBoolean cancelled = new AtomicBoolean(false); public ClientExecutorTask(RequestMode mode, Uri request, Headers headers, RequestBody postData, ResponseHandler response, boolean allowRedirect, boolean allowAllSsl, long requestTimeout, Cache cache) { this.response = response; this.requestUri = request; this.requestHeaders = headers; this.postData = postData; this.requestMode = mode; this.requestTimeout = requestTimeout; this.allowAllSsl = allowAllSsl; this.allowRedirect = allowRedirect; this.cache = cache; } @Override public boolean isCancelled() { return cancelled.get(); } @Override public void cancel() { cancelled.set(true); } @Override public void preExecute() { if (this.response != null) { this.response.getConnectionInfo().connectionUrl = requestUri.toString(); this.response.getConnectionInfo().connectionTime = System.currentTimeMillis(); this.response.getConnectionInfo().requestMethod = requestMode; this.response.getConnectionInfo().requestHeaders = requestHeaders; this.response.onSend(); } } @Override public F executeTask() { OkHttpClient httpClient = new OkHttpClient() .newBuilder() .followRedirects(allowRedirect) .followSslRedirects(allowRedirect) .connectTimeout(requestTimeout, TimeUnit.MILLISECONDS) .writeTimeout(requestTimeout, TimeUnit.MILLISECONDS) .readTimeout(requestTimeout, TimeUnit.MILLISECONDS) .cache(cache) .build(); if (allowAllSsl) { try { // Create a trust manager that does not validate certificate chains final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException{} @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException{} @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[]{}; } } }; final SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); httpClient = httpClient.newBuilder() .sslSocketFactory(sslSocketFactory) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }) .build(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } } try { System.setProperty("http.keepAlive", "false"); Request.Builder request = new Request.Builder() .url(requestUri.toString()); if (postData == null) { postData = RequestBody.create(null, new byte[0]); } postData = new CountingRequestBody(postData, new CountingRequestBody.Listener() { @Override public void onRequestProgress(byte[] buffer, long bufferCount, long bytesWritten, long contentLength) { if (response != null) { response.onByteChunkSent(buffer, bufferCount, bytesWritten, contentLength); transferProgress(new Packet(bytesWritten, contentLength, false)); } } }); if (requestMode == RequestMode.GET) { request = request.get(); } else if (requestMode == RequestMode.POST) { request = request.post(postData); } else if (requestMode == RequestMode.PUT) { request = request.put(postData); } else if (requestMode == RequestMode.DELETE) { request = request.delete(postData); } else if (requestMode == RequestMode.HEAD) { request = request.head(); } else if (requestMode == RequestMode.PATCH) { request = request.patch(postData); } else if (requestMode == RequestMode.OPTIONS) { request = request.method("OPTIONS", null); } request.header("Connection", "close"); if (requestHeaders != null) { request.headers(requestHeaders); } if ((requestMode == RequestMode.POST || requestMode == RequestMode.PUT || requestMode == RequestMode.DELETE || requestMode == RequestMode.PATCH) && postData != null) { final long contentLength = postData.contentLength(); if (this.response != null && !isCancelled()) { this.response.getConnectionInfo().connectionLength = contentLength; } } // Get the response Call call = httpClient.newCall(request.build()); Response response = call.execute(); int responseCode = response.code(); if (response.headers() != null && this.response != null) { this.response.getConnectionInfo().responseHeaders = response.headers(); } if (response.body() != null) { String encoding = response.header("Content-Encoding", ""); long contentLength = response.body().contentLength(); InputStream responseStream; InputStream stream = response.body().byteStream(); if ("gzip".equalsIgnoreCase(encoding)) { responseStream = new GZIPInputStream(new BufferedInputStream(stream, BUFFER_SIZE)); } else { responseStream = new BufferedInputStream(stream, BUFFER_SIZE); } if (this.response != null && !isCancelled()) { this.response.getConnectionInfo().responseCode = responseCode; } try { if (this.response != null && !isCancelled()) { this.response.onReceiveStream(responseStream, this, contentLength); this.response.generateContent(); } } catch (SocketTimeoutException timeout) { responseCode = 0; timeout.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { responseStream.close(); } } if (this.response != null && !isCancelled()) { this.response.getConnectionInfo().responseCode = responseCode; } } catch (Exception e) { e.printStackTrace(); } if (this.response != null && !isCancelled()) { this.response.getConnectionInfo().responseTime = System.currentTimeMillis(); this.response.beforeResponse(); if (this.response.getConnectionInfo().responseCode < 400 && this.response.getConnectionInfo().responseCode > 100) { this.response.onSuccess(); } else { this.response.onFailure(); } } if (this.response != null) { return (F)this.response.getContent(); } else { return null; } } @Override public void postExecute() { if (this.response != null && !isCancelled()) { this.response.beforeFinish(); this.response.onFinish(); } } @Override public void transferProgress(Packet packet) { if (this.response != null && !isCancelled()) { if (packet.isDownload) { this.response.onByteChunkReceivedProcessed(packet.length, packet.total); } else { this.response.onByteChunkSentProcessed(packet.length, packet.total); } } } }