Skip to content

Commit 4f65661

Browse files
authored
Merge pull request quarkusio#36431 from cescoffier/RST-flood-protection-test
Add a test case verifying the RST flood protection
2 parents d51864c + b3cd2bc commit 4f65661

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package io.quarkus.vertx.http.http2;
2+
3+
import static io.vertx.core.http.HttpMethod.GET;
4+
import static org.junit.jupiter.api.Assertions.fail;
5+
6+
import java.io.File;
7+
import java.net.URL;
8+
import java.util.concurrent.CountDownLatch;
9+
import java.util.concurrent.TimeUnit;
10+
11+
import jakarta.enterprise.context.ApplicationScoped;
12+
import jakarta.enterprise.event.Observes;
13+
14+
import org.junit.jupiter.api.Assertions;
15+
import org.junit.jupiter.api.Assumptions;
16+
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.api.extension.RegisterExtension;
18+
19+
import io.quarkus.test.QuarkusUnitTest;
20+
import io.quarkus.test.common.http.TestHTTPResource;
21+
import io.quarkus.vertx.core.runtime.VertxCoreRecorder;
22+
import io.vertx.core.http.HttpClient;
23+
import io.vertx.core.http.HttpClientOptions;
24+
import io.vertx.core.http.HttpClientRequest;
25+
import io.vertx.core.http.HttpVersion;
26+
import io.vertx.core.net.JdkSSLEngineOptions;
27+
import io.vertx.ext.web.Router;
28+
29+
/**
30+
* Reproduce CVE-2023-44487.
31+
*/
32+
public class Http2RSTFloodProtectionTest {
33+
34+
@TestHTTPResource(value = "/ping", ssl = true)
35+
URL sslUrl;
36+
37+
@TestHTTPResource(value = "/ping")
38+
URL url;
39+
40+
@RegisterExtension
41+
static final QuarkusUnitTest config = new QuarkusUnitTest()
42+
.withApplicationRoot((jar) -> jar
43+
.addClasses(MyBean.class)
44+
.addAsResource(new File("src/test/resources/conf/ssl-jks.conf"), "application.properties")
45+
.addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks"));
46+
47+
@Test
48+
void testRstFloodProtectionWithTlsEnabled() throws Exception {
49+
Assumptions.assumeTrue(JdkSSLEngineOptions.isAlpnAvailable()); //don't run on JDK8
50+
HttpClientOptions options = new HttpClientOptions()
51+
.setUseAlpn(true)
52+
.setProtocolVersion(HttpVersion.HTTP_2)
53+
.setSsl(true)
54+
.setTrustAll(true);
55+
56+
var client = VertxCoreRecorder.getVertx().get().createHttpClient(options);
57+
int port = sslUrl.getPort();
58+
run(client, port, false);
59+
}
60+
61+
@Test
62+
public void testRstFloodProtection() throws InterruptedException {
63+
HttpClientOptions options = new HttpClientOptions()
64+
.setProtocolVersion(HttpVersion.HTTP_2)
65+
.setHttp2ClearTextUpgrade(true);
66+
var client = VertxCoreRecorder.getVertx().get().createHttpClient(options);
67+
run(client, url.getPort(), true);
68+
}
69+
70+
void run(HttpClient client, int port, boolean plain) throws InterruptedException {
71+
CountDownLatch latch = new CountDownLatch(1);
72+
client.connectionHandler(conn -> conn.goAwayHandler(ga -> {
73+
Assertions.assertEquals(11, ga.getErrorCode());
74+
latch.countDown();
75+
}));
76+
77+
if (plain) {
78+
// Emit a first request to establish a connection.
79+
// It's HTTP/1 so, does not count in the number of requests.
80+
client.request(GET, port, "localhost", "/ping")
81+
.compose(HttpClientRequest::send);
82+
}
83+
84+
for (int i = 0; i < 250; i++) { // must be higher thant the NEtty limit (200 / 30s)
85+
client.request(GET, port, "localhost", "/ping")
86+
.onSuccess(req -> req.end().onComplete(v -> req.reset()));
87+
}
88+
89+
if (!latch.await(10, TimeUnit.SECONDS)) {
90+
fail("RST flood protection failed");
91+
}
92+
}
93+
94+
@ApplicationScoped
95+
public static class MyBean {
96+
97+
public void register(@Observes Router router) {
98+
router.get("/ping").handler(rc -> {
99+
// Do nothing.
100+
});
101+
}
102+
103+
}
104+
}

0 commit comments

Comments
 (0)