Merge pull request #464 from TerraNibble/gh-427
GH-427: Read initial ACK on channel open prior to direct stream upload & close streams prior to exit code handling
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java b/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java
index 7a794f4..d9223d4 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/client/DefaultScpClient.java
@@ -112,13 +112,17 @@
String cmd = ScpClient.createSendCommand(remote, options);
ClientSession session = getClientSession();
ChannelExec channel = openCommandChannel(session, cmd);
- try (InputStream invOut = channel.getInvertedOut();
- OutputStream invIn = channel.getInvertedIn()) {
- // NOTE: we use a mock file system since we expect no invocations for it
- ScpHelper helper = new ScpHelper(session, invOut, invIn, new MockFileSystem(remote), opener, listener);
- Path mockPath = new MockPath(remote);
- helper.sendStream(new DefaultScpStreamResolver(name, mockPath, perms, time, size, local, cmd),
- options.contains(Option.PreserveAttributes), ScpHelper.DEFAULT_SEND_BUFFER_SIZE);
+ try {
+ try (InputStream invOut = channel.getInvertedOut();
+ OutputStream invIn = channel.getInvertedIn()) {
+ // NOTE: we use a mock file system since we expect no invocations for it
+ ScpHelper helper = new ScpHelper(session, invOut, invIn, new MockFileSystem(remote), opener, listener);
+ Path mockPath = new MockPath(remote);
+ DefaultScpStreamResolver resolver = new DefaultScpStreamResolver(name, mockPath, perms, time, size, local, cmd);
+ helper.readAndValidateOperationAck(cmd, resolver);
+ helper.sendStream(resolver, options.contains(Option.PreserveAttributes),
+ ScpHelper.DEFAULT_SEND_BUFFER_SIZE);
+ }
handleCommandExitStatus(cmd, channel);
} finally {
channel.close(false);
diff --git a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java
index f6f37b9..bff83ff 100644
--- a/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java
+++ b/sshd-scp/src/main/java/org/apache/sshd/scp/common/ScpHelper.java
@@ -411,12 +411,8 @@
}
public void send(Collection<String> paths, boolean recursive, boolean preserve, int bufferSize) throws IOException {
- ScpAckInfo ackInfo = readAck(false);
boolean debugEnabled = log.isDebugEnabled();
- if (debugEnabled) {
- log.debug("send({}) ACK={}", paths, ackInfo);
- }
- validateOperationReadyCode("send", "Paths", ackInfo);
+ readAndValidateOperationAck("send", "Paths");
LinkOption[] options = IoUtils.getLinkOptions(true);
for (String pattern : paths) {
@@ -464,11 +460,7 @@
public void sendPaths(Collection<? extends Path> paths, boolean recursive, boolean preserve, int bufferSize)
throws IOException {
- ScpAckInfo ackInfo = readAck(false);
- if (log.isDebugEnabled()) {
- log.debug("sendPaths({}) ACK={}", paths, ackInfo);
- }
- validateOperationReadyCode("sendPaths", "Paths", ackInfo);
+ readAndValidateOperationAck("sendPaths", "Paths");
LinkOption[] options = IoUtils.getLinkOptions(true);
for (Path file : paths) {
@@ -750,6 +742,15 @@
return ScpAckInfo.readAck(in, csIn, canEof);
}
+ public void readAndValidateOperationAck(String cmd, Object location) throws IOException {
+ ScpAckInfo ackInfo = readAck(false);
+ boolean debugEnabled = log.isDebugEnabled();
+ if (debugEnabled) {
+ log.debug("readAndValidateOperationAck({}) ACK={}", location, ackInfo);
+ }
+ validateOperationReadyCode(cmd, location, ackInfo);
+ }
+
@Override
public String toString() {
return getClass().getSimpleName() + "[" + getSession() + "]";