diff --git a/avaje-j12/pom.xml b/avaje-j12/pom.xml new file mode 100644 index 00000000..c2ab3f03 --- /dev/null +++ b/avaje-j12/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + io.avaje + avaje-jex-parent + 2.6-SNAPSHOT + + + avaje-j12 + + + 21 + 21 + UTF-8 + 12.0.0 + false + + + + + io.avaje + avaje-jex + 2.6-SNAPSHOT + + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + + diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyBuilder.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyBuilder.java new file mode 100644 index 00000000..2382faa4 --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyBuilder.java @@ -0,0 +1,59 @@ +package io.avaje.jex.jetty; + +import io.avaje.jex.Jex; +import io.avaje.jex.JexConfig; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; + +/** + * Build the Jetty Server. + */ +class JettyBuilder { + + private static final Logger log = LoggerFactory.getLogger(JettyBuilder.class); + + private final JexConfig jexConfig; + private final JettyServerConfig jettyConfig; + + JettyBuilder(Jex jex, JettyServerConfig jettyConfig) { + this.jexConfig = jex.config(); + this.jettyConfig = jettyConfig; + } + + Server build() { + Server jetty = new Server(pool()); + ServerConnector connector = new ServerConnector(jetty); + connector.setPort(jexConfig.port()); + if (jexConfig.host() != null ) { + connector.setHost(jexConfig.host()); + } + jetty.setConnectors(new Connector[]{connector}); + return jetty; + } + + private ThreadPool pool() { + if (jexConfig.virtualThreads()) { + return virtualThreadBasePool(); + } else { + return jettyConfig.maxThreads() == 0 ? new QueuedThreadPool() : new QueuedThreadPool(jettyConfig.maxThreads()); + } + } + + private ThreadPool virtualThreadBasePool() { + try { + final Class aClass = Class.forName("io.avaje.jex.jetty.threadpool.VirtualThreadPool"); + final Constructor constructor = aClass.getConstructor(); + return (ThreadPool) constructor.newInstance(); + } catch (Exception e) { + throw new IllegalStateException("Failed to start Loom threadPool", e); + } + } + +} diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyJexServer.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyJexServer.java new file mode 100644 index 00000000..dfb76ae7 --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyJexServer.java @@ -0,0 +1,168 @@ +package io.avaje.jex.jetty; + +import io.avaje.jex.spi.SpiRoutes; +import io.avaje.jex.spi.SpiServiceManager; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.Uptime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +class JettyJexServer implements Jex.Server { + + private static final Logger log = LoggerFactory.getLogger(Jex.class); + + private final Jex jex; + private final SpiRoutes routes; + private final ServiceManager serviceManager; + private final JettyServerConfig config; + private final AppLifecycle lifecycle; + private final long startTime; + private final JexConfig jexConfig; + private Server server; + + JettyJexServer(Jex jex, SpiRoutes routes, SpiServiceManager serviceManager) { + this.startTime = System.currentTimeMillis(); + this.jex = jex; + this.jexConfig = jex.config(); + this.lifecycle = jex.lifecycle(); + this.routes = routes; + this.serviceManager = new ServiceManager(serviceManager);//, initMultiPart()); + this.config = initConfig(jex.serverConfig()); + } + + private JettyServerConfig initConfig(ServerConfig config) { + return config == null ? new JettyServerConfig() : (JettyServerConfig) config; + } + +// MultipartUtil initMultiPart() { +// return new MultipartUtil(initMultipartConfigElement(jexConfig.multipartConfig())); +// } +// +// MultipartConfigElement initMultipartConfigElement(UploadConfig uploadConfig) { +// if (uploadConfig == null) { +// final int fileThreshold = jexConfig.multipartFileThreshold(); +// return new MultipartConfigElement(System.getProperty("java.io.tmpdir"), -1, -1, fileThreshold); +// } +// return new MultipartConfigElement(uploadConfig.location(), uploadConfig.maxFileSize(), uploadConfig.maxRequestSize(), uploadConfig.fileSizeThreshold()); +// } + + @Override + public void onShutdown(Runnable onShutdown) { + lifecycle.onShutdown(onShutdown, Integer.MAX_VALUE); + } + + @Override + public void restart() { + try { + server.start(); + logOnStart(server); + lifecycle.status(AppLifecycle.Status.STARTED); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void shutdown() { + try { + log.trace("starting shutdown"); + lifecycle.status(AppLifecycle.Status.STOPPING); + routes.waitForIdle(30); + server.stop(); + log.trace("server http listeners stopped"); + lifecycle.status(AppLifecycle.Status.STOPPED); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public int port() { + return server.getURI().getPort(); + } + + protected Jex.Server start() { + try { + createServer(); + server.start(); + logOnStart(server); + lifecycle.registerShutdownHook(this::shutdown); + lifecycle.status(AppLifecycle.Status.STARTED); + return this; + } catch (Exception e) { + throw new IllegalStateException("Error starting server", e); + } + } + + protected void createServer() { + server = initServer(); + //server.setErrorHandler(); + server.setHandler(initJettyHandler()); + if (server.getStopAtShutdown()) { + // do not use Jetty ShutdownHook, use the AppLifecycle one instead + server.setStopAtShutdown(false); + } + config.server(server); + config.postConfigure(); + } + + protected Server initServer() { + Server server = config.server(); + if (server != null) { + return server; + } + return new JettyBuilder(jex, config).build(); + } + + protected Handler initJettyHandler() { + var baseHandler = new JexHandler(jex, routes, serviceManager, initStaticHandler()); + return baseHandler; +// if (!config.sessions()) { +// return baseHandler; +// } +// var sessionHandler = initSessionHandler(); +// sessionHandler.setHandler(baseHandler); +// return sessionHandler; + } + +// protected SessionHandler initSessionHandler() { +// SessionHandler sh = config.sessionHandler(); +// return sh == null ? defaultSessionHandler() : sh; +// } +// +// protected SessionHandler defaultSessionHandler() { +// SessionHandler sh = new SessionHandler(); +// sh.setHttpOnly(true); +// return sh; +// } + + protected StaticHandler initStaticHandler() { + final List fileSources = jex.staticFiles().getSources(); + if (fileSources == null || fileSources.isEmpty()) { + return null; + } + StaticHandlerFactory factory = new StaticHandlerFactory(); + return factory.build(server, jex, fileSources); + } + + private void logOnStart(org.eclipse.jetty.server.Server server) { + long startup = System.currentTimeMillis() - startTime; + for (Connector c : server.getConnectors()) { + String virtualThreads = jexConfig.virtualThreads() ? "with virtualThreads" : ""; + if (c instanceof ServerConnector) { + ServerConnector sc = (ServerConnector) c; + String host = (sc.getHost() == null) ? "0.0.0.0" : sc.getHost(); + log.info("Listening with {} {}:{} in {}ms @{}ms {}", sc.getProtocols(), host, sc.getLocalPort(), startup, Uptime.getUptime(), virtualThreads); + } else { + log.info("bind to {} in {}ms @{}ms {}", c, startup, Uptime.getUptime(), virtualThreads); + } + } + } + + +} diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyServerConfig.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyServerConfig.java new file mode 100644 index 00000000..069b519a --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyServerConfig.java @@ -0,0 +1,108 @@ +package io.avaje.jex.jetty; + +import io.avaje.jex.ServerConfig; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class JettyServerConfig implements ServerConfig { + + private boolean sessions = true; + private boolean security = true; + + /** + * Set maxThreads when using default QueuedThreadPool. Defaults to 200. + */ + private int maxThreads; +// private SessionHandler sessionHandler; + private Handler contextHandler; + private Server server; + private final List> configureCallback = new ArrayList<>(); + + public boolean sessions() { + return sessions; + } + + public JettyServerConfig sessions(boolean sessions) { + this.sessions = sessions; + return this; + } + + public boolean security() { + return security; + } + + public JettyServerConfig security(boolean security) { + this.security = security; + return this; + } + + public int maxThreads() { + return maxThreads; + } + + public JettyServerConfig maxThreads(int maxThreads) { + this.maxThreads = maxThreads; + return this; + } + +// public SessionHandler sessionHandler() { +// return sessionHandler; +// } +// +// /** +// * Set the SessionHandler to use. When not set one is created automatically. +// */ +// public JettyServerConfig sessionHandler(SessionHandler sessionHandler) { +// this.sessionHandler = sessionHandler; +// return this; +// } + + public Handler contextHandler() { + return contextHandler; + } + + /** + * Set the Jetty Handler to use. When not set one is created automatically. + */ + public JettyServerConfig contextHandler(Handler contextHandler) { + this.contextHandler = contextHandler; + return this; + } + + public Server server() { + return server; + } + + /** + * Set the Jetty Server to use. When not set one is created automatically. + */ + public JettyServerConfig server(Server server) { + this.server = server; + return this; + } + + /** + * Register a callback that is executed after the server and contextHandler have been + * created but before the server has started. + *

+ * When we use this to register filters to the ServletContextHandler or perform other + * changes prior to the server starting. + */ + public JettyServerConfig register(Consumer callback) { + configureCallback.add(callback); + return this; + } + + /** + * Run configuration callbacks prior to starting the server. + */ + void postConfigure() { + for (Consumer callback : configureCallback) { + callback.accept(this); + } + } +} diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyStartServer.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyStartServer.java new file mode 100644 index 00000000..4d572895 --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/JettyStartServer.java @@ -0,0 +1,17 @@ +package io.avaje.jex.jetty; + +import io.avaje.jex.Jex; +import io.avaje.jex.spi.SpiRoutes; +import io.avaje.jex.spi.SpiServiceManager; +import io.avaje.jex.spi.SpiStartServer; + +/** + * Configure and starts the underlying Jetty server. + */ +public class JettyStartServer implements SpiStartServer { + + @Override + public Jex.Server start(Jex jex, SpiRoutes routes, SpiServiceManager serviceManager) { + return new JettyJexServer(jex, routes, serviceManager).start(); + } +} diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/JexHandler.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/JexHandler.java new file mode 100644 index 00000000..c83e4806 --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/JexHandler.java @@ -0,0 +1,116 @@ +package io.avaje.jex.jetty; + +import io.avaje.jex.Context; +import io.avaje.jex.Jex; +import io.avaje.jex.Routing; +import io.avaje.jex.http.NotFoundResponse; +import io.avaje.jex.spi.SpiContext; +import io.avaje.jex.spi.SpiRoutes; +import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +import java.util.Map; + +class JexHandler extends Handler.Abstract { + + //private static final String X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override"; + private final SpiRoutes routes; + private final ServiceManager manager; + private final StaticHandler staticHandler; + + JexHandler(Jex jex, SpiRoutes routes, ServiceManager manager, StaticHandler staticHandler) { + this.routes = routes; + this.manager = manager; + this.staticHandler = staticHandler; + } + + @Override + public boolean handle(Request req, Response res, Callback callback) throws Exception { + + //@Override + //public void handle(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse res) { +// try { + final Routing.Type routeType = method(req); + HttpURI httpURI = req.getHttpURI(); + String uri = httpURI.getPathQuery(); + SpiRoutes.Entry route = routes.match(routeType, uri); + if (route == null) { + var ctx = new JexHttpContext(manager, req, res, uri); + routes.inc(); + try { + processNoRoute(ctx, uri, routeType); + routes.after(uri, ctx); + } catch (Exception e) { + handleException(ctx, e); + } finally { + routes.dec(); + } + } else { + final Map params = route.pathParams(uri); + var ctx = new JexHttpContext(manager, req, res, route.matchPath(), params); + route.inc(); + try { + processRoute(ctx, uri, route); + routes.after(uri, ctx); + callback.succeeded(); + } catch (Exception e) { + handleException(ctx, e); + } finally { + route.dec(); + } + } + return true; +// } + } + + private void handleException(SpiContext ctx, Exception e) { + manager.handleException(ctx, e); + } + + private void processRoute(JexHttpContext ctx, String uri, SpiRoutes.Entry route) { + routes.before(uri, ctx); + ctx.setMode(null); + route.handle(ctx); + } + + //String target, Request baseRequest, + private void processNoRoute(JexHttpContext ctx, String uri, Routing.Type routeType) { + routes.before(uri, ctx); + if (routeType == Routing.Type.HEAD && hasGetHandler(uri)) { + processHead(ctx); + return; + } +// if (routeType == Routing.Type.GET || routeType == Routing.Type.HEAD) { +// // check if handled by static resource +// if (staticHandler != null && staticHandler.handle(target, baseRequest, ctx.req(), ctx.res())) { +// return; +// } +// // todo: check if handled by singlePageHandler +// //if (config.inner.singlePageHandler.handle(ctx)) return@tryWithExceptionMapper +// } +// if (routeType == Routing.Type.OPTIONS && isCorsEnabled(config)) { // CORS is enabled, so we return 200 for OPTIONS +// return@tryWithExceptionMapper +// } +// if (prefer405) { +// //&& availableHandlerTypes.isNotEmpty() +// //val availableHandlerTypes = MethodNotAllowedUtil.findAvailableHttpHandlerTypes(matcher, requestUri) +// //throw MethodNotAllowedResponse(details = MethodNotAllowedUtil.getAvailableHandlerTypes(ctx, availableHandlerTypes)) +// } + throw new NotFoundResponse("uri: " + uri); + } + + private void processHead(Context ctx) { + ctx.status(200); + } + + private boolean hasGetHandler(String uri) { + return routes.match(Routing.Type.GET, uri) != null; + } + + private Routing.Type method(Request req) { + return manager.lookupRoutingType(req.getMethod()); + } +} diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/JexHttpContext.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/JexHttpContext.java new file mode 100644 index 00000000..31333b92 --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/JexHttpContext.java @@ -0,0 +1,508 @@ +package io.avaje.jex.jetty; + +import io.avaje.jex.Context; +import io.avaje.jex.Routing; +import io.avaje.jex.UploadedFile; +import io.avaje.jex.http.RedirectResponse; +import io.avaje.jex.spi.HeaderKeys; +import io.avaje.jex.spi.SpiContext; +import org.eclipse.jetty.http.HttpCookie; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.FormFields; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Fields; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.charset.Charset; +import java.time.Duration; +import java.util.*; +import java.util.stream.Stream; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; + +class JexHttpContext implements SpiContext { + + private final ServiceManager mgr; + protected final Request req; + private final Response res; + private final Map pathParams; + private final String matchedPath; + private String characterEncoding; + private Routing.Type mode; + private Map> formParamMap; + private Fields queryParams; + + JexHttpContext(ServiceManager mgr, Request req, Response res, String matchedPath) { + this.mgr = mgr; + this.req = req; + this.res = res; + this.matchedPath = matchedPath; + this.pathParams = emptyMap(); + } + + JexHttpContext(ServiceManager mgr, Request req, Response res, String matchedPath, Map pathParams) { + this.mgr = mgr; + this.req = req; + this.res = res; + this.matchedPath = matchedPath; + this.pathParams = pathParams; + } + + @Override + public void setMode(Routing.Type mode) { + this.mode = mode; + } + + private String characterEncoding() { + if (characterEncoding == null) { + characterEncoding = mgr.requestCharset(this); + } + return characterEncoding; + } + + public Request req() { + return req; + } + + public Response res() { + return res; + } + + @Override + public boolean isCommitted() { + return res.isCommitted(); + } + + @Override + public void reset() { + res.reset(); + } + + @Override + public Context attribute(String key, Object value) { + req.setAttribute(key, value); + return this; + } + + @SuppressWarnings("unchecked") + @Override + public T attribute(String key) { + return (T) req.getAttribute(key); + } + + @Override + public String cookie(String name) { + List cookies = Request.getCookies(req); + if (cookies != null) { + for (HttpCookie cookie : cookies) { + if (cookie.getName().equals(name)) { + return cookie.getValue(); + } + } + } + return null; + } + + @Override + public Map cookieMap() { + List cookies = Request.getCookies(req); + if (cookies == null) { + return emptyMap(); + } + final Map map = new LinkedHashMap<>(); + for (HttpCookie cookie : cookies) { + map.put(cookie.getName(), cookie.getValue()); + } + return map; + } + + @Override + public Context cookie(Cookie cookie) { + var newCookie = HttpCookie.build(cookie.name(), cookie.value()); + var path = cookie.path() == null ? "/" : cookie.path(); + newCookie.path(path); + final String domain = cookie.domain(); + if (domain != null) { + newCookie.domain(domain); + } + final Duration duration = cookie.maxAge(); + if (duration != null) { + newCookie.maxAge((int) duration.toSeconds()); + } + newCookie.httpOnly(cookie.httpOnly()); + newCookie.secure(cookie.secure()); + Response.addCookie(res, newCookie.build()); + return this; + } + + @Override + public Context cookie(String name, String value, int maxAge) { + var newCookie = HttpCookie.build(name, value) + .path("/") + .maxAge(maxAge) + .build(); + Response.addCookie(res, newCookie); + return this; + } + + @Override + public Context cookie(String name, String value) { + return cookie(name, value, -1); + } + + @Override + public Context removeCookie(String name) { + return removeCookie(name, null); + } + + @Override + public Context removeCookie(String name, String path) { + if (path == null) { + path = "/"; + } + var newCookie = HttpCookie.build(name, "") + .path(path) + .maxAge(0); + Response.addCookie(res, newCookie.build()); + return this; + } + + @Override + public void redirect(String location) { + redirect(location, 302); + } + + @Override + public void redirect(String location, int statusCode) { + //Response.sendRedirect(); + res.getHeaders().add(HeaderKeys.LOCATION, location); + status(statusCode); + if (mode == Routing.Type.BEFORE) { + throw new RedirectResponse(statusCode); + } + } + + @Override + public void performRedirect() { + // do nothing + } + + @Override + public String matchedPath() { + return matchedPath; + } + + @Override + public InputStream inputStream() { + return Request.asInputStream(req); + } + + @Override + public T bodyAsClass(Class clazz) { + return mgr.jsonRead(clazz, this); + } + + @Override + public byte[] bodyAsBytes() { + try { + return Content.Source.asByteBuffer(req).array(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public String body() { + try { + return Content.Source.asString(req, Charset.forName(characterEncoding())); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public long contentLength() { + return req.getLength(); + } + + @Override + public Map pathParamMap() { + return pathParams; + } + + @Override + public String pathParam(String name) { + return pathParams.get(name); + } + + private Fields _queryParams() { + if (queryParams == null) { + queryParams = Request.extractQueryParameters(req); + } + return queryParams; + } + + @Override + public String queryParam(String name) { + Fields.Field field = _queryParams().get(name); + return field == null ? null : field.getValue(); + } + + @Override + public List queryParams(String name) { + Fields.Field field = _queryParams().get(name); + return field == null ? emptyList() : field.getValues(); + } + + @Override + public Map queryParamMap() { + final Map map = new LinkedHashMap<>(); + for (Fields.Field field : _queryParams()) { + map.put(field.getName(), field.getValue()); + } + return map; + } + + @Override + public String queryString() { + return req.getHttpURI().getQuery(); + } + + @Override + public Map> formParamMap() { + if (formParamMap == null) { + formParamMap = initFormParamMap(); + } + return formParamMap; + } + + private Map> initFormParamMap() { + if (isMultipartFormData()) { + throw new IllegalStateException("not supported yet"); + } + Map> map = new LinkedHashMap<>(); + try { + for (Fields.Field formField : FormFields.from(req).get()) { + map.put(formField.getName(), formField.getValues()); + } + return map; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @Override + public String scheme() { + return req.getHttpURI().getScheme(); + } + + @Override + public Context sessionAttribute(String key, Object value) { + req.getSession(true).setAttribute(key, value); + return this; + } + + @SuppressWarnings("unchecked") + @Override + public T sessionAttribute(String key) { + var session = req.getSession(false); + return session == null ? null : (T) session.getAttribute(key); + } + + @Override + public Map sessionAttributeMap() { + final Map map = new LinkedHashMap<>(); + final var session = req.getSession(false); + if (session == null) { + return emptyMap(); + } + Set nameSet = session.getAttributeNameSet(); + for (String name : nameSet) { + map.put(name, session.getAttribute(name)); + } + return map; + } + + @Override + public String url() { + return req.getHttpURI().getPath(); + } + + @Override + public String fullUrl() { + return req.getHttpURI().getPathQuery(); + } + + @Override + public String contextPath() { + String path = req.getContext().getContextPath(); + return path == null ? "" : path; + } + + @Override + public Context status(int statusCode) { + res.setStatus(statusCode); + return this; + } + + @Override + public int status() { + return res.getStatus(); + } + + @Override + public String contentType() { + return req.getHeaders().get(HttpHeader.CONTENT_TYPE); + } + + @Override + public Context contentType(String contentType) { + res.getHeaders().add(HttpHeader.CONTENT_TYPE, contentType); + return this; + } + + public Map headerMap() { + Map map = new LinkedHashMap<>(); + for (HttpField header : req.getHeaders()) { + map.put(header.getName(), header.getValue()); + } + return map; + } + + @Override + public String responseHeader(String key) { + return res.getHeaders().get(key); + } + + @Override + public String header(String key) { + return req.getHeaders().get(key); + } + + @Override + public Context header(String key, String value) { + res.getHeaders().add(key, value); + return this; + } + + @Override + public String host() { + return req.getHttpURI().getHost(); + } + + @Override + public String ip() { + return Request.getRemoteAddr(req); + } + + @Override + public boolean isMultipart() { + final String type = header(HeaderKeys.CONTENT_TYPE); + return type != null && type.toLowerCase().contains("multipart/"); + } + + @Override + public boolean isMultipartFormData() { + final String type = header(HeaderKeys.CONTENT_TYPE); + return type != null && type.toLowerCase().contains("multipart/form-data"); + } + + @Override + public String method() { + return req.getMethod(); + } + + @Override + public String path() { + return req.getHttpURI().getDecodedPath(); + } + + @Override + public int port() { + return Request.getServerPort(req); + } + + @Override + public String protocol() { + //req.getHttpURI().getScheme(). + throw new UnsupportedOperationException(); + } + + @Override + public Context json(Object bean) { + contentType(APPLICATION_JSON); + mgr.jsonWrite(bean, this); + return this; + } + + @Override + public Context jsonStream(Stream stream) { + contentType(APPLICATION_X_JSON_STREAM); + mgr.jsonWriteStream(stream, this); + return this; + } + + @Override + public Context jsonStream(Iterator iterator) { + contentType(APPLICATION_X_JSON_STREAM); + mgr.jsonWriteStream(iterator, this); + return this; + } + + /** + * Write plain text content to the response. + */ + @Override + public Context text(String content) { + contentType(TEXT_PLAIN); + return write(content); + } + + /** + * Write html content to the response. + */ + @Override + public Context html(String content) { + contentType(TEXT_HTML); + return write(content); + } + + @Override + public Context write(String content) { + Content.Sink.write(res, true, content, null); + return this; + } + + @Override + public Context render(String name, Map model) { + mgr.render(this, name, model); + return this; + } + + @Override + public OutputStream outputStream() { + return Content.Sink.asOutputStream(res); + } + + @Override + public UploadedFile uploadedFile(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public List uploadedFiles(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public List uploadedFiles() { + throw new UnsupportedOperationException(); + } +} diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/PartUploadedFile.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/PartUploadedFile.java new file mode 100644 index 00000000..4b30c11a --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/PartUploadedFile.java @@ -0,0 +1,63 @@ +package io.avaje.jex.jetty; + +//import io.avaje.jex.UploadedFile; +//import jakarta.servlet.http.Part; +// +//import java.io.IOException; +//import java.io.InputStream; +//import java.io.UncheckedIOException; + +/** + * UploadedFile using servlet Part. + */ +class PartUploadedFile {//implements UploadedFile { + +// private final Part part; +// +// PartUploadedFile(Part part) { +// this.part = part; +// } +// +// @Override +// public String name() { +// return part.getName(); +// } +// +// @Override +// public String fileName() { +// return part.getSubmittedFileName(); +// } +// +// @Override +// public InputStream content() { +// try { +// return part.getInputStream(); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } +// } +// +// @Override +// public String contentType() { +// return part.getContentType(); +// } +// +// @Override +// public long size() { +// return part.getSize(); +// } +// +// @Override +// public void delete() { +// try { +// part.delete(); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } +// } +// +// @Override +// public String toString() { +// return "name:" + name() + " fileName:" + fileName() + " size:" + size(); +// } +} diff --git a/avaje-j12/src/main/java/io/avaje/jex/jetty/ServiceManager.java b/avaje-j12/src/main/java/io/avaje/jex/jetty/ServiceManager.java new file mode 100644 index 00000000..bea67db0 --- /dev/null +++ b/avaje-j12/src/main/java/io/avaje/jex/jetty/ServiceManager.java @@ -0,0 +1,40 @@ +package io.avaje.jex.jetty; + +//import io.avaje.jex.UploadedFile; +//import io.avaje.jex.spi.ProxyServiceManager; +//import io.avaje.jex.spi.SpiServiceManager; +//import jakarta.servlet.http.HttpServletRequest; +// +//import java.util.List; +//import java.util.Map; + +import io.avaje.jex.spi.ProxyServiceManager; +import io.avaje.jex.spi.SpiServiceManager; + +/** + * Jetty specific service manager. + */ +class ServiceManager extends ProxyServiceManager { + public ServiceManager(SpiServiceManager delegate) { + super(delegate); + } + +// private final MultipartUtil multipartUtil; +// +// ServiceManager(SpiServiceManager delegate, MultipartUtil multipartUtil) { +// super(delegate); +// this.multipartUtil = multipartUtil; +// } +// +// List uploadedFiles(HttpServletRequest req) { +// return null;// multipartUtil.uploadedFiles(req); +// } +// +// List uploadedFiles(HttpServletRequest req, String name) { +// return null; //multipartUtil.uploadedFiles(req, name); +// } + +// Map> multiPartForm(HttpServletRequest req) { +// return multipartUtil.fieldMap(req); +// } +} diff --git a/avaje-j12/src/test/java/io/avaje/jex/jetty/JettyBuilderTest.java b/avaje-j12/src/test/java/io/avaje/jex/jetty/JettyBuilderTest.java new file mode 100644 index 00000000..90787d7f --- /dev/null +++ b/avaje-j12/src/test/java/io/avaje/jex/jetty/JettyBuilderTest.java @@ -0,0 +1,13 @@ +package io.avaje.jex.jetty; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class JettyBuilderTest { + + @Test + void asd() { + + } +} diff --git a/avaje-jex-jetty/pom.xml b/avaje-jex-jetty/pom.xml index c815a24a..7810bbea 100644 --- a/avaje-jex-jetty/pom.xml +++ b/avaje-jex-jetty/pom.xml @@ -12,7 +12,7 @@ 11 11 - 11.0.15 + 12.0.0 false @@ -39,7 +39,7 @@ com.fasterxml.jackson.core jackson-databind - 2.14.0 + ${jackson.version} test diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ContextUtil.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ContextUtil.java index c986ffbe..cda07232 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ContextUtil.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ContextUtil.java @@ -1,41 +1,41 @@ package io.avaje.jex.jetty; -import jakarta.servlet.ServletInputStream; -import jakarta.servlet.http.HttpServletRequest; - -import java.io.*; +//import jakarta.servlet.ServletInputStream; +//import jakarta.servlet.http.HttpServletRequest; +// +//import java.io.*; class ContextUtil { - private static final int DEFAULT_BUFFER_SIZE = 8 * 1024; - - private static final int BUFFER_MAX = 65536; - - static byte[] readBody(HttpServletRequest req) { - try { - final ServletInputStream inputStream = req.getInputStream(); - - int bufferSize = inputStream.available(); - if (bufferSize < DEFAULT_BUFFER_SIZE) { - bufferSize = DEFAULT_BUFFER_SIZE; - } else if (bufferSize > BUFFER_MAX) { - bufferSize = BUFFER_MAX; - } - - ByteArrayOutputStream os = new ByteArrayOutputStream(bufferSize); - copy(inputStream, os, bufferSize); - return os.toByteArray(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - static void copy(InputStream in, OutputStream out, int bufferSize) throws IOException { - byte[] buffer = new byte[bufferSize]; - int len; - while ((len = in.read(buffer, 0, bufferSize)) > 0) { - out.write(buffer, 0, len); - } - } +// private static final int DEFAULT_BUFFER_SIZE = 8 * 1024; +// +// private static final int BUFFER_MAX = 65536; +// +// static byte[] readBody(HttpServletRequest req) { +// try { +// final ServletInputStream inputStream = req.getInputStream(); +// +// int bufferSize = inputStream.available(); +// if (bufferSize < DEFAULT_BUFFER_SIZE) { +// bufferSize = DEFAULT_BUFFER_SIZE; +// } else if (bufferSize > BUFFER_MAX) { +// bufferSize = BUFFER_MAX; +// } +// +// ByteArrayOutputStream os = new ByteArrayOutputStream(bufferSize); +// copy(inputStream, os, bufferSize); +// return os.toByteArray(); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } +// } +// +// static void copy(InputStream in, OutputStream out, int bufferSize) throws IOException { +// byte[] buffer = new byte[bufferSize]; +// int len; +// while ((len = in.read(buffer, 0, bufferSize)) > 0) { +// out.write(buffer, 0, len); +// } +// } } diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyJexServer.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyJexServer.java index 7253c3a7..d5b23b52 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyJexServer.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyJexServer.java @@ -3,12 +3,12 @@ import io.avaje.jex.*; import io.avaje.jex.spi.SpiRoutes; import io.avaje.jex.spi.SpiServiceManager; -import jakarta.servlet.MultipartConfigElement; +//import jakarta.servlet.MultipartConfigElement; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.session.SessionHandler; +//import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.util.Uptime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +34,7 @@ class JettyJexServer implements Jex.Server { this.jexConfig = jex.config(); this.lifecycle = jex.lifecycle(); this.routes = routes; - this.serviceManager = new ServiceManager(serviceManager, initMultiPart()); + this.serviceManager = new ServiceManager(serviceManager);//, initMultiPart()); this.config = initConfig(jex.serverConfig()); } @@ -42,17 +42,17 @@ private JettyServerConfig initConfig(ServerConfig config) { return config == null ? new JettyServerConfig() : (JettyServerConfig) config; } - MultipartUtil initMultiPart() { - return new MultipartUtil(initMultipartConfigElement(jexConfig.multipartConfig())); - } - - MultipartConfigElement initMultipartConfigElement(UploadConfig uploadConfig) { - if (uploadConfig == null) { - final int fileThreshold = jexConfig.multipartFileThreshold(); - return new MultipartConfigElement(System.getProperty("java.io.tmpdir"), -1, -1, fileThreshold); - } - return new MultipartConfigElement(uploadConfig.location(), uploadConfig.maxFileSize(), uploadConfig.maxRequestSize(), uploadConfig.fileSizeThreshold()); - } +// MultipartUtil initMultiPart() { +// return new MultipartUtil(initMultipartConfigElement(jexConfig.multipartConfig())); +// } +// +// MultipartConfigElement initMultipartConfigElement(UploadConfig uploadConfig) { +// if (uploadConfig == null) { +// final int fileThreshold = jexConfig.multipartFileThreshold(); +// return new MultipartConfigElement(System.getProperty("java.io.tmpdir"), -1, -1, fileThreshold); +// } +// return new MultipartConfigElement(uploadConfig.location(), uploadConfig.maxFileSize(), uploadConfig.maxRequestSize(), uploadConfig.fileSizeThreshold()); +// } @Override public void onShutdown(Runnable onShutdown) { @@ -104,6 +104,7 @@ protected Jex.Server start() { protected void createServer() { server = initServer(); + //server.setErrorHandler(); server.setHandler(initJettyHandler()); if (server.getStopAtShutdown()) { // do not use Jetty ShutdownHook, use the AppLifecycle one instead @@ -123,24 +124,25 @@ protected Server initServer() { protected Handler initJettyHandler() { var baseHandler = new JexHandler(jex, routes, serviceManager, initStaticHandler()); - if (!config.sessions()) { - return baseHandler; - } - var sessionHandler = initSessionHandler(); - sessionHandler.setHandler(baseHandler); - return sessionHandler; - } - - protected SessionHandler initSessionHandler() { - SessionHandler sh = config.sessionHandler(); - return sh == null ? defaultSessionHandler() : sh; - } - - protected SessionHandler defaultSessionHandler() { - SessionHandler sh = new SessionHandler(); - sh.setHttpOnly(true); - return sh; - } + return baseHandler; +// if (!config.sessions()) { +// return baseHandler; +// } +// var sessionHandler = initSessionHandler(); +// sessionHandler.setHandler(baseHandler); +// return sessionHandler; + } + +// protected SessionHandler initSessionHandler() { +// SessionHandler sh = config.sessionHandler(); +// return sh == null ? defaultSessionHandler() : sh; +// } +// +// protected SessionHandler defaultSessionHandler() { +// SessionHandler sh = new SessionHandler(); +// sh.setHttpOnly(true); +// return sh; +// } protected StaticHandler initStaticHandler() { final List fileSources = jex.staticFiles().getSources(); diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyServerConfig.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyServerConfig.java index f8c41a17..36349753 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyServerConfig.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JettyServerConfig.java @@ -3,7 +3,7 @@ import io.avaje.jex.ServerConfig; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.session.SessionHandler; +//import org.eclipse.jetty.server.session.SessionHandler; import java.util.ArrayList; import java.util.List; @@ -18,7 +18,7 @@ public class JettyServerConfig implements ServerConfig { * Set maxThreads when using default QueuedThreadPool. Defaults to 200. */ private int maxThreads; - private SessionHandler sessionHandler; +// private SessionHandler sessionHandler; private Handler contextHandler; private Server server; private final List> configureCallback = new ArrayList<>(); @@ -50,17 +50,17 @@ public JettyServerConfig maxThreads(int maxThreads) { return this; } - public SessionHandler sessionHandler() { - return sessionHandler; - } - - /** - * Set the SessionHandler to use. When not set one is created automatically. - */ - public JettyServerConfig sessionHandler(SessionHandler sessionHandler) { - this.sessionHandler = sessionHandler; - return this; - } +// public SessionHandler sessionHandler() { +// return sessionHandler; +// } +// +// /** +// * Set the SessionHandler to use. When not set one is created automatically. +// */ +// public JettyServerConfig sessionHandler(SessionHandler sessionHandler) { +// this.sessionHandler = sessionHandler; +// return this; +// } public Handler contextHandler() { return contextHandler; diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHandler.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHandler.java index 33bd44f3..d2a903e8 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHandler.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHandler.java @@ -6,14 +6,18 @@ import io.avaje.jex.http.NotFoundResponse; import io.avaje.jex.spi.SpiContext; import io.avaje.jex.spi.SpiRoutes; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +//import jakarta.servlet.http.HttpServletRequest; +//import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.Callback; import java.util.Map; -class JexHandler extends AbstractHandler { +class JexHandler extends Handler.Abstract { //private static final String X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override"; private final SpiRoutes routes; @@ -27,16 +31,20 @@ class JexHandler extends AbstractHandler { } @Override - public void handle(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse res) { - try { + public boolean handle(Request req, Response res, Callback callback) throws Exception { + + //@Override + //public void handle(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse res) { +// try { final Routing.Type routeType = method(req); - final String uri = req.getRequestURI(); + HttpURI httpURI = req.getHttpURI(); + String uri = httpURI.getPathQuery(); SpiRoutes.Entry route = routes.match(routeType, uri); if (route == null) { var ctx = new JexHttpContext(manager, req, res, uri); routes.inc(); try { - processNoRoute(target, baseRequest, ctx, uri, routeType); + processNoRoute(ctx, uri, routeType); routes.after(uri, ctx); } catch (Exception e) { handleException(ctx, e); @@ -50,15 +58,15 @@ public void handle(String target, Request baseRequest, HttpServletRequest req, H try { processRoute(ctx, uri, route); routes.after(uri, ctx); + callback.succeeded(); } catch (Exception e) { handleException(ctx, e); } finally { route.dec(); } } - } finally { - baseRequest.setHandled(true); - } + return true; +// } } private void handleException(SpiContext ctx, Exception e) { @@ -71,20 +79,21 @@ private void processRoute(JexHttpContext ctx, String uri, SpiRoutes.Entry route) route.handle(ctx); } - private void processNoRoute(String target, Request baseRequest, JexHttpContext ctx, String uri, Routing.Type routeType) { + //String target, Request baseRequest, + private void processNoRoute(JexHttpContext ctx, String uri, Routing.Type routeType) { routes.before(uri, ctx); if (routeType == Routing.Type.HEAD && hasGetHandler(uri)) { processHead(ctx); return; } - if (routeType == Routing.Type.GET || routeType == Routing.Type.HEAD) { - // check if handled by static resource - if (staticHandler != null && staticHandler.handle(target, baseRequest, ctx.req(), ctx.res())) { - return; - } - // todo: check if handled by singlePageHandler - //if (config.inner.singlePageHandler.handle(ctx)) return@tryWithExceptionMapper - } +// if (routeType == Routing.Type.GET || routeType == Routing.Type.HEAD) { +// // check if handled by static resource +// if (staticHandler != null && staticHandler.handle(target, baseRequest, ctx.req(), ctx.res())) { +// return; +// } +// // todo: check if handled by singlePageHandler +// //if (config.inner.singlePageHandler.handle(ctx)) return@tryWithExceptionMapper +// } // if (routeType == Routing.Type.OPTIONS && isCorsEnabled(config)) { // CORS is enabled, so we return 200 for OPTIONS // return@tryWithExceptionMapper // } @@ -104,7 +113,7 @@ private boolean hasGetHandler(String uri) { return routes.match(Routing.Type.GET, uri) != null; } - private Routing.Type method(HttpServletRequest req) { + private Routing.Type method(Request req) { return manager.lookupRoutingType(req.getMethod()); } } diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHttpContext.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHttpContext.java index 3c1935af..31333b92 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHttpContext.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/JexHttpContext.java @@ -6,9 +6,14 @@ import io.avaje.jex.http.RedirectResponse; import io.avaje.jex.spi.HeaderKeys; import io.avaje.jex.spi.SpiContext; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; +import org.eclipse.jetty.http.HttpCookie; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.FormFields; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Fields; import java.io.IOException; import java.io.InputStream; @@ -25,15 +30,16 @@ class JexHttpContext implements SpiContext { private final ServiceManager mgr; - protected final HttpServletRequest req; - private final HttpServletResponse res; + protected final Request req; + private final Response res; private final Map pathParams; private final String matchedPath; private String characterEncoding; private Routing.Type mode; private Map> formParamMap; + private Fields queryParams; - JexHttpContext(ServiceManager mgr, HttpServletRequest req, HttpServletResponse res, String matchedPath) { + JexHttpContext(ServiceManager mgr, Request req, Response res, String matchedPath) { this.mgr = mgr; this.req = req; this.res = res; @@ -41,7 +47,7 @@ class JexHttpContext implements SpiContext { this.pathParams = emptyMap(); } - JexHttpContext(ServiceManager mgr, HttpServletRequest req, HttpServletResponse res, String matchedPath, Map pathParams) { + JexHttpContext(ServiceManager mgr, Request req, Response res, String matchedPath, Map pathParams) { this.mgr = mgr; this.req = req; this.res = res; @@ -61,11 +67,11 @@ private String characterEncoding() { return characterEncoding; } - public HttpServletRequest req() { + public Request req() { return req; } - public HttpServletResponse res() { + public Response res() { return res; } @@ -93,9 +99,9 @@ public T attribute(String key) { @Override public String cookie(String name) { - final jakarta.servlet.http.Cookie[] cookies = req.getCookies(); + List cookies = Request.getCookies(req); if (cookies != null) { - for (jakarta.servlet.http.Cookie cookie : cookies) { + for (HttpCookie cookie : cookies) { if (cookie.getName().equals(name)) { return cookie.getValue(); } @@ -106,12 +112,12 @@ public String cookie(String name) { @Override public Map cookieMap() { - final jakarta.servlet.http.Cookie[] cookies = req.getCookies(); + List cookies = Request.getCookies(req); if (cookies == null) { return emptyMap(); } final Map map = new LinkedHashMap<>(); - for (jakarta.servlet.http.Cookie cookie : cookies) { + for (HttpCookie cookie : cookies) { map.put(cookie.getName(), cookie.getValue()); } return map; @@ -119,31 +125,30 @@ public Map cookieMap() { @Override public Context cookie(Cookie cookie) { - final jakarta.servlet.http.Cookie newCookie = new jakarta.servlet.http.Cookie(cookie.name(), cookie.value()); - newCookie.setPath(cookie.path()); - if (newCookie.getPath() == null) { - newCookie.setPath("/"); - } + var newCookie = HttpCookie.build(cookie.name(), cookie.value()); + var path = cookie.path() == null ? "/" : cookie.path(); + newCookie.path(path); final String domain = cookie.domain(); if (domain != null) { - newCookie.setDomain(domain); + newCookie.domain(domain); } final Duration duration = cookie.maxAge(); if (duration != null) { - newCookie.setMaxAge((int)duration.toSeconds()); + newCookie.maxAge((int) duration.toSeconds()); } - newCookie.setHttpOnly(cookie.httpOnly()); - newCookie.setSecure(cookie.secure()); - res.addCookie(newCookie); + newCookie.httpOnly(cookie.httpOnly()); + newCookie.secure(cookie.secure()); + Response.addCookie(res, newCookie.build()); return this; } @Override public Context cookie(String name, String value, int maxAge) { - final jakarta.servlet.http.Cookie cookie = new jakarta.servlet.http.Cookie(name, value); - cookie.setPath("/"); - cookie.setMaxAge(maxAge); - res.addCookie(cookie); + var newCookie = HttpCookie.build(name, value) + .path("/") + .maxAge(maxAge) + .build(); + Response.addCookie(res, newCookie); return this; } @@ -162,21 +167,22 @@ public Context removeCookie(String name, String path) { if (path == null) { path = "/"; } - final jakarta.servlet.http.Cookie cookie = new jakarta.servlet.http.Cookie(name, ""); - cookie.setPath(path); - cookie.setMaxAge(0); - res.addCookie(cookie); + var newCookie = HttpCookie.build(name, "") + .path(path) + .maxAge(0); + Response.addCookie(res, newCookie.build()); return this; } @Override public void redirect(String location) { - redirect(location, HttpServletResponse.SC_MOVED_TEMPORARILY); + redirect(location, 302); } @Override public void redirect(String location, int statusCode) { - res.setHeader(HeaderKeys.LOCATION, location); + //Response.sendRedirect(); + res.getHeaders().add(HeaderKeys.LOCATION, location); status(statusCode); if (mode == Routing.Type.BEFORE) { throw new RedirectResponse(statusCode); @@ -195,11 +201,7 @@ public String matchedPath() { @Override public InputStream inputStream() { - try { - return req.getInputStream(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return Request.asInputStream(req); } @Override @@ -209,17 +211,25 @@ public T bodyAsClass(Class clazz) { @Override public byte[] bodyAsBytes() { - return ContextUtil.readBody(req); + try { + return Content.Source.asByteBuffer(req).array(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } @Override public String body() { - return new String(bodyAsBytes(), Charset.forName(characterEncoding())); + try { + return Content.Source.asString(req, Charset.forName(characterEncoding())); + } catch (IOException e) { + throw new UncheckedIOException(e); + } } @Override public long contentLength() { - return req.getContentLengthLong(); + return req.getLength(); } @Override @@ -232,40 +242,37 @@ public String pathParam(String name) { return pathParams.get(name); } + private Fields _queryParams() { + if (queryParams == null) { + queryParams = Request.extractQueryParameters(req); + } + return queryParams; + } + @Override public String queryParam(String name) { - final String[] vals = req.getParameterValues(name); - if (vals == null || vals.length == 0) { - return null; - } else { - return vals[0]; - } + Fields.Field field = _queryParams().get(name); + return field == null ? null : field.getValue(); } @Override public List queryParams(String name) { - final String[] vals = req.getParameterValues(name); - if (vals == null) { - return emptyList(); - } else { - return Arrays.asList(vals); - } + Fields.Field field = _queryParams().get(name); + return field == null ? emptyList() : field.getValues(); } @Override public Map queryParamMap() { final Map map = new LinkedHashMap<>(); - final Enumeration names = req.getParameterNames(); - while (names.hasMoreElements()) { - final String key = names.nextElement(); - map.put(key, queryParam(key)); + for (Fields.Field field : _queryParams()) { + map.put(field.getName(), field.getValue()); } return map; } @Override public String queryString() { - return req.getQueryString(); + return req.getHttpURI().getQuery(); } @Override @@ -278,40 +285,46 @@ public Map> formParamMap() { private Map> initFormParamMap() { if (isMultipartFormData()) { - return mgr.multiPartForm(req); - } else { - return mgr.formParamMap(this, characterEncoding()); + throw new IllegalStateException("not supported yet"); + } + Map> map = new LinkedHashMap<>(); + try { + for (Fields.Field formField : FormFields.from(req).get()) { + map.put(formField.getName(), formField.getValues()); + } + return map; + } catch (Throwable e) { + throw new RuntimeException(e); } } @Override public String scheme() { - return req.getScheme(); + return req.getHttpURI().getScheme(); } @Override public Context sessionAttribute(String key, Object value) { - req.getSession().setAttribute(key, value); + req.getSession(true).setAttribute(key, value); return this; } @SuppressWarnings("unchecked") @Override public T sessionAttribute(String key) { - HttpSession session = req.getSession(false); + var session = req.getSession(false); return session == null ? null : (T) session.getAttribute(key); } @Override public Map sessionAttributeMap() { final Map map = new LinkedHashMap<>(); - final HttpSession session = req.getSession(false); + final var session = req.getSession(false); if (session == null) { return emptyMap(); } - final Enumeration names = session.getAttributeNames(); - while (names.hasMoreElements()) { - final String name = names.nextElement(); + Set nameSet = session.getAttributeNameSet(); + for (String name : nameSet) { map.put(name, session.getAttribute(name)); } return map; @@ -319,19 +332,17 @@ public Map sessionAttributeMap() { @Override public String url() { - return req.getRequestURL().toString(); + return req.getHttpURI().getPath(); } @Override public String fullUrl() { - final String url = url(); - final String qs = queryString(); - return qs == null ? url : url + "?" + qs; + return req.getHttpURI().getPathQuery(); } @Override public String contextPath() { - String path = req.getContextPath(); + String path = req.getContext().getContextPath(); return path == null ? "" : path; } @@ -348,49 +359,47 @@ public int status() { @Override public String contentType() { - return req.getContentType(); + return req.getHeaders().get(HttpHeader.CONTENT_TYPE); } @Override public Context contentType(String contentType) { - res.setContentType(contentType); + res.getHeaders().add(HttpHeader.CONTENT_TYPE, contentType); return this; } public Map headerMap() { Map map = new LinkedHashMap<>(); - final Enumeration names = req.getHeaderNames(); - while (names.hasMoreElements()) { - final String name = names.nextElement(); - map.put(name, req.getHeader(name)); + for (HttpField header : req.getHeaders()) { + map.put(header.getName(), header.getValue()); } return map; } @Override public String responseHeader(String key) { - return req.getHeader(key); + return res.getHeaders().get(key); } @Override public String header(String key) { - return req.getHeader(key); + return req.getHeaders().get(key); } @Override public Context header(String key, String value) { - res.setHeader(key, value); + res.getHeaders().add(key, value); return this; } @Override public String host() { - return req.getHeader(HeaderKeys.HOST); + return req.getHttpURI().getHost(); } @Override public String ip() { - return req.getRemoteAddr(); + return Request.getRemoteAddr(req); } @Override @@ -412,17 +421,18 @@ public String method() { @Override public String path() { - return req.getRequestURI(); + return req.getHttpURI().getDecodedPath(); } @Override public int port() { - return req.getServerPort(); + return Request.getServerPort(req); } @Override public String protocol() { - return req.getProtocol(); + //req.getHttpURI().getScheme(). + throw new UnsupportedOperationException(); } @Override @@ -466,12 +476,8 @@ public Context html(String content) { @Override public Context write(String content) { - try { - res.getWriter().write(content); - return this; - } catch (IOException e) { - throw new UncheckedIOException(e); - } + Content.Sink.write(res, true, content, null); + return this; } @Override @@ -482,34 +488,21 @@ public Context render(String name, Map model) { @Override public OutputStream outputStream() { - try { - return res.getOutputStream(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return Content.Sink.asOutputStream(res); } @Override public UploadedFile uploadedFile(String name) { - final List files = uploadedFiles(name); - return files.isEmpty() ? null : files.get(0); + throw new UnsupportedOperationException(); } @Override public List uploadedFiles(String name) { - if (!isMultipartFormData()) { - return emptyList(); - } else { - return mgr.uploadedFiles(req, name); - } + throw new UnsupportedOperationException(); } @Override public List uploadedFiles() { - if (!isMultipartFormData()) { - return emptyList(); - } else { - return mgr.uploadedFiles(req); - } + throw new UnsupportedOperationException(); } } diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/MultipartUtil.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/MultipartUtil.java index dea863bb..5687f61f 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/MultipartUtil.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/MultipartUtil.java @@ -1,103 +1,103 @@ package io.avaje.jex.jetty; -import io.avaje.jex.UploadedFile; -import jakarta.servlet.MultipartConfigElement; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.Part; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toList; +//import io.avaje.jex.UploadedFile; +//import jakarta.servlet.MultipartConfigElement; +//import jakarta.servlet.ServletException; +//import jakarta.servlet.http.HttpServletRequest; +//import jakarta.servlet.http.Part; +// +//import java.io.BufferedReader; +//import java.io.IOException; +//import java.io.InputStreamReader; +//import java.io.UncheckedIOException; +//import java.nio.charset.StandardCharsets; +//import java.util.ArrayList; +//import java.util.LinkedHashMap; +//import java.util.List; +//import java.util.Map; +// +//import static java.util.stream.Collectors.joining; +//import static java.util.stream.Collectors.toList; class MultipartUtil { - private final MultipartConfigElement config; - - MultipartUtil(MultipartConfigElement config) { - this.config = config; - } - - private void setConfig(HttpServletRequest req) { - req.setAttribute("org.eclipse.jetty.multipartConfig", config); - } - - List uploadedFiles(HttpServletRequest req) { - try { - setConfig(req); - return req.getParts().stream() - .filter(part -> isFile(part)) - .map(this::toUploaded) - .collect(toList()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } catch (ServletException e) { - throw new RuntimeException(e); - } - } - - List uploadedFiles(HttpServletRequest req, String partName) { - try { - setConfig(req); - return req.getParts().stream() - .filter(part -> part.getName().equals(partName) && isFile(part)) - .map(this::toUploaded) - .collect(toList()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } catch (ServletException e) { - throw new RuntimeException(e); - } - } - - UploadedFile toUploaded(Part part) { - return new PartUploadedFile(part); - } - - Map> fieldMap(HttpServletRequest req) { - setConfig(req); - try { - Map> map = new LinkedHashMap<>(); - for (Part part : req.getParts()) { - if (isField(part)) { - final String name = part.getName(); - final String value = readAsString(part); - map.computeIfAbsent(name, s -> new ArrayList<>()).add(value); - } - } - return map; - } catch (IOException e) { - throw new UncheckedIOException(e); - } catch (ServletException e) { - throw new RuntimeException(e); - } - } - - private String readAsString(Part part) { - try { - return new BufferedReader(new InputStreamReader(part.getInputStream(), StandardCharsets.UTF_8)) - .lines() - .collect(joining("\n")); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private static boolean isFile(Part filePart) { - return !isField(filePart); - } - - private static boolean isField(Part filePart) { - return filePart.getSubmittedFileName() == null; // this is what Apache FileUpload does ... - } +// private final MultipartConfigElement config; +// +// MultipartUtil(MultipartConfigElement config) { +// this.config = config; +// } +// +// private void setConfig(HttpServletRequest req) { +// req.setAttribute("org.eclipse.jetty.multipartConfig", config); +// } +// +// List uploadedFiles(HttpServletRequest req) { +// try { +// setConfig(req); +// return req.getParts().stream() +// .filter(part -> isFile(part)) +// .map(this::toUploaded) +// .collect(toList()); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } catch (ServletException e) { +// throw new RuntimeException(e); +// } +// } +// +// List uploadedFiles(HttpServletRequest req, String partName) { +// try { +// setConfig(req); +// return req.getParts().stream() +// .filter(part -> part.getName().equals(partName) && isFile(part)) +// .map(this::toUploaded) +// .collect(toList()); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } catch (ServletException e) { +// throw new RuntimeException(e); +// } +// } +// +// UploadedFile toUploaded(Part part) { +// return new PartUploadedFile(part); +// } +// +// Map> fieldMap(HttpServletRequest req) { +// setConfig(req); +// try { +// Map> map = new LinkedHashMap<>(); +// for (Part part : req.getParts()) { +// if (isField(part)) { +// final String name = part.getName(); +// final String value = readAsString(part); +// map.computeIfAbsent(name, s -> new ArrayList<>()).add(value); +// } +// } +// return map; +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } catch (ServletException e) { +// throw new RuntimeException(e); +// } +// } +// +// private String readAsString(Part part) { +// try { +// return new BufferedReader(new InputStreamReader(part.getInputStream(), StandardCharsets.UTF_8)) +// .lines() +// .collect(joining("\n")); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } +// } +// +// private static boolean isFile(Part filePart) { +// return !isField(filePart); +// } +// +// private static boolean isField(Part filePart) { +// return filePart.getSubmittedFileName() == null; // this is what Apache FileUpload does ... +// } } diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/PartUploadedFile.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/PartUploadedFile.java index 712ed3b6..4b30c11a 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/PartUploadedFile.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/PartUploadedFile.java @@ -1,63 +1,63 @@ package io.avaje.jex.jetty; -import io.avaje.jex.UploadedFile; -import jakarta.servlet.http.Part; - -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; +//import io.avaje.jex.UploadedFile; +//import jakarta.servlet.http.Part; +// +//import java.io.IOException; +//import java.io.InputStream; +//import java.io.UncheckedIOException; /** * UploadedFile using servlet Part. */ -class PartUploadedFile implements UploadedFile { - - private final Part part; - - PartUploadedFile(Part part) { - this.part = part; - } - - @Override - public String name() { - return part.getName(); - } - - @Override - public String fileName() { - return part.getSubmittedFileName(); - } - - @Override - public InputStream content() { - try { - return part.getInputStream(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public String contentType() { - return part.getContentType(); - } - - @Override - public long size() { - return part.getSize(); - } - - @Override - public void delete() { - try { - part.delete(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public String toString() { - return "name:" + name() + " fileName:" + fileName() + " size:" + size(); - } +class PartUploadedFile {//implements UploadedFile { + +// private final Part part; +// +// PartUploadedFile(Part part) { +// this.part = part; +// } +// +// @Override +// public String name() { +// return part.getName(); +// } +// +// @Override +// public String fileName() { +// return part.getSubmittedFileName(); +// } +// +// @Override +// public InputStream content() { +// try { +// return part.getInputStream(); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } +// } +// +// @Override +// public String contentType() { +// return part.getContentType(); +// } +// +// @Override +// public long size() { +// return part.getSize(); +// } +// +// @Override +// public void delete() { +// try { +// part.delete(); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } +// } +// +// @Override +// public String toString() { +// return "name:" + name() + " fileName:" + fileName() + " size:" + size(); +// } } diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ServiceManager.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ServiceManager.java index 5eb0ddbc..bea67db0 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ServiceManager.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/ServiceManager.java @@ -1,34 +1,40 @@ package io.avaje.jex.jetty; -import io.avaje.jex.UploadedFile; +//import io.avaje.jex.UploadedFile; +//import io.avaje.jex.spi.ProxyServiceManager; +//import io.avaje.jex.spi.SpiServiceManager; +//import jakarta.servlet.http.HttpServletRequest; +// +//import java.util.List; +//import java.util.Map; + import io.avaje.jex.spi.ProxyServiceManager; import io.avaje.jex.spi.SpiServiceManager; -import jakarta.servlet.http.HttpServletRequest; - -import java.util.List; -import java.util.Map; /** * Jetty specific service manager. */ class ServiceManager extends ProxyServiceManager { - - private final MultipartUtil multipartUtil; - - ServiceManager(SpiServiceManager delegate, MultipartUtil multipartUtil) { + public ServiceManager(SpiServiceManager delegate) { super(delegate); - this.multipartUtil = multipartUtil; } - List uploadedFiles(HttpServletRequest req) { - return multipartUtil.uploadedFiles(req); - } - - List uploadedFiles(HttpServletRequest req, String name) { - return multipartUtil.uploadedFiles(req, name); - } - - Map> multiPartForm(HttpServletRequest req) { - return multipartUtil.fieldMap(req); - } +// private final MultipartUtil multipartUtil; +// +// ServiceManager(SpiServiceManager delegate, MultipartUtil multipartUtil) { +// super(delegate); +// this.multipartUtil = multipartUtil; +// } +// +// List uploadedFiles(HttpServletRequest req) { +// return null;// multipartUtil.uploadedFiles(req); +// } +// +// List uploadedFiles(HttpServletRequest req, String name) { +// return null; //multipartUtil.uploadedFiles(req, name); +// } + +// Map> multiPartForm(HttpServletRequest req) { +// return multipartUtil.fieldMap(req); +// } } diff --git a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/StaticHandler.java b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/StaticHandler.java index 3694c115..172b1314 100644 --- a/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/StaticHandler.java +++ b/avaje-jex-jetty/src/main/java/io/avaje/jex/jetty/StaticHandler.java @@ -1,12 +1,14 @@ package io.avaje.jex.jetty; import io.avaje.jex.StaticFileSource; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +//import jakarta.servlet.http.HttpServletRequest; +//import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.util.resource.EmptyResource; +//import org.eclipse.jetty.util.resource.EmptyResource; +import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.resource.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,138 +32,142 @@ class StaticHandler { } void addStaticFileConfig(StaticFileSource config) { - ResourceHandler handler; - if ("/webjars".equals(config.getPath())) { - handler = new WebjarHandler(); - } else { - PrefixableHandler h = new PrefixableHandler(config.getUrlPathPrefix()); - h.setResourceBase(getResourcePath(config)); - h.setDirAllowed(false); - h.setEtags(true); - handler = h; - } - log.info("Static file handler added {}", config); - - try { - handler.setServer(server); - handler.start(); - } catch (Exception e) { - throw new RuntimeException("Error starting Jetty static resource handler", e); - } - handlers.add(handler); +// ResourceHandler handler; +// if ("/webjars".equals(config.getPath())) { +// handler = new WebjarHandler(); +// } else { +// PrefixableHandler h = new PrefixableHandler(config.getUrlPathPrefix()); +// //FIXME h.setResourceBase(getResourcePath(config)); +// h.setDirAllowed(false); +// h.setEtags(true); +// handler = h; +// } +// log.info("Static file handler added {}", config); +// +// try { +// handler.setServer(server); +// handler.start(); +// } catch (Exception e) { +// throw new RuntimeException("Error starting Jetty static resource handler", e); +// } +// handlers.add(handler); } - private String getResourcePath(StaticFileSource config) { - if (config.getLocation() == StaticFileSource.Location.CLASSPATH) { - var resource = Resource.newClassPathResource(config.getPath()); - if (resource == null) { - throw new RuntimeException(noSuchDir(config) + " Depending on your setup, empty folders might not get copied to classpath."); - } - return resource.toString(); - } - final File path = new File(config.getPath()); - if (!path.exists()) { - throw new RuntimeException(noSuchDir(config) + " path: " + path.getAbsolutePath()); - } - return config.getPath(); - } +// private String getResourcePath(StaticFileSource config) { +// if (config.getLocation() == StaticFileSource.Location.CLASSPATH) { +// var resource = Resource.newClassPathResource(config.getPath()); +// if (resource == null) { +// throw new RuntimeException(noSuchDir(config) + " Depending on your setup, empty folders might not get copied to classpath."); +// } +// return resource.toString(); +// } +// final File path = new File(config.getPath()); +// if (!path.exists()) { +// throw new RuntimeException(noSuchDir(config) + " path: " + path.getAbsolutePath()); +// } +// return config.getPath(); +// } private String noSuchDir(StaticFileSource config) { return "Static resource directory with path: '" + config.getPath() + "' does not exist."; } - boolean handle(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse res) { - for (ResourceHandler handler : handlers) { - try { - var resource = handler.getResource(target); - if (isFile(resource) || isDirectoryWithWelcomeFile(resource, handler, target)) { -// val maxAge = if (target.startsWith("/immutable/") || handler is WebjarHandler) 31622400 else 0 -// httpResponse.setHeader(HeaderKeys.CACHE_CONTROL, "max-age=$maxAge"); - - // Remove the default content type because Jetty will not set the correct one - // if the HTTP response already has a content type set -// if (precompressStaticFiles && PrecompressingResourceHandler.handle(resource, httpRequest, httpResponse)) { -// return true -// } - res.setContentType(null); - handler.handle(target, baseRequest, req, res); - req.setAttribute("handled-as-static-file", true); -// (httpResponse as JavalinResponseWrapper).outputStream.finalize() - return true; - } - } catch (Exception e) { // it's fine -// if (!Util.isClientAbortException(e)) { -// Javalin.log?.error("Exception occurred while handling static resource", e) -// } - log.error("Exception occurred while handling static resource", e); - } - } + boolean handle(Request req, Response res, Callback callback) { return false; } +// boolean handle(String target, Request baseRequest, HttpRequest req, HttpServletResponse res) { +// for (ResourceHandler handler : handlers) { +// try { +// var resource = handler.getResource(target); +// if (isFile(resource) || isDirectoryWithWelcomeFile(resource, handler, target)) { +//// val maxAge = if (target.startsWith("/immutable/") || handler is WebjarHandler) 31622400 else 0 +//// httpResponse.setHeader(HeaderKeys.CACHE_CONTROL, "max-age=$maxAge"); +// +// // Remove the default content type because Jetty will not set the correct one +// // if the HTTP response already has a content type set +//// if (precompressStaticFiles && PrecompressingResourceHandler.handle(resource, httpRequest, httpResponse)) { +//// return true +//// } +// res.setContentType(null); +// handler.handle(target, baseRequest, req, res); +// req.setAttribute("handled-as-static-file", true); +//// (httpResponse as JavalinResponseWrapper).outputStream.finalize() +// return true; +// } +// } catch (Exception e) { // it's fine +//// if (!Util.isClientAbortException(e)) { +//// Javalin.log?.error("Exception occurred while handling static resource", e) +//// } +// log.error("Exception occurred while handling static resource", e); +// } +// } +// return false; +// } + private boolean isFile(Resource resource) { return resource != null && resource.exists() && !resource.isDirectory(); } - private boolean isDirectoryWithWelcomeFile(Resource resource, ResourceHandler handler, String target) { - //String path = target.removeSuffix("/")+"/index.html"; - if (target.endsWith("/")) { - target = target.substring(0, target.length() - 1); - } - String path = target + "/index.html"; - if (resource == null || !resource.isDirectory()) { - return false; - } - try { - final Resource indexHtml = handler.getResource(path); - return indexHtml != null && indexHtml.exists(); - } catch (IOException e) { - log.warn("Error checking for welcome file", e); - return false; - } - } - - private static class WebjarHandler extends ResourceHandler { - @Override - public Resource getResource(String path) throws IOException { - final Resource resource = Resource.newClassPathResource("META-INF/resources" + path); - return (resource != null) ? resource : super.getResource(path); - } - } - - private static class PrefixableHandler extends ResourceHandler { - - private final String urlPathPrefix; - - PrefixableHandler(String urlPathPrefix) { - this.urlPathPrefix = urlPathPrefix; - } - - @Override - public Resource getResource(String path) throws IOException { - if (urlPathPrefix.equals("/")) { - return super.getResource(path); // same as regular ResourceHandler - } - String targetPath = target(path); - if ("".equals(targetPath)) { - return super.getResource("/"); // directory without trailing '/' - } - if (!path.startsWith(urlPathPrefix)) { - return EmptyResource.INSTANCE; - } - if (!targetPath.startsWith("/")) { - return EmptyResource.INSTANCE; - } else { - return super.getResource(targetPath); - } - } - - private String target(String path) { - if (path.startsWith(urlPathPrefix)) { - return path.substring(urlPathPrefix.length()); - } else { - return path; - } - } - } +// private boolean isDirectoryWithWelcomeFile(Resource resource, ResourceHandler handler, String target) { +// //String path = target.removeSuffix("/")+"/index.html"; +// if (target.endsWith("/")) { +// target = target.substring(0, target.length() - 1); +// } +// String path = target + "/index.html"; +// if (resource == null || !resource.isDirectory()) { +// return false; +// } +// try { +// final Resource indexHtml = handler.getResource(path); +// return indexHtml != null && indexHtml.exists(); +// } catch (IOException e) { +// log.warn("Error checking for welcome file", e); +// return false; +// } +// } + +// private static class WebjarHandler extends ResourceHandler { +// @Override +// public Resource getResource(String path) throws IOException { +// final Resource resource = Resource.newClassPathResource("META-INF/resources" + path); +// return (resource != null) ? resource : super.getResource(path); +// } +// } +// +// private static class PrefixableHandler extends ResourceHandler { +// +// private final String urlPathPrefix; +// +// PrefixableHandler(String urlPathPrefix) { +// this.urlPathPrefix = urlPathPrefix; +// } +// +// @Override +// public Resource getResource(String path) throws IOException { +// if (urlPathPrefix.equals("/")) { +// return super.getResource(path); // same as regular ResourceHandler +// } +// String targetPath = target(path); +// if ("".equals(targetPath)) { +// return super.getResource("/"); // directory without trailing '/' +// } +// if (!path.startsWith(urlPathPrefix)) { +// return EmptyResource.INSTANCE; +// } +// if (!targetPath.startsWith("/")) { +// return EmptyResource.INSTANCE; +// } else { +// return super.getResource(targetPath); +// } +// } +// +// private String target(String path) { +// if (path.startsWith(urlPathPrefix)) { +// return path.substring(urlPathPrefix.length()); +// } else { +// return path; +// } +// } +// } } diff --git a/avaje-jex-jetty/src/main/java/module-info.java b/avaje-jex-jetty/src/main/java/module-info.java index 8dbb3de8..0ccec789 100644 --- a/avaje-jex-jetty/src/main/java/module-info.java +++ b/avaje-jex-jetty/src/main/java/module-info.java @@ -8,7 +8,7 @@ requires transitive io.avaje.jex; //requires io.avaje.jex.jettyx; requires java.net.http; - requires transitive jetty.servlet.api; + //requires transitive jetty.servlet.api; requires transitive org.slf4j; requires transitive org.eclipse.jetty.http; requires transitive org.eclipse.jetty.server; diff --git a/pom.xml b/pom.xml index 9b17193c..84536c36 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,8 @@ avaje-jex-jetty avaje-jex-jdk avaje-jex-grizzly - + avaje-j12 +