This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit dc32e659da395065488fe6c948824fd2706c2ce5
Author: TungTV <vtt...@linagora.com>
AuthorDate: Tue Feb 11 10:48:58 2025 +0700

    JAMES-4104 [webadmin] Clone some Jetty embedded class from Spark
---
 .../webadmin/jettyserver/EmbeddedJettyFactory.java |  71 +++++++
 .../webadmin/jettyserver/EmbeddedJettyServer.java  | 204 +++++++++++++++++++++
 .../james/webadmin/jettyserver/JettyHandler.java   |  62 +++++++
 .../james/webadmin/jettyserver/JettyServer.java    |  73 ++++++++
 .../jettyserver/SocketConnectorFactory.java        | 171 +++++++++++++++++
 5 files changed, 581 insertions(+)

diff --git 
a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/EmbeddedJettyFactory.java
 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/EmbeddedJettyFactory.java
new file mode 100644
index 0000000000..c49acf2e0e
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/EmbeddedJettyFactory.java
@@ -0,0 +1,71 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.webadmin.jettyserver;
+
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+import spark.ExceptionMapper;
+import spark.embeddedserver.EmbeddedServer;
+import spark.embeddedserver.EmbeddedServerFactory;
+import spark.embeddedserver.jetty.JettyServerFactory;
+import spark.http.matching.MatcherFilter;
+import spark.route.Routes;
+import spark.staticfiles.StaticFilesConfiguration;
+
+public class EmbeddedJettyFactory implements EmbeddedServerFactory {
+    private final JettyServerFactory serverFactory;
+    private ThreadPool threadPool;
+    private boolean httpOnly = true;
+
+    public EmbeddedJettyFactory() {
+        this(new JettyServer());
+    }
+
+    public EmbeddedJettyFactory(JettyServerFactory serverFactory) {
+        this.serverFactory = serverFactory;
+    }
+
+    public EmbeddedServer create(Routes routeMatcher,
+                                 StaticFilesConfiguration 
staticFilesConfiguration,
+                                 ExceptionMapper exceptionMapper,
+                                 boolean hasMultipleHandler) {
+        MatcherFilter matcherFilter = new MatcherFilter(routeMatcher, 
staticFilesConfiguration, exceptionMapper, false, hasMultipleHandler);
+        matcherFilter.init(null);
+
+        return new EmbeddedJettyServer(serverFactory, httpOnly, 
matcherFilter).withThreadPool(threadPool);
+    }
+
+    /**
+     * Sets optional thread pool for jetty server.  This is useful for 
overriding the default thread pool
+     * behaviour for example 
io.dropwizard.metrics.jetty9.InstrumentedQueuedThreadPool.
+     *
+     * @param threadPool thread pool
+     * @return Builder pattern - returns this instance
+     */
+    public EmbeddedJettyFactory withThreadPool(ThreadPool threadPool) {
+        this.threadPool = threadPool;
+        return this;
+    }
+
+    public EmbeddedJettyFactory withHttpOnly(boolean httpOnly) {
+        this.httpOnly = httpOnly;
+        return this;
+    }
+}
diff --git 
a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/EmbeddedJettyServer.java
 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/EmbeddedJettyServer.java
new file mode 100644
index 0000000000..3a4b6e9c64
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/EmbeddedJettyServer.java
@@ -0,0 +1,204 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.webadmin.jettyserver;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Optional;
+
+import jakarta.servlet.DispatcherType;
+
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.SessionHandler;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.thread.ThreadPool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import spark.embeddedserver.EmbeddedServer;
+import spark.embeddedserver.VirtualThreadAware;
+import spark.embeddedserver.jetty.JettyServerFactory;
+import spark.embeddedserver.jetty.SocketConnectorFactory;
+import spark.embeddedserver.jetty.websocket.WebSocketHandlerWrapper;
+import 
spark.embeddedserver.jetty.websocket.WebSocketServletContextHandlerFactory;
+import spark.http.matching.MatcherFilter;
+import spark.ssl.SslStores;
+
+/**
+ * Fork from spark.embeddedserver.jetty.EmbeddedJettyServer
+ */
+public class EmbeddedJettyServer extends VirtualThreadAware.Proxy implements 
EmbeddedServer {
+
+    private static final int SPARK_DEFAULT_PORT = 4567;
+    private static final String NAME = "Spark";
+
+    private final JettyServerFactory serverFactory;
+    private final boolean httpOnly;
+    private final MatcherFilter matcherFilter;
+    private Server server;
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private Map<String, WebSocketHandlerWrapper> webSocketHandlers;
+    private Optional<Long> webSocketIdleTimeoutMillis;
+
+    private ThreadPool threadPool = null;
+    private boolean trustForwardHeaders = true; // true by default
+
+    public EmbeddedJettyServer(JettyServerFactory serverFactory, boolean 
httpOnly, MatcherFilter matcherFilter) {
+        super(serverFactory);
+        this.serverFactory = serverFactory;
+        this.httpOnly = httpOnly;
+        this.matcherFilter = matcherFilter;
+    }
+
+    @Override
+    public void configureWebSockets(Map<String, WebSocketHandlerWrapper> 
webSocketHandlers,
+                                    Optional<Long> webSocketIdleTimeoutMillis) 
{
+
+        this.webSocketHandlers = webSocketHandlers;
+        this.webSocketIdleTimeoutMillis = webSocketIdleTimeoutMillis;
+    }
+
+    @Override
+    public void trustForwardHeaders(boolean trust) {
+        this.trustForwardHeaders = trust;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int ignite(String host,
+                      int port,
+                      boolean useHTTP2,
+                      SslStores sslStores,
+                      int maxThreads,
+                      int minThreads,
+                      int threadIdleTimeoutMillis) throws Exception {
+
+        boolean hasCustomizedConnectors = false;
+
+        if (port == 0) {
+            try (ServerSocket s = new ServerSocket(0)) {
+                port = s.getLocalPort();
+            } catch (IOException e) {
+                logger.error("Could not get first available port (port set to 
0), using default: {}", SPARK_DEFAULT_PORT);
+                port = SPARK_DEFAULT_PORT;
+            }
+        }
+
+        // Create instance of jetty server with either default or supplied 
queued thread pool
+        if (threadPool == null) {
+            server = serverFactory.create(maxThreads, minThreads, 
threadIdleTimeoutMillis);
+        } else {
+            server = serverFactory.create(threadPool);
+        }
+
+        ServerConnector connector;
+
+        if (sslStores == null) {
+            connector = SocketConnectorFactory.createSocketConnector(server, 
host, port, useHTTP2, trustForwardHeaders);
+        } else {
+            connector = 
SocketConnectorFactory.createSecureSocketConnector(server, host, port, 
sslStores, useHTTP2, trustForwardHeaders);
+        }
+
+        Connector[] previousConnectors = server.getConnectors();
+        server = connector.getServer();
+        if (previousConnectors.length != 0) {
+            server.setConnectors(previousConnectors);
+            hasCustomizedConnectors = true;
+        } else {
+            server.setConnectors(new Connector[]{connector});
+        }
+
+        final ServletContextHandler webSocketServletContextHandler =
+            WebSocketServletContextHandlerFactory.create(webSocketHandlers, 
webSocketIdleTimeoutMillis);
+
+        final ServletContextHandler servletContextHandler = 
webSocketServletContextHandler == null ?
+            new ServletContextHandler() : webSocketServletContextHandler;
+
+        final SessionHandler sessionHandler = new SessionHandler();
+        sessionHandler.getSessionCookieConfig().setHttpOnly(httpOnly);
+        servletContextHandler.setSessionHandler(sessionHandler);
+
+        servletContextHandler.addFilter(matcherFilter, "/*", 
EnumSet.allOf(DispatcherType.class));
+
+        server.setHandler(servletContextHandler);
+
+        logger.info("== {} has ignited ...", NAME);
+        if (hasCustomizedConnectors) {
+            logger.info(">> Listening on Custom Server ports!");
+        } else {
+            logger.info(">> Listening on {}:{}", host, port);
+        }
+
+        server.start();
+        return port;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void join() throws InterruptedException {
+        server.join();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void extinguish() {
+        logger.info(">>> {} shutting down ...", NAME);
+        try {
+            if (server != null) {
+                server.stop();
+            }
+        } catch (Exception e) {
+            logger.error("stop failed", e);
+            System.exit(100); // NOSONAR
+        }
+        logger.info("done");
+    }
+
+    @Override
+    public int activeThreadCount() {
+        if (server == null) {
+            return 0;
+        }
+        return server.getThreadPool().getThreads() - 
server.getThreadPool().getIdleThreads();
+    }
+
+    /**
+     * Sets optional thread pool for jetty server.  This is useful for 
overriding the default thread pool
+     * behaviour for example 
io.dropwizard.metrics.jetty9.InstrumentedQueuedThreadPool.
+     * @param threadPool thread pool
+     * @return Builder pattern - returns this instance
+     */
+    public EmbeddedJettyServer withThreadPool(ThreadPool threadPool) {
+        this.threadPool = threadPool;
+        return this;
+    }
+}
\ No newline at end of file
diff --git 
a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/JettyHandler.java
 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/JettyHandler.java
new file mode 100644
index 0000000000..94e01578bb
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/JettyHandler.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.webadmin.jettyserver;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.ee10.servlet.ServletContextRequest;
+import org.eclipse.jetty.ee10.servlet.SessionHandler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
+
+import spark.embeddedserver.jetty.HttpRequestWrapper;
+
+/**
+ * Simple Jetty Handler
+ *
+ * @author Per Wendel
+ *
+ * @see <a href="https://github.com/nmondal/spark-11/pull/20";>Upstream 
issue</a>
+ */
+public class JettyHandler extends SessionHandler {
+    private final Filter filter;
+
+    public JettyHandler(Filter filter) {
+        this.filter = filter;
+    }
+
+    @Override
+    public boolean handle(Request request, Response response, Callback 
callback) throws Exception {
+        if (request instanceof ServletContextRequest servletContextRequest) {
+            final HttpServletResponse httpServletResponse = 
servletContextRequest.getHttpServletResponse();
+            final HttpServletRequest httpServletRequest = 
servletContextRequest.getServletApiRequest();
+            final HttpRequestWrapper wrapper = new 
HttpRequestWrapper(httpServletRequest);
+
+            filter.doFilter(wrapper, httpServletResponse, null);
+            callback.succeeded();
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git 
a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/JettyServer.java
 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/JettyServer.java
new file mode 100644
index 0000000000..aed89653f6
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/JettyServer.java
@@ -0,0 +1,73 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.webadmin.jettyserver;
+
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.VirtualThreads;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+import spark.embeddedserver.VirtualThreadAware;
+import spark.embeddedserver.jetty.JettyServerFactory;
+
+/**
+ * Creates Jetty Server instances.
+ * Clone from spark.embeddedserver.jetty.JettyServer
+ */
+class JettyServer extends VirtualThreadAware.Base implements 
JettyServerFactory {
+
+    /**
+     * Creates a Jetty server.
+     *
+     * @param maxThreads          maxThreads
+     * @param minThreads          minThreads
+     * @param threadTimeoutMillis threadTimeoutMillis
+     * @return a new jetty server instance
+     */
+    public Server create(int maxThreads, int minThreads, int 
threadTimeoutMillis) {
+        final QueuedThreadPool queuedThreadPool;
+        if (maxThreads > 0) {
+            int max = maxThreads;
+            int min = (minThreads > 0) ? minThreads : 8;
+            int idleTimeout = (threadTimeoutMillis > 0) ? threadTimeoutMillis 
: 60000;
+            queuedThreadPool = new QueuedThreadPool(max, min, idleTimeout);
+        } else {
+            queuedThreadPool = new QueuedThreadPool();
+        }
+        return create(queuedThreadPool);
+    }
+
+    /**
+     * Creates a Jetty server with supplied thread pool
+     * @param threadPool thread pool
+     * @return a new jetty server instance
+     */
+    @Override
+    public Server create(ThreadPool threadPool) {
+        if (threadPool == null) {
+            threadPool = new QueuedThreadPool();
+        }
+        if (useVThread && VirtualThreads.areSupported() && threadPool 
instanceof QueuedThreadPool) {
+            ((QueuedThreadPool) 
threadPool).setVirtualThreadsExecutor(VirtualThreads.getDefaultVirtualThreadsExecutor());
+        }
+        return new Server(threadPool);
+    }
+}
diff --git 
a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/SocketConnectorFactory.java
 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/SocketConnectorFactory.java
new file mode 100644
index 0000000000..30e529fc26
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/jettyserver/SocketConnectorFactory.java
@@ -0,0 +1,171 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.webadmin.jettyserver;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.AbstractConnectionFactory;
+import org.eclipse.jetty.server.ForwardedRequestCustomizer;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+import spark.ssl.SslStores;
+import spark.utils.Assert;
+
+/**
+ * Fork from spark.embeddedserver.jetty.SocketConnectorFactory
+ * Creates socket connectors.
+ */
+public class SocketConnectorFactory {
+
+    /**
+     * Creates an ordinary, non-secured Jetty server jetty.
+     *
+     * @param server  Jetty server
+     * @param host    host
+     * @param port    port
+     * @param usHTTP2 if true use HTTP2 connection, else HTTP1.x
+     * @return - a server jetty
+     */
+    public static ServerConnector createSocketConnector(Server server, String 
host, int port, boolean usHTTP2, boolean trustForwardHeaders) {
+        Assert.notNull(server, "'server' must not be null");
+        Assert.notNull(host, "'host' must not be null");
+
+        final AbstractConnectionFactory connectionFactory = usHTTP2 ?
+            createHttp2ConnectionFactory(trustForwardHeaders) : 
createHttpConnectionFactory(trustForwardHeaders);
+        ServerConnector connector = new ServerConnector(server, 
connectionFactory);
+        initializeConnector(connector, host, port);
+        return connector;
+    }
+
+    // jetty 12 verifies if a resource is readable, that breaks existing ( bad 
) behaviour of tests
+    public static boolean ENABLE_JETTY_11_COMPATIBILITY = false;
+
+    // a hacky way to insert non existing resource
+    static void forceInsertNonExistingResource(SslContextFactory.Server 
sslContextFactory, String someFieldName, String somePath) {
+        try {
+            Resource res = 
ResourceFactory.of(sslContextFactory).newResource(somePath);
+            Field myField = 
SslContextFactory.class.getDeclaredField(someFieldName);
+            myField.setAccessible(true);
+            myField.set(sslContextFactory, res);
+        } catch (Throwable ignore) {
+            // ignore
+        }
+    }
+
+    // run if fails runs the default
+    static void runOrDefault(Runnable call, Runnable def) {
+        try {
+            call.run();
+        } catch (RuntimeException rex) {
+            if (ENABLE_JETTY_11_COMPATIBILITY) {
+                def.run();
+            }
+        }
+    }
+
+    /**
+     * Creates a ssl jetty socket jetty. Keystore required, truststore
+     * optional. If truststore not specified keystore will be reused.
+     *
+     * @param server    Jetty server
+     * @param sslStores the security sslStores.
+     * @param host      host
+     * @param port      port
+     * @param useHTTP2  if true return HTTP2 enabled connector, else return 
HTTP1.x connector
+     * @return a ssl socket jetty
+     */
+    public static ServerConnector createSecureSocketConnector(Server server,
+                                                              String host,
+                                                              int port,
+                                                              SslStores 
sslStores,
+                                                              boolean useHTTP2,
+                                                              boolean 
trustForwardHeaders) {
+        Assert.notNull(server, "'server' must not be null");
+        Assert.notNull(host, "'host' must not be null");
+        Assert.notNull(sslStores, "'sslStores' must not be null");
+
+        final SslContextFactory.Server sslContextFactory = new 
SslContextFactory.Server();
+        runOrDefault(() -> 
sslContextFactory.setKeyStorePath(sslStores.keystoreFile()), () -> {
+            forceInsertNonExistingResource(sslContextFactory, 
"_keyStoreResource", sslStores.keystoreFile());
+        });
+
+        if (sslStores.keystorePassword() != null) {
+            
sslContextFactory.setKeyStorePassword(sslStores.keystorePassword());
+        }
+
+        if (sslStores.certAlias() != null) {
+            sslContextFactory.setCertAlias(sslStores.certAlias());
+        }
+
+        if (sslStores.trustStoreFile() != null) {
+            runOrDefault(() -> 
sslContextFactory.setTrustStorePath(sslStores.trustStoreFile()), () -> {
+                forceInsertNonExistingResource(sslContextFactory, 
"_trustStoreResource", sslStores.trustStoreFile());
+            });
+        }
+
+        if (sslStores.trustStorePassword() != null) {
+            
sslContextFactory.setTrustStorePassword(sslStores.trustStorePassword());
+        }
+
+        if (sslStores.needsClientCert()) {
+            sslContextFactory.setNeedClientAuth(true);
+            sslContextFactory.setWantClientAuth(true);
+        }
+
+        HttpConnectionFactory httpConnectionFactory = 
createHttpConnectionFactory(trustForwardHeaders);
+
+        ServerConnector connector = new ServerConnector(server, 
sslContextFactory, httpConnectionFactory);
+        initializeConnector(connector, host, port);
+        return connector;
+    }
+
+    private static void initializeConnector(ServerConnector connector, String 
host, int port) {
+        // Set some timeout options to make debugging easier.
+        connector.setIdleTimeout(TimeUnit.HOURS.toMillis(1));
+        connector.setHost(host);
+        connector.setPort(port);
+    }
+
+    private static HttpConnectionFactory createHttpConnectionFactory(boolean 
trustForwardHeaders) {
+        HttpConfiguration httpConfig = new HttpConfiguration();
+        httpConfig.setSecureScheme("https");
+        if (trustForwardHeaders) {
+            httpConfig.addCustomizer(new ForwardedRequestCustomizer());
+        }
+        return new HttpConnectionFactory(httpConfig);
+    }
+
+    private static HTTP2ServerConnectionFactory 
createHttp2ConnectionFactory(boolean trustForwardHeaders) {
+        HttpConfiguration httpConfig = new HttpConfiguration();
+        httpConfig.setSecureScheme("https");
+        if (trustForwardHeaders) {
+            httpConfig.addCustomizer(new ForwardedRequestCustomizer());
+        }
+        return new HTTP2ServerConnectionFactory(httpConfig);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to