Skip to content

Commit 38af9b6

Browse files
committedMar 20, 2025
add int tests (#986)
1 parent 60688ee commit 38af9b6

File tree

4 files changed

+99
-23
lines changed

4 files changed

+99
-23
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.hierynomus.sshj;
2+
3+
import net.schmizz.keepalive.BoundedKeepAliveProvider;
4+
import net.schmizz.sshj.Config;
5+
import net.schmizz.sshj.DefaultConfig;
6+
import net.schmizz.sshj.SSHClient;
7+
import net.schmizz.sshj.common.LoggerFactory;
8+
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
9+
import org.junit.jupiter.api.Assertions;
10+
import org.junit.jupiter.api.Test;
11+
import org.testcontainers.junit.jupiter.Container;
12+
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
16+
public class KeepAliveTest {
17+
@Container
18+
SshdContainer sshd = new SshdContainer(SshdContainer.Builder
19+
.defaultBuilder()
20+
.withAllKeys()
21+
.withPackages("iptables")
22+
.withPrivileged(true));
23+
24+
@Test
25+
void testKeepAlive() throws Exception {
26+
sshd.start();
27+
28+
Config config = new DefaultConfig();
29+
BoundedKeepAliveProvider p = new BoundedKeepAliveProvider(LoggerFactory.DEFAULT, 4);
30+
p.setKeepAliveInterval(1);
31+
p.setMaxKeepAliveCount(1);
32+
config.setKeepAliveProvider(p);
33+
List<SSHClient> clients = new ArrayList<>();
34+
for (int i=0; i<10; i++) {
35+
SSHClient c = new SSHClient(config);
36+
c.addHostKeyVerifier(new PromiscuousVerifier());
37+
c.connect("127.0.0.1", sshd.getFirstMappedPort());
38+
c.authPassword("sshj", "ultrapassword");
39+
var sess = c.startSession();
40+
sess.allocateDefaultPTY();
41+
clients.add(c);
42+
}
43+
44+
for (SSHClient client : clients) {
45+
Assertions.assertTrue(client.isConnected());
46+
}
47+
48+
var res = sshd.execInContainer("iptables", "-A", "INPUT", "-p", "tcp", "--dport", "22", "-j", "DROP");
49+
Assertions.assertEquals(0, res.getExitCode());
50+
// wait for keepalive to take action
51+
Thread.sleep(2000);
52+
53+
for (SSHClient client : clients) {
54+
Assertions.assertFalse(client.isConnected());
55+
}
56+
57+
p.shutdown();
58+
}
59+
}

‎src/itest/java/com/hierynomus/sshj/SshdContainer.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,24 @@ public static class Builder implements Consumer<DockerfileBuilder> {
106106
private List<String> hostKeys = new ArrayList<>();
107107
private List<String> certificates = new ArrayList<>();
108108
private @NotNull SshdConfigBuilder sshdConfig = SshdConfigBuilder.defaultBuilder();
109+
private boolean privileged = false;
110+
private List<String> packages = new ArrayList<>();
109111

110112
public static Builder defaultBuilder() {
111113
Builder b = new Builder();
112-
113114
return b;
114115
}
115116

117+
public @NotNull Builder withPrivileged(boolean privileged) {
118+
this.privileged = privileged;
119+
return this;
120+
}
121+
122+
public @NotNull Builder withPackages(@NotNull String... packages) {
123+
this.packages.addAll(List.of(packages));
124+
return this;
125+
}
126+
116127

117128
public @NotNull Builder withSshdConfig(@NotNull SshdConfigBuilder sshdConfig) {
118129
this.sshdConfig = sshdConfig;
@@ -153,6 +164,9 @@ public void accept(@NotNull DockerfileBuilder builder) {
153164
builder.expose(22);
154165
builder.copy("entrypoint.sh", "/entrypoint.sh");
155166

167+
if (!packages.isEmpty()) {
168+
builder.run("apk add --no-cache " + String.join(" ", packages));
169+
}
156170
builder.add("authorized_keys", "/home/sshj/.ssh/authorized_keys");
157171
builder.copy("test-container/trusted_ca_keys", "/etc/ssh/trusted_ca_keys");
158172

@@ -201,6 +215,9 @@ public SshdContainer() {
201215

202216
public SshdContainer(SshdContainer.Builder builder) {
203217
this(builder.buildInner());
218+
if (builder.privileged) {
219+
withPrivilegedMode(true);
220+
}
204221
}
205222

206223
public SshdContainer(@NotNull Future<String> future) {

‎src/main/java/net/schmizz/keepalive/BoundedKeepAliveProvider.java

+14-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.schmizz.keepalive;
22

33
import net.schmizz.sshj.Config;
4+
import net.schmizz.sshj.common.LoggerFactory;
45
import net.schmizz.sshj.connection.ConnectionException;
56
import net.schmizz.sshj.connection.ConnectionImpl;
67
import net.schmizz.sshj.transport.TransportException;
@@ -29,8 +30,8 @@ public class BoundedKeepAliveProvider extends KeepAliveProvider {
2930
protected final KeepAliveMonitor monitor;
3031

3132

32-
public BoundedKeepAliveProvider(Config config, int numberOfThreads) {
33-
this.monitor = new KeepAliveMonitor(config, numberOfThreads);
33+
public BoundedKeepAliveProvider(LoggerFactory loggerFactory, int numberOfThreads) {
34+
this.monitor = new KeepAliveMonitor(loggerFactory, numberOfThreads);
3435
}
3536

3637
public void setKeepAliveInterval(int interval) {
@@ -76,40 +77,38 @@ public void startKeepAlive() {
7677
}
7778

7879
protected static class KeepAliveMonitor {
80+
private final Logger logger;
7981

80-
private final int numberOfThreads;
81-
private final PriorityBlockingQueue<Wrapper> Q =
82+
private final PriorityBlockingQueue<Wrapper> q =
8283
new PriorityBlockingQueue<>(32, Comparator.comparingLong(w -> w.nextTimeMillis));
83-
private long idleSleepMillis = 100;
8484
private static final List<Thread> workerThreads = new ArrayList<>();
85+
86+
private volatile long idleSleepMillis = 100;
87+
private final int numberOfThreads;
88+
8589
volatile boolean started = false;
86-
private final Logger logger;
8790

8891
private final ReentrantLock lock = new ReentrantLock();
8992
private final Condition shutDown = lock.newCondition();
9093
private final AtomicInteger shutDownCnt = new AtomicInteger(0);
9194

92-
public KeepAliveMonitor(Config config, int numberOfThreads) {
95+
public KeepAliveMonitor(LoggerFactory loggerFactory, int numberOfThreads) {
9396
this.numberOfThreads = numberOfThreads;
94-
logger = config.getLoggerFactory().getLogger(KeepAliveMonitor.class);
97+
logger = loggerFactory.getLogger(KeepAliveMonitor.class);
9598
}
9699

97100
// made public for test
98101
public void register(KeepAlive keepAlive) {
99102
if (!started) {
100103
start();
101104
}
102-
Q.add(new Wrapper(keepAlive));
105+
q.add(new Wrapper(keepAlive));
103106
}
104107

105108
public void setIdleSleepMillis(long idleSleepMillis) {
106109
this.idleSleepMillis = idleSleepMillis;
107110
}
108111

109-
void unregister(KeepAlive keepAlive) {
110-
Q.removeIf(w -> keepAlive == w.keepAlive);
111-
}
112-
113112
private void sleep() {
114113
sleep(idleSleepMillis);
115114
}
@@ -140,7 +139,7 @@ private void doStart() {
140139
while (!Thread.currentThread().isInterrupted()) {
141140
Wrapper wrapper;
142141

143-
if (Q.isEmpty() || (wrapper = Q.poll()) == null) {
142+
if (q.isEmpty() || (wrapper = q.poll()) == null) {
144143
sleep();
145144
continue;
146145
}
@@ -154,7 +153,7 @@ private void doStart() {
154153

155154
try {
156155
wrapper.keepAlive.doKeepAlive();
157-
Q.add(wrapper.reschedule());
156+
q.add(wrapper.reschedule());
158157
} catch (Exception e) {
159158
// If we weren't interrupted, kill the transport, then this exception was unexpected.
160159
// Else we're in shutdown-mode already, so don't forcibly kill the transport.

‎src/test/java/com/hierynomus/sshj/keepalive/BoundedKeepAliveProviderTest.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import net.schmizz.keepalive.KeepAlive;
66
import net.schmizz.sshj.DefaultConfig;
77
import net.schmizz.sshj.SSHClient;
8+
import net.schmizz.sshj.common.LoggerFactory;
89
import net.schmizz.sshj.connection.ConnectionException;
910
import net.schmizz.sshj.connection.ConnectionImpl;
1011
import net.schmizz.sshj.transport.TransportException;
@@ -19,7 +20,7 @@
1920

2021
class EventuallyFailKeepAlive extends KeepAlive {
2122
// they can survive first 2 checks, and fail at 3rd
22-
int failAfter = 2;
23+
int failAfter = 1;
2324
volatile int current = 0;
2425

2526
protected EventuallyFailKeepAlive(ConnectionImpl conn, String name) {
@@ -45,7 +46,7 @@ public class BoundedKeepAliveProviderTest {
4546
@BeforeAll
4647
static void setUpBeforeClass() throws Exception {
4748

48-
kp = new BoundedKeepAliveProvider(defaultConfig, 2) {
49+
kp = new BoundedKeepAliveProvider(LoggerFactory.DEFAULT, 2) {
4950
@Override
5051
public KeepAlive provide(ConnectionImpl connection) {
5152
return new EventuallyFailKeepAlive(connection, "test") {
@@ -67,19 +68,19 @@ void testWithConnections(int numOfConnections) throws IOException, InterruptedEx
6768
fixture.connectClient(client);
6869
}
6970
// first two checks are ok
70-
Thread.sleep(2000);
71+
Thread.sleep(1000);
7172
Assertions.assertTrue(clients.stream().allMatch(SSHClient::isConnected));
7273

73-
// wait for 3rd check to take place, we wait additional 100ms for it to finish
74-
Thread.sleep(1100);
74+
// wait for 2nd check to take place, we wait additional 200ms for it to finish
75+
Thread.sleep(1200);
7576
Assertions.assertTrue(clients.stream().noneMatch(SSHClient::isConnected));
7677
Assertions.assertEquals(0, fixture.getServer().getActiveSessions().size());
7778
}
7879

7980
@Test
8081
void testBoundedKeepAlive() throws IOException, InterruptedException {
81-
// 2 threads can handle 64 connections
82-
testWithConnections(64);
82+
// 2 threads can handle 32 connections
83+
testWithConnections(32);
8384
}
8485

8586
private List<SSHClient> setupClients(int numOfConnections) {

0 commit comments

Comments
 (0)
Failed to load comments.