Initial Commit
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user