From 6374aa554ba88223baa05800acc66e364c9ff3e7 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Mar 2025 19:44:07 -0400
Subject: [PATCH 1/5] start
---
avaje-jex-helidon-spi/pom.xml | 20 ++
.../http/spi/GrizzlyHttpExchangeDelegate.java | 193 ++++++++++++++++
.../http/spi/HttpSpiContextHandler.java | 124 ++++++++++
.../helidon/http/spi/JettyHttpContext.java | 84 +++++++
.../helidon/http/spi/JettyHttpServer.java | 215 ++++++++++++++++++
.../spi/VirtualThreadsExecutorService.java | 65 ++++++
pom.xml | 1 +
7 files changed, 702 insertions(+)
create mode 100644 avaje-jex-helidon-spi/pom.xml
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java
diff --git a/avaje-jex-helidon-spi/pom.xml b/avaje-jex-helidon-spi/pom.xml
new file mode 100644
index 00000000..696df4c1
--- /dev/null
+++ b/avaje-jex-helidon-spi/pom.xml
@@ -0,0 +1,20 @@
+
+ 4.0.0
+
+ io.avaje
+ avaje-jex-parent
+ 3.0-RC23
+
+ avaje-jex-helidon-spi
+
+
+
+
+ org.glassfish.grizzly
+ grizzly-http-server
+ 4.1.0-M1
+
+
+
+
+
\ No newline at end of file
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
new file mode 100644
index 00000000..0a571a58
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
@@ -0,0 +1,193 @@
+package io.avaje.helidon.http.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpPrincipal;
+
+/** Jetty implementation of {@link com.sun.net.httpserver.HttpExchange} */
+public class GrizzlyHttpExchangeDelegate extends HttpExchange {
+ /** Set of headers that RFC9110 says will not have a value list */
+ private static final Set SINGLE_VALUE_HEADERS =
+ Set.of(
+ "authorization",
+ "content-length",
+ "date",
+ "expires",
+ "host",
+ "if-modified-since",
+ "if-unmodified-since",
+ "if-range",
+ "last-modified",
+ "location",
+ "referer",
+ "retry-after",
+ "user-agent");
+
+ private final HttpContext context;
+
+ private final Request request;
+
+ private final Headers responseHeaders = new Headers();
+
+ private Headers requestHeaders = new Headers();
+
+ private int statusCode = 0;
+
+ private InputStream inputStream;
+
+ private OutputStream outputStream;
+
+ private HttpPrincipal httpPrincipal;
+
+ private Response response;
+
+ GrizzlyHttpExchangeDelegate(HttpContext httpSpiContext, Request request) {
+ this.context = httpSpiContext;
+ this.request = request;
+ this.response = request.getResponse();
+ this.inputStream = request.getInputStream();
+ this.outputStream = response.getOutputStream();
+ }
+
+ @Override
+ public Headers getRequestHeaders() {
+
+ if (!requestHeaders.isEmpty()) {
+ return requestHeaders;
+ }
+ for (var name : request.getHeaderNames()) {
+
+ if (!SINGLE_VALUE_HEADERS.contains(name.toLowerCase())) {
+
+ for (String value : request.getHeaders(name)) {
+ requestHeaders.add(name, value);
+ }
+ } else {
+ requestHeaders.add(name, request.getHeader(name));
+ }
+ }
+ return requestHeaders;
+ }
+
+ @Override
+ public Headers getResponseHeaders() {
+ return responseHeaders;
+ }
+
+ @Override
+ public URI getRequestURI() {
+ return URI.create(request.getRequestURI());
+ }
+
+ @Override
+ public String getRequestMethod() {
+ return request.getMethod().getMethodString();
+ }
+
+ @Override
+ public HttpContext getHttpContext() {
+ return context;
+ }
+
+ @Override
+ public void close() {
+ try {
+ outputStream.close();
+ } catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ }
+
+ @Override
+ public InputStream getRequestBody() {
+ return inputStream;
+ }
+
+ @Override
+ public OutputStream getResponseBody() {
+ return outputStream;
+ }
+
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
+ this.statusCode = rCode;
+
+ for (Map.Entry> stringListEntry : responseHeaders.entrySet()) {
+ String name = stringListEntry.getKey();
+ List values = stringListEntry.getValue();
+ for (String value : values) {
+ response.addHeader(name, value);
+ }
+ }
+
+ if (responseLength == -1) {
+ response.setContentLengthLong(0);
+ } else if (responseLength == 0) {
+ response.setContentLengthLong(-1);
+ } else {
+ response.setContentLengthLong(responseLength);
+ }
+
+ response.setStatus(rCode);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress() {
+ return InetSocketAddress.createUnresolved(request.getRemoteAddr(), request.getRemotePort());
+ }
+
+ @Override
+ public int getResponseCode() {
+ return statusCode;
+ }
+
+ @Override
+ public InetSocketAddress getLocalAddress() {
+ return new InetSocketAddress(request.getLocalAddr(), request.getLocalPort());
+ }
+
+ @Override
+ public String getProtocol() {
+ return request.getProtocol().getProtocolString();
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return request.getAttribute(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object value) {
+ request.setAttribute(name, value);
+ }
+
+ @Override
+ public void setStreams(InputStream i, OutputStream o) {
+ assert inputStream != null;
+ if (i != null) inputStream = i;
+ if (o != null) outputStream = o;
+ }
+
+ @Override
+ public HttpPrincipal getPrincipal() {
+ return httpPrincipal;
+ }
+
+ public void setPrincipal(HttpPrincipal principal) {
+ this.httpPrincipal = principal;
+ }
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java
new file mode 100644
index 00000000..9103a6db
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java
@@ -0,0 +1,124 @@
+package io.avaje.helidon.http.spi;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.callback.Callback;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.glassfish.grizzly.http.server.Response;
+
+import com.sun.net.httpserver.Authenticator;
+import com.sun.net.httpserver.Authenticator.Result;
+import com.sun.net.httpserver.Filter.Chain;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpPrincipal;
+import com.sun.org.slf4j.internal.LoggerFactory;
+
+/**
+ * Jetty handler that bridges requests to {@link HttpHandler}.
+ */
+public class HttpSpiContextHandler extends ContextHandler
+{
+ public static final Logger LOG = LoggerFactory.getLogger(HttpSpiContextHandler.class);
+
+ private final HttpContext _httpContext;
+
+ private HttpHandler _httpHandler;
+
+ public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler)
+ {
+ this._httpContext = httpContext;
+ this._httpHandler = httpHandler;
+ // The default jax-ws web server allows posting to URLs that do not end
+ // with a trailing '/'; allow it too to be a drop-in replacement.
+ setAllowNullPathInContext(true);
+ super.setHandler(new Handler.Abstract()
+ {
+ @Override
+ public boolean handle(Request request, Response response, Callback callback)
+ {
+ try (HttpExchange jettyHttpExchange = request.isSecure()
+ ? new JettyHttpsExchange(_httpContext, request, response)
+ : new JettyHttpExchange(_httpContext, request, response))
+ {
+ Authenticator auth = _httpContext.getAuthenticator();
+ if (auth != null && handleAuthentication(request, response, callback, jettyHttpExchange, auth))
+ return true;
+
+ new Chain(_httpContext.getFilters(), _httpHandler).doFilter(jettyHttpExchange);
+ callback.succeeded();
+ }
+ catch (Exception ex)
+ {
+ LOG.debug("Failed to handle", ex);
+ Response.writeError(request, response, callback, 500, null, ex);
+ }
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public void setHandler(Handler handler)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ private boolean handleAuthentication(
+ Request request,
+ Response response,
+ Callback callback,
+ HttpExchange httpExchange,
+ Authenticator auth)
+ {
+ Result result = auth.authenticate(httpExchange);
+ if (result instanceof Authenticator.Failure)
+ {
+ int rc = ((Authenticator.Failure)result).getResponseCode();
+ for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet())
+ {
+ for (String value : header.getValue())
+ response.getHeaders().add(header.getKey(), value);
+ }
+ Response.writeError(request, response, callback, rc);
+ return true;
+ }
+
+ if (result instanceof Authenticator.Retry)
+ {
+ int rc = ((Authenticator.Retry)result).getResponseCode();
+ for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet())
+ {
+ for (String value : header.getValue())
+ {
+ response.getHeaders().add(header.getKey(), value);
+ }
+ }
+ Response.writeError(request, response, callback, rc);
+ return true;
+ }
+
+ if (result instanceof Authenticator.Success)
+ {
+ HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal();
+ ((JettyExchange)httpExchange).setPrincipal(principal);
+ return false;
+ }
+
+ Response.writeError(request, response, callback, 500);
+ return true;
+ }
+
+ public HttpHandler getHttpHandler()
+ {
+ return _httpHandler;
+ }
+
+ public void setHttpHandler(HttpHandler handler)
+ {
+ this._httpHandler = handler;
+ }
+}
\ No newline at end of file
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
new file mode 100644
index 00000000..8ff3292f
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
@@ -0,0 +1,84 @@
+package io.avaje.helidon.http.spi;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Jetty implementation of {@link com.sun.net.httpserver.HttpContext}
+ */
+public class JettyHttpContext extends com.sun.net.httpserver.HttpContext
+{
+
+ private final HttpSpiContextHandler _jettyContextHandler;
+
+ private final HttpServer _server;
+
+ private final Map _attributes = new HashMap();
+
+ private final List _filters = new ArrayList();
+
+ private Authenticator _authenticator;
+
+ protected JettyHttpContext(HttpServer server, String contextPath, HttpHandler handler)
+ {
+ this._server = server;
+ _jettyContextHandler = new HttpSpiContextHandler(this, handler);
+ _jettyContextHandler.setContextPath(contextPath);
+ }
+
+ protected HttpSpiContextHandler getJettyContextHandler()
+ {
+ return _jettyContextHandler;
+ }
+
+ @Override
+ public HttpHandler getHandler()
+ {
+ return _jettyContextHandler.getHttpHandler();
+ }
+
+ @Override
+ public void setHandler(HttpHandler h)
+ {
+ _jettyContextHandler.setHttpHandler(h);
+ }
+
+ @Override
+ public String getPath()
+ {
+ return _jettyContextHandler.getContextPath();
+ }
+
+ @Override
+ public HttpServer getServer()
+ {
+ return _server;
+ }
+
+ @Override
+ public Map getAttributes()
+ {
+ return _attributes;
+ }
+
+ @Override
+ public List getFilters()
+ {
+ return _filters;
+ }
+
+ @Override
+ public Authenticator setAuthenticator(Authenticator auth)
+ {
+ Authenticator previous = _authenticator;
+ _authenticator = auth;
+ return previous;
+ }
+
+ @Override
+ public Authenticator getAuthenticator()
+ {
+ return _authenticator;
+ }
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
new file mode 100644
index 00000000..d5d33eb2
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
@@ -0,0 +1,215 @@
+package io.avaje.helidon.http.spi;
+
+import java.io.IOException;
+import java.lang.System.Logger.Level;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+
+import com.sun.jdi.connect.Connector;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpHandler;
+
+import sun.nio.ch.ThreadPool;
+
+/** Jetty implementation of {@link com.sun.net.httpserver.HttpServer}. */
+public class JettyHttpServer extends com.sun.net.httpserver.HttpServer {
+ private static final System.Logger LOG =
+ System.getLogger(JettyHttpServer.class.getCanonicalName());
+ private final HttpServer _server;
+ private final boolean _serverShared;
+ private final Map _contexts = new HashMap<>();
+ private final Map _connectors = new HashMap<>();
+ private InetSocketAddress _addr;
+ private ServerConfiguration _httpConfiguration;
+
+ public JettyHttpServer(HttpServer server, boolean shared) {
+
+ this(server, shared, server.getServerConfiguration());
+ }
+
+ public JettyHttpServer(HttpServer server, boolean shared, ServerConfiguration configuration) {
+ this._server = server;
+ this._serverShared = shared;
+ this._httpConfiguration = configuration;
+ }
+
+ public ServerConfiguration getHttpConfiguration() {
+ return _httpConfiguration;
+ }
+
+ @Override
+ public void bind(InetSocketAddress addr, int backlog) throws IOException {
+ this._addr = addr;
+ // check if there is already a connector listening
+ var connectors = _server.getListeners();
+ if (connectors != null) {
+ for (var connector : connectors) {
+ if (connector.getPort() == addr.getPort()) {
+ LOG.log(
+ Level.DEBUG, "server already bound to port {}, no need to rebind", addr.getPort());
+ return;
+ }
+ }
+ }
+
+ if (_serverShared)
+ throw new IOException("grizzly server is not bound to port " + addr.getPort());
+
+ if (LOG.isLoggable(Level.DEBUG)) {
+ LOG.log(Level.DEBUG, "binding server to port " + addr.getPort());
+ }
+
+ NetworkListener listener = new NetworkListener("rizzly", addr.getHostName(), addr.getPort());
+ _server.addListener(listener);
+ _connectors.put(addr.getHostName() + addr.getPort(), listener);
+ }
+
+ protected HttpServer getServer() {
+ return _server;
+ }
+
+ protected NetworkListener newServerConnector(InetSocketAddress addr, int backlog) {
+ NetworkListener listener = new NetworkListener("rizzly", addr.getHostName(), addr.getPort());
+
+ return listener;
+ }
+
+ @Override
+ public InetSocketAddress getAddress() {
+ if (_addr.getPort() == 0 && _server.isStarted())
+ return new InetSocketAddress(_addr.getHostString(), _server.getListener("rizzly").getPort());
+ return _addr;
+ }
+
+ @Override
+ public void start() {
+ if (_serverShared) return;
+
+ try {
+ _server.start();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public void setExecutor(Executor executor) {
+ if (executor == null)
+ throw new IllegalArgumentException("missing required 'executor' argument");
+ }
+
+ @Override
+ public Executor getExecutor() {
+ ThreadPool threadPool = _server.getThreadPool();
+ if (threadPool instanceof DelegatingThreadPool)
+ return ((DelegatingThreadPool) _server.getThreadPool()).getExecutor();
+ return threadPool;
+ }
+
+ @Override
+ public void stop(int delay) {
+ cleanUpContexts();
+ cleanUpConnectors();
+
+ if (_serverShared) return;
+
+ _server.shutdown();
+ }
+
+ private void cleanUpContexts() {
+ for (Map.Entry stringJettyHttpContextEntry : _contexts.entrySet()) {
+ JettyHttpContext context = stringJettyHttpContextEntry.getValue();
+ _server.removeBean(context.getJettyContextHandler());
+ }
+ _contexts.clear();
+ }
+
+ private void cleanUpConnectors() {
+ for (Map.Entry stringConnectorEntry : _connectors.entrySet()) {
+ Connector connector = stringConnectorEntry.getValue();
+ try {
+ connector.stop();
+ } catch (Exception ex) {
+ LOG.warn("Unable to stop connector {}", connector, ex);
+ }
+ _server.removeConnector(connector);
+ }
+ _connectors.clear();
+ }
+
+ @Override
+ public HttpContext createContext(String path, HttpHandler httpHandler) {
+ checkIfContextIsFree(path);
+
+ JettyHttpContext context = new JettyHttpContext(this, path, httpHandler);
+ HttpSpiContextHandler jettyContextHandler = context.getJettyContextHandler();
+
+ ContextHandlerCollection contexts = _server.getDescendant(ContextHandlerCollection.class);
+
+ if (contexts == null)
+ throw new RuntimeException("could not find ContextHandlerCollection, you must configure one");
+
+ contexts.addHandler(jettyContextHandler);
+ if (contexts.isStarted()) {
+ try {
+ jettyContextHandler.start();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ _contexts.put(path, context);
+ return context;
+ }
+
+ @Override
+ public HttpContext createContext(String path) {
+ return createContext(path, null);
+ }
+
+ private void checkIfContextIsFree(String path) {
+ Handler serverHandler = _server.getHandler();
+ if (serverHandler instanceof ContextHandler) {
+ ContextHandler ctx = (ContextHandler) serverHandler;
+ if (ctx.getContextPath().equals(path))
+ throw new RuntimeException("another context already bound to path " + path);
+ }
+
+ List handlers = _server.getHandlers();
+ for (Handler handler : handlers) {
+ if (handler instanceof ContextHandler) {
+ ContextHandler ctx = (ContextHandler) handler;
+ if (ctx.getContextPath().equals(path))
+ throw new RuntimeException("another context already bound to path " + path);
+ }
+ }
+ }
+
+ @Override
+ public void removeContext(String path) throws IllegalArgumentException {
+ JettyHttpContext context = _contexts.remove(path);
+ if (context == null) return;
+ HttpSpiContextHandler handler = context.getJettyContextHandler();
+
+ ContextHandlerCollection chc = _server.getDescendant(ContextHandlerCollection.class);
+ try {
+ handler.stop();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ chc.removeHandler(handler);
+ }
+
+ @Override
+ public void removeContext(HttpContext context) {
+ removeContext(context.getPath());
+ }
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java
new file mode 100644
index 00000000..121c33d7
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java
@@ -0,0 +1,65 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package io.avaje.helidon.http.spi;
+
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Ondro Mihalyi
+ */
+public class VirtualThreadsExecutorService extends AbstractExecutorService {
+
+ VirtualThreadFactory threadFactory = new VirtualThreadFactory();
+ ExecutorService pool = Executors.newThreadPerTaskExecutor(threadFactory);
+
+ @Override
+ public void shutdown() {
+ pool.shutdown();
+ }
+
+ @Override
+ public List shutdownNow() {
+ return pool.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return pool.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return pool.isTerminated();
+ }
+
+ @Override
+ public void execute(Runnable r) {
+ pool.execute(r);
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return pool.awaitTermination(timeout, unit);
+ }
+
+
+ private static class VirtualThreadFactory implements ThreadFactory {
+
+ int threadIndex = 0;
+
+ String name = "virtual-thread";
+
+ @Override
+ public Thread newThread(Runnable r) {
+ return Thread.ofVirtual().name(name + "(" + threadIndex++ + ")").unstarted(r);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 06ec7825..7028ff5c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,6 +44,7 @@
avaje-jex-mustache
avaje-jex-htmx
avaje-jex-static-content
+ avaje-jex-helidon-spi
From 220388d995d0ccc9578f64acf470abec3e32d910 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Mar 2025 20:10:31 -0400
Subject: [PATCH 2/5] hmm
---
.../http/spi/GrizzlyHttpExchangeDelegate.java | 4 +-
.../avaje/helidon/http/spi/JettyExchange.java | 14 ++
.../helidon/http/spi/JettyHttpExchange.java | 129 +++++++++++++++++
.../helidon/http/spi/JettyHttpServer.java | 11 +-
.../helidon/http/spi/JettyHttpsExchange.java | 137 ++++++++++++++++++
5 files changed, 287 insertions(+), 8 deletions(-)
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
create mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
index 0a571a58..d307c9b5 100644
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
@@ -55,10 +55,10 @@ public class GrizzlyHttpExchangeDelegate extends HttpExchange {
private Response response;
- GrizzlyHttpExchangeDelegate(HttpContext httpSpiContext, Request request) {
+ GrizzlyHttpExchangeDelegate(HttpContext httpSpiContext, Request request, Response response) {
this.context = httpSpiContext;
this.request = request;
- this.response = request.getResponse();
+ this.response = response;
this.inputStream = request.getInputStream();
this.outputStream = response.getOutputStream();
}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
new file mode 100644
index 00000000..effc7a0e
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
@@ -0,0 +1,14 @@
+package io.avaje.helidon.http.spi;
+
+import com.sun.net.httpserver.HttpPrincipal;
+
+/**
+ *
+ */
+public interface JettyExchange
+{
+
+ HttpPrincipal getPrincipal();
+
+ void setPrincipal(HttpPrincipal principal);
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
new file mode 100644
index 00000000..04ce4a0b
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
@@ -0,0 +1,129 @@
+package io.avaje.helidon.http.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpPrincipal;
+
+public class JettyHttpExchange extends HttpExchange implements JettyExchange {
+ private final GrizzlyHttpExchangeDelegate delegate;
+
+ public JettyHttpExchange(HttpContext context, Request req, Response resp) {
+
+ delegate = new GrizzlyHttpExchangeDelegate(context, req, resp);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public Headers getRequestHeaders() {
+ return delegate.getRequestHeaders();
+ }
+
+ @Override
+ public Headers getResponseHeaders() {
+ return delegate.getResponseHeaders();
+ }
+
+ @Override
+ public URI getRequestURI() {
+ return delegate.getRequestURI();
+ }
+
+ @Override
+ public String getRequestMethod() {
+ return delegate.getRequestMethod();
+ }
+
+ @Override
+ public HttpContext getHttpContext() {
+ return delegate.getHttpContext();
+ }
+
+ @Override
+ public void close() {
+ delegate.close();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public InputStream getRequestBody() {
+ return delegate.getRequestBody();
+ }
+
+ @Override
+ public OutputStream getResponseBody() {
+ return delegate.getResponseBody();
+ }
+
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
+ delegate.sendResponseHeaders(rCode, responseLength);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress() {
+ return delegate.getRemoteAddress();
+ }
+
+ @Override
+ public int getResponseCode() {
+ return delegate.getResponseCode();
+ }
+
+ @Override
+ public InetSocketAddress getLocalAddress() {
+ return delegate.getLocalAddress();
+ }
+
+ @Override
+ public String getProtocol() {
+ return delegate.getProtocol();
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return delegate.getAttribute(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object value) {
+ delegate.setAttribute(name, value);
+ }
+
+ @Override
+ public void setStreams(InputStream i, OutputStream o) {
+ delegate.setStreams(i, o);
+ }
+
+ @Override
+ public HttpPrincipal getPrincipal() {
+ return delegate.getPrincipal();
+ }
+
+ @Override
+ public void setPrincipal(HttpPrincipal principal) {
+ delegate.setPrincipal(principal);
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
index d5d33eb2..98c7acd4 100644
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
@@ -14,7 +14,6 @@
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.http.server.ServerConfiguration;
-import com.sun.jdi.connect.Connector;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpHandler;
@@ -134,14 +133,14 @@ private void cleanUpContexts() {
}
private void cleanUpConnectors() {
- for (Map.Entry stringConnectorEntry : _connectors.entrySet()) {
- Connector connector = stringConnectorEntry.getValue();
+ for (var stringConnectorEntry : _connectors.entrySet()) {
+ var connector = stringConnectorEntry.getValue();
try {
- connector.stop();
+ connector.shutdownNow();
} catch (Exception ex) {
- LOG.warn("Unable to stop connector {}", connector, ex);
+ LOG.log(Level.WARNING, "Unable to stop connector {}", connector, ex);
}
- _server.removeConnector(connector);
+ _server.removeListener(stringConnectorEntry.getKey());
}
_connectors.clear();
}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
new file mode 100644
index 00000000..ad86bbb0
--- /dev/null
+++ b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
@@ -0,0 +1,137 @@
+package io.avaje.helidon.http.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+import javax.net.ssl.SSLSession;
+
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpPrincipal;
+import com.sun.net.httpserver.HttpsExchange;
+
+/** */
+public class JettyHttpsExchange extends HttpsExchange implements JettyExchange {
+ private final GrizzlyHttpExchangeDelegate _delegate;
+
+ public JettyHttpsExchange(HttpContext jaxWsContext, Request req, Response resp) {
+ super();
+ _delegate = new GrizzlyHttpExchangeDelegate(jaxWsContext, req, resp);
+ }
+
+ @Override
+ public int hashCode() {
+ return _delegate.hashCode();
+ }
+
+ @Override
+ public Headers getRequestHeaders() {
+ return _delegate.getRequestHeaders();
+ }
+
+ @Override
+ public Headers getResponseHeaders() {
+ return _delegate.getResponseHeaders();
+ }
+
+ @Override
+ public URI getRequestURI() {
+ return _delegate.getRequestURI();
+ }
+
+ @Override
+ public String getRequestMethod() {
+ return _delegate.getRequestMethod();
+ }
+
+ @Override
+ public HttpContext getHttpContext() {
+ return _delegate.getHttpContext();
+ }
+
+ @Override
+ public void close() {
+ _delegate.close();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return _delegate.equals(obj);
+ }
+
+ @Override
+ public InputStream getRequestBody() {
+ return _delegate.getRequestBody();
+ }
+
+ @Override
+ public OutputStream getResponseBody() {
+ return _delegate.getResponseBody();
+ }
+
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
+ _delegate.sendResponseHeaders(rCode, responseLength);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress() {
+ return _delegate.getRemoteAddress();
+ }
+
+ @Override
+ public int getResponseCode() {
+ return _delegate.getResponseCode();
+ }
+
+ @Override
+ public InetSocketAddress getLocalAddress() {
+ return _delegate.getLocalAddress();
+ }
+
+ @Override
+ public String getProtocol() {
+ return _delegate.getProtocol();
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return _delegate.getAttribute(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object value) {
+ _delegate.setAttribute(name, value);
+ }
+
+ @Override
+ public void setStreams(InputStream i, OutputStream o) {
+ _delegate.setStreams(i, o);
+ }
+
+ @Override
+ public HttpPrincipal getPrincipal() {
+ return _delegate.getPrincipal();
+ }
+
+ @Override
+ public void setPrincipal(HttpPrincipal principal) {
+ _delegate.setPrincipal(principal);
+ }
+
+ @Override
+ public String toString() {
+ return _delegate.toString();
+ }
+
+ @Override
+ public SSLSession getSSLSession() {
+ return null;
+ }
+}
From 1ff92e5d883522b944e12af9a250d10262bde8c7 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Fri, 21 Mar 2025 18:47:10 -0400
Subject: [PATCH 3/5] start
---
avaje-jex-grizzly-spi/pom.xml | 25 ++
.../helidon/http/spi/GrizzlyHandler.java | 87 +++++++
.../http/spi/GrizzlyHttpExchangeDelegate.java | 0
.../http/spi/GrizzlyHttpServerProvider.java | 50 ++++
.../helidon/http/spi/HttpServerBuilder.java | 80 +++++++
.../avaje/helidon/http/spi/JettyExchange.java | 10 +
.../helidon/http/spi/JettyHttpContext.java | 78 +++++++
.../helidon/http/spi/JettyHttpExchange.java | 0
.../helidon/http/spi/JettyHttpServer.java | 173 ++++++++++++++
.../helidon/http/spi/JettyHttpsExchange.java | 3 +-
avaje-jex-helidon-spi/pom.xml | 20 --
.../http/spi/HttpSpiContextHandler.java | 124 ----------
.../avaje/helidon/http/spi/JettyExchange.java | 14 --
.../helidon/http/spi/JettyHttpContext.java | 84 -------
.../helidon/http/spi/JettyHttpServer.java | 214 ------------------
.../spi/VirtualThreadsExecutorService.java | 65 ------
pom.xml | 6 +-
17 files changed, 507 insertions(+), 526 deletions(-)
create mode 100644 avaje-jex-grizzly-spi/pom.xml
create mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java
rename {avaje-jex-helidon-spi => avaje-jex-grizzly-spi}/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java (100%)
create mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpServerProvider.java
create mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java
create mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
create mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
rename {avaje-jex-helidon-spi => avaje-jex-grizzly-spi}/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java (100%)
create mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
rename {avaje-jex-helidon-spi => avaje-jex-grizzly-spi}/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java (99%)
delete mode 100644 avaje-jex-helidon-spi/pom.xml
delete mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java
delete mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
delete mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
delete mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
delete mode 100644 avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java
diff --git a/avaje-jex-grizzly-spi/pom.xml b/avaje-jex-grizzly-spi/pom.xml
new file mode 100644
index 00000000..56054d21
--- /dev/null
+++ b/avaje-jex-grizzly-spi/pom.xml
@@ -0,0 +1,25 @@
+
+ 4.0.0
+
+ io.avaje
+ avaje-jex-parent
+ 3.0-RC23
+
+ avaje-jex-grizzly-spi
+
+
+
+ org.glassfish.grizzly
+ grizzly-http-server
+ 4.1.0-M1
+
+
+ io.avaje
+ avaje-spi-service
+ provided
+ true
+
+
+
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java
new file mode 100644
index 00000000..38bb61fe
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java
@@ -0,0 +1,87 @@
+package io.avaje.helidon.http.spi;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.List;
+import java.util.Map;
+
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+
+import com.sun.net.httpserver.Authenticator;
+import com.sun.net.httpserver.Authenticator.Result;
+import com.sun.net.httpserver.Filter.Chain;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpPrincipal;
+
+class GrizzlyHandler extends org.glassfish.grizzly.http.server.HttpHandler {
+
+ private final HttpContext httpContext;
+
+ private HttpHandler handler;
+
+ GrizzlyHandler(HttpContext httpContext, HttpHandler httpHandler) {
+ super(httpContext.getPath());
+ this.httpContext = httpContext;
+ this.handler = httpHandler;
+ }
+
+ @Override
+ public void service(Request request, Response response) {
+
+ try (HttpExchange exchange =
+ request.isSecure()
+ ? new JettyHttpsExchange(httpContext, request, response)
+ : new JettyHttpExchange(httpContext, request, response)) {
+ Authenticator auth = httpContext.getAuthenticator();
+
+ if (auth != null && handleAuthentication(request, response, exchange, auth)) {
+ return;
+ }
+ new Chain(httpContext.getFilters(), handler).doFilter(exchange);
+
+ } catch (IOException ex) {
+ throw new UncheckedIOException(null);
+ }
+ }
+
+ public HttpHandler getHttpHandler() {
+ return handler;
+ }
+
+ public void setHttpHandler(HttpHandler handler) {
+ this.handler = handler;
+ }
+
+ private boolean handleAuthentication(
+ Request request, Response response, HttpExchange httpExchange, Authenticator auth)
+ throws IOException {
+ Result result = auth.authenticate(httpExchange);
+ if (result instanceof Authenticator.Failure fail) {
+ response.sendError(fail.getResponseCode(), "");
+ for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet()) {
+ for (String value : header.getValue()) response.addHeader(header.getKey(), value);
+ }
+ return true;
+ }
+
+ if (result instanceof Authenticator.Retry ry) {
+ for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet()) {
+ for (String value : header.getValue()) {
+ response.addHeader(header.getKey(), value);
+ }
+ }
+ response.sendError(ry.getResponseCode(), "Failed to Authenticate");
+ return true;
+ }
+
+ if (result instanceof Authenticator.Success s) {
+ HttpPrincipal principal = s.getPrincipal();
+ ((JettyExchange) httpExchange).setPrincipal(principal);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
similarity index 100%
rename from avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpServerProvider.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpServerProvider.java
new file mode 100644
index 00000000..997625f7
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpServerProvider.java
@@ -0,0 +1,50 @@
+package io.avaje.helidon.http.spi;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+
+import com.sun.net.httpserver.HttpsServer;
+import com.sun.net.httpserver.spi.HttpServerProvider;
+
+import io.avaje.spi.ServiceProvider;
+
+@ServiceProvider
+public class GrizzlyHttpServerProvider extends HttpServerProvider {
+
+ private org.glassfish.grizzly.http.server.HttpServer server;
+
+ public GrizzlyHttpServerProvider(HttpServer server) {
+
+ this.server = server;
+ }
+
+ public GrizzlyHttpServerProvider() {
+
+ this.server = new HttpServer();
+ }
+
+ @Override
+ public com.sun.net.httpserver.HttpServer createHttpServer(InetSocketAddress addr, int backlog)
+ throws IOException {
+
+ return createServer(addr, backlog);
+ }
+
+ @Override
+ public HttpsServer createHttpsServer(InetSocketAddress addr, int backlog) throws IOException {
+ return createServer(addr, backlog);
+ }
+
+ private com.sun.net.httpserver.HttpsServer createServer(InetSocketAddress addr, int backlog)
+ throws IOException {
+ if (server == null) {
+ server = new HttpServer();
+ }
+
+ JettyHttpServer jettyHttpServer = new JettyHttpServer(server);
+ if (addr != null) jettyHttpServer.bind(addr, backlog);
+ return jettyHttpServer;
+ }
+}
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java
new file mode 100644
index 00000000..6989a0a8
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java
@@ -0,0 +1,80 @@
+package io.avaje.helidon.http.spi;
+
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpHandlerRegistration;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
+import org.glassfish.grizzly.utils.Charsets;
+
+public class HttpServerBuilder {
+
+ private int port = -1;
+ private String host = "0.0.0.0";
+ private boolean secure;
+ private SSLEngineConfigurator sslEngineConfigurator;
+
+ private final HttpServer server = new HttpServer();
+
+ public HttpServerBuilder setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public HttpServerBuilder host(String host) {
+ this.host = host;
+ return this;
+ }
+
+ public HttpServerBuilder sslEngineConfigurator(SSLEngineConfigurator sslEngineConfigurator) {
+ this.sslEngineConfigurator = sslEngineConfigurator;
+ return this;
+ }
+
+ public HttpServerBuilder secure(boolean secure) {
+ this.secure = secure;
+ return this;
+ }
+
+ /**
+ * Add a handler with the given context.
+ */
+ public HttpServerBuilder handler(HttpHandler handler, String context) {
+ handler(handler, HttpHandlerRegistration.fromString("/" + context + "/*"));
+ return this;
+ }
+
+ /**
+ * Add a handler given the paths.
+ */
+ public HttpServerBuilder handler(HttpHandler handler, HttpHandlerRegistration... paths) {
+ server.getServerConfiguration().addHttpHandler(handler, paths);
+ return this;
+ }
+
+ /**
+ * Build and return the grizzly http server.
+ */
+ public HttpServer build() {
+
+ int serverPort = serverPort();
+ NetworkListener listener = new NetworkListener("grizzly", host, serverPort);
+
+ // TODO: Configure to use loom thread factory
+ // listener.getTransport().getWorkerThreadPoolConfig().setThreadFactory()
+ listener.setSecure(secure);
+ if (sslEngineConfigurator != null) {
+ listener.setSSLEngineConfig(sslEngineConfigurator);
+ }
+ server.addListener(listener);
+ ServerConfiguration config = server.getServerConfiguration();
+ config.setPassTraceRequest(true);
+ config.setDefaultQueryEncoding(Charsets.UTF8_CHARSET);
+ return server;
+ }
+
+ protected int serverPort() {
+ return port != -1 ? port : secure ? 8443 : 7001;
+ }
+}
\ No newline at end of file
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
new file mode 100644
index 00000000..ac1e82d9
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
@@ -0,0 +1,10 @@
+package io.avaje.helidon.http.spi;
+
+import com.sun.net.httpserver.HttpPrincipal;
+
+public interface JettyExchange {
+
+ HttpPrincipal getPrincipal();
+
+ void setPrincipal(HttpPrincipal principal);
+}
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
new file mode 100644
index 00000000..2e69770c
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
@@ -0,0 +1,78 @@
+package io.avaje.helidon.http.spi;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.net.httpserver.Authenticator;
+import com.sun.net.httpserver.Filter;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+/** Jetty implementation of {@link com.sun.net.httpserver.HttpContext} */
+public class JettyHttpContext extends com.sun.net.httpserver.HttpContext {
+
+ private final GrizzlyHandler grizzlyHandler;
+ private final HttpServer server;
+
+ private final Map attributes = new HashMap<>();
+
+ private final List filters = new ArrayList<>();
+
+ private Authenticator authenticator;
+
+ private String contextPath;
+
+ protected JettyHttpContext(HttpServer server, String contextPath, HttpHandler handler) {
+ this.server = server;
+ this.grizzlyHandler = new GrizzlyHandler(this, handler);
+ this.contextPath = contextPath;
+ }
+
+ GrizzlyHandler getGrizzlyHandler() {
+ return grizzlyHandler;
+ }
+
+ @Override
+ public HttpHandler getHandler() {
+ return grizzlyHandler.getHttpHandler();
+ }
+
+ @Override
+ public void setHandler(HttpHandler h) {
+ grizzlyHandler.setHttpHandler(h);
+ }
+
+ @Override
+ public String getPath() {
+ return contextPath;
+ }
+
+ @Override
+ public HttpServer getServer() {
+ return server;
+ }
+
+ @Override
+ public Map getAttributes() {
+ return attributes;
+ }
+
+ @Override
+ public List getFilters() {
+ return filters;
+ }
+
+ @Override
+ public Authenticator setAuthenticator(Authenticator auth) {
+ Authenticator previous = authenticator;
+ authenticator = auth;
+ return previous;
+ }
+
+ @Override
+ public Authenticator getAuthenticator() {
+ return authenticator;
+ }
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
similarity index 100%
rename from avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
new file mode 100644
index 00000000..fa6c2015
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
@@ -0,0 +1,173 @@
+package io.avaje.helidon.http.spi;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.System.Logger.Level;
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
+
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpsConfigurator;
+
+/** Jetty implementation of {@link com.sun.net.httpserver.HttpServer}. */
+public class JettyHttpServer extends com.sun.net.httpserver.HttpsServer {
+ private static final System.Logger LOG =
+ System.getLogger(JettyHttpServer.class.getCanonicalName());
+ private final HttpServer server;
+ private final Map contexts = new HashMap<>();
+ private InetSocketAddress addr;
+ private ServerConfiguration httpConfiguration;
+ private ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
+ private HttpsConfigurator httpsConfig;
+
+ public JettyHttpServer(HttpServer server) {
+
+ this(server, server.getServerConfiguration());
+ }
+
+ public JettyHttpServer(HttpServer server, ServerConfiguration configuration) {
+ this.server = server;
+ this.httpConfiguration = configuration;
+ }
+
+ public ServerConfiguration getHttpConfiguration() {
+ return httpConfiguration;
+ }
+
+ @Override
+ public void bind(InetSocketAddress addr, int backlog) throws IOException {
+
+ this.addr = addr;
+ // check if there is already a connector listening
+ var connectors = server.getListeners();
+ if (connectors != null) {
+ for (var connector : connectors) {
+ if (connector.getPort() == addr.getPort()) {
+ LOG.log(
+ Level.DEBUG, "server already bound to port {}, no need to rebind", addr.getPort());
+ return;
+ }
+ }
+ }
+
+ if (LOG.isLoggable(Level.DEBUG)) {
+ LOG.log(Level.DEBUG, "binding server to port " + addr.getPort());
+ }
+ var listener = new NetworkListener("rizzly", addr.getHostName(), addr.getPort());
+ listener.getTransport().setWorkerThreadPool(executor);
+ if (backlog != 0) {
+ listener.getTransport().setServerConnectionBackLog(backlog);
+ }
+ if (httpsConfig != null) {
+ listener.setSSLEngineConfig(new SSLEngineConfigurator(httpsConfig.getSSLContext()));
+ }
+
+ server.addListener(listener);
+ }
+
+ protected HttpServer getServer() {
+ return server;
+ }
+
+ @Override
+ public InetSocketAddress getAddress() {
+ if (addr.getPort() == 0 && server.isStarted())
+ return new InetSocketAddress(addr.getHostString(), server.getListener("rizzly").getPort());
+ return addr;
+ }
+
+ @Override
+ public void start() {
+
+ try {
+ server.start();
+ } catch (IOException e) {
+
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public void setExecutor(Executor executor) {
+ if (executor instanceof ExecutorService service) {
+ this.executor = service;
+ } else {
+ throw new IllegalArgumentException("Grizzly only accepts ExecutorService");
+ }
+ }
+
+ @Override
+ public Executor getExecutor() {
+ return executor;
+ }
+
+ @Override
+ public void stop(int delay) {
+
+ for (var context : contexts.values()) {
+ httpConfiguration.removeHttpHandler(context.getGrizzlyHandler());
+ }
+ contexts.clear();
+
+ server.shutdown();
+ }
+
+ @Override
+ public HttpContext createContext(String path, HttpHandler httpHandler) {
+
+ JettyHttpContext context = new JettyHttpContext(this, path, httpHandler);
+ GrizzlyHandler jettyContextHandler = context.getGrizzlyHandler();
+
+ httpConfiguration.addHttpHandler(
+ jettyContextHandler, path.transform(this::prependSlash).transform(this::appendSlash));
+
+ contexts.put(path, context);
+ return context;
+ }
+
+ private String prependSlash(String s) {
+ return s.startsWith("/") ? s : "/" + s;
+ }
+
+ private String appendSlash(String s) {
+ return s.endsWith("/") ? s + "*" : s + "/*";
+ }
+
+ @Override
+ public HttpContext createContext(String path) {
+ return createContext(path, null);
+ }
+
+ @Override
+ public void removeContext(String path) throws IllegalArgumentException {
+ JettyHttpContext context = contexts.remove(path);
+ if (context == null) return;
+ GrizzlyHandler handler = context.getGrizzlyHandler();
+ httpConfiguration.removeHttpHandler(handler);
+ }
+
+ @Override
+ public void removeContext(HttpContext context) {
+ removeContext(context.getPath());
+ }
+
+ @Override
+ public void setHttpsConfigurator(HttpsConfigurator config) {
+ httpsConfig = config;
+ }
+
+ @Override
+ public HttpsConfigurator getHttpsConfigurator() {
+ return httpsConfig;
+ }
+}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
similarity index 99%
rename from avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
index ad86bbb0..16b261cd 100644
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
@@ -16,12 +16,11 @@
import com.sun.net.httpserver.HttpPrincipal;
import com.sun.net.httpserver.HttpsExchange;
-/** */
+
public class JettyHttpsExchange extends HttpsExchange implements JettyExchange {
private final GrizzlyHttpExchangeDelegate _delegate;
public JettyHttpsExchange(HttpContext jaxWsContext, Request req, Response resp) {
- super();
_delegate = new GrizzlyHttpExchangeDelegate(jaxWsContext, req, resp);
}
diff --git a/avaje-jex-helidon-spi/pom.xml b/avaje-jex-helidon-spi/pom.xml
deleted file mode 100644
index 696df4c1..00000000
--- a/avaje-jex-helidon-spi/pom.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
- 4.0.0
-
- io.avaje
- avaje-jex-parent
- 3.0-RC23
-
- avaje-jex-helidon-spi
-
-
-
-
- org.glassfish.grizzly
- grizzly-http-server
- 4.1.0-M1
-
-
-
-
-
\ No newline at end of file
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java
deleted file mode 100644
index 9103a6db..00000000
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/HttpSpiContextHandler.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package io.avaje.helidon.http.spi;
-
-import java.util.List;
-import java.util.Map;
-
-import javax.security.auth.callback.Callback;
-
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.glassfish.grizzly.http.server.Response;
-
-import com.sun.net.httpserver.Authenticator;
-import com.sun.net.httpserver.Authenticator.Result;
-import com.sun.net.httpserver.Filter.Chain;
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpPrincipal;
-import com.sun.org.slf4j.internal.LoggerFactory;
-
-/**
- * Jetty handler that bridges requests to {@link HttpHandler}.
- */
-public class HttpSpiContextHandler extends ContextHandler
-{
- public static final Logger LOG = LoggerFactory.getLogger(HttpSpiContextHandler.class);
-
- private final HttpContext _httpContext;
-
- private HttpHandler _httpHandler;
-
- public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler)
- {
- this._httpContext = httpContext;
- this._httpHandler = httpHandler;
- // The default jax-ws web server allows posting to URLs that do not end
- // with a trailing '/'; allow it too to be a drop-in replacement.
- setAllowNullPathInContext(true);
- super.setHandler(new Handler.Abstract()
- {
- @Override
- public boolean handle(Request request, Response response, Callback callback)
- {
- try (HttpExchange jettyHttpExchange = request.isSecure()
- ? new JettyHttpsExchange(_httpContext, request, response)
- : new JettyHttpExchange(_httpContext, request, response))
- {
- Authenticator auth = _httpContext.getAuthenticator();
- if (auth != null && handleAuthentication(request, response, callback, jettyHttpExchange, auth))
- return true;
-
- new Chain(_httpContext.getFilters(), _httpHandler).doFilter(jettyHttpExchange);
- callback.succeeded();
- }
- catch (Exception ex)
- {
- LOG.debug("Failed to handle", ex);
- Response.writeError(request, response, callback, 500, null, ex);
- }
- return true;
- }
- });
- }
-
- @Override
- public void setHandler(Handler handler)
- {
- throw new UnsupportedOperationException();
- }
-
- private boolean handleAuthentication(
- Request request,
- Response response,
- Callback callback,
- HttpExchange httpExchange,
- Authenticator auth)
- {
- Result result = auth.authenticate(httpExchange);
- if (result instanceof Authenticator.Failure)
- {
- int rc = ((Authenticator.Failure)result).getResponseCode();
- for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet())
- {
- for (String value : header.getValue())
- response.getHeaders().add(header.getKey(), value);
- }
- Response.writeError(request, response, callback, rc);
- return true;
- }
-
- if (result instanceof Authenticator.Retry)
- {
- int rc = ((Authenticator.Retry)result).getResponseCode();
- for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet())
- {
- for (String value : header.getValue())
- {
- response.getHeaders().add(header.getKey(), value);
- }
- }
- Response.writeError(request, response, callback, rc);
- return true;
- }
-
- if (result instanceof Authenticator.Success)
- {
- HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal();
- ((JettyExchange)httpExchange).setPrincipal(principal);
- return false;
- }
-
- Response.writeError(request, response, callback, 500);
- return true;
- }
-
- public HttpHandler getHttpHandler()
- {
- return _httpHandler;
- }
-
- public void setHttpHandler(HttpHandler handler)
- {
- this._httpHandler = handler;
- }
-}
\ No newline at end of file
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
deleted file mode 100644
index effc7a0e..00000000
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.avaje.helidon.http.spi;
-
-import com.sun.net.httpserver.HttpPrincipal;
-
-/**
- *
- */
-public interface JettyExchange
-{
-
- HttpPrincipal getPrincipal();
-
- void setPrincipal(HttpPrincipal principal);
-}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
deleted file mode 100644
index 8ff3292f..00000000
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package io.avaje.helidon.http.spi;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Jetty implementation of {@link com.sun.net.httpserver.HttpContext}
- */
-public class JettyHttpContext extends com.sun.net.httpserver.HttpContext
-{
-
- private final HttpSpiContextHandler _jettyContextHandler;
-
- private final HttpServer _server;
-
- private final Map _attributes = new HashMap();
-
- private final List _filters = new ArrayList();
-
- private Authenticator _authenticator;
-
- protected JettyHttpContext(HttpServer server, String contextPath, HttpHandler handler)
- {
- this._server = server;
- _jettyContextHandler = new HttpSpiContextHandler(this, handler);
- _jettyContextHandler.setContextPath(contextPath);
- }
-
- protected HttpSpiContextHandler getJettyContextHandler()
- {
- return _jettyContextHandler;
- }
-
- @Override
- public HttpHandler getHandler()
- {
- return _jettyContextHandler.getHttpHandler();
- }
-
- @Override
- public void setHandler(HttpHandler h)
- {
- _jettyContextHandler.setHttpHandler(h);
- }
-
- @Override
- public String getPath()
- {
- return _jettyContextHandler.getContextPath();
- }
-
- @Override
- public HttpServer getServer()
- {
- return _server;
- }
-
- @Override
- public Map getAttributes()
- {
- return _attributes;
- }
-
- @Override
- public List getFilters()
- {
- return _filters;
- }
-
- @Override
- public Authenticator setAuthenticator(Authenticator auth)
- {
- Authenticator previous = _authenticator;
- _authenticator = auth;
- return previous;
- }
-
- @Override
- public Authenticator getAuthenticator()
- {
- return _authenticator;
- }
-}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
deleted file mode 100644
index 98c7acd4..00000000
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
+++ /dev/null
@@ -1,214 +0,0 @@
-package io.avaje.helidon.http.spi;
-
-import java.io.IOException;
-import java.lang.System.Logger.Level;
-import java.net.InetSocketAddress;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executor;
-
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.server.handler.ContextHandlerCollection;
-import org.glassfish.grizzly.http.server.HttpServer;
-import org.glassfish.grizzly.http.server.NetworkListener;
-import org.glassfish.grizzly.http.server.ServerConfiguration;
-
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpHandler;
-
-import sun.nio.ch.ThreadPool;
-
-/** Jetty implementation of {@link com.sun.net.httpserver.HttpServer}. */
-public class JettyHttpServer extends com.sun.net.httpserver.HttpServer {
- private static final System.Logger LOG =
- System.getLogger(JettyHttpServer.class.getCanonicalName());
- private final HttpServer _server;
- private final boolean _serverShared;
- private final Map _contexts = new HashMap<>();
- private final Map _connectors = new HashMap<>();
- private InetSocketAddress _addr;
- private ServerConfiguration _httpConfiguration;
-
- public JettyHttpServer(HttpServer server, boolean shared) {
-
- this(server, shared, server.getServerConfiguration());
- }
-
- public JettyHttpServer(HttpServer server, boolean shared, ServerConfiguration configuration) {
- this._server = server;
- this._serverShared = shared;
- this._httpConfiguration = configuration;
- }
-
- public ServerConfiguration getHttpConfiguration() {
- return _httpConfiguration;
- }
-
- @Override
- public void bind(InetSocketAddress addr, int backlog) throws IOException {
- this._addr = addr;
- // check if there is already a connector listening
- var connectors = _server.getListeners();
- if (connectors != null) {
- for (var connector : connectors) {
- if (connector.getPort() == addr.getPort()) {
- LOG.log(
- Level.DEBUG, "server already bound to port {}, no need to rebind", addr.getPort());
- return;
- }
- }
- }
-
- if (_serverShared)
- throw new IOException("grizzly server is not bound to port " + addr.getPort());
-
- if (LOG.isLoggable(Level.DEBUG)) {
- LOG.log(Level.DEBUG, "binding server to port " + addr.getPort());
- }
-
- NetworkListener listener = new NetworkListener("rizzly", addr.getHostName(), addr.getPort());
- _server.addListener(listener);
- _connectors.put(addr.getHostName() + addr.getPort(), listener);
- }
-
- protected HttpServer getServer() {
- return _server;
- }
-
- protected NetworkListener newServerConnector(InetSocketAddress addr, int backlog) {
- NetworkListener listener = new NetworkListener("rizzly", addr.getHostName(), addr.getPort());
-
- return listener;
- }
-
- @Override
- public InetSocketAddress getAddress() {
- if (_addr.getPort() == 0 && _server.isStarted())
- return new InetSocketAddress(_addr.getHostString(), _server.getListener("rizzly").getPort());
- return _addr;
- }
-
- @Override
- public void start() {
- if (_serverShared) return;
-
- try {
- _server.start();
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void setExecutor(Executor executor) {
- if (executor == null)
- throw new IllegalArgumentException("missing required 'executor' argument");
- }
-
- @Override
- public Executor getExecutor() {
- ThreadPool threadPool = _server.getThreadPool();
- if (threadPool instanceof DelegatingThreadPool)
- return ((DelegatingThreadPool) _server.getThreadPool()).getExecutor();
- return threadPool;
- }
-
- @Override
- public void stop(int delay) {
- cleanUpContexts();
- cleanUpConnectors();
-
- if (_serverShared) return;
-
- _server.shutdown();
- }
-
- private void cleanUpContexts() {
- for (Map.Entry stringJettyHttpContextEntry : _contexts.entrySet()) {
- JettyHttpContext context = stringJettyHttpContextEntry.getValue();
- _server.removeBean(context.getJettyContextHandler());
- }
- _contexts.clear();
- }
-
- private void cleanUpConnectors() {
- for (var stringConnectorEntry : _connectors.entrySet()) {
- var connector = stringConnectorEntry.getValue();
- try {
- connector.shutdownNow();
- } catch (Exception ex) {
- LOG.log(Level.WARNING, "Unable to stop connector {}", connector, ex);
- }
- _server.removeListener(stringConnectorEntry.getKey());
- }
- _connectors.clear();
- }
-
- @Override
- public HttpContext createContext(String path, HttpHandler httpHandler) {
- checkIfContextIsFree(path);
-
- JettyHttpContext context = new JettyHttpContext(this, path, httpHandler);
- HttpSpiContextHandler jettyContextHandler = context.getJettyContextHandler();
-
- ContextHandlerCollection contexts = _server.getDescendant(ContextHandlerCollection.class);
-
- if (contexts == null)
- throw new RuntimeException("could not find ContextHandlerCollection, you must configure one");
-
- contexts.addHandler(jettyContextHandler);
- if (contexts.isStarted()) {
- try {
- jettyContextHandler.start();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- _contexts.put(path, context);
- return context;
- }
-
- @Override
- public HttpContext createContext(String path) {
- return createContext(path, null);
- }
-
- private void checkIfContextIsFree(String path) {
- Handler serverHandler = _server.getHandler();
- if (serverHandler instanceof ContextHandler) {
- ContextHandler ctx = (ContextHandler) serverHandler;
- if (ctx.getContextPath().equals(path))
- throw new RuntimeException("another context already bound to path " + path);
- }
-
- List handlers = _server.getHandlers();
- for (Handler handler : handlers) {
- if (handler instanceof ContextHandler) {
- ContextHandler ctx = (ContextHandler) handler;
- if (ctx.getContextPath().equals(path))
- throw new RuntimeException("another context already bound to path " + path);
- }
- }
- }
-
- @Override
- public void removeContext(String path) throws IllegalArgumentException {
- JettyHttpContext context = _contexts.remove(path);
- if (context == null) return;
- HttpSpiContextHandler handler = context.getJettyContextHandler();
-
- ContextHandlerCollection chc = _server.getDescendant(ContextHandlerCollection.class);
- try {
- handler.stop();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- chc.removeHandler(handler);
- }
-
- @Override
- public void removeContext(HttpContext context) {
- removeContext(context.getPath());
- }
-}
diff --git a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java b/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java
deleted file mode 100644
index 121c33d7..00000000
--- a/avaje-jex-helidon-spi/src/main/java/io/avaje/helidon/http/spi/VirtualThreadsExecutorService.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
- * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
- */
-package io.avaje.helidon.http.spi;
-
-import java.util.List;
-import java.util.concurrent.AbstractExecutorService;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author Ondro Mihalyi
- */
-public class VirtualThreadsExecutorService extends AbstractExecutorService {
-
- VirtualThreadFactory threadFactory = new VirtualThreadFactory();
- ExecutorService pool = Executors.newThreadPerTaskExecutor(threadFactory);
-
- @Override
- public void shutdown() {
- pool.shutdown();
- }
-
- @Override
- public List shutdownNow() {
- return pool.shutdownNow();
- }
-
- @Override
- public boolean isShutdown() {
- return pool.isShutdown();
- }
-
- @Override
- public boolean isTerminated() {
- return pool.isTerminated();
- }
-
- @Override
- public void execute(Runnable r) {
- pool.execute(r);
- }
-
- @Override
- public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
- return pool.awaitTermination(timeout, unit);
- }
-
-
- private static class VirtualThreadFactory implements ThreadFactory {
-
- int threadIndex = 0;
-
- String name = "virtual-thread";
-
- @Override
- public Thread newThread(Runnable r) {
- return Thread.ofVirtual().name(name + "(" + threadIndex++ + ")").unstarted(r);
- }
- }
-
-}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 7028ff5c..6942c814 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,12 +39,12 @@
avaje-jex
- avaje-jex-test
avaje-jex-freemarker
- avaje-jex-mustache
+ avaje-jex-grizzly-spi
avaje-jex-htmx
+ avaje-jex-mustache
avaje-jex-static-content
- avaje-jex-helidon-spi
+ avaje-jex-test
From 5eb8957c5287ad5b4c2b2a7a49b647264c152878 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Fri, 21 Mar 2025 19:30:54 -0400
Subject: [PATCH 4/5] working
---
avaje-jex-grizzly-spi/pom.xml | 20 ++-
.../helidon/http/spi/GrizzlyHandler.java | 87 -------------
.../helidon/http/spi/HttpServerBuilder.java | 80 ------------
.../grizzly/spi/GrizzlyExchange.java} | 4 +-
.../avaje/jex/grizzly/spi/GrizzlyHandler.java | 48 +++++++
.../grizzly/spi/GrizzlyHttpContext.java} | 7 +-
.../grizzly/spi/GrizzlyHttpExchange.java} | 6 +-
.../spi/GrizzlyHttpExchangeDelegate.java | 8 +-
.../grizzly/spi/GrizzlyHttpServer.java} | 42 +++----
.../spi/GrizzlyHttpServerProvider.java | 4 +-
.../grizzly/spi/GrizzlyHttpsExchange.java} | 59 ++++-----
.../src/main/java/module-info.java | 15 +++
.../io/avaje/jex/grizzly/spi/FilterTest.java | 118 ++++++++++++++++++
13 files changed, 248 insertions(+), 250 deletions(-)
delete mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java
delete mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java
rename avaje-jex-grizzly-spi/src/main/java/io/avaje/{helidon/http/spi/JettyExchange.java => jex/grizzly/spi/GrizzlyExchange.java} (52%)
create mode 100644 avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHandler.java
rename avaje-jex-grizzly-spi/src/main/java/io/avaje/{helidon/http/spi/JettyHttpContext.java => jex/grizzly/spi/GrizzlyHttpContext.java} (84%)
rename avaje-jex-grizzly-spi/src/main/java/io/avaje/{helidon/http/spi/JettyHttpExchange.java => jex/grizzly/spi/GrizzlyHttpExchange.java} (92%)
rename avaje-jex-grizzly-spi/src/main/java/io/avaje/{helidon/http => jex/grizzly}/spi/GrizzlyHttpExchangeDelegate.java (96%)
rename avaje-jex-grizzly-spi/src/main/java/io/avaje/{helidon/http/spi/JettyHttpServer.java => jex/grizzly/spi/GrizzlyHttpServer.java} (75%)
rename avaje-jex-grizzly-spi/src/main/java/io/avaje/{helidon/http => jex/grizzly}/spi/GrizzlyHttpServerProvider.java (91%)
rename avaje-jex-grizzly-spi/src/main/java/io/avaje/{helidon/http/spi/JettyHttpsExchange.java => jex/grizzly/spi/GrizzlyHttpsExchange.java} (57%)
create mode 100644 avaje-jex-grizzly-spi/src/main/java/module-info.java
create mode 100644 avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java
diff --git a/avaje-jex-grizzly-spi/pom.xml b/avaje-jex-grizzly-spi/pom.xml
index 56054d21..d9bfb46a 100644
--- a/avaje-jex-grizzly-spi/pom.xml
+++ b/avaje-jex-grizzly-spi/pom.xml
@@ -7,6 +7,7 @@
avaje-jex-parent
3.0-RC23
+ 0.1
avaje-jex-grizzly-spi
@@ -15,11 +16,18 @@
grizzly-http-server
4.1.0-M1
-
- io.avaje
- avaje-spi-service
- provided
- true
-
+
+ io.avaje
+ avaje-spi-service
+ provided
+ true
+
+
+
+ io.avaje
+ avaje-jex-test
+ test
+
+
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java
deleted file mode 100644
index 38bb61fe..00000000
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHandler.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package io.avaje.helidon.http.spi;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.List;
-import java.util.Map;
-
-import org.glassfish.grizzly.http.server.Request;
-import org.glassfish.grizzly.http.server.Response;
-
-import com.sun.net.httpserver.Authenticator;
-import com.sun.net.httpserver.Authenticator.Result;
-import com.sun.net.httpserver.Filter.Chain;
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpPrincipal;
-
-class GrizzlyHandler extends org.glassfish.grizzly.http.server.HttpHandler {
-
- private final HttpContext httpContext;
-
- private HttpHandler handler;
-
- GrizzlyHandler(HttpContext httpContext, HttpHandler httpHandler) {
- super(httpContext.getPath());
- this.httpContext = httpContext;
- this.handler = httpHandler;
- }
-
- @Override
- public void service(Request request, Response response) {
-
- try (HttpExchange exchange =
- request.isSecure()
- ? new JettyHttpsExchange(httpContext, request, response)
- : new JettyHttpExchange(httpContext, request, response)) {
- Authenticator auth = httpContext.getAuthenticator();
-
- if (auth != null && handleAuthentication(request, response, exchange, auth)) {
- return;
- }
- new Chain(httpContext.getFilters(), handler).doFilter(exchange);
-
- } catch (IOException ex) {
- throw new UncheckedIOException(null);
- }
- }
-
- public HttpHandler getHttpHandler() {
- return handler;
- }
-
- public void setHttpHandler(HttpHandler handler) {
- this.handler = handler;
- }
-
- private boolean handleAuthentication(
- Request request, Response response, HttpExchange httpExchange, Authenticator auth)
- throws IOException {
- Result result = auth.authenticate(httpExchange);
- if (result instanceof Authenticator.Failure fail) {
- response.sendError(fail.getResponseCode(), "");
- for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet()) {
- for (String value : header.getValue()) response.addHeader(header.getKey(), value);
- }
- return true;
- }
-
- if (result instanceof Authenticator.Retry ry) {
- for (Map.Entry> header : httpExchange.getResponseHeaders().entrySet()) {
- for (String value : header.getValue()) {
- response.addHeader(header.getKey(), value);
- }
- }
- response.sendError(ry.getResponseCode(), "Failed to Authenticate");
- return true;
- }
-
- if (result instanceof Authenticator.Success s) {
- HttpPrincipal principal = s.getPrincipal();
- ((JettyExchange) httpExchange).setPrincipal(principal);
- return false;
- }
- return true;
- }
-}
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java
deleted file mode 100644
index 6989a0a8..00000000
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/HttpServerBuilder.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package io.avaje.helidon.http.spi;
-
-import org.glassfish.grizzly.http.server.HttpHandler;
-import org.glassfish.grizzly.http.server.HttpHandlerRegistration;
-import org.glassfish.grizzly.http.server.HttpServer;
-import org.glassfish.grizzly.http.server.NetworkListener;
-import org.glassfish.grizzly.http.server.ServerConfiguration;
-import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
-import org.glassfish.grizzly.utils.Charsets;
-
-public class HttpServerBuilder {
-
- private int port = -1;
- private String host = "0.0.0.0";
- private boolean secure;
- private SSLEngineConfigurator sslEngineConfigurator;
-
- private final HttpServer server = new HttpServer();
-
- public HttpServerBuilder setPort(int port) {
- this.port = port;
- return this;
- }
-
- public HttpServerBuilder host(String host) {
- this.host = host;
- return this;
- }
-
- public HttpServerBuilder sslEngineConfigurator(SSLEngineConfigurator sslEngineConfigurator) {
- this.sslEngineConfigurator = sslEngineConfigurator;
- return this;
- }
-
- public HttpServerBuilder secure(boolean secure) {
- this.secure = secure;
- return this;
- }
-
- /**
- * Add a handler with the given context.
- */
- public HttpServerBuilder handler(HttpHandler handler, String context) {
- handler(handler, HttpHandlerRegistration.fromString("/" + context + "/*"));
- return this;
- }
-
- /**
- * Add a handler given the paths.
- */
- public HttpServerBuilder handler(HttpHandler handler, HttpHandlerRegistration... paths) {
- server.getServerConfiguration().addHttpHandler(handler, paths);
- return this;
- }
-
- /**
- * Build and return the grizzly http server.
- */
- public HttpServer build() {
-
- int serverPort = serverPort();
- NetworkListener listener = new NetworkListener("grizzly", host, serverPort);
-
- // TODO: Configure to use loom thread factory
- // listener.getTransport().getWorkerThreadPoolConfig().setThreadFactory()
- listener.setSecure(secure);
- if (sslEngineConfigurator != null) {
- listener.setSSLEngineConfig(sslEngineConfigurator);
- }
- server.addListener(listener);
- ServerConfiguration config = server.getServerConfiguration();
- config.setPassTraceRequest(true);
- config.setDefaultQueryEncoding(Charsets.UTF8_CHARSET);
- return server;
- }
-
- protected int serverPort() {
- return port != -1 ? port : secure ? 8443 : 7001;
- }
-}
\ No newline at end of file
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyExchange.java
similarity index 52%
rename from avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyExchange.java
index ac1e82d9..10c7fb4f 100644
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyExchange.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyExchange.java
@@ -1,8 +1,8 @@
-package io.avaje.helidon.http.spi;
+package io.avaje.jex.grizzly.spi;
import com.sun.net.httpserver.HttpPrincipal;
-public interface JettyExchange {
+sealed interface GrizzlyExchange permits GrizzlyHttpExchange, GrizzlyHttpsExchange {
HttpPrincipal getPrincipal();
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHandler.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHandler.java
new file mode 100644
index 00000000..4ea79c85
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHandler.java
@@ -0,0 +1,48 @@
+package io.avaje.jex.grizzly.spi;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+
+import com.sun.net.httpserver.Filter.Chain;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+final class GrizzlyHandler extends org.glassfish.grizzly.http.server.HttpHandler {
+
+ private final HttpContext httpContext;
+
+ private HttpHandler handler;
+
+ GrizzlyHandler(HttpContext httpContext, HttpHandler httpHandler) {
+ super(httpContext.getPath());
+ this.httpContext = httpContext;
+ this.handler = httpHandler;
+ }
+
+ @Override
+ public void service(Request request, Response response) {
+
+ try (HttpExchange exchange =
+ request.isSecure()
+ ? new GrizzlyHttpsExchange(httpContext, request, response)
+ : new GrizzlyHttpExchange(httpContext, request, response)) {
+
+ new Chain(httpContext.getFilters(), handler).doFilter(exchange);
+
+ } catch (IOException ex) {
+ throw new UncheckedIOException(null);
+ }
+ }
+
+ public HttpHandler getHttpHandler() {
+ return handler;
+ }
+
+ public void setHttpHandler(HttpHandler handler) {
+ this.handler = handler;
+ }
+}
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpContext.java
similarity index 84%
rename from avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpContext.java
index 2e69770c..5ede6a56 100644
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpContext.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpContext.java
@@ -1,4 +1,4 @@
-package io.avaje.helidon.http.spi;
+package io.avaje.jex.grizzly.spi;
import java.util.ArrayList;
import java.util.HashMap;
@@ -10,8 +10,7 @@
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
-/** Jetty implementation of {@link com.sun.net.httpserver.HttpContext} */
-public class JettyHttpContext extends com.sun.net.httpserver.HttpContext {
+final class GrizzlyHttpContext extends com.sun.net.httpserver.HttpContext {
private final GrizzlyHandler grizzlyHandler;
private final HttpServer server;
@@ -24,7 +23,7 @@ public class JettyHttpContext extends com.sun.net.httpserver.HttpContext {
private String contextPath;
- protected JettyHttpContext(HttpServer server, String contextPath, HttpHandler handler) {
+ protected GrizzlyHttpContext(HttpServer server, String contextPath, HttpHandler handler) {
this.server = server;
this.grizzlyHandler = new GrizzlyHandler(this, handler);
this.contextPath = contextPath;
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpExchange.java
similarity index 92%
rename from avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpExchange.java
index 04ce4a0b..38fc59c8 100644
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpExchange.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpExchange.java
@@ -1,4 +1,4 @@
-package io.avaje.helidon.http.spi;
+package io.avaje.jex.grizzly.spi;
import java.io.IOException;
import java.io.InputStream;
@@ -14,10 +14,10 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
-public class JettyHttpExchange extends HttpExchange implements JettyExchange {
+final class GrizzlyHttpExchange extends HttpExchange implements GrizzlyExchange {
private final GrizzlyHttpExchangeDelegate delegate;
- public JettyHttpExchange(HttpContext context, Request req, Response resp) {
+ public GrizzlyHttpExchange(HttpContext context, Request req, Response resp) {
delegate = new GrizzlyHttpExchangeDelegate(context, req, resp);
}
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpExchangeDelegate.java
similarity index 96%
rename from avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpExchangeDelegate.java
index d307c9b5..a6b6982c 100644
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpExchangeDelegate.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpExchangeDelegate.java
@@ -1,4 +1,4 @@
-package io.avaje.helidon.http.spi;
+package io.avaje.jex.grizzly.spi;
import java.io.IOException;
import java.io.InputStream;
@@ -18,8 +18,8 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
-/** Jetty implementation of {@link com.sun.net.httpserver.HttpExchange} */
-public class GrizzlyHttpExchangeDelegate extends HttpExchange {
+final class GrizzlyHttpExchangeDelegate extends HttpExchange {
+
/** Set of headers that RFC9110 says will not have a value list */
private static final Set SINGLE_VALUE_HEADERS =
Set.of(
@@ -141,7 +141,7 @@ public void sendResponseHeaders(int rCode, long responseLength) throws IOExcepti
} else {
response.setContentLengthLong(responseLength);
}
-
+
response.setStatus(rCode);
}
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpServer.java
similarity index 75%
rename from avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpServer.java
index fa6c2015..cdd9f79c 100644
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpServer.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpServer.java
@@ -1,11 +1,9 @@
-package io.avaje.helidon.http.spi;
+package io.avaje.jex.grizzly.spi;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.System.Logger.Level;
import java.net.InetSocketAddress;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -19,23 +17,21 @@
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpsConfigurator;
-/** Jetty implementation of {@link com.sun.net.httpserver.HttpServer}. */
-public class JettyHttpServer extends com.sun.net.httpserver.HttpsServer {
+final class GrizzlyHttpServer extends com.sun.net.httpserver.HttpsServer {
private static final System.Logger LOG =
- System.getLogger(JettyHttpServer.class.getCanonicalName());
+ System.getLogger(GrizzlyHttpServer.class.getCanonicalName());
private final HttpServer server;
- private final Map contexts = new HashMap<>();
private InetSocketAddress addr;
private ServerConfiguration httpConfiguration;
private ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
private HttpsConfigurator httpsConfig;
- public JettyHttpServer(HttpServer server) {
+ public GrizzlyHttpServer(HttpServer server) {
this(server, server.getServerConfiguration());
}
- public JettyHttpServer(HttpServer server, ServerConfiguration configuration) {
+ public GrizzlyHttpServer(HttpServer server, ServerConfiguration configuration) {
this.server = server;
this.httpConfiguration = configuration;
}
@@ -81,8 +77,9 @@ protected HttpServer getServer() {
@Override
public InetSocketAddress getAddress() {
- if (addr.getPort() == 0 && server.isStarted())
+ if (addr.getPort() == 0 && server.isStarted()) {
return new InetSocketAddress(addr.getHostString(), server.getListener("rizzly").getPort());
+ }
return addr;
}
@@ -92,7 +89,6 @@ public void start() {
try {
server.start();
} catch (IOException e) {
-
throw new UncheckedIOException(e);
}
}
@@ -102,7 +98,7 @@ public void setExecutor(Executor executor) {
if (executor instanceof ExecutorService service) {
this.executor = service;
} else {
- throw new IllegalArgumentException("Grizzly only accepts ExecutorService");
+ throw new IllegalArgumentException("Grizzly only accepts an instance of ExecutorService");
}
}
@@ -113,25 +109,18 @@ public Executor getExecutor() {
@Override
public void stop(int delay) {
-
- for (var context : contexts.values()) {
- httpConfiguration.removeHttpHandler(context.getGrizzlyHandler());
- }
- contexts.clear();
-
- server.shutdown();
+ server.shutdownNow();
}
@Override
public HttpContext createContext(String path, HttpHandler httpHandler) {
- JettyHttpContext context = new JettyHttpContext(this, path, httpHandler);
+ GrizzlyHttpContext context = new GrizzlyHttpContext(this, path, httpHandler);
GrizzlyHandler jettyContextHandler = context.getGrizzlyHandler();
httpConfiguration.addHttpHandler(
jettyContextHandler, path.transform(this::prependSlash).transform(this::appendSlash));
- contexts.put(path, context);
return context;
}
@@ -149,16 +138,15 @@ public HttpContext createContext(String path) {
}
@Override
- public void removeContext(String path) throws IllegalArgumentException {
- JettyHttpContext context = contexts.remove(path);
- if (context == null) return;
- GrizzlyHandler handler = context.getGrizzlyHandler();
- httpConfiguration.removeHttpHandler(handler);
+ public void removeContext(String path) {
+
+ throw new UnsupportedOperationException("notImplemented");
}
@Override
public void removeContext(HttpContext context) {
- removeContext(context.getPath());
+
+ throw new UnsupportedOperationException("notImplemented");
}
@Override
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpServerProvider.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpServerProvider.java
similarity index 91%
rename from avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpServerProvider.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpServerProvider.java
index 997625f7..bd21367d 100644
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/GrizzlyHttpServerProvider.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpServerProvider.java
@@ -1,4 +1,4 @@
-package io.avaje.helidon.http.spi;
+package io.avaje.jex.grizzly.spi;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -43,7 +43,7 @@ private com.sun.net.httpserver.HttpsServer createServer(InetSocketAddress addr,
server = new HttpServer();
}
- JettyHttpServer jettyHttpServer = new JettyHttpServer(server);
+ GrizzlyHttpServer jettyHttpServer = new GrizzlyHttpServer(server);
if (addr != null) jettyHttpServer.bind(addr, backlog);
return jettyHttpServer;
}
diff --git a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpsExchange.java
similarity index 57%
rename from avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
rename to avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpsExchange.java
index 16b261cd..d1e686d6 100644
--- a/avaje-jex-grizzly-spi/src/main/java/io/avaje/helidon/http/spi/JettyHttpsExchange.java
+++ b/avaje-jex-grizzly-spi/src/main/java/io/avaje/jex/grizzly/spi/GrizzlyHttpsExchange.java
@@ -1,4 +1,4 @@
-package io.avaje.helidon.http.spi;
+package io.avaje.jex.grizzly.spi;
import java.io.IOException;
import java.io.InputStream;
@@ -16,117 +16,106 @@
import com.sun.net.httpserver.HttpPrincipal;
import com.sun.net.httpserver.HttpsExchange;
+final class GrizzlyHttpsExchange extends HttpsExchange implements GrizzlyExchange {
+ private final GrizzlyHttpExchangeDelegate delegate;
-public class JettyHttpsExchange extends HttpsExchange implements JettyExchange {
- private final GrizzlyHttpExchangeDelegate _delegate;
-
- public JettyHttpsExchange(HttpContext jaxWsContext, Request req, Response resp) {
- _delegate = new GrizzlyHttpExchangeDelegate(jaxWsContext, req, resp);
- }
-
- @Override
- public int hashCode() {
- return _delegate.hashCode();
+ public GrizzlyHttpsExchange(HttpContext jaxWsContext, Request req, Response resp) {
+ delegate = new GrizzlyHttpExchangeDelegate(jaxWsContext, req, resp);
}
@Override
public Headers getRequestHeaders() {
- return _delegate.getRequestHeaders();
+ return delegate.getRequestHeaders();
}
@Override
public Headers getResponseHeaders() {
- return _delegate.getResponseHeaders();
+ return delegate.getResponseHeaders();
}
@Override
public URI getRequestURI() {
- return _delegate.getRequestURI();
+ return delegate.getRequestURI();
}
@Override
public String getRequestMethod() {
- return _delegate.getRequestMethod();
+ return delegate.getRequestMethod();
}
@Override
public HttpContext getHttpContext() {
- return _delegate.getHttpContext();
+ return delegate.getHttpContext();
}
@Override
public void close() {
- _delegate.close();
- }
-
- @Override
- public boolean equals(Object obj) {
- return _delegate.equals(obj);
+ delegate.close();
}
@Override
public InputStream getRequestBody() {
- return _delegate.getRequestBody();
+ return delegate.getRequestBody();
}
@Override
public OutputStream getResponseBody() {
- return _delegate.getResponseBody();
+ return delegate.getResponseBody();
}
@Override
public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
- _delegate.sendResponseHeaders(rCode, responseLength);
+ delegate.sendResponseHeaders(rCode, responseLength);
}
@Override
public InetSocketAddress getRemoteAddress() {
- return _delegate.getRemoteAddress();
+ return delegate.getRemoteAddress();
}
@Override
public int getResponseCode() {
- return _delegate.getResponseCode();
+ return delegate.getResponseCode();
}
@Override
public InetSocketAddress getLocalAddress() {
- return _delegate.getLocalAddress();
+ return delegate.getLocalAddress();
}
@Override
public String getProtocol() {
- return _delegate.getProtocol();
+ return delegate.getProtocol();
}
@Override
public Object getAttribute(String name) {
- return _delegate.getAttribute(name);
+ return delegate.getAttribute(name);
}
@Override
public void setAttribute(String name, Object value) {
- _delegate.setAttribute(name, value);
+ delegate.setAttribute(name, value);
}
@Override
public void setStreams(InputStream i, OutputStream o) {
- _delegate.setStreams(i, o);
+ delegate.setStreams(i, o);
}
@Override
public HttpPrincipal getPrincipal() {
- return _delegate.getPrincipal();
+ return delegate.getPrincipal();
}
@Override
public void setPrincipal(HttpPrincipal principal) {
- _delegate.setPrincipal(principal);
+ delegate.setPrincipal(principal);
}
@Override
public String toString() {
- return _delegate.toString();
+ return delegate.toString();
}
@Override
diff --git a/avaje-jex-grizzly-spi/src/main/java/module-info.java b/avaje-jex-grizzly-spi/src/main/java/module-info.java
new file mode 100644
index 00000000..a848f5f2
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/main/java/module-info.java
@@ -0,0 +1,15 @@
+import com.sun.net.httpserver.spi.HttpServerProvider;
+
+module io.avaje.jex.grizzly {
+
+ exports io.avaje.jex.grizzly.spi;
+
+ requires transitive jdk.httpserver;
+ requires transitive org.glassfish.grizzly.http.server;
+ requires transitive org.glassfish.grizzly.http;
+ requires transitive org.glassfish.grizzly;
+
+ requires static io.avaje.spi;
+
+ provides HttpServerProvider with io.avaje.jex.grizzly.spi.GrizzlyHttpServerProvider;
+}
diff --git a/avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java b/avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java
new file mode 100644
index 00000000..b4e14ea6
--- /dev/null
+++ b/avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java
@@ -0,0 +1,118 @@
+package io.avaje.jex.grizzly.spi;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.net.http.HttpHeaders;
+import java.net.http.HttpResponse;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.LockSupport;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+
+import io.avaje.jex.Jex;
+
+class FilterTest {
+
+ static final TestPair pair = init();
+ static final AtomicReference afterAll = new AtomicReference<>();
+ static final AtomicReference afterTwo = new AtomicReference<>();
+
+ static TestPair init() {
+ final Jex app =
+ Jex.create()
+ .routing(
+ routing ->
+ routing
+ .get("/", ctx -> ctx.text("roo"))
+ .get(
+ "/noResponse",
+ ctx -> {
+ ctx.header("Content-Type", "");
+ })
+ .get("/one", ctx -> ctx.text("one"))
+ .get("/two", ctx -> ctx.text("two"))
+ .get("/two/{id}", ctx -> ctx.text("two-id"))
+ .before(ctx -> ctx.header("before-all", "set"))
+ .filter(
+ (ctx, chain) -> {
+ if (ctx.path().contains("/two/")) {
+ ctx.header("before-two", "set");
+ }
+ chain.proceed();
+ })
+ .after(ctx -> afterAll.set("set"))
+ .filter(
+ (ctx, chain) -> {
+ chain.proceed();
+ if (ctx.path().contains("/two/")) {
+ afterTwo.set("set");
+ }
+ })
+ .get("/dummy", ctx -> ctx.text("dummy")));
+
+ return TestPair.create(app);
+ }
+
+ @AfterAll
+ static void end() {
+ pair.shutdown();
+ }
+
+ void clearAfter() {
+ afterAll.set(null);
+ afterTwo.set(null);
+ }
+
+ @Test
+ void get() {
+ clearAfter();
+ HttpResponse res = pair.request().GET().asString();
+ assertHasBeforeAfterAll(res);
+ assertNoBeforeAfterTwo(res);
+
+ clearAfter();
+ res = pair.request().path("one").GET().asString();
+ assertHasBeforeAfterAll(res);
+ assertNoBeforeAfterTwo(res);
+
+ clearAfter();
+ res = pair.request().path("two").GET().asString();
+ assertHasBeforeAfterAll(res);
+ assertNoBeforeAfterTwo(res);
+ }
+
+ @Test
+ void getNoResponse() {
+ clearAfter();
+ HttpResponse res = pair.request().path("noResponse").GET().asString();
+ assertThat(res.statusCode()).isEqualTo(204);
+ assertHasBeforeAfterAll(res);
+ assertNoBeforeAfterTwo(res);
+ }
+
+ @Test
+ void get_two_expect_extraFilters() {
+ clearAfter();
+ HttpResponse res = pair.request().path("two/42").GET().asString();
+
+ final HttpHeaders headers = res.headers();
+ assertHasBeforeAfterAll(res);
+ assertThat(headers.firstValue("before-two")).get().isEqualTo("set");
+ assertThat(afterTwo.get()).isEqualTo("set");
+ }
+
+ private void assertNoBeforeAfterTwo(HttpResponse res) {
+ assertThat(res.statusCode()).isLessThan(300);
+ assertThat(res.headers().firstValue("before-two")).isEmpty();
+ assertThat(afterTwo.get()).isNull();
+ }
+
+ private void assertHasBeforeAfterAll(HttpResponse res) {
+ assertThat(res.statusCode()).isLessThan(300);
+ assertThat(res.headers().firstValue("before-all")).get().isEqualTo("set");
+ LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(2));
+ assertThat(afterAll.get()).isEqualTo("set");
+ }
+}
From 248a812848cb855c1f7c429e725d64b72cd03b03 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Fri, 21 Mar 2025 19:44:40 -0400
Subject: [PATCH 5/5] basic test
---
avaje-jex-grizzly-spi/pom.xml | 6 ++++++
avaje-jex-grizzly-spi/src/main/java/module-info.java | 2 ++
.../src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java | 1 +
.../src/main/java/io/avaje/jex/core/RoutingHandler.java | 2 +-
4 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/avaje-jex-grizzly-spi/pom.xml b/avaje-jex-grizzly-spi/pom.xml
index d9bfb46a..e7c61d20 100644
--- a/avaje-jex-grizzly-spi/pom.xml
+++ b/avaje-jex-grizzly-spi/pom.xml
@@ -11,6 +11,12 @@
avaje-jex-grizzly-spi
+
+
+ io.avaje
+ avaje-jex
+
+
org.glassfish.grizzly
grizzly-http-server
diff --git a/avaje-jex-grizzly-spi/src/main/java/module-info.java b/avaje-jex-grizzly-spi/src/main/java/module-info.java
index a848f5f2..6378b580 100644
--- a/avaje-jex-grizzly-spi/src/main/java/module-info.java
+++ b/avaje-jex-grizzly-spi/src/main/java/module-info.java
@@ -4,12 +4,14 @@
exports io.avaje.jex.grizzly.spi;
+ requires transitive io.avaje.jex;
requires transitive jdk.httpserver;
requires transitive org.glassfish.grizzly.http.server;
requires transitive org.glassfish.grizzly.http;
requires transitive org.glassfish.grizzly;
requires static io.avaje.spi;
+ requires static java.net.http;
provides HttpServerProvider with io.avaje.jex.grizzly.spi.GrizzlyHttpServerProvider;
}
diff --git a/avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java b/avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java
index b4e14ea6..fc6cc485 100644
--- a/avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java
+++ b/avaje-jex-grizzly-spi/src/test/java/io/avaje/jex/grizzly/spi/FilterTest.java
@@ -12,6 +12,7 @@
import org.junit.jupiter.api.Test;
import io.avaje.jex.Jex;
+import io.avaje.jex.test.TestPair;
class FilterTest {
diff --git a/avaje-jex/src/main/java/io/avaje/jex/core/RoutingHandler.java b/avaje-jex/src/main/java/io/avaje/jex/core/RoutingHandler.java
index cd91a6ef..f224049d 100644
--- a/avaje-jex/src/main/java/io/avaje/jex/core/RoutingHandler.java
+++ b/avaje-jex/src/main/java/io/avaje/jex/core/RoutingHandler.java
@@ -60,7 +60,7 @@ public void handle(HttpExchange exchange) {
}
private void handleNoResponse(HttpExchange exchange) throws IOException {
- if (exchange.getResponseCode() == -1) {
+ if (exchange.getResponseCode() < 1) {
exchange.sendResponseHeaders(204, -1);
}
}