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
{{ message }}
Repository navigation
Commit ef20528
Browse filesBrowse the repository at this point in the historyBrowse files
HttpServer.stop(delay) can wait out the full delay with nothing in
flight: when a client closes an idle keep-alive connection just before
stop, the JDK never counts its finished latch down (JDK 25 ServerImpl).
OpenApiServer now counts in-flight requests with a filter on every
context it creates. stop(delay) closes the listener at once through a
background HttpServer.stop(delay), waits on the counter for at most
the delay, then calls HttpServer.stop(0). Later and concurrent stop
calls wait for the first. inFlightRequests() exposes the count.
Copy file name to clipboardExpand all lines: CLAUDE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,7 +27,7 @@ Request flow when `OpenApiServer` boots (`src/main/java/com/retailsvc/http/OpenA
27
27
28
28
1.`HttpServer` is created on a port with a virtual-thread-per-task executor.
29
29
2. One `HttpContext` is registered per spec binding at `spec.basePath()` (the first `servers[].url` path from the OpenAPI doc). Unless a binding owns `/`, a catch-all `/` context serves extra routes via `ExtrasRouter` and 404s everything else; `ExceptionFilter` wraps that context only.
30
-
3. On a binding context, two filters run in order, then the handler:
30
+
3.Every context the server creates, the binding contexts and the extras one, starts with an `InFlightCounter` filter that counts the exchange for `OpenApiServer.inFlightRequests()` and `stop(int)`. On a binding context, two more filters run in order, then the handler:
31
31
-`RequestPreparationFilter` — reads the request body through `RequestBodyReader` (which decodes a registered `Content-Encoding` — gzip is built in — under a size cap), resolves the route, runs OpenAPI parameter + body validation via `DefaultValidator`, and binds the resulting `Request` into the `DispatchHandler.CURRENT` scoped value. It renders its own failures through the `ExceptionHandler` rather than relying on `ExceptionFilter`.
32
32
-`SecurityFilter` — enforces the spec's `securitySchemes` / `security`, re-binding the `Request` with resolved principals. It writes its 401/403 responses straight to the exchange.
33
33
-`DispatchHandler` — looks up the `RequestHandler` registered for the resolved `operationId` in the user-supplied map and invokes it, applying interceptors and response decorators. Handler coverage is verified at boot, so the lookup never returns `null`.
Copy file name to clipboardExpand all lines: README.md
+16Lines changed: 16 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -574,6 +574,22 @@ try (var server = OpenApiServer.builder()
574
574
`stop(int)` and `shutdownTimeoutSeconds(int)` reject negative values with
575
575
`IllegalArgumentException`.
576
576
577
+
Stopping closes the listening socket at once, so no new connection is accepted. Requests on
578
+
keep-alive connections that are already open are served until the JDK sees no exchange in flight,
579
+
which can come before the after-response runnables of the last request have finished; the JDK then
580
+
closes every connection, keep-alive ones included. The wait ends as soon as `inFlightRequests()` reads `0`, or when the delay elapses; every connection
581
+
is then closed, which cuts off what is still running. A request counts as in flight from the first
582
+
filter until its after-response runnables (`Request.afterResponse`, hooks) have run, so `stop` waits
583
+
for those too. A request the JDK rejects before it dispatches it, such as a malformed one, is not
584
+
counted.
585
+
586
+
Only the first `stop` call stops the server. A later or concurrent call waits until that stop is
587
+
complete, then returns (or throws `IllegalStateException` if that stop failed); its own delay is
588
+
ignored. A thread interrupted while it waits stops waiting and keeps its interrupt flag.
589
+
590
+
`inFlightRequests()` is the server's own count, readable at any time, for example to log it or to
0 commit comments