From a49db969b2892047724f723c3b8cdb2fd6488bab Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Sat, 31 Oct 2026 22:17:14 -0400 Subject: [PATCH 1/2] Fix locally hosted skin servers hosted on http sometimes dropping connections This happens because the java http client defaults to http/2 and sends `Upgrade: h2c` for plaintext connections, which is deprecated and not understood by some servers. Setting the version to 1.1 for http urls should fix this. This is based on the pull request open-telemetry/opentelemetry-java#8985. --- .../hdskins/util/net/MoreHttpResponses.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java b/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java index a5409e8d..d0510e4e 100644 --- a/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java +++ b/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java @@ -6,6 +6,7 @@ import com.mojang.util.UUIDTypeAdapter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import net.minecraft.util.Util; import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; @@ -13,12 +14,14 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; +import java.net.URI; import java.net.http.*; import java.net.http.HttpResponse.BodyHandlers; import java.nio.channels.UnresolvedAddressException; import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.UUID; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -27,20 +30,32 @@ @FunctionalInterface public interface MoreHttpResponses { int SC_MULTIPLE_CHOICES = 300; - HttpClient CLIENT = HttpClient.newHttpClient(); Gson GSON = new GsonBuilder() .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()) .registerTypeHierarchyAdapter(SkinType.class, SkinType.adapter()) .create(); + Function HTTP_CLIENTS = Util.memoize((hostname) -> { + var builder = HttpClient.newBuilder(); + // The java.net.http client defaults to using http/2 for all requests. + // Some servers don't behave with http/2 on an insecure local connection. + // So set the http version to 1.1 for those servers. + if ("http".equalsIgnoreCase(hostname.getScheme())) { + builder.version(HttpClient.Version.HTTP_1_1); + } + return builder.build(); + }); + static MoreHttpResponses execute(HttpRequest.Builder request) throws IOException { return execute(request.build()); } static MoreHttpResponses execute(HttpRequest request) throws IOException { HDSkinsServer.LOGGER.debug("Executing web request to {}", request::uri); + // Get a client from the url resolved to its root + var httpClient = HTTP_CLIENTS.apply(request.uri().resolve("/")); try { - HttpResponse response = CLIENT.send(request, BodyHandlers.ofInputStream()); + HttpResponse response = httpClient.send(request, BodyHandlers.ofInputStream()); return () -> response; } catch (InterruptedException | UnresolvedAddressException e) { throw new IOException(e); From 43c5acf440d87cff0f4b1b90fd62c9910bc2bd19 Mon Sep 17 00:00:00 2001 From: Matthew Messinger Date: Thu, 5 Nov 2026 12:30:23 -0500 Subject: [PATCH 2/2] Move http version selection logic to `ValhallaSkinServer.newHttpBuilder` --- .../hdskins/server/ValhallaSkinServer.java | 2 ++ .../hdskins/util/net/MoreHttpResponses.java | 19 ++----------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/minelittlepony/hdskins/server/ValhallaSkinServer.java b/src/main/java/com/minelittlepony/hdskins/server/ValhallaSkinServer.java index 6894b216..b4c38241 100644 --- a/src/main/java/com/minelittlepony/hdskins/server/ValhallaSkinServer.java +++ b/src/main/java/com/minelittlepony/hdskins/server/ValhallaSkinServer.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.time.Duration; import java.util.*; @@ -93,6 +94,7 @@ private static HttpRequest.Builder newHttpBuilder(URI uri) { private static HttpRequest.Builder newHttpBuilder(URI uri, @Nullable String accessToken) { var builder = HttpRequest.newBuilder(uri) + .version("http".equals(uri.getScheme()) ? HttpClient.Version.HTTP_1_1 : HttpClient.Version.HTTP_2) .header(FileTypes.HEADER_ACCEPT, FileTypes.APPLICATION_JSON) .header(FileTypes.HEADER_USER_AGENT, PlatformVersionUtil.getUserAgent()); if (accessToken != null) { diff --git a/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java b/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java index d0510e4e..a5409e8d 100644 --- a/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java +++ b/src/main/java/com/minelittlepony/hdskins/util/net/MoreHttpResponses.java @@ -6,7 +6,6 @@ import com.mojang.util.UUIDTypeAdapter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import net.minecraft.util.Util; import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; @@ -14,14 +13,12 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; -import java.net.URI; import java.net.http.*; import java.net.http.HttpResponse.BodyHandlers; import java.nio.channels.UnresolvedAddressException; import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.UUID; -import java.util.function.Function; import java.util.function.Supplier; /** @@ -30,32 +27,20 @@ @FunctionalInterface public interface MoreHttpResponses { int SC_MULTIPLE_CHOICES = 300; + HttpClient CLIENT = HttpClient.newHttpClient(); Gson GSON = new GsonBuilder() .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()) .registerTypeHierarchyAdapter(SkinType.class, SkinType.adapter()) .create(); - Function HTTP_CLIENTS = Util.memoize((hostname) -> { - var builder = HttpClient.newBuilder(); - // The java.net.http client defaults to using http/2 for all requests. - // Some servers don't behave with http/2 on an insecure local connection. - // So set the http version to 1.1 for those servers. - if ("http".equalsIgnoreCase(hostname.getScheme())) { - builder.version(HttpClient.Version.HTTP_1_1); - } - return builder.build(); - }); - static MoreHttpResponses execute(HttpRequest.Builder request) throws IOException { return execute(request.build()); } static MoreHttpResponses execute(HttpRequest request) throws IOException { HDSkinsServer.LOGGER.debug("Executing web request to {}", request::uri); - // Get a client from the url resolved to its root - var httpClient = HTTP_CLIENTS.apply(request.uri().resolve("/")); try { - HttpResponse response = httpClient.send(request, BodyHandlers.ofInputStream()); + HttpResponse response = CLIENT.send(request, BodyHandlers.ofInputStream()); return () -> response; } catch (InterruptedException | UnresolvedAddressException e) { throw new IOException(e);