diff --git a/jetty-fcgi/fcgi-server/pom.xml b/jetty-fcgi/fcgi-server/pom.xml
index bed9361cede4..0a2226e4b81d 100644
--- a/jetty-fcgi/fcgi-server/pom.xml
+++ b/jetty-fcgi/fcgi-server/pom.xml
@@ -69,5 +69,11 @@
${project.version}
test
+
+ org.eclipse.jetty
+ jetty-unixdomain-server
+ ${project.version}
+ test
+
diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
index a543c22df221..e22b81c70d62 100644
--- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
+++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServlet.java
@@ -14,6 +14,7 @@
package org.eclipse.jetty.fcgi.server.proxy;
import java.net.URI;
+import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -36,6 +37,7 @@
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.proxy.AsyncProxyServlet;
import org.eclipse.jetty.util.ProcessorUtils;
@@ -62,6 +64,8 @@
* to force the FastCGI {@code HTTPS} parameter to the value {@code on}
*
{@code fastCGI.envNames}, optional, a comma separated list of environment variable
* names read via {@link System#getenv(String)} that are forwarded as FastCGI parameters.
+ * {@code unixDomainPath}, optional, that specifies the Unix-Domain path the FastCGI
+ * server listens to.
*
*
* @see TryFilesFilter
@@ -122,11 +126,23 @@ protected HttpClient newHttpClient()
String scriptRoot = config.getInitParameter(SCRIPT_ROOT_INIT_PARAM);
if (scriptRoot == null)
throw new IllegalArgumentException("Mandatory parameter '" + SCRIPT_ROOT_INIT_PARAM + "' not configured");
- int selectors = Math.max(1, ProcessorUtils.availableProcessors() / 2);
- String value = config.getInitParameter("selectors");
- if (value != null)
- selectors = Integer.parseInt(value);
- return new HttpClient(new ProxyHttpClientTransportOverFCGI(selectors, scriptRoot));
+
+ ClientConnector connector;
+ String unixDomainPath = config.getInitParameter("unixDomainPath");
+ if (unixDomainPath != null)
+ {
+ connector = ClientConnector.forUnixDomain(Path.of(unixDomainPath));
+ }
+ else
+ {
+ int selectors = Math.max(1, ProcessorUtils.availableProcessors() / 2);
+ String value = config.getInitParameter("selectors");
+ if (value != null)
+ selectors = Integer.parseInt(value);
+ connector = new ClientConnector();
+ connector.setSelectors(selectors);
+ }
+ return new HttpClient(new ProxyHttpClientTransportOverFCGI(connector, scriptRoot));
}
@Override
@@ -261,9 +277,9 @@ protected void customizeFastCGIHeaders(Request proxyRequest, HttpFields.Mutable
private class ProxyHttpClientTransportOverFCGI extends HttpClientTransportOverFCGI
{
- private ProxyHttpClientTransportOverFCGI(int selectors, String scriptRoot)
+ private ProxyHttpClientTransportOverFCGI(ClientConnector connector, String scriptRoot)
{
- super(selectors, scriptRoot);
+ super(connector, scriptRoot);
}
@Override
diff --git a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
index 6df325b47312..1498be1b98fc 100644
--- a/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
+++ b/jetty-fcgi/fcgi-server/src/test/java/org/eclipse/jetty/fcgi/server/proxy/FastCGIProxyServletTest.java
@@ -14,9 +14,12 @@
package org.eclipse.jetty.fcgi.server.proxy;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -29,18 +32,22 @@
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.fcgi.server.ServerFCGIConnectionFactory;
import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.unixdomain.server.UnixDomainServerConnector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledForJreRange;
+import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@@ -49,19 +56,13 @@
public class FastCGIProxyServletTest
{
- public static Stream factories()
- {
- return Stream.of(
- true, // send status 200
- false // don't send status 200
- ).map(Arguments::of);
- }
-
+ private final Map fcgiParams = new HashMap<>();
private Server server;
private ServerConnector httpConnector;
- private ServerConnector fcgiConnector;
+ private Connector fcgiConnector;
private ServletContextHandler context;
private HttpClient client;
+ private Path unixDomainPath;
public void prepare(boolean sendStatus200, HttpServlet servlet) throws Exception
{
@@ -71,19 +72,32 @@ public void prepare(boolean sendStatus200, HttpServlet servlet) throws Exception
httpConnector = new ServerConnector(server);
server.addConnector(httpConnector);
- fcgiConnector = new ServerConnector(server, new ServerFCGIConnectionFactory(new HttpConfiguration(), sendStatus200));
+ ServerFCGIConnectionFactory fcgi = new ServerFCGIConnectionFactory(new HttpConfiguration(), sendStatus200);
+ if (unixDomainPath == null)
+ {
+ fcgiConnector = new ServerConnector(server, fcgi);
+ }
+ else
+ {
+ UnixDomainServerConnector connector = new UnixDomainServerConnector(server, fcgi);
+ connector.setUnixDomainPath(unixDomainPath);
+ fcgiConnector = connector;
+ }
server.addConnector(fcgiConnector);
- final String contextPath = "/";
+ String contextPath = "/";
context = new ServletContextHandler(server, contextPath);
- final String servletPath = "/script";
+ String servletPath = "/script";
FastCGIProxyServlet fcgiServlet = new FastCGIProxyServlet()
{
@Override
protected String rewriteTarget(HttpServletRequest request)
{
- return "http://localhost:" + fcgiConnector.getLocalPort() + servletPath + request.getServletPath();
+ String uri = "http://localhost";
+ if (unixDomainPath == null)
+ uri += ":" + ((ServerConnector)fcgiConnector).getLocalPort();
+ return uri + servletPath + request.getServletPath();
}
};
ServletHolder fcgiServletHolder = new ServletHolder(fcgiServlet);
@@ -91,6 +105,7 @@ protected String rewriteTarget(HttpServletRequest request)
fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, "/scriptRoot");
fcgiServletHolder.setInitParameter("proxyTo", "http://localhost");
fcgiServletHolder.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+?\\.php)");
+ fcgiParams.forEach(fcgiServletHolder::setInitParameter);
context.addServlet(fcgiServletHolder, "*.php");
context.addServlet(new ServletHolder(servlet), servletPath + "/*");
@@ -111,36 +126,36 @@ public void dispose() throws Exception
}
@ParameterizedTest(name = "[{index}] sendStatus200={0}")
- @MethodSource("factories")
+ @ValueSource(booleans = {true, false})
public void testGETWithSmallResponseContent(boolean sendStatus200) throws Exception
{
testGETWithResponseContent(sendStatus200, 1024, 0);
}
@ParameterizedTest(name = "[{index}] sendStatus200={0}")
- @MethodSource("factories")
+ @ValueSource(booleans = {true, false})
public void testGETWithLargeResponseContent(boolean sendStatus200) throws Exception
{
testGETWithResponseContent(sendStatus200, 16 * 1024 * 1024, 0);
}
@ParameterizedTest(name = "[{index}] sendStatus200={0}")
- @MethodSource("factories")
+ @ValueSource(booleans = {true, false})
public void testGETWithLargeResponseContentWithSlowClient(boolean sendStatus200) throws Exception
{
testGETWithResponseContent(sendStatus200, 16 * 1024 * 1024, 1);
}
- private void testGETWithResponseContent(boolean sendStatus200, int length, final long delay) throws Exception
+ private void testGETWithResponseContent(boolean sendStatus200, int length, long delay) throws Exception
{
- final byte[] data = new byte[length];
+ byte[] data = new byte[length];
new Random().nextBytes(data);
- final String path = "/foo/index.php";
+ String path = "/foo/index.php";
prepare(sendStatus200, new HttpServlet()
{
@Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException
{
assertTrue(request.getRequestURI().endsWith(path));
response.setContentLength(data.length);
@@ -173,16 +188,20 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
}
@ParameterizedTest(name = "[{index}] sendStatus200={0}")
- @MethodSource("factories")
+ @ValueSource(booleans = {true, false})
public void testURIRewrite(boolean sendStatus200) throws Exception
{
String originalPath = "/original/index.php";
String originalQuery = "foo=bar";
String remotePath = "/remote/index.php";
+ String pathAttribute = "_path_attribute";
+ String queryAttribute = "_query_attribute";
+ fcgiParams.put(FastCGIProxyServlet.ORIGINAL_URI_ATTRIBUTE_INIT_PARAM, pathAttribute);
+ fcgiParams.put(FastCGIProxyServlet.ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM, queryAttribute);
prepare(sendStatus200, new HttpServlet()
{
@Override
- protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ protected void service(HttpServletRequest request, HttpServletResponse response)
{
assertThat((String)request.getAttribute(FCGI.Headers.REQUEST_URI), Matchers.startsWith(originalPath));
assertEquals(originalQuery, request.getAttribute(FCGI.Headers.QUERY_STRING));
@@ -190,11 +209,6 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
}
});
context.stop();
- String pathAttribute = "_path_attribute";
- String queryAttribute = "_query_attribute";
- ServletHolder fcgi = context.getServletHandler().getServlet("fcgi");
- fcgi.setInitParameter(FastCGIProxyServlet.ORIGINAL_URI_ATTRIBUTE_INIT_PARAM, pathAttribute);
- fcgi.setInitParameter(FastCGIProxyServlet.ORIGINAL_QUERY_ATTRIBUTE_INIT_PARAM, queryAttribute);
context.insertHandler(new HandlerWrapper()
{
@Override
@@ -216,4 +230,34 @@ public void handle(String target, org.eclipse.jetty.server.Request baseRequest,
assertEquals(HttpStatus.OK_200, response.getStatus());
}
+
+ @Test
+ @EnabledForJreRange(min = JRE.JAVA_16)
+ public void testUnixDomain() throws Exception
+ {
+ int maxUnixDomainPathLength = 108;
+ Path path = Files.createTempFile("unix", ".sock");
+ if (path.normalize().toAbsolutePath().toString().length() > maxUnixDomainPathLength)
+ path = Files.createTempFile(Path.of("/tmp"), "unix", ".sock");
+ assertTrue(Files.deleteIfExists(path));
+ unixDomainPath = path;
+ fcgiParams.put("unixDomainPath", path.toString());
+ byte[] content = new byte[512];
+ new Random().nextBytes(content);
+ prepare(true, new HttpServlet()
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ response.getOutputStream().write(content);
+ }
+ });
+
+ ContentResponse response = client.newRequest("localhost", httpConnector.getLocalPort())
+ .path("/index.php")
+ .send();
+
+ assertEquals(HttpStatus.OK_200, response.getStatus());
+ assertArrayEquals(content, response.getContent());
+ }
}
diff --git a/jetty-fcgi/fcgi-server/src/test/resources/jetty-logging.properties b/jetty-fcgi/fcgi-server/src/test/resources/jetty-logging.properties
index 4e7406f1b548..9d915cb3b003 100644
--- a/jetty-fcgi/fcgi-server/src/test/resources/jetty-logging.properties
+++ b/jetty-fcgi/fcgi-server/src/test/resources/jetty-logging.properties
@@ -1,3 +1,3 @@
-# Jetty Logging using jetty-slf4j-impl
+#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.client.LEVEL=DEBUG
#org.eclipse.jetty.fcgi.LEVEL=DEBUG
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
index b7f3b4bbeac8..f0a4c987e17f 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
@@ -49,6 +49,11 @@ public class ClientConnector extends ContainerLifeCycle
public static final String CONNECTION_PROMISE_CONTEXT_KEY = CLIENT_CONNECTOR_CONTEXT_KEY + ".connectionPromise";
private static final Logger LOG = LoggerFactory.getLogger(ClientConnector.class);
+ public static ClientConnector forUnixDomain(Path path)
+ {
+ return new ClientConnector(SocketChannelWithAddress.Factory.forUnixDomain(path));
+ }
+
private final SocketChannelWithAddress.Factory factory;
private Executor executor;
private Scheduler scheduler;
@@ -67,7 +72,7 @@ public ClientConnector()
this((address, context) -> new SocketChannelWithAddress(SocketChannel.open(), address));
}
- public ClientConnector(SocketChannelWithAddress.Factory factory)
+ private ClientConnector(SocketChannelWithAddress.Factory factory)
{
this.factory = Objects.requireNonNull(factory);
}
@@ -393,23 +398,23 @@ protected void connectionFailed(SelectableChannel channel, Throwable failure, Ob
/**
* A pair/record holding a {@link SocketChannel} and a {@link SocketAddress} to connect to.
*/
- public static class SocketChannelWithAddress
+ private static class SocketChannelWithAddress
{
private final SocketChannel channel;
private final SocketAddress address;
- public SocketChannelWithAddress(SocketChannel channel, SocketAddress address)
+ private SocketChannelWithAddress(SocketChannel channel, SocketAddress address)
{
this.channel = channel;
this.address = address;
}
- public SocketChannel getSocketChannel()
+ private SocketChannel getSocketChannel()
{
return channel;
}
- public SocketAddress getSocketAddress()
+ private SocketAddress getSocketAddress()
{
return address;
}
@@ -417,9 +422,9 @@ public SocketAddress getSocketAddress()
/**
* A factory for {@link SocketChannelWithAddress} instances.
*/
- public interface Factory
+ private interface Factory
{
- public static Factory forUnixDomain(Path path)
+ private static Factory forUnixDomain(Path path)
{
return (address, context) ->
{
diff --git a/jetty-unixdomain-server/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java b/jetty-unixdomain-server/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java
index bf08d6d4ec97..29e770eaa2ba 100644
--- a/jetty-unixdomain-server/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java
+++ b/jetty-unixdomain-server/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java
@@ -13,8 +13,6 @@
package org.eclipse.jetty.unixdomain.server;
-import java.io.IOException;
-import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -49,7 +47,6 @@
import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1;
import static org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2;
-import static org.eclipse.jetty.io.ClientConnector.SocketChannelWithAddress.Factory.forUnixDomain;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -133,7 +130,7 @@ public void handle(String target, Request jettyRequest, HttpServletRequest reque
}
});
- ClientConnector clientConnector = new ClientConnector(forUnixDomain(unixDomainPath));
+ ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath);
HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector));
httpClient.start();
try
@@ -168,15 +165,7 @@ public void handle(String target, Request jettyRequest, HttpServletRequest reque
}
});
- ClientConnector clientConnector = new ClientConnector((address, context) ->
- {
- if (address instanceof InetSocketAddress)
- {
- if (((InetSocketAddress)address).getPort() == fakeProxyPort)
- return forUnixDomain(unixDomainPath).newSocketChannelWithAddress(address, context);
- }
- throw new IOException("request was not proxied");
- });
+ ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath);
HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector));
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", fakeProxyPort));
@@ -230,7 +219,7 @@ else if ("/v2".equals(target))
});
// Java 11+ portable way to implement SocketChannelWithAddress.Factory.
- ClientConnector clientConnector = new ClientConnector(forUnixDomain(unixDomainPath));
+ ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath);
HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector));
httpClient.start();
diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
index 2df7decafe4c..1cc0b7d61351 100644
--- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
+++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
@@ -313,7 +313,7 @@ public void testUnixSocket() throws Exception
File war = distribution.resolveArtifact("org.eclipse.jetty.demos:demo-jsp-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "test");
- try (JettyHomeTester.Run run2 = distribution.start("jetty.unixsocket.path=" + sockFile.toString()))
+ try (JettyHomeTester.Run run2 = distribution.start("jetty.unixsocket.path=" + sockFile))
{
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
@@ -933,7 +933,7 @@ public void testUnixDomain() throws Exception
{
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
- ClientConnector connector = new ClientConnector(ClientConnector.SocketChannelWithAddress.Factory.forUnixDomain(path));
+ ClientConnector connector = ClientConnector.forUnixDomain(path);
client = new HttpClient(new HttpClientTransportDynamic(connector));
client.start();
ContentResponse response = client.GET("http://localhost/path");