diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 82be6786b..445a41707 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,6 +32,8 @@ jobs:
cache: maven
- name: Test with Maven
run: mvn clean install -Dmaven.test.skip=true -B -U --file pom.xml
+ - name: Test HTTP transport with Spring Boot BOM first
+ run: python3 trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/run.py
JDK21:
runs-on: ubuntu-latest
steps:
@@ -48,6 +50,8 @@ jobs:
cache: maven
- name: Test with Maven
run: mvn clean install -Dmaven.test.skip=true -B -U --file pom.xml
+ - name: Test HTTP transport with Spring Boot BOM first
+ run: python3 trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/run.py
JDK17-master:
runs-on: ubuntu-latest
steps:
diff --git a/trpc-dependencies/trpc-dependencies-bom/pom.xml b/trpc-dependencies/trpc-dependencies-bom/pom.xml
index 9dca91b50..f36efab38 100644
--- a/trpc-dependencies/trpc-dependencies-bom/pom.xml
+++ b/trpc-dependencies/trpc-dependencies-bom/pom.xml
@@ -96,8 +96,7 @@
4.0.2
2.1.1
2.0.1.Final
- 8.1.13.v20181017
- 11.0.25
+ 12.1.14
2.7
2.4.14
5.12.2
@@ -577,85 +576,18 @@
${javax.servlet-api.version}
- jetty-server
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-servlet
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-io
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-util
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-util-ajax
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-security
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-http
- org.eclipse.jetty
- ${jetty.version}
-
-
- http2-server
- org.eclipse.jetty.http2
- ${jetty.version}
-
-
- http2-common
- org.eclipse.jetty.http2
- ${jetty.version}
-
-
- http2-hpack
- org.eclipse.jetty.http2
- ${jetty.version}
-
-
- jetty-alpn-server
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-alpn-conscrypt-server
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-alpn-java-server
- org.eclipse.jetty
- ${jetty.version}
-
-
- jetty-alpn-openjdk8-server
org.eclipse.jetty
+ jetty-bom
${jetty.version}
+ pom
+ import
- org.eclipse.jetty
- jetty-webapp
+ org.eclipse.jetty.ee10
+ jetty-ee10-bom
${jetty.version}
-
-
- alpn-boot
- org.mortbay.jetty.alpn
- runtime
- ${jetty.alpn.boot.version}
+ pom
+ import
diff --git a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/server/DefaultHttpExecutorTest.java b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/server/DefaultHttpExecutorTest.java
index 6df61e884..5d0a7fe1d 100644
--- a/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/server/DefaultHttpExecutorTest.java
+++ b/trpc-proto/trpc-proto-http/src/test/java/com/tencent/trpc/proto/http/server/DefaultHttpExecutorTest.java
@@ -21,10 +21,9 @@
import com.tencent.trpc.core.rpc.def.DefProviderInvoker;
import com.tencent.trpc.core.utils.NetUtils;
import com.tencent.trpc.proto.standard.common.TRPCProtocol;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import javax.ws.rs.HttpMethod;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
@@ -69,9 +68,8 @@ public void testRegist() {
connector.setAcceptQueueSize(60);
server.addConnector(connector);
- HttpChannel httpChannel = org.mockito.Mockito.mock(HttpChannel.class);
- Request request = org.mockito.Mockito.mock(Request.class);
- Response response = org.mockito.Mockito.mock(Response.class);
+ HttpServletRequest request = org.mockito.Mockito.mock(HttpServletRequest.class);
+ HttpServletResponse response = org.mockito.Mockito.mock(HttpServletResponse.class);
org.mockito.Mockito.when(request.getMethod()).thenReturn(HttpMethod.GET);
org.mockito.Mockito.when(request.getPathInfo()).thenReturn("/trpc.test.rpc.Hello/sayHello");
diff --git a/trpc-spring-support/trpc-spring-cloud-gateway/pom.xml b/trpc-spring-support/trpc-spring-cloud-gateway/pom.xml
index b47d8bbfc..33827dfc5 100644
--- a/trpc-spring-support/trpc-spring-cloud-gateway/pom.xml
+++ b/trpc-spring-support/trpc-spring-cloud-gateway/pom.xml
@@ -45,8 +45,8 @@
trpc-rpc-support
- org.eclipse.jetty
- jetty-webapp
+ org.eclipse.jetty.ee10
+ jetty-ee10-webapp
com.tencent.trpc
@@ -64,4 +64,4 @@
-
\ No newline at end of file
+
diff --git a/trpc-transport/trpc-transport-http/pom.xml b/trpc-transport/trpc-transport-http/pom.xml
index 72c0c34f1..f70fa1b0d 100644
--- a/trpc-transport/trpc-transport-http/pom.xml
+++ b/trpc-transport/trpc-transport-http/pom.xml
@@ -25,22 +25,17 @@
jetty-server
- org.eclipse.jetty
- jetty-servlet
+ org.eclipse.jetty.ee10
+ jetty-ee10-servlet
org.eclipse.jetty.http2
- http2-server
+ jetty-http2-server
org.eclipse.jetty
jetty-alpn-server
-
- org.mortbay.jetty.alpn
- alpn-boot
- runtime
-
org.eclipse.jetty
jetty-alpn-conscrypt-server
diff --git a/trpc-transport/trpc-transport-http/src/main/java/com/tencent/trpc/transport/http/support/jetty/JettyHttpServer.java b/trpc-transport/trpc-transport-http/src/main/java/com/tencent/trpc/transport/http/support/jetty/JettyHttpServer.java
index abbc6093b..a0cc63b88 100755
--- a/trpc-transport/trpc-transport-http/src/main/java/com/tencent/trpc/transport/http/support/jetty/JettyHttpServer.java
+++ b/trpc-transport/trpc-transport-http/src/main/java/com/tencent/trpc/transport/http/support/jetty/JettyHttpServer.java
@@ -20,11 +20,11 @@
import com.tencent.trpc.transport.http.ExecutorDispatcher;
import com.tencent.trpc.transport.http.HttpExecutor;
import com.tencent.trpc.transport.http.common.ServletManager;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
/**
@@ -120,9 +120,10 @@ private ServletContextHandler getServletContextHandler(Server server) {
servletHandler.addServletWithMapping(ExecutorDispatcher.class, "/*");
servletHolder.setAsyncSupported(true);
servletHolder.setInitOrder(1);
- ServletContextHandler context =
- new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
+ ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+ context.setContextPath("/");
context.setServletHandler(servletHandler);
+ server.setHandler(context);
return context;
}
diff --git a/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/README.md b/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/README.md
new file mode 100644
index 000000000..05379070f
--- /dev/null
+++ b/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/README.md
@@ -0,0 +1,52 @@
+# Spring Boot BOM compatibility regression
+
+This consumer reproduces the dependency-management order from issue
+[#152](https://github.com/trpc-group/trpc-java/issues/152): Spring Boot's BOM precedes
+tRPC's BOM. It reuses the transport tests against the installed tRPC artifacts,
+covering HTTP/1.1, HTTPS, h2c, h2 with ALPN, request bodies and asynchronous Servlet
+responses. All server sockets bind explicitly to `127.0.0.1`.
+
+Run from the repository root with JDK 17 or 21:
+
+```sh
+mvn -B -ntp -pl trpc-dependencies/trpc-dependencies-bom,trpc-dependencies/trpc-bom,trpc-transport/trpc-transport-http -am -Dmaven.test.skip=true install
+mvn -B -ntp -f trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/pom.xml test
+```
+
+To test the released implementation as a negative control, add
+`-Dtrpc.version=2.0.5` to the second command. With Spring Boot 3.5.16 this mixes
+Jetty 12 core artifacts with Jetty 11 Servlet/HTTP2 artifacts and fails during
+server initialization. The fixed snapshot uses Jetty 12 core and EE10 BOMs,
+EE10 Servlet coordinates, and the renamed HTTP/2 artifacts.
+
+The JDK 17 and JDK 21 Maven install CI jobs run the compatibility matrix after
+installing the reactor artifacts:
+
+```sh
+python3 trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/run.py
+```
+
+The runner reads the current `springboot.version` from tRPC's dependency BOM and
+Maven Central's release metadata. It selects every published stable patch of
+that baseline's minor line and all later stable versions, plus the latest patch
+of the two preceding minor lines within the same major. For the 3.5.0 baseline,
+this covers every 3.5.x patch and
+later stable releases, with 3.4.x and 3.3.x as backward compatibility samples.
+Milestones, release candidates and snapshots are excluded. Boot 3.1 and Jetty 11
+are outside this support range; their incompatibility is not a release blocker.
+New stable versions are picked up on subsequent CI runs, rather than assumed
+compatible in advance.
+
+Jetty core and EE10 BOMs default to the 12.1 line. Boot 3.5's BOM manages both
+core and EE10 artifacts, selecting its aligned 12.0.x versions. Boot 4 manages
+EE11 instead of EE10, so tRPC must provide a 12.1-compatible EE10 default to
+avoid mixing EE10 12.0 with a 12.1 core. The Servlet-based tRPC executor contract
+is unchanged. These tests cover the HTTP transport under each BOM, not the full
+Spring Boot starter, application auto-configuration or gateway application.
+
+Each version runs in a separate Maven invocation. Results and dependency trees
+are saved under the repository's `target/jetty-boot-compatibility/` directory.
+The normal reactor `test` command does not invoke this fixture. Additional
+arguments to `run.py` are passed to Maven, so isolated settings and repository
+options can be reused. For a single version, use the standalone POM command
+above with `-Dspring-boot.version=...`.
diff --git a/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/pom.xml b/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/pom.xml
new file mode 100644
index 000000000..e25a5e836
--- /dev/null
+++ b/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/pom.xml
@@ -0,0 +1,97 @@
+
+
+ 4.0.0
+
+ com.tencent.trpc.tests
+ jetty-boot-bom-first
+ 1.0-SNAPSHOT
+
+
+ 2.0.5-SNAPSHOT
+ 3.5.16
+ 17
+ UTF-8
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot.version}
+ pom
+ import
+
+
+ com.tencent.trpc
+ trpc-dependencies-bom
+ ${trpc.version}
+ pom
+ import
+
+
+
+
+
+
+ com.tencent.trpc
+ trpc-transport-http
+ ${trpc.version}
+
+
+ org.apache.httpcomponents.core5
+ httpcore5-h2
+ test
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
+
+ org.apache.httpcomponents
+ httpcore
+ test
+
+
+ org.apache.httpcomponents
+ httpclient
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+ ${project.basedir}/../../java
+
+
+ ${project.basedir}/../../resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M9
+
+
+
+
diff --git a/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/run.py b/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/run.py
new file mode 100644
index 000000000..b1247e06f
--- /dev/null
+++ b/trpc-transport/trpc-transport-http/src/test/it/boot-bom-first/run.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+"""Run the HTTP transport contract against the supported Boot BOM range."""
+
+import json
+from pathlib import Path
+import re
+import subprocess
+import sys
+import time
+import urllib.request
+import xml.etree.ElementTree as ET
+
+
+METADATA = (
+ "https://repo.maven.apache.org/maven2/org/springframework/boot/"
+ "spring-boot-dependencies/maven-metadata.xml"
+)
+
+
+def select_versions(metadata, baseline):
+ versions = sorted({
+ tuple(map(int, item.text.split(".")))
+ for item in metadata.findall("versioning/versions/version")
+ if item.text and re.fullmatch(r"\d+\.\d+\.\d+", item.text)
+ })
+ if baseline not in versions:
+ raise ValueError("The framework's Boot baseline is absent from Maven Central")
+ previous_lines = sorted({
+ version[:2] for version in versions
+ if version[0] == baseline[0] and version[:2] < baseline[:2]
+ })[-2:]
+ previous = [max(v for v in versions if v[:2] == line) for line in previous_lines]
+ supported = previous + [v for v in versions if v[:2] >= baseline[:2]]
+ return [".".join(map(str, v)) for v in supported]
+
+
+def main():
+ fixture = Path(__file__).resolve().parent
+ repository = fixture.parents[5]
+ ns = {"m": "http://maven.apache.org/POM/4.0.0"}
+ bom = ET.parse(repository / "trpc-dependencies/trpc-dependencies-bom/pom.xml")
+ baseline_text = bom.findtext("m:properties/m:springboot.version", namespaces=ns)
+ baseline = tuple(map(int, baseline_text.split(".")))
+ with urllib.request.urlopen(METADATA, timeout=30) as response:
+ metadata = ET.fromstring(response.read())
+ versions = select_versions(metadata, baseline)
+ output = repository / "target/jetty-boot-compatibility"
+ output.mkdir(parents=True, exist_ok=True)
+ (output / "summary.json").write_text("[]\n")
+ print("HTTP transport Boot BOM versions: " + ", ".join(versions), flush=True)
+ results = []
+ for version in versions:
+ log = output / (version + ".log")
+ command = [
+ "mvn", "-B", "-ntp", "-f", str(fixture / "pom.xml"), *sys.argv[1:],
+ "-Dspring-boot.version=" + version,
+ "org.apache.maven.plugins:maven-dependency-plugin:3.8.1:tree",
+ "-Dincludes=org.eclipse.jetty*:*,jakarta.servlet:*", "clean", "test",
+ ]
+ started = time.time()
+ with log.open("w") as stream:
+ process = subprocess.run(command, stdout=stream, stderr=subprocess.STDOUT)
+ counts = dict.fromkeys(("tests", "failures", "errors", "skipped"), 0)
+ for report in (fixture / "target/surefire-reports").glob("TEST-*.xml"):
+ if report.stat().st_mtime < started:
+ continue
+ suite = ET.parse(report).getroot()
+ for key in counts:
+ counts[key] += int(suite.get(key, "0"))
+ passed = process.returncode == 0 and counts["tests"] > 0 and not any(
+ counts[key] for key in ("failures", "errors", "skipped")
+ )
+ text = log.read_text()
+ jetty = re.search(r"org.eclipse.jetty:jetty-server:jar:([^:]+):", text)
+ result = {"boot": version, "passed": passed, "exit_code": process.returncode,
+ "jetty": jetty.group(1) if jetty else None, **counts, "log": str(log)}
+ results.append(result)
+ (output / "summary.json").write_text(json.dumps(results, indent=2) + "\n")
+ print(json.dumps(result), flush=True)
+ if not passed:
+ errors = [line for line in text.splitlines() if "[ERROR]" in line or "Caused by:" in line]
+ print("\n".join(errors[:15]), flush=True)
+ return 0 if all(result["passed"] for result in results) else 1
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2ServerTest.java b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2ServerTest.java
index 0b2e39a2c..42305a713 100644
--- a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2ServerTest.java
+++ b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2ServerTest.java
@@ -62,7 +62,7 @@ public class JettyHttp2ServerTest {
public static void beforeClass() throws CertificateException, NoSuchAlgorithmException,
KeyStoreException, IOException, KeyManagementException, InterruptedException {
ProtocolConfig protocolConfig = ProtocolConfig.newInstance();
- protocolConfig.setIp("localhost");
+ protocolConfig.setIp("127.0.0.1");
protocolConfig.setPort(18082);
protocolConfig.setProtocol(HTTP2_SCHEME);
protocolConfig.setTransporter("jetty");
diff --git a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2cServerTest.java b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2cServerTest.java
index af653d798..edfc97b7c 100644
--- a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2cServerTest.java
+++ b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttp2cServerTest.java
@@ -52,7 +52,7 @@ public class JettyHttp2cServerTest {
@BeforeAll
public static void beforeClass() throws InterruptedException {
ProtocolConfig protocolConfig = ProtocolConfig.newInstance();
- protocolConfig.setIp("localhost");
+ protocolConfig.setIp("127.0.0.1");
protocolConfig.setPort(18081);
protocolConfig.setProtocol(HTTP2_SCHEME);
protocolConfig.setTransporter("jetty");
diff --git a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttpsServerTest.java b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttpsServerTest.java
index 597d40d00..8535b7491 100644
--- a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttpsServerTest.java
+++ b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyHttpsServerTest.java
@@ -62,7 +62,7 @@ public class JettyHttpsServerTest {
public static void beforeClass() throws CertificateException, NoSuchAlgorithmException,
KeyStoreException, IOException, KeyManagementException, InterruptedException {
ProtocolConfig protocolConfig = ProtocolConfig.newInstance();
- protocolConfig.setIp("localhost");
+ protocolConfig.setIp("127.0.0.1");
protocolConfig.setPort(18083);
protocolConfig.setProtocol(HTTP_SCHEME);
protocolConfig.setTransporter("jetty");
diff --git a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyServerTest.java b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyServerTest.java
index 3cd114064..c56b6ef86 100644
--- a/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyServerTest.java
+++ b/trpc-transport/trpc-transport-http/src/test/java/com/tencent/trpc/transport/http/JettyServerTest.java
@@ -19,8 +19,11 @@
import com.tencent.trpc.core.logger.LoggerFactory;
import com.tencent.trpc.transport.http.common.ServletManager;
import com.tencent.trpc.transport.http.spi.HttpServerFactory;
+import jakarta.servlet.AsyncContext;
+import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
@@ -36,7 +39,7 @@ public class JettyServerTest {
@BeforeAll
public static void beforeClass() {
ProtocolConfig protocolConfig = ProtocolConfig.newInstance();
- protocolConfig.setIp("localhost");
+ protocolConfig.setIp("127.0.0.1");
protocolConfig.setPort(18080);
protocolConfig.setProtocol("http");
protocolConfig.setTransporter("jetty");
@@ -49,6 +52,20 @@ public static void beforeClass() {
String pathInfo = request.getPathInfo();
if ("/".equals(pathInfo)) {
response.setStatus(HttpStatus.OK_200);
+ } else if ("/echo".equals(pathInfo)) {
+ response.getWriter().write(request.getMethod() + ":" + pathInfo + ":"
+ + request.getParameter("id") + ":" + request.getReader().readLine());
+ } else if ("/async".equals(pathInfo)) {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.start(() -> {
+ try {
+ asyncContext.getResponse().getWriter().write("async response");
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ asyncContext.complete();
+ }
+ });
} else {
response.sendError(HttpStatus.NOT_FOUND_404);
}
@@ -122,4 +139,44 @@ public void testNotExistRequest() throws Exception {
}
}
+ @Test
+ public void testPostRequestBody() throws Exception {
+ HttpURLConnection connection = (HttpURLConnection)
+ new URL("http://127.0.0.1:18080/echo?id=42").openConnection();
+ try {
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "text/plain; charset=UTF-8");
+ connection.setConnectTimeout(1000);
+ connection.setReadTimeout(5000);
+ connection.setDoOutput(true);
+ try (var output = connection.getOutputStream()) {
+ output.write("request body".getBytes(StandardCharsets.UTF_8));
+ }
+ Assertions.assertEquals(200, connection.getResponseCode());
+ try (var input = connection.getInputStream()) {
+ Assertions.assertEquals("POST:/echo:42:request body",
+ new String(input.readAllBytes(), StandardCharsets.UTF_8));
+ }
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ @Test
+ public void testAsyncRequest() throws Exception {
+ HttpURLConnection connection = (HttpURLConnection)
+ new URL("http://127.0.0.1:18080/async").openConnection();
+ try {
+ connection.setConnectTimeout(1000);
+ connection.setReadTimeout(5000);
+ Assertions.assertEquals(200, connection.getResponseCode());
+ try (var input = connection.getInputStream()) {
+ Assertions.assertEquals("async response",
+ new String(input.readAllBytes(), StandardCharsets.UTF_8));
+ }
+ } finally {
+ connection.disconnect();
+ }
+ }
+
}