GH-3827: Fix RemoteFile GET STREAM session leak

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

The `AbstractRemoteFileOutboundGateway.doGet()` for a `STREAM` option
does not close the `session` in case of error.
This may lead to some leaks or exhausted caches

* Close `session` in the `catch()` of the `AbstractRemoteFileOutboundGateway.doGet()`
* Adjust the `SftpServerOutboundTests` to configure a `CachingSessionFactory`
for the `testStream()` to verify there is no leaks attempting to
`GET STREAM` non-existing remote file twice

**Cherry-pick to `5.5.x`**
This commit is contained in:
Artem Bilan
2022-07-18 12:38:15 -04:00
committed by Gary Russell
parent d9a42c9e14
commit 8f44870837
3 changed files with 22 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
@@ -673,6 +673,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
.setHeader(IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE, session);
}
catch (IOException e) {
session.close();
throw new MessageHandlingException(requestMessage,
"Error handling message in the [" + this
+ "]. Failed to get the remote file [" + remoteFilePath + "] as a stream", e);

View File

@@ -191,7 +191,13 @@
auto-create-directory="true"
remote-file-separator="/" />
<int-sftp:outbound-gateway session-factory="sftpSessionFactory"
<bean id="cachingSessionFactory" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg name="sessionFactory" ref="sftpSessionFactory"/>
<constructor-arg name="sessionCacheSize" value="1"/>
<property name="sessionWaitTimeout" value="100"/>
</bean>
<int-sftp:outbound-gateway session-factory="cachingSessionFactory"
request-channel="inboundGetStream"
command="get"
command-options="-stream"

View File

@@ -70,6 +70,7 @@ import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
@@ -273,7 +274,7 @@ public class SftpServerOutboundTests extends SftpTestSupport {
@Test
@SuppressWarnings("unchecked")
void testLSRecursive() throws IOException {
void testLSRecursive() {
String dir = "sftpSource/";
this.inboundLSRecursive.send(new GenericMessage<Object>(dir));
Message<?> result = this.output.receive(1000);
@@ -291,7 +292,7 @@ public class SftpServerOutboundTests extends SftpTestSupport {
@Test
@SuppressWarnings("unchecked")
void testLSRecursiveALL() throws IOException {
void testLSRecursiveALL() {
String dir = "sftpSource/";
this.inboundLSRecursiveALL.send(new GenericMessage<Object>(dir));
Message<?> result = this.output.receive(1000);
@@ -481,7 +482,7 @@ public class SftpServerOutboundTests extends SftpTestSupport {
while (output.receive(0) != null) {
// drain
}
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
this.inboundMPut.send(new GenericMessage<>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertThat(out).isNotNull();
@@ -655,6 +656,15 @@ public class SftpServerOutboundTests extends SftpTestSupport {
.containsEntry(FileHeaders.REMOTE_DIRECTORY, "sftpSource/")
.containsEntry(FileHeaders.REMOTE_FILE, " sftpSource1.txt");
verify(session).close();
assertThatExceptionOfType(MessageHandlingException.class)
.isThrownBy(() -> this.inboundGetStream.send(new GenericMessage<>(dir + "doesNotExist.txt")))
.withStackTraceContaining("No such file or directory");
// No leak for not closed session after the previous failure
assertThatExceptionOfType(MessageHandlingException.class)
.isThrownBy(() -> this.inboundGetStream.send(new GenericMessage<>(dir + "doesNotExist.txt")))
.withStackTraceContaining("No such file or directory");
}
@Test