1package dev.rafex.ether.http.jetty12;
29import java.nio.charset.StandardCharsets;
30import java.util.LinkedHashMap;
34import org.eclipse.jetty.server.Handler;
35import org.eclipse.jetty.server.Request;
36import org.eclipse.jetty.server.Response;
37import org.eclipse.jetty.util.Callback;
38import org.eclipse.jetty.util.MultiMap;
39import org.eclipse.jetty.util.UrlEncoded;
41import dev.rafex.ether.json.JsonCodec;
42import dev.rafex.ether.http.core.DefaultErrorMapper;
43import dev.rafex.ether.http.core.ErrorMapper;
44import dev.rafex.ether.http.core.HttpResource;
45import dev.rafex.ether.http.core.Route;
46import dev.rafex.ether.http.core.RouteMatcher;
59 this.jsonCodec = jsonCodec;
60 this.errorMapper = errorMapper;
71 public boolean handle(
final Request request,
final Response response,
final Callback callback) {
72 final var path = request.getHttpURI().getPath();
73 if (!matchesBasePath(path)) {
77 final var relPath = normalizeRelPath(path);
79 if (match.isEmpty()) {
80 errorResponses.notFound(response, callback, path);
84 final var routeMatch = match.get();
85 final var x =
new JettyHttpExchange(request, response, callback, routeMatch.pathParams(), parseQueryMap(request),
86 routeMatch.route().allowedMethods(), jsonCodec);
88 final var method = request.getMethod().toUpperCase();
89 if (!routeMatch.route().allows(method) && !
"OPTIONS".equals(method)) {
95 return dispatch(method, x);
96 }
catch (
final Exception e) {
97 final var mapped = errorMapper.map(e);
98 errorResponses.error(response, callback, mapped, path);
103 private boolean dispatch(
final String method,
final JettyHttpExchange x)
throws Exception {
104 return switch (method) {
105 case "GET" ->
get(x);
106 case "POST" ->
post(x);
107 case "PUT" ->
put(x);
108 case "DELETE" ->
delete(x);
109 case "PATCH" ->
patch(x);
112 x.methodNotAllowed();
118 private String normalizeRelPath(
final String absolutePath) {
120 if (absolutePath.length() == base.length()) {
123 final var rel = absolutePath.substring(base.length());
124 return rel.isEmpty() ?
"/" : rel;
127 private boolean matchesBasePath(
final String path) {
129 if (
"/".equals(base)) {
130 return path !=
null && path.startsWith(
"/");
132 if (base.equals(path)) {
135 return path.startsWith(base +
"/");
138 private static Map<String, List<String>> parseQueryMap(
final Request request) {
139 final MultiMap<String> params =
new MultiMap<>();
140 final var rawQuery = request.getHttpURI().getQuery();
141 if (rawQuery !=
null && !rawQuery.isEmpty()) {
142 UrlEncoded.decodeTo(rawQuery, params, StandardCharsets.UTF_8);
145 final var out =
new LinkedHashMap<String, List<String>>();
146 for (
final var key : params.keySet()) {
147 final var values = params.getValues(key);
148 out.put(key, values ==
null ? List.of() : List.copyOf(values));
155 if (value ==
null || value.isBlank()) {
static Optional< RouteMatch > match(final String relPath, final List< Route > routes)
String queryFirst(final String name)
static String queryParam(final JettyHttpExchange x, final String key)
NonBlockingResourceHandler(final JsonCodec jsonCodec, final ErrorMapper errorMapper)
abstract String basePath()
NonBlockingResourceHandler(final JsonCodec jsonCodec)
boolean handle(final Request request, final Response response, final Callback callback)
default Set< String > supportedMethods()
default boolean patch(final HttpExchange x)
default boolean delete(final HttpExchange x)
default boolean put(final HttpExchange x)
default boolean options(final HttpExchange x)
default boolean post(final HttpExchange x)