Replaced Grizzly HTTP Server with Java HTTP Server (#1010)

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
This commit is contained in:
David Handermann
2025-04-22 04:59:58 -05:00
committed by GitHub
parent 0e4a8f675f
commit 857d56a679
4 changed files with 38 additions and 48 deletions

View File

@@ -98,7 +98,6 @@ testing {
implementation "org.apache.sshd:sshd-sftp:$sshdVersion" implementation "org.apache.sshd:sshd-sftp:$sshdVersion"
implementation "org.apache.sshd:sshd-scp:$sshdVersion" implementation "org.apache.sshd:sshd-scp:$sshdVersion"
implementation "ch.qos.logback:logback-classic:1.5.18" implementation "ch.qos.logback:logback-classic:1.5.18"
implementation 'org.glassfish.grizzly:grizzly-http-server:3.0.1'
} }
targets { targets {

View File

@@ -17,7 +17,6 @@ package com.hierynomus.sshj.connection.channel.forwarded;
import com.hierynomus.sshj.test.HttpServer; import com.hierynomus.sshj.test.HttpServer;
import com.hierynomus.sshj.test.SshServerExtension; import com.hierynomus.sshj.test.SshServerExtension;
import com.hierynomus.sshj.test.util.FileUtil;
import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder; import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
import net.schmizz.sshj.connection.channel.direct.Parameters; import net.schmizz.sshj.connection.channel.direct.Parameters;
@@ -29,13 +28,10 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.file.Files;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class LocalPortForwarderTest { public class LocalPortForwarderTest {
private static final String LOCALHOST_URL = "http://127.0.0.1:8080";
@RegisterExtension @RegisterExtension
public SshServerExtension fixture = new SshServerExtension(); public SshServerExtension fixture = new SshServerExtension();
@@ -43,21 +39,19 @@ public class LocalPortForwarderTest {
public HttpServer httpServer = new HttpServer(); public HttpServer httpServer = new HttpServer();
@BeforeEach @BeforeEach
public void setUp() throws IOException { public void setUp() {
fixture.getServer().setForwardingFilter(new AcceptAllForwardingFilter()); fixture.getServer().setForwardingFilter(new AcceptAllForwardingFilter());
File file = Files.createFile(httpServer.getDocRoot().toPath().resolve("index.html")).toFile();
FileUtil.writeToFile(file, "<html><head/><body><h1>Hi!</h1></body></html>");
} }
@Test @Test
public void shouldHaveWorkingHttpServer() throws IOException { public void shouldHaveWorkingHttpServer() throws IOException {
assertEquals(200, httpGet()); assertEquals(HttpURLConnection.HTTP_NOT_FOUND, httpGet());
} }
@Test @Test
public void shouldHaveHttpServerThatClosesConnectionAfterResponse() throws IOException { public void shouldHaveHttpServerThatClosesConnectionAfterResponse() throws IOException {
// Just to check that the test server does close connections before we try through the forwarder... // Just to check that the test server does close connections before we try through the forwarder...
httpGetAndAssertConnectionClosedByServer(8080); httpGetAndAssertConnectionClosedByServer(httpServer.getServerUrl().getPort());
} }
@Test @Test
@@ -68,7 +62,8 @@ public class LocalPortForwarderTest {
ServerSocket serverSocket = new ServerSocket(); ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true); serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress("0.0.0.0", 12345)); serverSocket.bind(new InetSocketAddress("0.0.0.0", 12345));
LocalPortForwarder localPortForwarder = sshClient.newLocalPortForwarder(new Parameters("0.0.0.0", 12345, "localhost", 8080), serverSocket); final int serverPort = httpServer.getServerUrl().getPort();
LocalPortForwarder localPortForwarder = sshClient.newLocalPortForwarder(new Parameters("0.0.0.0", 12345, "localhost", serverPort), serverSocket);
new Thread(() -> { new Thread(() -> {
try { try {
localPortForwarder.listen(); localPortForwarder.listen();
@@ -90,7 +85,7 @@ public class LocalPortForwarderTest {
// It returns 400 Bad Request because it's missing a bunch of info, but the HTTP response doesn't matter, we just want to test the connection closing. // It returns 400 Bad Request because it's missing a bunch of info, but the HTTP response doesn't matter, we just want to test the connection closing.
OutputStream outputStream = socket.getOutputStream(); OutputStream outputStream = socket.getOutputStream();
PrintWriter writer = new PrintWriter(outputStream); PrintWriter writer = new PrintWriter(outputStream);
writer.println("GET / HTTP/1.1"); writer.println("GET / HTTP/1.1\r\n");
writer.println(""); writer.println("");
writer.flush(); writer.flush();
@@ -111,7 +106,7 @@ public class LocalPortForwarderTest {
} }
private int httpGet() throws IOException { private int httpGet() throws IOException {
final URL url = new URL(LOCALHOST_URL); final URL url = httpServer.getServerUrl().toURL();
final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(3000); urlConnection.setConnectTimeout(3000);
urlConnection.setRequestMethod("GET"); urlConnection.setRequestMethod("GET");

View File

@@ -17,7 +17,6 @@ package com.hierynomus.sshj.connection.channel.forwarded;
import com.hierynomus.sshj.test.HttpServer; import com.hierynomus.sshj.test.HttpServer;
import com.hierynomus.sshj.test.SshServerExtension; import com.hierynomus.sshj.test.SshServerExtension;
import com.hierynomus.sshj.test.util.FileUtil;
import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.ConnectionException; import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder; import net.schmizz.sshj.connection.channel.forwarded.RemotePortForwarder;
@@ -27,20 +26,18 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL; import java.net.URL;
import java.nio.file.Files;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class RemotePortForwarderTest { public class RemotePortForwarderTest {
private static final PortRange RANGE = new PortRange(9000, 9999); private static final PortRange RANGE = new PortRange(9000, 9999);
private static final String LOCALHOST = "127.0.0.1"; private static final String LOCALHOST = "127.0.0.1";
private static final String LOCALHOST_URL_FORMAT = "http://127.0.0.1:%d"; private static final String URL_FORMAT = "http://%s:%d";
private static final InetSocketAddress HTTP_SERVER_SOCKET_ADDR = new InetSocketAddress(LOCALHOST, 8080);
@RegisterExtension @RegisterExtension
public SshServerExtension fixture = new SshServerExtension(); public SshServerExtension fixture = new SshServerExtension();
@@ -49,21 +46,21 @@ public class RemotePortForwarderTest {
public HttpServer httpServer = new HttpServer(); public HttpServer httpServer = new HttpServer();
@BeforeEach @BeforeEach
public void setUp() throws IOException { public void setUp() {
fixture.getServer().setForwardingFilter(new AcceptAllForwardingFilter()); fixture.getServer().setForwardingFilter(new AcceptAllForwardingFilter());
File file = Files.createFile(httpServer.getDocRoot().toPath().resolve("index.html")).toFile();
FileUtil.writeToFile(file, "<html><head/><body><h1>Hi!</h1></body></html>");
} }
@Test @Test
public void shouldHaveWorkingHttpServer() throws IOException { public void shouldHaveWorkingHttpServer() throws IOException {
assertEquals(200, httpGet(8080)); final URI serverUrl = httpServer.getServerUrl();
assertEquals(HttpURLConnection.HTTP_NOT_FOUND, httpGet(serverUrl.getHost(), serverUrl.getPort()));
} }
@Test @Test
public void shouldDynamicallyForwardPortForLocalhost() throws IOException { public void shouldDynamicallyForwardPortForLocalhost() throws IOException {
SSHClient sshClient = getFixtureClient(); SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", new SinglePort(0)); RemotePortForwarder.Forward bind = forwardPort(sshClient, LOCALHOST, new SinglePort(0));
assertHttpGetSuccess(bind); assertHttpGetSuccess(bind);
} }
@@ -84,7 +81,7 @@ public class RemotePortForwarderTest {
@Test @Test
public void shouldForwardPortForLocalhost() throws IOException { public void shouldForwardPortForLocalhost() throws IOException {
SSHClient sshClient = getFixtureClient(); SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", RANGE); RemotePortForwarder.Forward bind = forwardPort(sshClient, LOCALHOST, RANGE);
assertHttpGetSuccess(bind); assertHttpGetSuccess(bind);
} }
@@ -103,17 +100,22 @@ public class RemotePortForwarderTest {
} }
private void assertHttpGetSuccess(final RemotePortForwarder.Forward bind) throws IOException { private void assertHttpGetSuccess(final RemotePortForwarder.Forward bind) throws IOException {
assertEquals(200, httpGet(bind.getPort())); final String bindAddress = bind.getAddress();
final String address = bindAddress.isEmpty() ? LOCALHOST : bindAddress;
final int port = bind.getPort();
assertEquals(HttpURLConnection.HTTP_NOT_FOUND, httpGet(address, port));
} }
private RemotePortForwarder.Forward forwardPort(SSHClient sshClient, String address, PortRange portRange) throws IOException { private RemotePortForwarder.Forward forwardPort(SSHClient sshClient, String address, PortRange portRange) throws IOException {
while (true) { while (true) {
final URI serverUrl = httpServer.getServerUrl();
final InetSocketAddress serverAddress = new InetSocketAddress(serverUrl.getHost(), serverUrl.getPort());
try { try {
return sshClient.getRemotePortForwarder().bind( return sshClient.getRemotePortForwarder().bind(
// where the server should listen // where the server should listen
new RemotePortForwarder.Forward(address, portRange.nextPort()), new RemotePortForwarder.Forward(address, portRange.nextPort()),
// what we do with incoming connections that are forwarded to us // what we do with incoming connections that are forwarded to us
new SocketForwardingConnectListener(HTTP_SERVER_SOCKET_ADDR)); new SocketForwardingConnectListener(serverAddress));
} catch (ConnectionException ce) { } catch (ConnectionException ce) {
if (!portRange.hasNext()) { if (!portRange.hasNext()) {
throw ce; throw ce;
@@ -122,8 +124,8 @@ public class RemotePortForwarderTest {
} }
} }
private int httpGet(final int port) throws IOException { private int httpGet(final String address, final int port) throws IOException {
final URL url = new URL(String.format(LOCALHOST_URL_FORMAT, port)); final URL url = new URL(String.format(URL_FORMAT, address, port));
final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(3000); urlConnection.setConnectTimeout(3000);
urlConnection.setRequestMethod("GET"); urlConnection.setRequestMethod("GET");

View File

@@ -19,42 +19,36 @@ import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import java.io.File; import java.net.InetSocketAddress;
import java.nio.file.Files; import java.net.URI;
/** /**
* Can be used to setup a test HTTP server * Can be used to setup a test HTTP server
*/ */
public class HttpServer implements BeforeEachCallback, AfterEachCallback { public class HttpServer implements BeforeEachCallback, AfterEachCallback {
private org.glassfish.grizzly.http.server.HttpServer httpServer; private static final String BIND_ADDRESS = "127.0.0.1";
private com.sun.net.httpserver.HttpServer httpServer;
private File docRoot ;
@Override @Override
public void afterEach(ExtensionContext context) throws Exception { public void afterEach(ExtensionContext context) {
try { try {
httpServer.shutdownNow(); httpServer.stop(0);
} catch (Exception e) {} } catch (Exception ignored) {}
try {
docRoot.delete();
} catch (Exception e) {}
} }
@Override @Override
public void beforeEach(ExtensionContext context) throws Exception { public void beforeEach(ExtensionContext context) throws Exception {
docRoot = Files.createTempDirectory("sshj").toFile(); httpServer = com.sun.net.httpserver.HttpServer.create();
httpServer = org.glassfish.grizzly.http.server.HttpServer.createSimpleServer(docRoot.getAbsolutePath()); final InetSocketAddress socketAddress = new InetSocketAddress(BIND_ADDRESS, 0);
httpServer.bind(socketAddress, 10);
httpServer.start(); httpServer.start();
} }
public org.glassfish.grizzly.http.server.HttpServer getHttpServer() { public URI getServerUrl() {
return httpServer; final InetSocketAddress bindAddress = httpServer.getAddress();
} final String serverUrl = String.format("http://%s:%d", BIND_ADDRESS, bindAddress.getPort());
return URI.create(serverUrl);
public File getDocRoot() {
return docRoot;
} }
} }