From 4cf51013e69e124302b1118b2dd99fd060a0a236 Mon Sep 17 00:00:00 2001 From: Luis Uguina Date: Mon, 1 Jun 2020 19:15:44 +1000 Subject: [PATCH] Replace standard Java HTTP libraries with okHTTP (#237) * Replace standard Java HTTP libraries with okHTTP --- .idea/compiler.xml | 2 +- .idea/misc.xml | 2 +- build.gradle | 2 + src/com/sheepit/client/Client.java | 2 + src/com/sheepit/client/Server.java | 535 ++++++++++++----------------- 5 files changed, 230 insertions(+), 313 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index a1757ae..95a88ae 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -5,4 +5,4 @@ - \ No newline at end of file + diff --git a/.idea/misc.xml b/.idea/misc.xml index bc8d0a3..e860487 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,4 +4,4 @@ - \ No newline at end of file + diff --git a/build.gradle b/build.gradle index c0f700e..8e2f5c4 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,8 @@ dependencies { compile 'org.simpleframework:simple-xml:2.7.1' compile 'javax.xml.bind:jaxb-api:2.3.1' implementation 'com.formdev:flatlaf:0.30' + implementation 'com.squareup.okhttp3:okhttp:4.7.2' + implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.7.2' } jar { diff --git a/src/com/sheepit/client/Client.java b/src/com/sheepit/client/Client.java index b66b5eb..c9812ae 100644 --- a/src/com/sheepit/client/Client.java +++ b/src/com/sheepit/client/Client.java @@ -685,6 +685,8 @@ import lombok.Data; // must download the archive int ret = this.server.HTTPGetFile(url, local_path, this.gui, update_ui); + + // Try to check the download file even if a download error has occurred (MD5 file check will delete the file if partially downloaded) boolean md5_check = this.checkFile(ajob, local_path, md5_server); int attempts = 1; diff --git a/src/com/sheepit/client/Server.java b/src/com/sheepit/client/Server.java index 53ce7a7..8c9a8fe 100644 --- a/src/com/sheepit/client/Server.java +++ b/src/com/sheepit/client/Server.java @@ -19,45 +19,47 @@ package com.sheepit.client; -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; +import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; -import java.net.ConnectException; -import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.NoRouteToHostException; -import java.net.URLDecoder; -import java.net.UnknownHostException; -import java.net.URL; -import java.net.URLEncoder; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; +import java.net.*; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; 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 java.security.cert.CertificateException; +import lombok.Getter; +import org.simpleframework.xml.core.Persister; + +import okhttp3.Call; +import okhttp3.FormBody; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.JavaNetCookieJar; + +import com.sheepit.client.Configuration.ComputeType; +import com.sheepit.client.Error.ServerCode; import com.sheepit.client.datamodel.CacheFileMD5; import com.sheepit.client.datamodel.FileMD5; import com.sheepit.client.datamodel.HeartBeatInfos; @@ -65,11 +67,6 @@ import com.sheepit.client.datamodel.JobInfos; import com.sheepit.client.datamodel.JobValidation; import com.sheepit.client.datamodel.RequestEndPoint; import com.sheepit.client.datamodel.ServerConfig; -import lombok.Getter; -import org.simpleframework.xml.core.Persister; - -import com.sheepit.client.Configuration.ComputeType; -import com.sheepit.client.Error.ServerCode; import com.sheepit.client.exception.FermeException; import com.sheepit.client.exception.FermeExceptionBadResponseFromServer; import com.sheepit.client.exception.FermeExceptionNoRendererAvailable; @@ -82,8 +79,10 @@ import com.sheepit.client.exception.FermeExceptionSessionDisabled; import com.sheepit.client.exception.FermeServerDown; import com.sheepit.client.os.OS; -public class Server extends Thread implements HostnameVerifier, X509TrustManager { + +public class Server extends Thread { private String base_url; + private final OkHttpClient httpClient; @Getter private ServerConfig serverConfig; @@ -102,8 +101,10 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager this.lastRequestTime = 0; this.keepmealive_duration = 15 * 60 * 1000; // default 15min - CookieManager cookies = new CookieManager(); - CookieHandler.setDefault(cookies); + // OkHttp performs best when we create a single OkHttpClient instance and reuse it for all of the HTTP calls. This is because each client holds its own + // connection pool and thread pools.Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request + // wastes resources on idle pools. + this.httpClient = getOkHttpClient(); } public void run() { @@ -115,22 +116,28 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager long current_time = new Date().getTime(); if ((current_time - this.lastRequestTime) > this.keepmealive_duration) { try { - String args = ""; + HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("keepmealive"))).newBuilder(); + if (this.client != null && this.client.getRenderingJob() != null) { - args = "?frame=" + this.client.getRenderingJob().getFrameNumber() + "&job=" + this.client.getRenderingJob().getId(); - if (this.client.getRenderingJob().getExtras() != null && this.client.getRenderingJob().getExtras().isEmpty() == false) { - args += "&extras=" + this.client.getRenderingJob().getExtras(); + Job job = this.client.getRenderingJob(); + + urlBuilder.addQueryParameter("frame", job.getFrameNumber()).addQueryParameter("job", job.getId()); + if (job.getExtras() != null && !job.getExtras().isEmpty()) { + urlBuilder.addQueryParameter("extras", job.getExtras()); } - if (this.client.getRenderingJob().getProcessRender() != null) { - args += "&rendertime=" + this.client.getRenderingJob().getProcessRender().getDuration(); - args += "&remainingtime=" + this.client.getRenderingJob().getProcessRender().getRemainingDuration(); + + RenderProcess process = job.getProcessRender(); + if (process != null) { + urlBuilder.addQueryParameter("rendertime", String.valueOf(process.getDuration())) + .addQueryParameter("remainingtime", String.valueOf(process.getRemainingDuration())); } } - HttpURLConnection connection = this.HTTPRequest(this.getPage("keepmealive") + args); + Response response = this.HTTPRequest(urlBuilder); - if (connection.getResponseCode() == HttpURLConnection.HTTP_OK && connection.getContentType().startsWith("text/xml")) { - DataInputStream in = new DataInputStream(connection.getInputStream()); + if (response.code() == HttpURLConnection.HTTP_OK && response.body().contentType().toString().startsWith("text/xml")) { + String in = response.body().string(); + try { HeartBeatInfos heartBeartInfos = new Persister().read(HeartBeatInfos.class, in); ServerCode serverCode = ServerCode.fromInt(heartBeartInfos.getStatus()); @@ -181,27 +188,33 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager public Error.Type getConfiguration() { OS os = OS.getOS(); - HttpURLConnection connection = null; String publickey = null; try { - String url_remote = this.base_url + "/server/config.php"; - String parameters = String - .format("login=%s&password=%s&cpu_family=%s&cpu_model=%s&cpu_model_name=%s&cpu_cores=%s&os=%s&ram=%s&bits=%s&version=%s&hostname=%s&ui=%s&extras=%s", - URLEncoder.encode(this.user_config.getLogin(), "UTF-8"), URLEncoder.encode(this.user_config.getPassword(), "UTF-8"), - URLEncoder.encode(os.getCPU().family(), "UTF-8"), URLEncoder.encode(os.getCPU().model(), "UTF-8"), - URLEncoder.encode(os.getCPU().name(), "UTF-8"), - ((this.user_config.getNbCores() == -1) ? os.getCPU().cores() : this.user_config.getNbCores()), - URLEncoder.encode(os.name(), "UTF-8"), os.getMemory(), URLEncoder.encode(os.getCPU().arch(), "UTF-8"), - this.user_config.getJarVersion(), URLEncoder.encode(this.user_config.getHostname(), "UTF-8"), - this.client.getGui().getClass().getSimpleName(), this.user_config.getExtras()); - this.log.debug("Server::getConfiguration url " + url_remote); + HttpUrl.Builder remoteURL = Objects.requireNonNull(HttpUrl.parse(this.base_url + "/server/config.php")).newBuilder(); + FormBody formBody = new FormBody.Builder() + .add("login", user_config.getLogin()) + .add("password", user_config.getPassword()) + .add("cpu_family", os.getCPU().family()) + .add("cpu_model", os.getCPU().model()) + .add("cpu_model_name", os.getCPU().name()) + .add("cpu_cores", String.valueOf(user_config.getNbCores() == -1 ? os.getCPU().cores() : user_config.getNbCores())) + .add("os", os.name()) + .add("ram", String.valueOf(os.getMemory())) + .add("bits", os.getCPU().arch()) + .add("version", user_config.getJarVersion()) + .add("hostname", user_config.getHostname()) + .add("ui", client.getGui().getClass().getSimpleName()) + .add("extras", user_config.getExtras()) + .build(); - connection = this.HTTPRequest(url_remote, parameters); - int r = connection.getResponseCode(); - String contentType = connection.getContentType(); + this.log.debug("Server::getConfiguration url " + remoteURL.build().toString()); + + Response response = this.HTTPRequest(remoteURL, formBody); + int r = response.code(); + String contentType = response.body().contentType().toString(); if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) { - DataInputStream in = new DataInputStream(connection.getInputStream()); + String in = response.body().string(); serverConfig = new Persister().read(ServerConfig.class, in); if (ServerCode.fromInt(serverConfig.getStatus()) != ServerCode.OK) { @@ -237,11 +250,6 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager this.log.error("Server::getConfiguration: exception Exception " + e); return Error.Type.UNKNOWN; } - finally { - if (connection != null) { - connection.disconnect(); - } - } this.client.getGui().successfulAuthenticationEvent(publickey); @@ -252,7 +260,6 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager this.log.debug("Server::requestJob"); String url_contents = ""; - HttpURLConnection connection = null; try { OS os = OS.getOS(); long maxMemory = this.user_config.getMaxMemory(); @@ -263,28 +270,26 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager else if (freeMemory > 0 && maxMemory > 0) { maxMemory = Math.min(maxMemory, freeMemory); } - String url = String - .format("%s?computemethod=%s&cpu_cores=%s&ram_max=%s&rendertime_max=%s", this.getPage("request-job"), this.user_config.computeMethodToInt(), - ((this.user_config.getNbCores() == -1) ? os.getCPU().cores() : this.user_config.getNbCores()), maxMemory, - this.user_config.getMaxRenderTime()); - if (this.user_config.getComputeMethod() != ComputeType.CPU && this.user_config.getGPUDevice() != null) { - String gpu_model = ""; - try { - gpu_model = URLEncoder.encode(this.user_config.getGPUDevice().getModel(), "UTF-8"); - } - catch (UnsupportedEncodingException e) { - } - url += "&gpu_model=" + gpu_model + "&gpu_ram=" + this.user_config.getGPUDevice().getMemory() + "&gpu_type=" + this.user_config.getGPUDevice() - .getType(); + + HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("request-job"))).newBuilder() + .addQueryParameter("computemethod", String.valueOf(user_config.computeMethodToInt())) + .addQueryParameter("cpu_cores", String.valueOf(user_config.getNbCores() == -1 ? os.getCPU().cores() : user_config.getNbCores())) + .addQueryParameter("ram_max", String.valueOf(maxMemory)) + .addQueryParameter("rendertime_max", String.valueOf(user_config.getMaxRenderTime())); + + if (user_config.getComputeMethod() != ComputeType.CPU && user_config.getGPUDevice() != null) { + urlBuilder.addQueryParameter("gpu_model", user_config.getGPUDevice().getModel()) + .addQueryParameter("gpu_ram", String.valueOf(user_config.getGPUDevice().getMemory())) + .addQueryParameter("gpu_type", user_config.getGPUDevice().getType()); } + + Response response = this.HTTPRequest(urlBuilder, RequestBody.create(MediaType.parse("application/xml"), this.generateXMLForMD5cache())); - connection = this.HTTPRequest(url, this.generateXMLForMD5cache()); - - int r = connection.getResponseCode(); - String contentType = connection.getContentType(); + int r = response.code(); + String contentType = response.body().contentType().toString(); if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) { - DataInputStream in = new DataInputStream(connection.getInputStream()); + String in = response.body().string(); JobInfos jobData = new Persister().read(JobInfos.class, in); @@ -358,13 +363,7 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager else if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) { throw new FermeExceptionBadResponseFromServer(); } - InputStream in = connection.getInputStream(); - String line; - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - while ((line = reader.readLine()) != null) { - System.out.print(line); - } - System.out.println(""); + System.out.println(response.body().string()); } } catch (FermeException e) { @@ -382,103 +381,87 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager e.printStackTrace(pw); throw new FermeException("error requestJob: unknown exception " + e + " stacktrace: " + sw.toString()); } - finally { - if (connection != null) { - connection.disconnect(); - } - } throw new FermeException("error requestJob, end of function"); } - public HttpURLConnection HTTPRequest(String url_) throws IOException { - return this.HTTPRequest(url_, null); + public Response HTTPRequest(String url) throws IOException { + HttpUrl.Builder httpUrlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder(); + return this.HTTPRequest(httpUrlBuilder, null); } - public HttpURLConnection HTTPRequest(String url_, String data_) throws IOException { - this.log.debug("Server::HTTPRequest url(" + url_ + ")"); - HttpURLConnection connection = null; - URL url = new URL(url_); + public Response HTTPRequest(HttpUrl.Builder httpUrlBuilder) throws IOException { + return this.HTTPRequest(httpUrlBuilder, null); + } + + public Response HTTPRequest(HttpUrl.Builder httpUrlBuilder, RequestBody data_) throws IOException { + String url = httpUrlBuilder.build().toString(); + Request.Builder builder = new Request.Builder(); + builder.url(url); - connection = (HttpURLConnection) url.openConnection(); - connection.setDoInput(true); - connection.setDoOutput(true); - connection.setInstanceFollowRedirects(true); - connection.setRequestMethod("GET"); - - if (url_.startsWith("https://")) { - try { - SSLContext sc; - sc = SSLContext.getInstance("SSL"); - sc.init(null, new TrustManager[] { this }, null); - SSLSocketFactory factory = sc.getSocketFactory(); - ((HttpsURLConnection) connection).setSSLSocketFactory(factory); - ((HttpsURLConnection) connection).setHostnameVerifier(this); - } - catch (NoSuchAlgorithmException e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - this.log.debug("Server::HTTPRequest NoSuchAlgorithmException " + e + " stacktrace: " + sw.toString()); - return null; - } - catch (KeyManagementException e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - this.log.debug("Server::HTTPRequest KeyManagementException " + e + " stacktrace: " + sw.toString()); - return null; - } - } + this.log.debug("Server::HTTPRequest url(" + url + ")"); if (data_ != null) { - connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); - connection.setRequestMethod("POST"); - OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); - out.write(data_); - out.flush(); - out.close(); + builder.post(data_); } - // actually use the connection to, in case of timeout, generate an exception - connection.getResponseCode(); + Request request = builder.build(); + Response response = null; - this.lastRequestTime = new Date().getTime(); - - return connection; + try { + response = httpClient.newCall(request).execute(); + + if (!response.isSuccessful()) { + throw new IOException("Unexpected code " + response); + } + + this.lastRequestTime = new Date().getTime(); + return response; + } + catch (IOException e) { + throw new IOException("Unexpected response from HTTP Stack" + e.getMessage()); + } } public int HTTPGetFile(String url_, String destination_, Gui gui_, String status_) throws FermeExceptionNoSpaceLeftOnDevice { // the destination_ parent directory must exist try { - HttpURLConnection httpCon = this.HTTPRequest(url_); + Response response = this.HTTPRequest(url_); - InputStream inStrm = httpCon.getInputStream(); - if (httpCon.getResponseCode() != HttpURLConnection.HTTP_OK) { - this.log.error("Server::HTTPGetFile(" + url_ + ", ...) HTTP code is not " + HttpURLConnection.HTTP_OK + " it's " + httpCon.getResponseCode()); + if (response.code() != HttpURLConnection.HTTP_OK) { + this.log.error("Server::HTTPGetFile(" + url_ + ", ...) HTTP code is not " + HttpURLConnection.HTTP_OK + " it's " + response.code()); return -1; } - int size = httpCon.getContentLength(); - long start = new Date().getTime(); - FileOutputStream fos = new FileOutputStream(destination_); - byte[] ch = new byte[512 * 1024]; - int nb; + long start = new Date().getTime(); + InputStream is = response.body().byteStream(); + OutputStream output = new FileOutputStream(destination_); + + long size = response.body().contentLength(); + byte[] buffer = new byte[8 * 1024]; + int len = 0; long written = 0; - long last_gui_update = 0; // size in byte - while ((nb = inStrm.read(ch)) != -1) { - fos.write(ch, 0, nb); - written += nb; - if ((written - last_gui_update) > 1000000) { // only update the gui every 1MB + long lastUpd = 0; // last GUI progress update + + while ((len = is.read(buffer)) != -1) { + output.write(buffer, 0, len); + written += len; + + if ((written - lastUpd) > 1000000) { // only update the gui every 1MB gui_.status(String.format(status_, (int) (100.0 * written / size))); - last_gui_update = written; + lastUpd = written; } } - fos.close(); - inStrm.close(); + + output.flush(); + output.close(); + is.close(); + gui_.status(String.format(status_, 100)); + long end = new Date().getTime(); this.log.debug(String.format("File downloaded at %.1f kB/s, written %d B", ((float) (size / 1000)) / ((float) (end - start) / 1000), written)); this.lastRequestTime = new Date().getTime(); + return 0; } catch (Exception e) { @@ -490,111 +473,62 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager e.printStackTrace(new PrintWriter(sw)); this.log.error("Server::HTTPGetFile Exception " + e + " stacktrace " + sw.toString()); } - this.log.debug("Server::HTTPGetFile(" + url_ + ", ...) will failed (end of function)"); + this.log.debug(String.format("Server::HTTPGetFile(%s) did fail", url_)); return -2; } public ServerCode HTTPSendFile(String surl, String file1) { this.log.debug("Server::HTTPSendFile(" + surl + "," + file1 + ")"); - HttpURLConnection conn = null; - DataOutputStream dos = null; - BufferedReader inStream = null; - - String exsistingFileName = file1; - File fFile2Snd = new File(exsistingFileName); - - String lineEnd = "\r\n"; - String twoHyphens = "--"; - String boundary = "***232404jkg4220957934FW**"; - - int bytesRead, bytesAvailable, bufferSize; - byte[] buffer; - int maxBufferSize = 1 * 1024 * 1024; - - String urlString = surl; - try { - FileInputStream fileInputStream = new FileInputStream(new File(exsistingFileName)); - URL url = new URL(urlString); + String fileMimeType = Files.probeContentType(Paths.get(file1)); - conn = (HttpURLConnection) url.openConnection(); - conn.setDoInput(true); - conn.setDoOutput(true); - conn.setInstanceFollowRedirects(true); - conn.setUseCaches(false); + MediaType MEDIA_TYPE = MediaType.parse("image/" + fileMimeType); // e.g. "image/png" - conn.setRequestMethod("POST"); - conn.setRequestProperty("Connection", "Keep-Alive"); - conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); + RequestBody uploadContent = new MultipartBody.Builder().setType(MultipartBody.FORM) + .addFormDataPart("file", new File(file1).getName(), RequestBody.create(MEDIA_TYPE, new File(file1))).build(); - if (urlString.startsWith("https://")) { + Request request = new Request.Builder().url(surl).post(uploadContent).build(); + + Call call = httpClient.newCall(request); + Response response = call.execute(); + + int r = response.code(); + String contentType = response.body().contentType().toString(); + + if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) { try { - SSLContext sc; - sc = SSLContext.getInstance("SSL"); - sc.init(null, new TrustManager[] { this }, null); - SSLSocketFactory factory = sc.getSocketFactory(); - ((HttpsURLConnection) conn).setSSLSocketFactory(factory); - ((HttpsURLConnection) conn).setHostnameVerifier(this); - } - catch (NoSuchAlgorithmException e) { - this.log.error("Server::HTTPSendFile, exception NoSuchAlgorithmException " + e); - try { - fileInputStream.close(); - } - catch (Exception e1) { + String in = response.body().string(); + JobValidation jobValidation = new Persister().read(JobValidation.class, in); - } - return ServerCode.UNKNOWN; - } - catch (KeyManagementException e) { - this.log.error("Server::HTTPSendFile, exception KeyManagementException " + e); - try { - fileInputStream.close(); - } - catch (Exception e1) { + this.lastRequestTime = new Date().getTime(); + ServerCode serverCode = ServerCode.fromInt(jobValidation.getStatus()); + if (serverCode != ServerCode.OK) { + this.log.error("Server::HTTPSendFile wrong status (is " + serverCode + ")"); + return serverCode; } - return ServerCode.UNKNOWN; } + catch (Exception e) { // for the .read + e.printStackTrace(); + } + + return ServerCode.OK; + } + else if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) { + return ServerCode.ERROR_BAD_RESPONSE; + } + else { + System.out.println(response.body().string()); } - dos = new DataOutputStream(conn.getOutputStream()); - dos.writeBytes(twoHyphens + boundary + lineEnd); - dos.writeBytes("Content-Disposition: form-data; name=\"file\";" + " filename=\"" + fFile2Snd.getName() + "\"" + lineEnd); - dos.writeBytes(lineEnd); - - bytesAvailable = fileInputStream.available(); - bufferSize = Math.min(bytesAvailable, maxBufferSize); - buffer = new byte[bufferSize]; - - bytesRead = fileInputStream.read(buffer, 0, bufferSize); - - while (bytesRead > 0) { - dos.write(buffer, 0, bufferSize); - bytesAvailable = fileInputStream.available(); - bufferSize = Math.min(bytesAvailable, maxBufferSize); - bytesRead = fileInputStream.read(buffer, 0, bufferSize); - } - - dos.writeBytes(lineEnd); - dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); - fileInputStream.close(); - dos.flush(); - dos.close(); - } - catch (MalformedURLException e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - this.log.error("Server::HTTPSendFile, MalformedURLException " + e + " stacktrace " + sw.toString()); return ServerCode.UNKNOWN; } catch (IOException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); - this.log.error("Server::HTTPSendFile, IOException " + e + " stacktrace " + sw.toString()); + this.log.error(String.format("Server::HTTPSendFile Error in upload process. Exception %s stacktrace ", e.getMessage(), sw.toString())); return ServerCode.UNKNOWN; } catch (OutOfMemoryError e) { @@ -611,68 +545,6 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager this.log.error("Server::HTTPSendFile, Exception " + e + " stacktrace " + sw.toString()); return ServerCode.UNKNOWN; } - - int r; - try { - r = conn.getResponseCode(); - } - catch (IOException e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - this.log.debug("Server::HTTPSendFile IOException " + e + " stacktrace: " + sw.toString()); - return ServerCode.UNKNOWN; - } - String contentType = conn.getContentType(); - - if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) { - DataInputStream in; - try { - in = new DataInputStream(conn.getInputStream()); - } - catch (IOException e) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - this.log.debug("Server::HTTPSendFile IOException " + e + " stacktrace: " + sw.toString()); - return ServerCode.UNKNOWN; - } - - try { - JobValidation jobValidation = new Persister().read(JobValidation.class, in); - - this.lastRequestTime = new Date().getTime(); - - ServerCode serverCode = ServerCode.fromInt(jobValidation.getStatus()); - if (serverCode != ServerCode.OK) { - this.log.error("Server::HTTPSendFile wrong status (is " + serverCode + ")"); - return serverCode; - } - } - catch (Exception e) { // for the .read - e.printStackTrace(); - } - - return ServerCode.OK; - } - else if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) { - return ServerCode.ERROR_BAD_RESPONSE; - } - else { - try { - inStream = new BufferedReader(new InputStreamReader(conn.getInputStream())); - - String str; - while ((str = inStream.readLine()) != null) { - System.out.println(str); - System.out.println(""); - } - inStream.close(); - } - catch (IOException ioex) { - } - } - return ServerCode.UNKNOWN; } private String generateXMLForMD5cache() { @@ -731,17 +603,58 @@ public class Server extends Thread implements HostnameVerifier, X509TrustManager return ""; } - @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { - } - - @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { - } - - @Override public X509Certificate[] getAcceptedIssuers() { - return null; - } - - @Override public boolean verify(String arg0, SSLSession arg1) { - return true; // trust every ssl certificate + private OkHttpClient getOkHttpClient() { + try { + final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + + @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws 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(); + + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); + + CookieManager cookieManager = new CookieManager(); + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); + builder.cookieJar(new JavaNetCookieJar(cookieManager)); // Cookie store to maintain the session across calls + + builder.connectTimeout(30, TimeUnit.SECONDS); // Cancel the HTTP Request if the connection to server takes more than 10 seconds + builder.writeTimeout(60, TimeUnit.SECONDS); // Cancel the upload if the client cannot send any byte in 60 seconds + + // If the user has selected a proxy, then we must increase the download timeout. Reason being the way proxies work. To download a large file (i.e. + // a 500MB job), the proxy must first download the file to the proxy cache and then the information is sent fast to the SheepIt client. From a client + // viewpoint, the HTTP connection will make the CONNECT step really fast but then the time until the fist byte is received (the time measured by + // readTimeout) will be really long (minutes). Without a proxy in the middle, a connection that does receive nothing in 60 seconds might be + // considered a dead connection. + if (this.user_config.getProxy() != null) { + builder.readTimeout(10, TimeUnit.MINUTES); // Proxy enabled - 10 minutes + } + else { + builder.readTimeout(1, TimeUnit.MINUTES); // No proxy - 60 seconds max + } + + builder.hostnameVerifier(new HostnameVerifier() { + @Override public boolean verify(String hostname, SSLSession session) { + return true; // Accept all certificates + } + }); + + return builder.build(); + } + catch (Exception e) { + throw new RuntimeException(e); + } } }