Initial Commit

This commit is contained in:
CodingPhoenix
2026-05-08 11:04:40 +02:00
commit 392658d54e
25 changed files with 1055 additions and 0 deletions
@@ -0,0 +1,88 @@
package dev.coph.nextusweb.server;
import dev.coph.nextusweb.server.router.Request;
import dev.coph.nextusweb.server.router.Response;
import dev.coph.nextusweb.server.router.Router;
import dev.coph.nextusweb.server.router.exception.BadRequestException;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public final class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
private static final Executor VT_EXECUTOR =
Executors.newVirtualThreadPerTaskExecutor();
private final Router router;
public HttpRequestHandler(Router router) {
this.router = router;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
req.retain();
VT_EXECUTOR.execute(() -> {
try {
handle(ctx, req);
} finally {
req.release();
}
});
}
private void handle(ChannelHandlerContext ctx, FullHttpRequest raw) {
String path = new QueryStringDecoder(raw.uri()).path();
Router.Resolution resolution = router.resolve(raw.method(), path);
Response res = new Response();
switch (resolution) {
case Router.Resolution.Match m -> {
Request request = new Request(raw, m.pathParams());
try {
for (var mw : router.middlewares()) mw.accept(request, res);
m.handler().handle(request, res);
} catch (BadRequestException e) {
res.status(400).json("{\"error\":\"" + e.getMessage() + "\"}");
} catch (Exception e) {
res.status(500).text("Internal Server Error: " + e.getMessage());
}
}
case Router.Resolution.MethodNotAllowed mna -> {
String allow = mna.allowedMethods().stream()
.map(HttpMethod::name)
.sorted()
.collect(Collectors.joining(", "));
res.status(405)
.header(HttpHeaderNames.ALLOW.toString(), allow)
.json("{\"error\":\"Method Not Allowed\",\"allowed\":\"" + allow + "\"}");
}
case Router.Resolution.NotFound nf -> res.status(404).json("{\"error\":\"Not Found\"}");
}
send(ctx, res);
}
private void send(ChannelHandlerContext ctx, Response res) {
var nettyRes = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.valueOf(res.status()),
Unpooled.wrappedBuffer(res.body())
);
nettyRes.headers().add(res.headers());
nettyRes.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, res.body().length);
ctx.writeAndFlush(nettyRes).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
}