test(core): read bound ports back from the app instead of guessing free ones

Every integration test picked a port by opening ServerSocket(0), closing it and
reusing the number, which races anything else on the machine between the close
and the rebind. FlashApp.port() reports the port the listener actually bound,
so the guess is gone: 18 freePort() helpers deleted, 40 call sites now pass
port(0) and read the result back.

Two ServerSocket(0) uses remain and are correct. ConnectionRunnerTest accepts
on its socket rather than using it to pick a number. H2LoadMeasurementTest
hands its port to an external nghttpd process, which has no equivalent of
port() to read back; that one is now commented to say so.

HttpServerTlsTest's two-listener case reads both back through ports().

HttpServerTest and HttpServerConcurrencyTest also move from @BeforeEach to
@BeforeAll — every test in them is read-only against the same routes, so 11 and
3 boots respectively become 1. HttpServerConcurrencyTest's lazy-compile test
keeps building its own app, since a freshly compiled router is the thing it
tests. HttpServerTest now runs 11 tests in 0.06s.

The http2 interop suites (curl, nghttp, grpcurl, h2spec), the load measurement
and the soak test are skipped without their external binaries or system
property, so those edits are compile-verified here and exercised in CI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Zakaria El Orche
2026-09-09 11:05:06 +00:00
co-authored by Claude Opus 5
parent 1c207cf94c
commit e0ad83eb2f
23 changed files with 132 additions and 235 deletions
@@ -4,11 +4,10 @@ import dev.relism.flash.extension.FlashApp;
import dev.relism.flash.extension.FlashConfiguration;
import dev.relism.flash.http.ContentType;
import dev.relism.flash.models.Response;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.net.ServerSocket;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -25,20 +24,17 @@ import static org.junit.jupiter.api.Assertions.*;
class HttpServerConcurrencyTest {
private FlashApp app;
private int port;
private HttpClient httpClient;
@BeforeEach
void setUp() throws Exception {
try (ServerSocket s = new ServerSocket(0)) {
port = s.getLocalPort();
}
private static FlashApp app;
private static int port;
private static HttpClient httpClient;
@BeforeAll
static void setUp() {
app = FlashApp.create(FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.build());
port = app.port();
app.get("/ping", (req, res) -> "pong");
app.post("/echo", (req, res) -> {
@@ -53,9 +49,9 @@ class HttpServerConcurrencyTest {
.build();
}
@AfterEach
void tearDown() {
if (app != null) app.stop();
@AfterAll
static void tearDown() {
if (app != null) app.stop().join();
}
// --- helpers ---
@@ -173,15 +169,13 @@ class HttpServerConcurrencyTest {
*/
@Test
void concurrent_lazyCompile_noRaceCondition() throws Exception {
int freshPort;
try (ServerSocket s = new ServerSocket(0)) {
freshPort = s.getLocalPort();
}
// Deliberately a brand-new app: this test is about compiling routes lazily on first
// use, so it must not share the class-scoped server.
FlashApp freshApp = FlashApp.create(FlashConfiguration.builder()
.port(freshPort)
.port(0)
.host("127.0.0.1")
.build());
int freshPort = freshApp.port();
for (int i = 0; i < 10; i++) {
final int idx = i;
@@ -2,15 +2,14 @@ package dev.relism.flash;
import dev.relism.flash.extension.FlashApp;
import dev.relism.flash.extension.FlashConfiguration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
@@ -18,19 +17,17 @@ import static org.junit.jupiter.api.Assertions.*;
class HttpServerTest {
private FlashApp app;
private int port;
@BeforeEach
void setUp() throws Exception {
try (ServerSocket s = new ServerSocket(0)) {
port = s.getLocalPort();
}
// Every test here is read-only against the same routes, so one boot for the class.
private static FlashApp app;
private static int port;
@BeforeAll
static void setUp() {
app = FlashApp.create(FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.build());
port = app.port();
app.get("/api/ping", (req, res) -> "pong");
@@ -61,9 +58,9 @@ class HttpServerTest {
app.start();
}
@AfterEach
void tearDown() {
if (app != null) app.stop();
@AfterAll
static void tearDown() {
if (app != null) app.stop().join();
}
// --- helpers ---
@@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
@@ -30,21 +29,15 @@ class HttpServerTimeoutTest {
if (app != null) app.stop();
}
private int freePort() throws Exception {
try (ServerSocket s = new ServerSocket(0)) {
return s.getLocalPort();
}
}
@Test
void slowlorisHeaderDribble_disconnectedWithinHeaderReadTimeout() throws Exception {
int headerTimeoutMs = 300;
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.headerReadTimeoutMs(headerTimeoutMs)
.idleKeepAliveTimeoutMs(60_000)
.build());
int port = app.port();
app.get("/", (req, res) -> "ok");
app.start();
@@ -71,12 +64,12 @@ class HttpServerTimeoutTest {
@Test
void idleKeepAliveConnection_disconnectedWithinIdleTimeout() throws Exception {
int idleTimeoutMs = 300;
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.headerReadTimeoutMs(10_000)
.idleKeepAliveTimeoutMs(idleTimeoutMs)
.build());
int port = app.port();
app.get("/", (req, res) -> "ok");
app.start();
@@ -96,13 +89,13 @@ class HttpServerTimeoutTest {
@Test
void slowBodyDribble_disconnectedWithinBodyReadTimeout() throws Exception {
int bodyTimeoutMs = 300;
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.headerReadTimeoutMs(10_000)
.idleKeepAliveTimeoutMs(10_000)
.bodyReadTimeoutMs(bodyTimeoutMs)
.build());
int port = app.port();
app.post("/echo", (req, res) -> req.body().bytes());
app.start();
@@ -130,12 +123,12 @@ class HttpServerTimeoutTest {
int headerTimeoutMs = 300;
Path ks = TestKeystores.build(dir, "timeout.p12", "changeit",
TestKeystores.Entry.of("only", "timeout.test"));
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.keystore(ks, "changeit"))
.headerReadTimeoutMs(headerTimeoutMs)
.build());
int port = app.port();
app.get("/", (req, res) -> "ok");
app.start();
@@ -157,13 +150,13 @@ class HttpServerTimeoutTest {
@Test
void wellBehavedRequest_wellWithinTimeouts_unaffected() throws Exception {
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.headerReadTimeoutMs(300)
.idleKeepAliveTimeoutMs(300)
.bodyReadTimeoutMs(300)
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -60,9 +60,6 @@ class HttpServerTlsTest {
if (app != null) app.stop();
}
private static int freePort() throws IOException {
try (ServerSocket s = new ServerSocket(0)) { return s.getLocalPort(); }
}
private static String httpGet(SSLSocket socket, String path) throws IOException {
socket.setSoTimeout(SOCKET_TIMEOUT_MS);
@@ -79,11 +76,11 @@ class HttpServerTlsTest {
@Test
void httpsRequest_servedOverModernTls(@TempDir java.nio.file.Path dir) throws Exception {
var ks = TestKeystores.build(dir, "id.p12", "changeit", TestKeystores.Entry.of("srv", "localhost"));
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.keystore(ks, "changeit"))
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -103,11 +100,11 @@ class HttpServerTlsTest {
var ks = TestKeystores.build(dir, "sni.p12", "changeit",
TestKeystores.Entry.of("a", "a.test"),
TestKeystores.Entry.of("b", "b.test"));
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.keystore(ks, "changeit"))
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -143,11 +140,11 @@ class HttpServerTlsTest {
@Test
void mTls_requireRejectsClientWithNoCertificate(@TempDir java.nio.file.Path dir) throws Exception {
var ks = TestKeystores.build(dir, "id.p12", "changeit", TestKeystores.Entry.of("srv", "localhost"));
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.keystore(ks, "changeit").clientAuth(ClientAuth.REQUIRE))
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -180,11 +177,11 @@ class HttpServerTlsTest {
SSLContext serverCtx = SSLContext.getInstance("TLS");
serverCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.ofContext(serverCtx).clientAuth(ClientAuth.REQUIRE))
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -219,12 +216,12 @@ class HttpServerTlsTest {
@Test
void multipleListeners_plainAndTlsServeTheSameApp(@TempDir java.nio.file.Path dir) throws Exception {
var ks = TestKeystores.build(dir, "id.p12", "changeit", TestKeystores.Entry.of("srv", "localhost"));
int plainPort = freePort();
int tlsPort = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.listener(new FlashConfiguration.Listener(plainPort, "127.0.0.1", null))
.listener(new FlashConfiguration.Listener(tlsPort, "127.0.0.1", TlsConfig.keystore(ks, "changeit")))
.listener(new FlashConfiguration.Listener(0, "127.0.0.1", null))
.listener(new FlashConfiguration.Listener(0, "127.0.0.1", TlsConfig.keystore(ks, "changeit")))
.build());
int plainPort = app.ports().get(0);
int tlsPort = app.ports().get(1);
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -287,11 +284,11 @@ class HttpServerTlsTest {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(managers, null, null);
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.ofContext(ctx))
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -364,11 +361,11 @@ class HttpServerTlsTest {
SSLContext[] boxedCtx = new SSLContext[1];
RecordingKeyManager recorder = buildRecordingContext(ks, boxedCtx);
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.ofContext(boxedCtx[0]).applicationProtocols("acme-tls/1", "http/1.1"))
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();
@@ -422,11 +419,11 @@ class HttpServerTlsTest {
@Test
void request_isSecureAndSessionAvailableOverTls(@TempDir java.nio.file.Path dir) throws Exception {
var ks = TestKeystores.build(dir, "id.p12", "changeit", TestKeystores.Entry.of("srv", "localhost"));
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.keystore(ks, "changeit"))
.build());
int port = app.port();
app.get("/secure-info", (req, res) -> {
SSLSession session = req.sslSession();
return req.isSecure() + ":" + (session != null) + ":" + (session != null ? session.getCipherSuite() : "");
@@ -456,13 +453,13 @@ class HttpServerTlsTest {
SSLContext serverCtx = SSLContext.getInstance("TLS");
serverCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
// OPTIONAL, not REQUIRE: proves getPeerCertificates() works without also
// re-testing the REQUIRE-rejection path already covered elsewhere in this file.
.tls(TlsConfig.ofContext(serverCtx).clientAuth(ClientAuth.OPTIONAL))
.build());
int port = app.port();
app.get("/secure-info", (req, res) -> {
try {
X509Certificate peer = (X509Certificate) req.sslSession().getPeerCertificates()[0];
@@ -488,8 +485,8 @@ class HttpServerTlsTest {
@Test
void request_isNotSecureAndSessionIsNullOnPlainListener() throws Exception {
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder().port(port).host("127.0.0.1").build());
app = FlashApp.create(FlashConfiguration.builder().port(0).host("127.0.0.1").build());
int port = app.port();
app.get("/secure-info", (req, res) -> req.isSecure() + ":" + (req.sslSession() == null));
app.start();
@@ -524,11 +521,11 @@ class HttpServerTlsTest {
@Test
void wss_sessionIsSecureAndExposesSslSession(@TempDir java.nio.file.Path dir) throws Exception {
var ks = TestKeystores.build(dir, "id.p12", "changeit", TestKeystores.Entry.of("srv", "localhost"));
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.tls(TlsConfig.keystore(ks, "changeit"))
.build());
int port = app.port();
AtomicReference<Boolean> observedSecure = new AtomicReference<>();
AtomicReference<SSLSession> observedSession = new AtomicReference<>();
@@ -13,7 +13,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@@ -29,14 +28,11 @@ class HttpServerWebSocketTest {
@BeforeEach
void setUp() throws Exception {
try (ServerSocket s = new ServerSocket(0)) {
port = s.getLocalPort();
}
app = FlashApp.create(FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.build());
port = app.port();
app.ws("/chat", new WebSocketHandler() {
@Override
@@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.ServerSocket;
import static org.junit.jupiter.api.Assertions.*;
@@ -20,14 +19,11 @@ class FlashAppWebSocketTest {
@BeforeEach
void setUp() throws Exception {
try (ServerSocket s = new ServerSocket(0)) {
port = s.getLocalPort();
}
app = FlashApp.create(FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.build());
port = app.port();
}
@AfterEach
@@ -32,7 +32,6 @@ class CurlInteropTest {
@Test
void tlsGetPostLargeUploadAndLargeDownload(@TempDir Path directory) throws Exception {
int port = freePort();
Path keystore =
TestKeystores.build(
directory,
@@ -43,23 +42,24 @@ class CurlInteropTest {
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
exercise(directory, "https://localhost:" + port, "--http2", "--insecure");
}
@Test
void cleartextGetPostLargeUploadAndLargeDownload(@TempDir Path directory) throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
exercise(directory, "http://127.0.0.1:" + port, "--http2-prior-knowledge");
}
@@ -112,9 +112,4 @@ class CurlInteropTest {
return result;
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -28,14 +28,14 @@ class GrpcInteropTest {
@Test
void grpcurlCompletesUnaryStreamingAndErrorCalls(@TempDir Path directory) throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.post("/flash.test.Echo/Unary", (request, response) ->
response.type("application/grpc")
.body(request.body().bytes())
@@ -156,11 +156,6 @@ class GrpcInteropTest {
return count;
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
private record Result(int exitCode, String output) {}
}
@@ -42,16 +42,16 @@ class H2LoadMeasurementTest {
@Test
void measureFlashAndNghttpdMatrix(@TempDir Path directory) throws Exception {
int flashPort = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(flashPort)
.port(0)
.http2CleartextEnabled(true)
.h2MaxStreamsCreatedPerInterval(Integer.MAX_VALUE)
.h2MaxStreamsPerConnection(0)
.build());
int flashPort = app.port();
app.get("/index.html", (request, response) -> "flash-load");
app.start();
@@ -133,6 +133,10 @@ class H2LoadMeasurementTest {
if (path != null) builder.environment().put("LD_LIBRARY_PATH", path);
}
/**
* Still needed here: this port is handed to an external nghttpd process, which has no
* equivalent of {@code FlashApp.port()} to read an OS-assigned port back from.
*/
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
@@ -36,14 +36,14 @@ class H2SpecComplianceTest {
@Test
void cleartextSuiteHasNoFailuresOrSkips(@TempDir Path directory) throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
registerProbeRoutes();
app.start();
@@ -52,7 +52,6 @@ class H2SpecComplianceTest {
@Test
void tlsSuiteHasNoFailuresOrSkips(@TempDir Path directory) throws Exception {
int port = freePort();
Path keystore =
TestKeystores.build(
directory,
@@ -63,10 +62,11 @@ class H2SpecComplianceTest {
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
registerProbeRoutes();
app.start();
@@ -145,11 +145,6 @@ class H2SpecComplianceTest {
assertEquals(0, skipped.getLength(), output);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
private record ProcessResult(int exitCode, String output) {}
}
@@ -28,14 +28,14 @@ class H2cPriorKnowledgeTest {
@Test
void priorKnowledgeRequiresItsIndependentOptIn() throws Exception {
int disabledPort = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(disabledPort)
.port(0)
.http2Enabled(true)
.build());
int disabledPort = app.port();
app.get("/", (request, response) -> "wrong protocol");
app.start();
@@ -47,14 +47,14 @@ class H2cPriorKnowledgeTest {
}
app.stop().join();
int enabledPort = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(enabledPort)
.port(0)
.http2CleartextEnabled(true)
.build());
int enabledPort = app.port();
app.get("/", (request, response) -> "h2c");
app.start();
@@ -125,9 +125,4 @@ class H2cPriorKnowledgeTest {
payload);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -183,15 +183,15 @@ class Http2AbuseTest {
@Test
void idleOpenStreamIsCancelledWithinConfiguredDeadline() throws Exception {
int port = freePort();
FlashApp app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.h2StreamIdleTimeoutMs(20)
.build());
int port = app.port();
app.post("/idle", (request, response) -> request.body().bytes());
app.start();
ByteWriter headers = new ByteWriter(64);
@@ -295,11 +295,6 @@ class Http2AbuseTest {
throw new AssertionError("missing " + expected);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
private record Run(List<Http2TestFrames.WireFrame> frames) {
int lastGoAwayError() {
@@ -29,16 +29,16 @@ class Http2ConcurrencyTest {
@Test
void thousandMultiplexedStreamsCompleteOnOneConnection() throws Exception {
int port = freePort();
AtomicInteger handled = new AtomicInteger();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.h2MaxStreamsCreatedPerInterval(2_000)
.build());
int port = app.port();
app.get("/work", (request, response) -> Integer.toString(handled.incrementAndGet()));
app.start();
@@ -110,9 +110,4 @@ class Http2ConcurrencyTest {
payload);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -28,14 +28,14 @@ class Http2ConnectTest {
@Test
void connectTunnelCanExchangeDataBeforeEitherSideCloses() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.connect("tunnel", (request, response) ->
response.type(ContentType.NONE).streaming(output -> {
byte[] bytes = new byte[16];
@@ -100,9 +100,4 @@ class Http2ConnectTest {
return text.getBytes(StandardCharsets.US_ASCII);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -48,7 +48,6 @@ class Http2ConnectionIntegrationTest {
@Test
void javaHttpClientUsesHttp2AgainstAnExistingParameterizedRoute(@TempDir Path directory)
throws Exception {
int port = freePort();
Path keystore =
TestKeystores.build(
directory,
@@ -58,11 +57,12 @@ class Http2ConnectionIntegrationTest {
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
app.get(
"/users/{id}", (request, response) -> request.param("id") + ":" + request.header("host"));
app.start();
@@ -87,7 +87,6 @@ class Http2ConnectionIntegrationTest {
@Test
void javaHttpClientUploadsAndDownloadsFlowControlledBodies(@TempDir Path directory)
throws Exception {
int port = freePort();
Path keystore =
TestKeystores.build(
directory,
@@ -102,11 +101,12 @@ class Http2ConnectionIntegrationTest {
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
app.post("/echo", (request, response) -> request.body().bytes());
app.get("/fixed", (request, response) -> response.body(download));
app.get(
@@ -143,7 +143,6 @@ class Http2ConnectionIntegrationTest {
@Test
void pushStreamingAppliesBackpressureAcrossMultipleWindows(@TempDir Path directory)
throws Exception {
int port = freePort();
int length = 2 * 1024 * 1024 + 31;
Path keystore =
TestKeystores.build(
@@ -154,11 +153,12 @@ class Http2ConnectionIntegrationTest {
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
app.get(
"/push",
(request, response) ->
@@ -197,7 +197,6 @@ class Http2ConnectionIntegrationTest {
@Test
void hundredMegabyteUploadAndDownloadRemainStreaming(@TempDir Path directory) throws Exception {
int port = freePort();
long length = 100L * 1024 * 1024;
Path keystore =
TestKeystores.build(
@@ -208,11 +207,12 @@ class Http2ConnectionIntegrationTest {
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
app.post(
"/upload",
(request, response) -> {
@@ -250,14 +250,14 @@ class Http2ConnectionIntegrationTest {
@Test
void bodylessGetRunsExistingRouteAndReturnsHeadersAndData() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.get("/api/ping", (request, response) -> "pong");
app.start();
@@ -314,15 +314,15 @@ class Http2ConnectionIntegrationTest {
@Test
void resetQueuedStreamCannotReleaseOrReuseItBeforeDispatchObservesCancellation()
throws Exception {
int port = freePort();
AtomicInteger calls = new AtomicInteger();
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.get(
"/queued",
(request, response) -> {
@@ -384,15 +384,15 @@ class Http2ConnectionIntegrationTest {
@Test
void h2cTransportDispatchesControlFramesWithoutRunningApplicationWork() throws Exception {
int port = freePort();
AtomicBoolean handlerEntered = new AtomicBoolean();
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.get(
"/",
(request, response) -> {
@@ -442,15 +442,15 @@ class Http2ConnectionIntegrationTest {
@Test
void serverStopInitiatesTwoStageGoAwayOnIdleConnection() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.http2CleartextEnabled(true)
.shutdownDrainTimeoutMs(5_000)
.build());
int port = app.port();
app.start();
try (Socket socket = new Socket("127.0.0.1", port)) {
@@ -485,14 +485,14 @@ class Http2ConnectionIntegrationTest {
@Test
void protocolStateDoesNotLeakAcrossConsecutiveConnections() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.start();
try (Socket first = new Socket("127.0.0.1", port)) {
@@ -537,7 +537,6 @@ class Http2ConnectionIntegrationTest {
@Test
void tlsListenerOffersAndNegotiatesH2WhenHttp2IsEnabled(@TempDir Path directory)
throws Exception {
int port = freePort();
Path keystore =
TestKeystores.build(
directory,
@@ -547,11 +546,12 @@ class Http2ConnectionIntegrationTest {
app =
FlashApp.create(
FlashConfiguration.builder()
.port(port)
.port(0)
.host("127.0.0.1")
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
app.start();
try (SSLSocket socket =
@@ -630,11 +630,6 @@ class Http2ConnectionIntegrationTest {
payload);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
private static String ascii(dev.relism.fpr.core.ByteView view) {
byte[] bytes = new byte[view.length()];
@@ -31,7 +31,6 @@ class Http2MisdirectedRequestTest {
@Test
void authorityOutsideSelectedCertificateReceives421(@TempDir Path directory) throws Exception {
int port = freePort();
Path keystore =
TestKeystores.build(
directory,
@@ -42,10 +41,11 @@ class Http2MisdirectedRequestTest {
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.tls(TlsConfig.keystore(keystore, "changeit"))
.http2Enabled(true)
.build());
int port = app.port();
app.get("/", (request, response) -> "must not run");
app.start();
@@ -114,9 +114,4 @@ class Http2MisdirectedRequestTest {
}
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -47,14 +47,14 @@ class Http2RegressionCorpusTest {
@Test
void headersOnHalfClosedRemoteStreamUseStreamErrorBeforeDispatch() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.get("/", (request, response) -> "ok");
app.start();
@@ -101,9 +101,4 @@ class Http2RegressionCorpusTest {
payload);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -35,18 +35,18 @@ class Http2SoakTest {
@Test
void sustainedMixedTrafficRetainsBoundedHeapAndCompletesRequests() throws Exception {
long seconds = Long.getLong("flash.http2.soak.seconds", 600L);
int port = freePort();
byte[] streamBody = new byte[8 * 1024];
Arrays.fill(streamBody, (byte) 's');
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.h2MaxStreamsCreatedPerInterval(100_000)
.h2MaxStreamsPerConnection(0)
.build());
int port = app.port();
app.get("/get", (request, response) -> "get");
app.post("/post", (request, response) -> request.body().bytes());
app.get("/stream", (request, response) -> response.chunked(new ByteArrayInputStream(streamBody)));
@@ -182,9 +182,4 @@ class Http2SoakTest {
payload);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -27,14 +27,14 @@ class Http2TrailersTest {
@Test
void requestTrailersReachHandlerAfterBodyEof() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.post("/trailers", (request, response) -> {
assertEquals("abc", new String(request.body().bytes(), StandardCharsets.US_ASCII));
return request.trailers().first("grpc-status");
@@ -99,14 +99,14 @@ class Http2TrailersTest {
}
private int startBlockingRoute() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.post("/trailers", (request, response) -> request.body().bytes());
app.start();
return port;
@@ -152,9 +152,4 @@ class Http2TrailersTest {
payload);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -38,9 +38,8 @@ class NghttpInteropTest {
}
private void exercise(Path directory, boolean tls) throws Exception {
int port = freePort();
FlashConfiguration.FlashConfigurationBuilder builder =
FlashConfiguration.builder().host("127.0.0.1").port(port);
FlashConfiguration.builder().host("127.0.0.1").port(0);
if (tls) {
Path keystore =
TestKeystores.build(
@@ -54,6 +53,7 @@ class NghttpInteropTest {
}
byte[] large = new byte[2 * 1024 * 1024 + 29];
app = FlashApp.create(builder.build());
int port = app.port();
app.get("/get", (request, response) -> "nghttp-get");
app.post("/post", (request, response) -> "uploaded-" + request.body().bytes().length);
app.get("/large", (request, response) -> response.body(large));
@@ -93,9 +93,4 @@ class NghttpInteropTest {
assertTrue(trace.contains("recv DATA frame"), trace);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -23,15 +23,15 @@ class WebSocketOverH2Test {
@Test
void opensEchoesFragmentsAndCarriesAMessageLargerThanTheFlowWindow() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.wsFrameBufferSize(2 * 1024 * 1024)
.build());
int port = app.port();
app.ws(
"/chat",
new WebSocketHandler() {
@@ -67,9 +67,4 @@ class WebSocketOverH2Test {
}
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -28,14 +28,14 @@ class WebSocketParityTest {
@Test
void oneRouteAndHandlerEchoTheSameMessageOverHttp1AndHttp2() throws Exception {
int port = freePort();
app =
FlashApp.create(
FlashConfiguration.builder()
.host("127.0.0.1")
.port(port)
.port(0)
.http2CleartextEnabled(true)
.build());
int port = app.port();
app.ws(
"/parity",
new WebSocketHandler() {
@@ -135,9 +135,4 @@ class WebSocketParityTest {
return input.readNBytes(length);
}
private static int freePort() throws Exception {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
}
@@ -28,22 +28,17 @@ class ServerLifecycleGracefulShutdownTest {
if (app != null) app.stop();
}
private static int freePort() throws Exception {
try (ServerSocket s = new ServerSocket(0)) {
return s.getLocalPort();
}
}
@Test
void inFlightRequest_completesWithConnectionClose_duringShutdown() throws Exception {
int port = freePort();
CountDownLatch handlerStarted = new CountDownLatch(1);
CountDownLatch releaseHandler = new CountDownLatch(1);
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.shutdownDrainTimeoutMs(5_000)
.build());
int port = app.port();
app.get("/slow", (req, res) -> {
handlerStarted.countDown();
assertTrue(releaseHandler.await(5, TimeUnit.SECONDS));
@@ -80,11 +75,11 @@ class ServerLifecycleGracefulShutdownTest {
@Test
void stop_closesListener_soNewConnectionsAreRefused() throws Exception {
int port = freePort();
app = FlashApp.create(FlashConfiguration.builder()
.port(port).host("127.0.0.1")
.port(0).host("127.0.0.1")
.shutdownDrainTimeoutMs(500)
.build());
int port = app.port();
app.get("/ping", (req, res) -> "pong");
app.start();