You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+
## Project
6
+
7
+
A lightweight Java 25 library that wraps the JDK's built-in `com.sun.net.httpserver.HttpServer` and exposes endpoints declared in an OpenAPI 3.1.x specification. Consumers register `HttpHandler` instances by OpenAPI `operationId`. The library is published as a JAR; the example launcher under `src/test/java/.../start/ServerLauncher.java` is for local development only.
8
+
9
+
Java 25 is required (see `.java-version`). The server uses thread-per-request with virtual threads.
- Single test class: `mvn test -Dtest=OpenApiServerTest`
17
+
- Single test method: `mvn test -Dtest=OpenApiServerTest#methodName`
18
+
- Coverage report: produced at `target/site/jacoco/` after `mvn verify`
19
+
- POM is sort-checked by `sortpom-maven-plugin` during `validate`; fix with `mvn sortpom:sort`
20
+
- Pre-commit hooks (Google Java formatter, commitlint, editorconfig, etc.) run via `pre-commit`; install with `pre-commit install --hook-type pre-commit --hook-type commit-msg`
21
+
- Run example server locally: `mvn test-compile exec:java -Dexec.mainClass=com.retailsvc.http.start.ServerLauncher -Dexec.classpathScope=test` (or run `ServerLauncher` from the IDE). Test schema lives at `src/test/resources/openapi.json`.
22
+
- Acceptance/load probes: k6 scripts under `acceptance/k6/`. ZAP scan via `./zap.sh`.
23
+
24
+
## Architecture
25
+
26
+
Request flow when `OpenApiServer` boots (`src/main/java/com/retailsvc/http/OpenApiServer.java`):
27
+
28
+
1.`HttpServer` is created on a port with a virtual-thread-per-task executor.
29
+
2. A single `HttpContext` is registered at `spec.basePath()` (the first `servers[].url` path from the OpenAPI doc). A catch-all `/` context returns 404.
30
+
3. Three filters run in order on every request:
31
+
-`ExceptionFilter` — wraps the chain; delegates uncaught exceptions to the user-supplied `ExceptionHandler` (default in `Handlers`).
32
+
-`RequestPreparationFilter` — reads the raw request body, stashes it as an exchange attribute, runs OpenAPI parameter + body validation via `DefaultValidator`, and stores the resolved `operationId` on the exchange.
33
+
-`DispatchHandler` — looks up the `HttpHandler` registered for that `operationId` in the user-supplied map and invokes it. Missing handler → `MissingOperationHandlerException`.
34
+
35
+
Key abstractions:
36
+
37
+
-`com.retailsvc.http.spec.Spec` — parsed from a consumer-supplied `Map<String, Object>` via `Spec.from(raw)`. No JSON library dependency in the library itself; callers use Gson, Jackson, SnakeYAML, etc. to produce the map.
-`com.retailsvc.http.validate.DefaultValidator` — single class using `switch` pattern-match over `Schema` subtypes. Validation failures produce RFC 7807 `application/problem+json` 400 responses.
40
+
-`com.retailsvc.http.internal.Router` — two indexes: exact path map and templated path list. Resolves `operationId` + extracted path variables for each request.
41
+
-`JsonMapper` — `@FunctionalInterface`; single method `Object mapFrom(byte[])`. Callers supply a lambda (see README).
42
+
-`com.retailsvc.http.Request` — static helper; `Request.bytes(exchange)` returns raw body bytes, `Request.parsed(exchange)` returns the `Object` produced by the `JsonMapper`.
43
+
44
+
## Conventions
45
+
46
+
- Code is formatted with the Google Java Formatter (enforced by pre-commit). Do not hand-format.
47
+
- Commit messages must satisfy commitlint (Conventional Commits).
48
+
- Integration tests are named `*IT.java` and run only under `mvn verify`, not `mvn test`.
49
+
- The library has `slf4j-api` as `provided` — never add a transitive logging binding to main scope.
For YAML, replace the JSON parsing line with SnakeYAML:
93
+
```java
94
+
Map<String, Object> raw =newYaml().load(Files.newInputStream(Path.of("openapi.yaml")));
95
+
```
96
+
The rest is identical.
97
+
90
98
## Features
91
99
- OpenAPI specification support
92
100
- Automatic request body parsing for JSON arrays and objects
@@ -112,4 +120,17 @@ Schemas are located under test resources folder.
112
120
- Example requests can be found under `acceptance/k6` that can be a base for exploring the functionality.
113
121
- The logger in the configuration needs to be enabled to get some insight into the code.
114
122
123
+
## Performance and caveats
124
+
125
+
The library wraps the JDK's bundled `com.sun.net.httpserver.HttpServer` and uses a virtual-thread-per-request executor. On a developer laptop (Apple Silicon, single instance, default JVM flags) it sustains roughly:
126
+
127
+
-**~32k requests/second** for small JSON GETs and POSTs (~300 byte bodies), measured via `k6` at 30 sustained VUs over 45 seconds (1.4M requests, **100% of checks passing**, 0% HTTP failures).
128
+
129
+
A few things to know:
130
+
131
+
-**Single-process model.** No horizontal scaling primitives are bundled; run multiple instances behind a load balancer for production scale.
132
+
-**JDK HttpServer is the throughput ceiling.** It's documented as a low-throughput / dev-test server. If you need to go materially above the rates above, deploy the same filter/validator/router stack on Jetty, Helidon Níma, or Netty — the spec and validation code is server-agnostic.
133
+
-**Per-request state uses `ScopedValue`** (Java 25, JEP 506), not `HttpExchange.setAttribute`. This matters if a handler offloads work to an executor that's not a `StructuredTaskScope`-managed child thread: the `ScopedValue` is not visible there, so the handler must capture the values it needs (e.g. `byte[] body = Request.bytes();`) before submitting.
134
+
-**`HttpExchange.sendResponseHeaders(rCode, length)` gotcha.** When a handler has no response body, pass `-1` (`Content-Length: 0`, no body); passing `0` produces a chunked response with zero chunks, which is technically non-conformant.
0 commit comments