This is an automated email from the ASF dual-hosted git repository.

twolf pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit a85462b49cc76c06b565e8d311864808995a3ac9
Author: Thomas Wolf <tw...@apache.org>
AuthorDate: Thu Mar 14 23:58:50 2024 +0100

    Improve javadoc on ClientChannel.setIn(InputStream)
    
    Closing that input stream when the channel closes is in my view a design
    mistake. The stream is obtained by the application code from somewhere,
    and it should be the application's responsibility to close it when
    appropriate. While changing the implementation (not closing the stream
    in AbstractClientChannel.getInnerCloseable()) would work, it appears to
    be too risky a change for a minor version bump.
    
    So just explain in the javadoc what to do if the stream should _not_ be
    closed. (For instance, System.in should never be closed and doing so may
    even cause hangs on Windows.)
    
    Also add a (somewhat contrived) test that chains two remote shells by
    setting the output of one shell as the input of another shell.
---
 .../apache/sshd/client/channel/ClientChannel.java  |  3 +-
 .../sshd/server/channel/ChannelSessionTest.java    | 55 ++++++++++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/channel/ClientChannel.java 
b/sshd-core/src/main/java/org/apache/sshd/client/channel/ClientChannel.java
index aad26a385..d1f809b05 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/channel/ClientChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/channel/ClientChannel.java
@@ -139,7 +139,8 @@ public interface ClientChannel extends Channel, 
StreamingChannel, ClientSessionH
      * {@link #getInvertedIn()} method instead and write data directly.
      * <p>
      * The stream must be set before the channel is opened. When the channel 
closes, it will {@link InputStream#close()
-     * close} the given stream.
+     * close} the given stream. If the stream should <em>not</em> be closed 
(for instance, if it is {@link System#in
+     * System.in}), wrap it in an {@link 
org.apache.sshd.common.util.io.input.NoCloseInputStream NoCloseInputStream}.
      * </p>
      *
      * @param in an {@link InputStream} to be polled and forwarded
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
index 0562fb623..538de04ff 100644
--- 
a/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
+++ 
b/sshd-core/src/test/java/org/apache/sshd/server/channel/ChannelSessionTest.java
@@ -111,6 +111,61 @@ public class ChannelSessionTest extends BaseTestSupport {
         }
     }
 
+    private void chainedCommands(ClientSession session) throws Exception {
+        try (ClientChannel channel = 
session.createChannel(Channel.CHANNEL_SHELL)) {
+            channel.open().verify(OPEN_TIMEOUT);
+            try (ClientChannel second = 
session.createChannel(Channel.CHANNEL_SHELL)) {
+                // Chain stdout of the first command to stdin of the second.
+                second.setIn(channel.getInvertedOut());
+                second.open().verify(OPEN_TIMEOUT);
+
+                // Write to the first command
+                OutputStream invertedIn = channel.getInvertedIn();
+                String cmdSent = "echo foo\nexit\n";
+                invertedIn.write(cmdSent.getBytes());
+                invertedIn.flush();
+
+                long waitStart = System.currentTimeMillis();
+                Collection<ClientChannelEvent> result = 
second.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 10_000);
+                long waitEnd = System.currentTimeMillis();
+                assertTrue("Wrong channel state after " + (waitEnd - 
waitStart) + " ms.: " + result,
+                        
result.containsAll(EnumSet.of(ClientChannelEvent.CLOSED)));
+                // Read from the second command's stdout and check the result.
+                try (InputStream invertedOut = second.getInvertedOut()) {
+                    byte[] b = new byte[1024];
+                    int l = invertedOut.read(b);
+                    String cmdReceived = (l > 0) ? new String(b, 0, l) : "";
+                    assertEquals("Mismatched echoed command", cmdSent, 
cmdReceived);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void pipedInputStream() throws Exception {
+        try (SshServer server = setupTestServer();
+             SshClient client = setupTestClient()) {
+
+            server.setShellFactory(session -> new CommandExecutionHelper(null) 
{
+                @Override
+                protected boolean handleCommandLine(String command) throws 
Exception {
+                    OutputStream out = getOutputStream();
+                    out.write((command + 
"\n").getBytes(StandardCharsets.UTF_8));
+                    return !"exit".equals(command);
+                }
+            });
+            server.start();
+            client.start();
+
+            try (ClientSession session = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, server.getPort())
+                    .verify(CONNECT_TIMEOUT).getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(AUTH_TIMEOUT);
+                chainedCommands(session);
+            }
+        }
+    }
+
     @Test
     public void testNoFlush() throws Exception {
         try (SshServer server = setupTestServer();

Reply via email to