diff --git a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java index 6e7b26b73..d444b0f21 100644 --- a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java +++ b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketClientConnector.java @@ -18,11 +18,8 @@ package org.newsclub.net.unix.jetty; import java.io.IOException; -import java.net.SocketAddress; import java.nio.channels.Selector; -import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; -import java.util.Map; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.SelectorManager; @@ -70,13 +67,10 @@ protected Selector newSelector() throws IOException { } private static Configurator configuratorFor(AFSocketAddress addr) { - return new Configurator() { - @Override - public ChannelWithAddress newChannelWithAddress(ClientConnector clientConnector, - SocketAddress address, Map context) throws IOException { - SocketChannel socketChannel = addr.getAddressFamily().newSocketChannel(); - return new ChannelWithAddress(socketChannel, addr); - } - }; + if (JettyCompat.hasTransportClass()) { + return new AFSocketConfiguratorWithTransport(addr); + } else { + return new AFSocketConfigurator(addr); + } } } diff --git a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketConfigurator.java b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketConfigurator.java new file mode 100644 index 000000000..85bb217dd --- /dev/null +++ b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketConfigurator.java @@ -0,0 +1,49 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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.newsclub.net.unix.jetty; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.SocketChannel; +import java.util.Map; + +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.io.ClientConnector.Configurator; +import org.newsclub.net.unix.AFSocketAddress; + +/** + * A {@link ClientConnector.Configurator} for junixsocket {@link SocketChannel}s. + * + * @author Christian Kohlschütter + */ +@Deprecated +class AFSocketConfigurator extends Configurator { + protected final AFSocketAddress addr; + + AFSocketConfigurator(AFSocketAddress addr) { + super(); + this.addr = addr; + } + + @Override + public ChannelWithAddress newChannelWithAddress(ClientConnector clientConnector, + SocketAddress address, Map context) throws IOException { + SocketChannel socketChannel = addr.getAddressFamily().newSocketChannel(); + return new ChannelWithAddress(socketChannel, addr); + } +} diff --git a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketConfiguratorWithTransport.java b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketConfiguratorWithTransport.java new file mode 100644 index 000000000..f5752ab4c --- /dev/null +++ b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketConfiguratorWithTransport.java @@ -0,0 +1,42 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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.newsclub.net.unix.jetty; + +import java.nio.channels.SocketChannel; + +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.io.Transport; +import org.newsclub.net.unix.AFSocketAddress; + +/** + * A {@link ClientConnector.Configurator} for junixsocket {@link SocketChannel}s, as required by + * jetty 12.0.7. + * + * @author Christian Kohlschütter + */ +@Deprecated +final class AFSocketConfiguratorWithTransport extends AFSocketConfigurator { + AFSocketConfiguratorWithTransport(AFSocketAddress addr) { + super(addr); + } + + @Override + public Transport newTransport() { + return new AFSocketTransport.WithSocketChannel(addr); + } +} diff --git a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketTransport.java b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketTransport.java new file mode 100644 index 000000000..893b99d48 --- /dev/null +++ b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/AFSocketTransport.java @@ -0,0 +1,134 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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.newsclub.net.unix.jetty; + +import java.io.IOException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.Objects; + +import org.eclipse.jetty.io.DatagramChannelEndPoint; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.ManagedSelector; +import org.eclipse.jetty.io.SocketChannelEndPoint; +import org.eclipse.jetty.io.Transport; +import org.eclipse.jetty.util.thread.Scheduler; +import org.newsclub.net.unix.AFSocketAddress; + +/** + * A {@link Transport} implementation for junixsocket socket and datagram channels (Unix domains + * etc.) + *

+ * This implementation should work with jetty version 12.0.7 or newer. + * + * @author Christian Kohlschütter + */ +public class AFSocketTransport extends Transport.Socket { + private final AFSocketAddress socketAddress; + + private AFSocketTransport(AFSocketAddress socketAddress) { + super(); + this.socketAddress = socketAddress; + } + + /** + * Constructs an {@link AFSocketTransport} that establishes a {@link SocketChannel} to the given + * address. + * + * @param addr The target address. + * @return The {@link Transport} instance. + */ + public static AFSocketTransport withSocketChannel(AFSocketAddress addr) { + return new WithSocketChannel(addr); + } + + /** + * Constructs an {@link AFSocketTransport} that establishes a {@link DatagramChannel} to the given + * address. + * + * @param addr The target address. + * @return The {@link Transport} instance. + */ + public static AFSocketTransport withDatagramChannel(AFSocketAddress addr) { + return new WithDatagramChannel(addr); + } + + @Override + public AFSocketAddress getSocketAddress() { + return socketAddress; + } + + @Override + public int hashCode() { + return Objects.hash(socketAddress); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AFSocketTransport) { + return Objects.equals(socketAddress, ((AFSocketTransport) obj).socketAddress); + } + return false; + } + + @Override + public String toString() { + return super.toString() + "[" + socketAddress.toString() + "]"; + } + + static class WithSocketChannel extends AFSocketTransport { + public WithSocketChannel(AFSocketAddress address) { + super(address); + } + + @Override + public SelectableChannel newSelectableChannel() throws IOException { + return getSocketAddress().getAddressFamily().getSelectorProvider().openSocketChannel(); + } + + @Override + public EndPoint newEndPoint(Scheduler scheduler, ManagedSelector selector, + SelectableChannel selectable, SelectionKey selectionKey) { + return new SocketChannelEndPoint((SocketChannel) selectable, selector, selectionKey, + scheduler); + } + } + + static class WithDatagramChannel extends AFSocketTransport { + public WithDatagramChannel(AFSocketAddress address) { + super(address); + } + + @Override + public SelectableChannel newSelectableChannel() throws IOException { + return getSocketAddress().getAddressFamily().getSelectorProvider().openDatagramChannel(); + } + + @Override + public EndPoint newEndPoint(Scheduler scheduler, ManagedSelector selector, + SelectableChannel selectable, SelectionKey selectionKey) { + return new DatagramChannelEndPoint((DatagramChannel) selectable, selector, selectionKey, + scheduler); + } + } +} diff --git a/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/JettyCompat.java b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/JettyCompat.java new file mode 100644 index 000000000..3d371ae25 --- /dev/null +++ b/junixsocket-jetty/src/main/java/org/newsclub/net/unix/jetty/JettyCompat.java @@ -0,0 +1,41 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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.newsclub.net.unix.jetty; + +final class JettyCompat { + private static final boolean HAVE_TRANSPORT_CLASS; + + static { + boolean found; + try { + Class.forName("org.eclipse.jetty.io.Transport"); + found = true; + } catch (Exception e) { // NOPMD + found = false; + } + HAVE_TRANSPORT_CLASS = found; + } + + private JettyCompat() { + throw new IllegalStateException("No instances"); + } + + static boolean hasTransportClass() { + return HAVE_TRANSPORT_CLASS; + } +} diff --git a/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java b/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java index fdc7ced9a..add27ff2c 100644 --- a/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java +++ b/junixsocket-jetty/src/test/java/org/eclipse/jetty/unixdomain/server/UnixDomainTest.java @@ -46,6 +46,7 @@ import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Objects; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -53,6 +54,7 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpProxy; +import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1; import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2; import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; @@ -79,6 +81,7 @@ import org.newsclub.net.unix.AFUNIXSocketAddress; import org.newsclub.net.unix.jetty.AFSocketClientConnector; import org.newsclub.net.unix.jetty.AFSocketServerConnector; +import org.newsclub.net.unix.jetty.AFSocketTransport; public class UnixDomainTest { private static final Class unixDomainSocketAddressClass = probe(); @@ -190,11 +193,17 @@ public boolean handle(Request request, Response response, Callback callback) }); // ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath); - ClientConnector clientConnector = AFSocketClientConnector.withSocketAddress(AFUNIXSocketAddress - .of(unixDomainPath)); + + AFUNIXSocketAddress unixDomainAddress = AFUNIXSocketAddress.of(unixDomainPath); + ClientConnector clientConnector = AFSocketClientConnector.withSocketAddress(unixDomainAddress); + + // HttpProxy proxy = new HttpProxy("localhost", fakeProxyPort); // this worked until 12.0.6 + HttpProxy proxy = new HttpProxy(new Origin("http", new Origin.Address("localhost", + fakeProxyPort), null, new Origin.Protocol(List.of("http/1.1"), false), AFSocketTransport + .withSocketChannel(unixDomainAddress)), null); HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector)); - httpClient.getProxyConfiguration().addProxy(new HttpProxy("localhost", fakeProxyPort)); + httpClient.getProxyConfiguration().addProxy(proxy); httpClient.start(); try { ContentResponse response = httpClient.newRequest("localhost", fakeServerPort).timeout(5,