Skip to content

Commit

Permalink
Switch to java.net.http http client
Browse files Browse the repository at this point in the history
This was added in Java 11 and supports HTTP/2.
  • Loading branch information
Adam- committed Mar 11, 2024
1 parent 0a2be40 commit 602ae31
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 40 deletions.
105 changes: 72 additions & 33 deletions src/main/java/net/runelite/launcher/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.google.common.hash.HashingOutputStream;
import com.google.common.io.ByteStreams;
import com.google.gson.Gson;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
Expand All @@ -47,9 +46,10 @@
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -96,6 +96,8 @@ public class Launcher
static final String LAUNCHER_EXECUTABLE_NAME_WIN = "RuneLite.exe";
static final String LAUNCHER_EXECUTABLE_NAME_OSX = "RuneLite";

private static HttpClient httpClient;

public static void main(String[] args)
{
OptionParser parser = new OptionParser(false);
Expand Down Expand Up @@ -244,12 +246,19 @@ public static void main(String[] args)
if (settings.isSkipTlsVerification())
{
TrustManagerUtil.setupInsecureTrustManager();
// This is the only way to disable hostname verification with HttpClient - https://stackoverflow.com/a/52995420
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
}
else
{
TrustManagerUtil.setupTrustManager();
}

// setup http client after the default SSLContext is set
httpClient = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();

if (postInstall)
{
postInstall();
Expand Down Expand Up @@ -447,34 +456,55 @@ private static void setJvmParams(final Map<String, String> params)

private static Bootstrap getBootstrap() throws IOException, CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, VerificationException
{
URL u = new URL(LauncherProperties.getBootstrap());
URL signatureUrl = new URL(LauncherProperties.getBootstrapSig());
HttpRequest bootstrapReq = HttpRequest.newBuilder()
.uri(URI.create(LauncherProperties.getBootstrap()))
.header("User-Agent", USER_AGENT)
.GET()
.build();

URLConnection conn = u.openConnection();
URLConnection signatureConn = signatureUrl.openConnection();
HttpRequest bootstrapSigReq = HttpRequest.newBuilder()
.uri(URI.create(LauncherProperties.getBootstrapSig()))
.header("User-Agent", USER_AGENT)
.GET()
.build();

conn.setRequestProperty("User-Agent", USER_AGENT);
signatureConn.setRequestProperty("User-Agent", USER_AGENT);
HttpResponse<byte[]> bootstrapResp, bootstrapSigResp;

try (InputStream i = conn.getInputStream();
InputStream signatureIn = signatureConn.getInputStream())
try
{
byte[] bytes = ByteStreams.toByteArray(i);
byte[] signature = ByteStreams.toByteArray(signatureIn);
bootstrapResp = httpClient.send(bootstrapReq, HttpResponse.BodyHandlers.ofByteArray());
bootstrapSigResp = httpClient.send(bootstrapSigReq, HttpResponse.BodyHandlers.ofByteArray());
}
catch (InterruptedException ex)
{
throw new IOException(ex);
}

Certificate certificate = getCertificate();
Signature s = Signature.getInstance("SHA256withRSA");
s.initVerify(certificate);
s.update(bytes);
if (bootstrapResp.statusCode() != 200)
{
throw new IOException("Unable to download bootstrap (status code " + bootstrapResp.statusCode() + "): " + new String(bootstrapResp.body()));
}

if (!s.verify(signature))
{
throw new VerificationException("Unable to verify bootstrap signature");
}
if (bootstrapSigResp.statusCode() != 200)
{
throw new IOException("Unable to download bootstrap signature (status code " + bootstrapSigResp.statusCode() + "): " + new String(bootstrapSigResp.body()));
}

final byte[] bytes = bootstrapResp.body();
final byte[] signature = bootstrapSigResp.body();

Certificate certificate = getCertificate();
Signature s = Signature.getInstance("SHA256withRSA");
s.initVerify(certificate);
s.update(bytes);

Gson g = new Gson();
return g.fromJson(new InputStreamReader(new ByteArrayInputStream(bytes)), Bootstrap.class);
if (!s.verify(signature))
{
throw new VerificationException("Unable to verify bootstrap signature");
}

Gson g = new Gson();
return g.fromJson(new InputStreamReader(new ByteArrayInputStream(bytes)), Bootstrap.class);
}

private static boolean jvmOutdated(Bootstrap bootstrap)
Expand Down Expand Up @@ -829,21 +859,30 @@ static int compareVersion(String a, String b)

static void download(String path, String hash, IntConsumer progress, OutputStream out) throws IOException, VerificationException
{
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("User-Agent", USER_AGENT);
conn.getResponseCode();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(path))
.header("User-Agent", USER_AGENT)
.GET()
.build();

HttpResponse<InputStream> response;
try
{
response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
}
catch (InterruptedException ex)
{
throw new IOException(ex);
}

InputStream err = conn.getErrorStream();
if (err != null)
if (response.statusCode() != 200)
{
err.close();
throw new IOException("Unable to download " + path + " - " + conn.getResponseMessage());
throw new IOException("Unable to download " + path + " (status code " + response.statusCode() + ")");
}

int downloaded = 0;
HashingOutputStream hout = new HashingOutputStream(Hashing.sha256(), out);
try (InputStream in = conn.getInputStream())
try (InputStream in = response.body())
{
int i;
byte[] buffer = new byte[1024 * 1024];
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/net/runelite/launcher/TrustManagerUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
Expand Down Expand Up @@ -169,9 +168,6 @@ public X509Certificate[] getAcceptedIssuers()
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{combiningTrustManager}, new SecureRandom());
SSLContext.setDefault(sc);
// HttpsURLConnection has its own SSLSocketFactory cache which defaults to SSLContext.getDefault().getSocketFactory()
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// SSLSocketFactory also has its own SSLSocketFactory cache, but it can't be changed
}

static void setupInsecureTrustManager() throws NoSuchAlgorithmException, KeyManagementException
Expand Down Expand Up @@ -199,8 +195,5 @@ public X509Certificate[] getAcceptedIssuers()
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{trustManager}, new SecureRandom());
SSLContext.setDefault(sc);
// HttpsURLConnection has its own SSLSocketFactory cache which defaults to SSLContext.getDefault().getSocketFactory()
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
}
}

0 comments on commit 602ae31

Please sign in to comment.