Skip to content

Commit f24ea84

Browse files
authored
fix: Default basePath to / when server URL has no path (#74)
* fix: Default basePath to / when server URL has no path URI.create("http://127.0.0.1:4444").getPath() returns an empty string, not null, so the previous Optional.ofNullable fallback left basePath empty and HttpServer.createContext rejected it. * test: Verify handler dispatch when basePath is / The catch-all 404 context at / collided with the spec context when the server URL had no path. Skip the catch-all in that case.
1 parent ffb8af1 commit f24ea84

4 files changed

Lines changed: 58 additions & 3 deletions

File tree

src/main/java/com/retailsvc/http/OpenApiServer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ record HandlerConfig(
9393
this.httpServer = HttpServer.create(socketAddress, 0);
9494
httpServer.setExecutor(newThreadPerTaskExecutor(ofVirtual().name("http-", 0).factory()));
9595

96-
HttpContext ctx = httpServer.createContext(Optional.ofNullable(spec.basePath()).orElse("/"));
96+
String basePath = Optional.ofNullable(spec.basePath()).orElse("/");
97+
HttpContext ctx = httpServer.createContext(basePath);
9798
ctx.getFilters().add(new ExceptionFilter(exceptionHandler));
9899
ctx.getFilters().add(new RequestPreparationFilter(spec, router, validator, bodyMappers));
99100
ctx.getFilters()
@@ -117,7 +118,9 @@ record HandlerConfig(
117118
extraCtx.setHandler(e.getValue());
118119
}
119120

120-
httpServer.createContext("/", Handlers.notFoundHandler());
121+
if (!"/".equals(basePath)) {
122+
httpServer.createContext("/", Handlers.notFoundHandler());
123+
}
121124
httpServer.start();
122125

123126
this.shutdownTimeoutSeconds = shutdownTimeoutSeconds;

src/main/java/com/retailsvc/http/spec/Spec.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ private static String computeBasePath(List<Server> servers) {
245245
if (servers.isEmpty()) {
246246
throw new IllegalStateException("no servers declared");
247247
}
248-
return Optional.ofNullable(URI.create(servers.getFirst().url()).getPath()).orElse("");
248+
String path = URI.create(servers.getFirst().url()).getPath();
249+
return (path == null || path.isEmpty()) ? "/" : path;
249250
}
250251

251252
private static <T> Map<String, T> indexByRef(Map<String, T> components, String prefix) {

src/test/java/com/retailsvc/http/OpenApiServerTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,45 @@ void shouldBindOnlyToLoopbackWhenBindAddressIsLoopback() throws Exception {
8989
}
9090
}
9191

92+
@Test
93+
void shouldDispatchHandlerOnRootPathWhenServerUrlHasNoPath() throws Exception {
94+
Map<String, Object> raw =
95+
Map.of(
96+
"openapi", "3.1.0",
97+
"info", Map.of("title", "Test API", "version", "1.0"),
98+
"servers", List.of(Map.of("url", "http://127.0.0.1:4444")),
99+
"paths",
100+
Map.of(
101+
"/",
102+
Map.of(
103+
"get",
104+
Map.of(
105+
"operationId",
106+
"root",
107+
"responses",
108+
Map.of("204", Map.of("description", "ok"))))));
109+
Spec spec = Spec.from(raw);
110+
RequestHandler rootHandler = request -> Response.empty();
111+
try (var server =
112+
OpenApiServer.builder()
113+
.spec(spec)
114+
.handlers(Map.of("root", rootHandler))
115+
.port(0)
116+
.bindAddress(InetAddress.getLoopbackAddress())
117+
.build()) {
118+
int port = server.listenPort();
119+
HttpClient client =
120+
HttpClient.newBuilder()
121+
.executor(newVirtualThreadPerTaskExecutor())
122+
.version(HTTP_1_1)
123+
.build();
124+
HttpRequest request =
125+
HttpRequest.newBuilder().uri(URI.create("http://127.0.0.1:" + port + "/")).GET().build();
126+
HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
127+
assertThat(response.statusCode()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
128+
}
129+
}
130+
92131
@Test
93132
void shouldBindToWildcardWhenBindAddressIsUnset() throws IOException {
94133
try (var server =

src/test/java/com/retailsvc/http/spec/SpecTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ void parsesMinimalSpec() {
3333
assertThat(spec.operations()).isEmpty();
3434
}
3535

36+
@Test
37+
void basePathDefaultsToRootWhenServerUrlHasNoPath() {
38+
Map<String, Object> raw =
39+
Map.of(
40+
"openapi", "3.1.0",
41+
"info", Map.of("title", "x", "version", "1"),
42+
"servers", List.of(Map.of("url", "http://127.0.0.1:4444")),
43+
"paths", Map.of());
44+
Spec spec = Spec.from(raw);
45+
assertThat(spec.basePath()).isEqualTo("/");
46+
}
47+
3648
@Test
3749
void parsesPathsWithMethods() {
3850
Map<String, Object> raw =

0 commit comments

Comments
 (0)