GH-8786: Make FtpSession.finalizeRaw() robust

Fixes https://github.com/spring-projects/spring-integration/issues/8786

If `FtpSession.readRaw()` fails, the next `FtpSession.finalizeRaw()` call
would lead to `FTPClient.completePendingCommand()` failure since
there is no command to finish.

* Fix `FtpSession.finalizeRaw()` to exit earlier positively in case of
`FTPReply.isNegativePermanent()` for the current reply code set by the failure
from a previous `FtpSession.readRaw()`

**Cherry-pick to `6.1.x`, `6.0.x` & `5.5.x`**
This commit is contained in:
Artem Bilan
2023-11-03 11:10:06 -04:00
committed by Christian Tzolov
parent 264b21eda7
commit d95bc681dd
2 changed files with 21 additions and 1 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -111,6 +111,10 @@ public class FtpSession implements Session<FTPFile> {
if (!this.readingRaw.compareAndSet(true, false)) {
throw new IOException("Raw read is not in process");
}
if (FTPReply.isNegativePermanent(this.client.getReplyCode())) {
// The 'readRaw()' has failed - nothing to complete.
return true;
}
if (this.client.completePendingCommand()) {
int replyCode = this.client.getReplyCode();
if (LOGGER.isDebugEnabled()) {

View File

@@ -758,6 +758,22 @@ public class FtpServerOutboundTests extends FtpTestSupport {
this.config.latch = null;
}
@Test
void finalizeRawIsOkEvenIfReadRawIsNot() throws IOException {
Session<FTPFile> session = this.sessionFactory.getSession();
IOException expectedException = null;
try (InputStream stream = session.readRaw("no_such_file")) {
stream.read(); // Just to avoid empty 'try' block
}
catch (IOException ex) {
expectedException = ex;
}
finally {
assertThat(session.finalizeRaw()).isTrue();
}
assertThat(expectedException).hasMessage("Failed to obtain InputStream for remote file no_such_file: 550");
}
private void resetSessionCache() {
((CachingSessionFactory<?>) this.sessionFactory).resetCache();
}