Skip to content

Commit 4264989

Browse files
committed
test: Add integration tests for extras wildcard matching
1 parent 93f74d8 commit 4264989

1 file changed

Lines changed: 146 additions & 0 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.retailsvc.http;
2+
3+
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
4+
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
5+
import static java.net.HttpURLConnection.HTTP_OK;
6+
import static org.assertj.core.api.Assertions.assertThat;
7+
8+
import java.net.URI;
9+
import java.net.http.HttpClient;
10+
import java.net.http.HttpRequest;
11+
import java.net.http.HttpResponse;
12+
import java.net.http.HttpResponse.BodyHandlers;
13+
import java.util.Map;
14+
import org.junit.jupiter.api.Test;
15+
16+
class ExtrasWildcardIT extends ServerBaseTest {
17+
18+
@Test
19+
void singleStarMatchesOneSegment() throws Exception {
20+
RequestHandler ok = req -> Response.of(HTTP_OK, "ok");
21+
try (var s =
22+
newBuilder()
23+
.spec(spec)
24+
.handlers(stubAllHandlers(Map.of()))
25+
.port(0)
26+
.extraRoute("/static/*", ok)
27+
.build();
28+
var client = httpClient()) {
29+
30+
assertThat(get(client, s, "/static/x.css").statusCode()).isEqualTo(HTTP_OK);
31+
assertThat(get(client, s, "/static/a/b").statusCode()).isEqualTo(HTTP_NOT_FOUND);
32+
assertThat(get(client, s, "/static/").statusCode()).isEqualTo(HTTP_NOT_FOUND);
33+
}
34+
}
35+
36+
@Test
37+
void doubleStarMatchesAnyDepth() throws Exception {
38+
RequestHandler ok = req -> Response.of(HTTP_OK, "ok");
39+
try (var s =
40+
newBuilder()
41+
.spec(spec)
42+
.handlers(stubAllHandlers(Map.of()))
43+
.port(0)
44+
.extraRoute("/files/**", ok)
45+
.build();
46+
var client = httpClient()) {
47+
48+
assertThat(get(client, s, "/files/a").statusCode()).isEqualTo(HTTP_OK);
49+
assertThat(get(client, s, "/files/a/b/c").statusCode()).isEqualTo(HTTP_OK);
50+
assertThat(get(client, s, "/filesx/a").statusCode()).isEqualTo(HTTP_NOT_FOUND);
51+
}
52+
}
53+
54+
@Test
55+
void midPathDoubleStar() throws Exception {
56+
RequestHandler ok = req -> Response.of(HTTP_OK, "ok");
57+
try (var s =
58+
newBuilder()
59+
.spec(spec)
60+
.handlers(stubAllHandlers(Map.of()))
61+
.port(0)
62+
.extraRoute("/schemas/**/openapi.yaml", ok)
63+
.build();
64+
var client = httpClient()) {
65+
66+
assertThat(get(client, s, "/schemas/a/openapi.yaml").statusCode()).isEqualTo(HTTP_OK);
67+
assertThat(get(client, s, "/schemas/a/b/openapi.yaml").statusCode()).isEqualTo(HTTP_OK);
68+
assertThat(get(client, s, "/schemas/openapi.yaml").statusCode()).isEqualTo(HTTP_NOT_FOUND);
69+
}
70+
}
71+
72+
@Test
73+
void exactExtraStillWorks() throws Exception {
74+
try (var s =
75+
newBuilder()
76+
.spec(spec)
77+
.handlers(stubAllHandlers(Map.of()))
78+
.port(0)
79+
.extraRoute("/alive", Handlers.aliveHandler())
80+
.build();
81+
var client = httpClient()) {
82+
83+
assertThat(get(client, s, "/alive").statusCode()).isEqualTo(204);
84+
assertThat(get(client, s, "/alive232").statusCode()).isEqualTo(HTTP_NOT_FOUND);
85+
}
86+
}
87+
88+
@Test
89+
void traversalReturns400() throws Exception {
90+
RequestHandler ok = req -> Response.of(HTTP_OK, "ok");
91+
try (var s =
92+
newBuilder()
93+
.spec(spec)
94+
.handlers(stubAllHandlers(Map.of()))
95+
.port(0)
96+
.extraRoute("/files/**", ok)
97+
.build();
98+
var client = httpClient()) {
99+
100+
assertThat(get(client, s, "/files/../etc/passwd").statusCode()).isEqualTo(HTTP_BAD_REQUEST);
101+
assertThat(get(client, s, "/files/%2e%2e/etc/passwd").statusCode())
102+
.isEqualTo(HTTP_BAD_REQUEST);
103+
assertThat(get(client, s, "/files/%252e%252e/etc/passwd").statusCode())
104+
.isEqualTo(HTTP_BAD_REQUEST);
105+
// %2f is a percent-encoded slash — reject encoded path separators
106+
assertThat(get(client, s, "/files/%2fetc").statusCode()).isEqualTo(HTTP_BAD_REQUEST);
107+
// %5c is a backslash — reject encoded backslashes
108+
assertThat(get(client, s, "/files/x%5cy").statusCode()).isEqualTo(HTTP_BAD_REQUEST);
109+
// %00 is a null byte — java.net.URI rejects raw NUL in the path; defense at the
110+
// router is still valid for clients that bypass URI parsing, but we cannot express
111+
// this vector via java.net.http.HttpClient (URI.create throws before the wire).
112+
// assertThat(get(client, s, "/files/x%00").statusCode()).isEqualTo(HTTP_BAD_REQUEST);
113+
// %0a is a line-feed — same reason as %00: JDK URI rejects it pre-wire.
114+
// assertThat(get(client, s, "/files/x%0ay").statusCode()).isEqualTo(HTTP_BAD_REQUEST);
115+
assertThat(get(client, s, "/files//x").statusCode()).isEqualTo(HTTP_BAD_REQUEST);
116+
assertThat(get(client, s, "/files/.").statusCode()).isEqualTo(HTTP_BAD_REQUEST);
117+
}
118+
}
119+
120+
@Test
121+
void basePathSpecRouteWinsOverExtras() throws Exception {
122+
RequestHandler greedy = req -> Response.of(HTTP_OK, "should not see this");
123+
try (var s =
124+
newBuilder()
125+
.spec(spec)
126+
.handlers(stubAllHandlers(Map.of()))
127+
.port(0)
128+
.extraRoute("/**", greedy)
129+
.build();
130+
var client = httpClient()) {
131+
132+
assertThat(get(client, s, "/api/v1/data").statusCode()).isEqualTo(HTTP_OK);
133+
assertThat(get(client, s, "/totally-not-a-spec-route").statusCode()).isEqualTo(HTTP_OK);
134+
}
135+
}
136+
137+
private HttpResponse<String> get(HttpClient client, OpenApiServer s, String path)
138+
throws Exception {
139+
var req =
140+
HttpRequest.newBuilder()
141+
.uri(URI.create("http://localhost:" + s.listenPort() + path))
142+
.GET()
143+
.build();
144+
return client.send(req, BodyHandlers.ofString());
145+
}
146+
}

0 commit comments

Comments
 (0)