Reformat code for improved readability and consistency across classes
CI - Test, Publish and Release / run-tests (push) Failing after 16s
CI - Test, Publish and Release / create-release (push) Has been skipped
CI - Test, Publish and Release / check-and-publish (push) Has been skipped

This commit is contained in:
2026-06-15 07:50:17 +02:00
parent 893bb0b7bd
commit 02688a2f47
4 changed files with 72 additions and 88 deletions
@@ -63,8 +63,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
/** /**
* Executor running one virtual thread per task, used to offload blocking handler work. * Executor running one virtual thread per task, used to offload blocking handler work.
*/ */
private static final Executor VT_EXECUTOR = private static final Executor VT_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
Executors.newVirtualThreadPerTaskExecutor();
/** /**
* Router resolving requests to handlers. * Router resolving requests to handlers.
@@ -112,8 +111,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
* @param authGate the auth gate, or {@code null} to disable the auth layer * @param authGate the auth gate, or {@code null} to disable the auth layer
* @param trustedProxies the trusted-proxy policy, or {@code null} for {@link TrustedProxies#none()} * @param trustedProxies the trusted-proxy policy, or {@code null} for {@link TrustedProxies#none()}
*/ */
public HttpRequestHandler(Router router, CorsHandler cors, RateLimitGate rateLimit, public HttpRequestHandler(Router router, CorsHandler cors, RateLimitGate rateLimit, AuthGate authGate, TrustedProxies trustedProxies) {
AuthGate authGate, TrustedProxies trustedProxies) {
this(router, cors, rateLimit, authGate, trustedProxies, null, null, null, false); this(router, cors, rateLimit, authGate, trustedProxies, null, null, null, false);
} }
@@ -130,10 +128,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
* @param securityHeaders the security-header policy, or {@code null} to add no security headers * @param securityHeaders the security-header policy, or {@code null} to add no security headers
* @param secure whether the server's connections are secured by TLS (gates HSTS) * @param secure whether the server's connections are secured by TLS (gates HSTS)
*/ */
public HttpRequestHandler(Router router, CorsHandler cors, RateLimitGate rateLimit, public HttpRequestHandler(Router router, CorsHandler cors, RateLimitGate rateLimit, AuthGate authGate, TrustedProxies trustedProxies, WebSocketRouter wsRouter, WebSocketConfig wsConfig, SecurityHeaders securityHeaders, boolean secure) {
AuthGate authGate, TrustedProxies trustedProxies,
WebSocketRouter wsRouter, WebSocketConfig wsConfig,
SecurityHeaders securityHeaders, boolean secure) {
this.router = router; this.router = router;
this.cors = cors; this.cors = cors;
this.rateLimit = rateLimit; this.rateLimit = rateLimit;
@@ -156,9 +151,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
* @param wsRouter the WebSocket router, or {@code null} to disable WebSocket support * @param wsRouter the WebSocket router, or {@code null} to disable WebSocket support
* @param wsConfig the WebSocket configuration, used only when {@code wsRouter} is non-null * @param wsConfig the WebSocket configuration, used only when {@code wsRouter} is non-null
*/ */
public HttpRequestHandler(Router router, CorsHandler cors, RateLimitGate rateLimit, public HttpRequestHandler(Router router, CorsHandler cors, RateLimitGate rateLimit, AuthGate authGate, TrustedProxies trustedProxies, WebSocketRouter wsRouter, WebSocketConfig wsConfig) {
AuthGate authGate, TrustedProxies trustedProxies,
WebSocketRouter wsRouter, WebSocketConfig wsConfig) {
this(router, cors, rateLimit, authGate, trustedProxies, wsRouter, wsConfig, null, false); this(router, cors, rateLimit, authGate, trustedProxies, wsRouter, wsConfig, null, false);
} }
@@ -264,21 +257,15 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
if (wsConfig.idleTimeout() != null) { if (wsConfig.idleTimeout() != null) {
long secs = Math.max(1, wsConfig.idleTimeout().toSeconds()); long secs = Math.max(1, wsConfig.idleTimeout().toSeconds());
pipeline.addBefore(myName, "ws-idle", pipeline.addBefore(myName, "ws-idle", new IdleStateHandler(0, 0, secs, TimeUnit.SECONDS));
new IdleStateHandler(0, 0, secs, TimeUnit.SECONDS));
} }
if (wsConfig.compression()) { if (wsConfig.compression()) {
pipeline.addBefore(myName, "ws-deflate", pipeline.addBefore(myName, "ws-deflate", new WebSocketServerCompressionHandler());
new WebSocketServerCompressionHandler());
} }
pipeline.addBefore(myName, "ws-proto", pipeline.addBefore(myName, "ws-proto", new WebSocketServerProtocolHandler(protoCfg));
new WebSocketServerProtocolHandler(protoCfg));
pipeline.addBefore(myName, "ws-aggregator", pipeline.addBefore(myName, "ws-aggregator", new WebSocketFrameAggregator(wsConfig.maxAggregatedMessageSize()));
new WebSocketFrameAggregator(wsConfig.maxAggregatedMessageSize())); pipeline.addBefore(myName, "ws-frames", WebSocketFrameHandlerFactory.create(resolution.handler(), path, resolution.pathParams(), principal, wsConfig.maxQueuedMessages()));
pipeline.addBefore(myName, "ws-frames",
WebSocketFrameHandlerFactory.create(resolution.handler(), path, resolution.pathParams(),
principal, wsConfig.maxQueuedMessages()));
ChannelHandlerContext anchor = pipeline.context(HttpObjectAggregator.class); ChannelHandlerContext anchor = pipeline.context(HttpObjectAggregator.class);
if (anchor == null) anchor = pipeline.firstContext(); if (anchor == null) anchor = pipeline.firstContext();
@@ -315,8 +302,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
String clientIp = resolveClientIp(ctx, raw); String clientIp = resolveClientIp(ctx, raw);
Router.Resolution resolution = router.resolve(raw.method(), path); Router.Resolution resolution = router.resolve(raw.method(), path);
Map<String, String> params = resolution instanceof Router.Resolution.Match m Map<String, String> params = resolution instanceof Router.Resolution.Match m ? m.pathParams() : Map.of();
? m.pathParams() : Map.of();
Request request = new Request(raw, params); Request request = new Request(raw, params);
request.clientIp(clientIp); request.clientIp(clientIp);
@@ -346,11 +332,11 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
switch (resolution) { switch (resolution) {
case Router.Resolution.Match m -> { case Router.Resolution.Match m -> {
try { try {
for (var mw : router.middlewares()) mw.accept(request, res); for (var mw : router.middlewares())
mw.accept(request, res);
m.handler().handle(request, res); m.handler().handle(request, res);
} catch (BadRequestException e) { } catch (BadRequestException e) {
res.status(400).json(Map.of("error", res.status(400).json(Map.of("error", e.getMessage() == null ? "Bad Request" : e.getMessage()));
e.getMessage() == null ? "Bad Request" : e.getMessage()));
} catch (Exception e) { } catch (Exception e) {
LOG.log(Level.ERROR, "Handler failed for " + raw.method() + " " + path, e); LOG.log(Level.ERROR, "Handler failed for " + raw.method() + " " + path, e);
res.status(500).json("{\"error\":\"Internal Server Error\"}"); res.status(500).json("{\"error\":\"Internal Server Error\"}");
@@ -403,8 +389,7 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHt
*/ */
private String resolveClientIp(ChannelHandlerContext ctx, FullHttpRequest raw) { private String resolveClientIp(ChannelHandlerContext ctx, FullHttpRequest raw) {
SocketAddress addr = ctx.channel().remoteAddress(); SocketAddress addr = ctx.channel().remoteAddress();
String socketIp = (addr instanceof InetSocketAddress isa && isa.getAddress() != null) String socketIp = (addr instanceof InetSocketAddress isa && isa.getAddress() != null) ? isa.getAddress().getHostAddress() : "unknown";
? isa.getAddress().getHostAddress() : "unknown";
String forwarded = raw.headers().get(ClientIp.FORWARDED_FOR_HEADER); String forwarded = raw.headers().get(ClientIp.FORWARDED_FOR_HEADER);
return ClientIp.resolve(socketIp, forwarded, trustedProxies); return ClientIp.resolve(socketIp, forwarded, trustedProxies);
} }
@@ -199,7 +199,8 @@ public final class HttpServer {
* @return this instance, for fluent chaining * @return this instance, for fluent chaining
*/ */
public HttpServer maxHttpContentLength(int bytes) { public HttpServer maxHttpContentLength(int bytes) {
if (bytes <= 0) throw new IllegalArgumentException("maxHttpContentLength must be > 0"); if (bytes <= 0)
throw new IllegalArgumentException("maxHttpContentLength must be > 0");
this.maxHttpContentLength = bytes; this.maxHttpContentLength = bytes;
return this; return this;
} }
@@ -292,14 +293,11 @@ public final class HttpServer {
pipeline.addLast("ssl", tlsCfg.newHandler(ch.alloc())); pipeline.addLast("ssl", tlsCfg.newHandler(ch.alloc()));
} }
if (readTimeoutSeconds > 0) { if (readTimeoutSeconds > 0) {
pipeline.addLast("read-timeout", pipeline.addLast("read-timeout", new ReadTimeoutHandler(readTimeoutSeconds, TimeUnit.SECONDS));
new ReadTimeoutHandler(readTimeoutSeconds, TimeUnit.SECONDS));
} }
pipeline.addLast(new HttpServerCodec()) pipeline.addLast(new HttpServerCodec())
.addLast(new HttpObjectAggregator(maxContent)) .addLast(new HttpObjectAggregator(maxContent))
.addLast(new HttpRequestHandler(router, corsHandler, rateLimitGate, .addLast(new HttpRequestHandler(router, corsHandler, rateLimitGate, auth, proxies, websocketRouter, websocketConfig, secHeaders, tlsEnabled));
auth, proxies, websocketRouter, websocketConfig,
secHeaders, tlsEnabled));
} }
}) })
.bind(port).sync().channel().closeFuture().sync(); .bind(port).sync().channel().closeFuture().sync();
@@ -37,6 +37,7 @@ public final class AuthConfig {
* Optional {@code WWW-Authenticate} challenge sent with {@code 401} responses. * Optional {@code WWW-Authenticate} challenge sent with {@code 401} responses.
*/ */
private final String challenge; private final String challenge;
private AuthConfig(Builder b) { private AuthConfig(Builder b) {
this.globalRule = b.globalRule; this.globalRule = b.globalRule;
this.exactPathRules = Map.copyOf(b.exactPathRules); this.exactPathRules = Map.copyOf(b.exactPathRules);