Compare commits

...

8 Commits

Author SHA1 Message Date
Jeroen van Erp
1398b190ec Prepare release 0.39.0
Some checks failed
Build SSHJ / Build with Java 11 (push) Has been cancelled
Build SSHJ / Integration test (push) Has been cancelled
2025-05-13 11:33:00 +02:00
Jeroen van Erp
acad163e50 Reduce code duplication
Some checks failed
Build SSHJ / Build with Java 11 (push) Has been cancelled
Build SSHJ / Integration test (push) Has been cancelled
Signed-off-by: Jeroen van Erp <jeroen@hierynomus.com>
2025-04-22 14:36:09 +02:00
Jeroen van Erp
8e9e644bb8 Merge branch 'master' into pr/uttamgupta/976
Signed-off-by: Jeroen van Erp <jeroen@hierynomus.com>
2025-04-22 14:35:35 +02:00
David Handermann
857d56a679 Replaced Grizzly HTTP Server with Java HTTP Server (#1010)
Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2025-04-22 11:59:58 +02:00
David Handermann
0e4a8f675f Upgraded SSHD from 2.14.0 to 2.15.0 (#1009) 2025-04-22 09:53:03 +02:00
Dmitry Sulman
95aab0088e Upgrade dependencies (#1007)
Some checks failed
Build SSHJ / Build with Java 11 (push) Has been cancelled
Build SSHJ / Integration test (push) Has been cancelled
- Upgrade Gradle to 8.13
- Upgrade SLF4J to 2.0.17
- Upgrade Mockito to 5.16.1
- Upgrade AssertJ to 3.27.3
- Upgrade Logback to 1.5.18
- Upgrade Testcontainers to 1.20.6

Signed-off-by: Dmitry Sulman <dmitry.sulman@gmail.com>
2025-03-24 14:10:44 +01:00
Jeroen van Erp
c3236a7405 Merge branch 'master' into pr/uttamgupta/976 2025-02-18 13:27:31 +01:00
Uttam Gupta
bfa82b4e44 Issue-973 (part1) - Removing direct dependency on BouncyCastle library and using JCE.
- This is first change towards removing direct dependency on BouncyCastle.
- Removed the imports org.bouncycastle.crypto.prng.RandomGenerator and
  org.bouncycastle.crypto.prng.VMPCRandomGenerator from the file
  BouncyCastleRandom.java, eliminating the direct dependency on BouncyCastle.
  Now using JCE with a provider, allowing SecureRandom to utilize the BC provider.
- Added a new class, BouncyCastleFipsRandom, similar to BouncyCastleRandom.java,
  which leverages the BCFIPS provider to create SecureRandom instances.
- Upgraded gradle plugin, groovy and Mockito versions to ensure compatibility with Java 21.
  from org.spockframework:spock-core:2.3-groovy-3.0 to org.spockframework:spock-core:2.4-M5-groovy-4.0
  from org.mockito:mockito-core:4.11.0 to org.mockito:mockito-core:5.15.2
  from gradle-8.2-bin.zip to gradle-8.11-bin.zip
 Testing: I have run gradle clean build with java 11 and Java 21 and works.
Test Gradle Test Executor 2; Executed: 470/469/0
✓ Test Gradle Test Run :test; Executed: 470/469/0
2025-01-13 14:54:54 -08:00
11 changed files with 198 additions and 130 deletions

View File

@@ -110,6 +110,18 @@ Issue tracker: https://github.com/hierynomus/sshj/issues
Fork away!
== Release history
SSHJ 0.39.0 (2024-02-20)::
* Upgraded dependencies
* Remove hard dependencies on BouncyCastle, making it optional.
* Merged https://github.com/hierynomus/sshj/pull/993[#993]: Remove EDDSA dependency
* Merged https://github.com/hierynomus/sshj/pull/959[#959]: Improve Curve25519 public key handling
* Merged https://github.com/hierynomus/sshj/pull/911[#911]: Fix for bad packet received with heartbeat enabled
* Merged https://github.com/hierynomus/sshj/pull/926[#926]: Close session when closing SFTP client
* Merged https://github.com/hierynomus/sshj/pull/928[#928]: Improve file-listing performance
* Merged https://github.com/hierynomus/sshj/pull/934[#934]: Don't send keep-alive before KEX done
* Merged https://github.com/hierynomus/sshj/pull/936[#936]: Improve Base64 decoding error handling
* Merged https://github.com/hierynomus/sshj/pull/925[#925]: Allow passing connected sockets
* Merged https://github.com/hierynomus/sshj/pull/922[#922]: Fix bug in known_hosts parsing
SSHJ 0.38.0 (2024-01-02)::
* Mitigated CVE-2023-48795 - Terrapin
* Merged https://github.com/hierynomus/sshj/pull/917[#917]: Implement OpenSSH strict key exchange extension

View File

@@ -47,10 +47,10 @@ compileJava {
configurations.implementation.transitive = false
def bouncycastleVersion = "1.80"
def sshdVersion = "2.14.0"
def sshdVersion = "2.15.0"
dependencies {
implementation "org.slf4j:slf4j-api:2.0.16"
implementation "org.slf4j:slf4j-api:2.0.17"
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
implementation "com.hierynomus:asn-one:0.6.0"
@@ -89,16 +89,15 @@ testing {
configureEach {
useJUnitJupiter()
dependencies {
implementation "org.slf4j:slf4j-api:2.0.16"
implementation "org.slf4j:slf4j-api:2.0.17"
implementation 'org.spockframework:spock-core:2.3-groovy-3.0'
implementation "org.mockito:mockito-core:4.11.0"
implementation "org.assertj:assertj-core:3.24.2"
implementation "org.mockito:mockito-core:5.16.1"
implementation "org.assertj:assertj-core:3.27.3"
implementation "ru.vyarus:spock-junit5:1.2.0"
implementation "org.apache.sshd:sshd-core:$sshdVersion"
implementation "org.apache.sshd:sshd-sftp:$sshdVersion"
implementation "org.apache.sshd:sshd-scp:$sshdVersion"
implementation "ch.qos.logback:logback-classic:1.3.15"
implementation 'org.glassfish.grizzly:grizzly-http-server:3.0.1'
implementation "ch.qos.logback:logback-classic:1.5.18"
}
targets {
@@ -133,8 +132,8 @@ testing {
integrationTest(JvmTestSuite) {
dependencies {
implementation project()
implementation 'org.testcontainers:testcontainers:1.20.4'
implementation 'org.testcontainers:junit-jupiter:1.20.4'
implementation platform('org.testcontainers:testcontainers-bom:1.20.6')
implementation 'org.testcontainers:junit-jupiter'
}
sources {

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -59,7 +59,8 @@ import java.util.Properties;
* net.schmizz.sshj.transport.mac.HMACMD596}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setCompressionFactories Compression}: {@link net.schmizz.sshj.transport.compression.NoneCompression}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyAlgorithms KeyAlgorithm}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory PRNG}: {@link net.schmizz.sshj.transport.random.BouncyCastleRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory BC}: {@link net.schmizz.sshj.transport.random.BouncyCastleRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory BCFIPS}: {@link net.schmizz.sshj.transport.random.BouncyCastleFipsRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setFileKeyProviderFactories Key file support}: {@link net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile}*, {@link
* net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile}*</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setVersion Client version}: {@code "NET_3_0"}</li>

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* 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 net.schmizz.sshj.transport.random;
/**
* BouncyCastle <code>Random</code>. This pseudo random number generator uses BouncyCastle fips.
* The JRE random will be used when creating a new generator to add some random data to the seed.
*/
public class BouncyCastleFipsRandom extends SecureRandomProvider {
/** Named factory for the BouncyCastle <code>Random</code> */
public static class Factory
implements net.schmizz.sshj.common.Factory<Random> {
@Override
public Random create() {
return new BouncyCastleFipsRandom();
}
}
public BouncyCastleFipsRandom() {
super("DEFAULT", "BCFIPS");
}
}

View File

@@ -13,23 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C)2009 - SSHJ Contributors
*
* 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 net.schmizz.sshj.transport.random;
import org.bouncycastle.crypto.prng.RandomGenerator;
import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.SecureRandom;
/**
* BouncyCastle <code>Random</code>. This pseudo random number generator uses the a very fast PRNG from BouncyCastle.
* The JRE random will be used when creating a new generator to add some random data to the seed.
*/
public class BouncyCastleRandom
implements Random {
private static final Logger logger = LoggerFactory.getLogger(BouncyCastleRandom.class);
* BouncyCastle <code>Random</code>. This pseudo random number generator uses BouncyCastle non fips.
* The JRE random will be used when creating a new generator to add some random data to the seed.
*/
public class BouncyCastleRandom extends SecureRandomProvider {
/** Named factory for the BouncyCastle <code>Random</code> */
public static class Factory
@@ -42,24 +47,7 @@ public class BouncyCastleRandom
}
private final RandomGenerator random = new VMPCRandomGenerator();
public BouncyCastleRandom() {
logger.info("Generating random seed from SecureRandom.");
long t = System.currentTimeMillis();
byte[] seed = new SecureRandom().generateSeed(8);
logger.debug("Creating random seed took {} ms", System.currentTimeMillis() - t);
random.addSeedMaterial(seed);
super("DEFAULT", "BC");
}
@Override
public void fill(byte[] bytes, int start, int len) {
random.nextBytes(bytes, start, len);
}
@Override
public void fill(byte[] bytes) {
random.nextBytes(bytes);
}
}

View File

@@ -15,16 +15,11 @@
*/
package net.schmizz.sshj.transport.random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.SecureRandom;
/** A {@link Random} implementation using the built-in {@link SecureRandom} PRNG. */
public class JCERandom
implements Random {
private static final Logger logger = LoggerFactory.getLogger(JCERandom.class);
public class JCERandom extends SecureRandomProvider {
/** Named factory for the JCE {@link Random} */
public static class Factory
implements net.schmizz.sshj.common.Factory.Named<Random> {
@@ -41,39 +36,7 @@ public class JCERandom
}
private byte[] tmp = new byte[16];
private final SecureRandom random;
JCERandom() {
logger.info("Creating new SecureRandom.");
long t = System.currentTimeMillis();
random = new SecureRandom();
logger.debug("Random creation took {} ms", System.currentTimeMillis() - t);
}
/**
* Fill the given byte-array with random bytes from this PRNG.
*
* @param foo the byte-array
* @param start the offset to start at
* @param len the number of bytes to fill
*/
@Override
public synchronized void fill(byte[] foo, int start, int len) {
if (start == 0 && len == foo.length) {
random.nextBytes(foo);
} else {
synchronized (this) {
if (len > tmp.length)
tmp = new byte[len];
random.nextBytes(tmp);
System.arraycopy(tmp, 0, foo, start, len);
}
}
}
@Override
public void fill(final byte[] bytes) {
random.nextBytes(bytes);
super();
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* 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 net.schmizz.sshj.transport.random;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SecureRandomProvider implements Random{
private static final Logger logger = LoggerFactory.getLogger(SecureRandomProvider.class);
private byte[] tmp = new byte[16];
private SecureRandom random;
protected SecureRandomProvider() {
this.random = newRandom();
}
protected SecureRandomProvider(String algorithm, String provider) {
this.random = newRandom(algorithm, provider);
}
private static SecureRandom newRandom() {
return new SecureRandom();
}
private static SecureRandom newRandom(String algorithm, String provider) {
logger.info("Generating random seed from SecureRandom of {}.", provider);
long t = System.currentTimeMillis();
try {
// Use SecureRandom with the provider
return SecureRandom.getInstance(algorithm, provider);
} catch (NoSuchProviderException e) {
throw new RuntimeException(String.format("%s provider is not in the classpath", provider), e);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to initialize SecureRandom with %s provider", provider), e);
} finally {
logger.debug("Creating random seed took {} ms", System.currentTimeMillis() - t);
}
}
@Override
public synchronized void fill(byte[] bytes, int start, int len) {
if (start == 0 && len == bytes.length) {
random.nextBytes(bytes);
} else {
synchronized (this) {
if (len > tmp.length) tmp = new byte[len];
random.nextBytes(tmp);
System.arraycopy(tmp, 0, bytes, start, len);
}
}
}
@Override
public void fill(byte[] bytes) {
random.nextBytes(bytes);
}
}

View File

@@ -17,7 +17,6 @@ package com.hierynomus.sshj.connection.channel.forwarded;
import com.hierynomus.sshj.test.HttpServer;
import com.hierynomus.sshj.test.SshServerExtension;
import com.hierynomus.sshj.test.util.FileUtil;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
import net.schmizz.sshj.connection.channel.direct.Parameters;
@@ -29,13 +28,10 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import java.io.*;
import java.net.*;
import java.nio.file.Files;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class LocalPortForwarderTest {
private static final String LOCALHOST_URL = "http://127.0.0.1:8080";
@RegisterExtension
public SshServerExtension fixture = new SshServerExtension();
@@ -43,21 +39,19 @@ public class LocalPortForwarderTest {
public HttpServer httpServer = new HttpServer();
@BeforeEach
public void setUp() throws IOException {
public void setUp() {
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
public void shouldHaveWorkingHttpServer() throws IOException {
assertEquals(200, httpGet());
assertEquals(HttpURLConnection.HTTP_NOT_FOUND, httpGet());
}
@Test
public void shouldHaveHttpServerThatClosesConnectionAfterResponse() throws IOException {
// Just to check that the test server does close connections before we try through the forwarder...
httpGetAndAssertConnectionClosedByServer(8080);
httpGetAndAssertConnectionClosedByServer(httpServer.getServerUrl().getPort());
}
@Test
@@ -68,7 +62,8 @@ public class LocalPortForwarderTest {
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
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(() -> {
try {
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.
OutputStream outputStream = socket.getOutputStream();
PrintWriter writer = new PrintWriter(outputStream);
writer.println("GET / HTTP/1.1");
writer.println("GET / HTTP/1.1\r\n");
writer.println("");
writer.flush();
@@ -111,7 +106,7 @@ public class LocalPortForwarderTest {
}
private int httpGet() throws IOException {
final URL url = new URL(LOCALHOST_URL);
final URL url = httpServer.getServerUrl().toURL();
final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(3000);
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.SshServerExtension;
import com.hierynomus.sshj.test.util.FileUtil;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.ConnectionException;
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.extension.RegisterExtension;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class RemotePortForwarderTest {
private static final PortRange RANGE = new PortRange(9000, 9999);
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 InetSocketAddress HTTP_SERVER_SOCKET_ADDR = new InetSocketAddress(LOCALHOST, 8080);
private static final String URL_FORMAT = "http://%s:%d";
@RegisterExtension
public SshServerExtension fixture = new SshServerExtension();
@@ -49,21 +46,21 @@ public class RemotePortForwarderTest {
public HttpServer httpServer = new HttpServer();
@BeforeEach
public void setUp() throws IOException {
public void setUp() {
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
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
public void shouldDynamicallyForwardPortForLocalhost() throws IOException {
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);
}
@@ -84,7 +81,7 @@ public class RemotePortForwarderTest {
@Test
public void shouldForwardPortForLocalhost() throws IOException {
SSHClient sshClient = getFixtureClient();
RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", RANGE);
RemotePortForwarder.Forward bind = forwardPort(sshClient, LOCALHOST, RANGE);
assertHttpGetSuccess(bind);
}
@@ -103,17 +100,22 @@ public class RemotePortForwarderTest {
}
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 {
while (true) {
final URI serverUrl = httpServer.getServerUrl();
final InetSocketAddress serverAddress = new InetSocketAddress(serverUrl.getHost(), serverUrl.getPort());
try {
return sshClient.getRemotePortForwarder().bind(
// where the server should listen
new RemotePortForwarder.Forward(address, portRange.nextPort()),
// what we do with incoming connections that are forwarded to us
new SocketForwardingConnectListener(HTTP_SERVER_SOCKET_ADDR));
new SocketForwardingConnectListener(serverAddress));
} catch (ConnectionException ce) {
if (!portRange.hasNext()) {
throw ce;
@@ -122,8 +124,8 @@ public class RemotePortForwarderTest {
}
}
private int httpGet(final int port) throws IOException {
final URL url = new URL(String.format(LOCALHOST_URL_FORMAT, port));
private int httpGet(final String address, final int port) throws IOException {
final URL url = new URL(String.format(URL_FORMAT, address, port));
final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(3000);
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.ExtensionContext;
import java.io.File;
import java.nio.file.Files;
import java.net.InetSocketAddress;
import java.net.URI;
/**
* Can be used to setup a test HTTP server
*/
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 File docRoot ;
private com.sun.net.httpserver.HttpServer httpServer;
@Override
public void afterEach(ExtensionContext context) throws Exception {
public void afterEach(ExtensionContext context) {
try {
httpServer.shutdownNow();
} catch (Exception e) {}
try {
docRoot.delete();
} catch (Exception e) {}
httpServer.stop(0);
} catch (Exception ignored) {}
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
docRoot = Files.createTempDirectory("sshj").toFile();
httpServer = org.glassfish.grizzly.http.server.HttpServer.createSimpleServer(docRoot.getAbsolutePath());
httpServer = com.sun.net.httpserver.HttpServer.create();
final InetSocketAddress socketAddress = new InetSocketAddress(BIND_ADDRESS, 0);
httpServer.bind(socketAddress, 10);
httpServer.start();
}
public org.glassfish.grizzly.http.server.HttpServer getHttpServer() {
return httpServer;
}
public File getDocRoot() {
return docRoot;
public URI getServerUrl() {
final InetSocketAddress bindAddress = httpServer.getAddress();
final String serverUrl = String.format("http://%s:%d", BIND_ADDRESS, bindAddress.getPort());
return URI.create(serverUrl);
}
}