LocalPortForwarder interrupts its thread on close()

This commit is contained in:
Jeroen van Erp
2016-12-29 16:06:57 +01:00
parent 516abb0282
commit 20879a4aa5
2 changed files with 70 additions and 3 deletions

View File

@@ -107,6 +107,7 @@ public class LocalPortForwarder {
private final Connection conn;
private final Parameters parameters;
private final ServerSocket serverSocket;
private Thread runningThread;
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket, LoggerFactory loggerFactory) {
this.conn = conn;
@@ -132,10 +133,20 @@ public class LocalPortForwarder {
*
* @throws IOException
*/
public void listen()
throws IOException {
public void listen() throws IOException {
listen(Thread.currentThread());
}
/**
* Start listening for incoming connections and forward to remote host as a channel and ensure that the thread is registered.
* This is useful if for instance {@link #close() is called from another thread}
*
* @throws IOException
*/
public void listen(Thread runningThread) throws IOException {
this.runningThread = runningThread;
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
while (!Thread.currentThread().isInterrupted()) {
while (!runningThread.isInterrupted()) {
try {
final Socket socket = serverSocket.accept();
log.debug("Got connection from {}", socket.getRemoteSocketAddress());
@@ -162,6 +173,7 @@ public class LocalPortForwarder {
if (!serverSocket.isClosed()) {
log.info("Closing listener on {}", serverSocket.getLocalSocketAddress());
serverSocket.close();
runningThread.interrupt();
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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 com.hierynomus.sshj.connection.channel.direct
import com.hierynomus.sshj.test.SshFixture
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder
import org.junit.Rule
import spock.lang.Specification
class LocalPortForwarderSpec extends Specification {
@Rule
SshFixture tunnelFixture = new SshFixture()
@Rule
SshFixture realServer = new SshFixture()
def "should not hang when disconnect tunnel"() {
given:
def client = tunnelFixture.setupConnectedDefaultClient()
client.authPassword("test", "test")
def socket = new ServerSocket(0)
def lpf = client.newLocalPortForwarder(new LocalPortForwarder.Parameters("localhost", socket.getLocalPort(), "localhost", realServer.server.port), socket)
def thread = new Thread(new Runnable() {
@Override
void run() {
lpf.listen()
}
})
when:
thread.start()
then:
thread.isAlive()
when:
lpf.close()
then:
socket.isClosed()
}
}