Skip to content

Commit 611581d

Browse files
ylangiscdkocher
authored andcommitted
Review and add tests.
Signed-off-by: David Kocher <[email protected]>
1 parent a0b9fb3 commit 611581d

File tree

2 files changed

+101
-5
lines changed

2 files changed

+101
-5
lines changed

src/main/java/net/schmizz/sshj/sftp/RemoteFile.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ public class ReadAheadRemoteFileInputStream
224224
private final byte[] b = new byte[1];
225225

226226
private final int maxUnconfirmedReads;
227-
private final long maxOffset;
227+
private final long readAheadLimit;
228228
private final Queue<Promise<Response, SFTPException>> unconfirmedReads = new LinkedList<Promise<Response, SFTPException>>();
229229
private final Queue<Long> unconfirmedReadOffsets = new LinkedList<Long>();
230230

@@ -240,15 +240,15 @@ public ReadAheadRemoteFileInputStream(int maxUnconfirmedReads) {
240240
*
241241
* @param maxUnconfirmedReads Maximum number of unconfirmed requests to send
242242
* @param fileOffset Initial offset in file to read from
243-
* @param maxLength Maximum length to read
243+
* @param readAheadLimit Read ahead is disabled after this limit has been reached
244244
*/
245-
public ReadAheadRemoteFileInputStream(int maxUnconfirmedReads, long fileOffset, long maxLength) {
245+
public ReadAheadRemoteFileInputStream(int maxUnconfirmedReads, long fileOffset, long readAheadLimit) {
246246
assert 0 <= maxUnconfirmedReads;
247247
assert 0 <= fileOffset;
248248

249249
this.maxUnconfirmedReads = maxUnconfirmedReads;
250250
this.requestOffset = this.responseOffset = fileOffset;
251-
this.maxOffset = maxLength > 0 ? fileOffset + maxLength : Long.MAX_VALUE;
251+
this.readAheadLimit = readAheadLimit > 0 ? fileOffset + readAheadLimit : Long.MAX_VALUE;
252252
}
253253

254254
private ByteArrayInputStream pending = new ByteArrayInputStream(new byte[0]);
@@ -299,10 +299,16 @@ public int read(byte[] into, int off, int len) throws IOException {
299299
while (unconfirmedReads.size() <= maxUnconfirmedReads) {
300300
// Send read requests as long as there is no EOF and we have not reached the maximum parallelism
301301
int reqLen = Math.max(1024, len); // don't be shy!
302+
if (readAheadLimit > requestOffset) {
303+
long remaining = readAheadLimit - requestOffset;
304+
if (reqLen > remaining) {
305+
reqLen = (int) remaining;
306+
}
307+
}
302308
unconfirmedReads.add(RemoteFile.this.asyncRead(requestOffset, reqLen));
303309
unconfirmedReadOffsets.add(requestOffset);
304310
requestOffset += reqLen;
305-
if (requestOffset >= maxOffset) {
311+
if (requestOffset >= readAheadLimit) {
306312
break;
307313
}
308314
}

src/test/java/com/hierynomus/sshj/sftp/RemoteFileTest.java

+90
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import net.schmizz.sshj.sftp.OpenMode;
2121
import net.schmizz.sshj.sftp.RemoteFile;
2222
import net.schmizz.sshj.sftp.SFTPEngine;
23+
import net.schmizz.sshj.sftp.SFTPException;
2324
import org.junit.Rule;
2425
import org.junit.Test;
2526
import org.junit.rules.TemporaryFolder;
@@ -32,6 +33,7 @@
3233

3334
import static org.hamcrest.CoreMatchers.equalTo;
3435
import static org.hamcrest.MatcherAssert.assertThat;
36+
import static org.junit.Assert.fail;
3537

3638
public class RemoteFileTest {
3739
@Rule
@@ -84,4 +86,92 @@ public void shouldNotGoOutOfBoundsInReadAheadInputStream() throws IOException {
8486

8587
assertThat("The written and received data should match", data, equalTo(test2));
8688
}
89+
90+
@Test
91+
public void shouldNotReadAheadAfterLimitInputStream() throws IOException {
92+
SSHClient ssh = fixture.setupConnectedDefaultClient();
93+
ssh.authPassword("test", "test");
94+
SFTPEngine sftp = new SFTPEngine(ssh).init();
95+
96+
RemoteFile rf;
97+
File file = temp.newFile("SftpReadAheadLimitTest.bin");
98+
rf = sftp.open(file.getPath(), EnumSet.of(OpenMode.WRITE, OpenMode.CREAT));
99+
byte[] data = new byte[8192];
100+
new Random(53).nextBytes(data);
101+
data[3072] = 1;
102+
rf.write(0, data, 0, data.length);
103+
rf.close();
104+
105+
assertThat("The file should exist", file.exists());
106+
107+
rf = sftp.open(file.getPath());
108+
InputStream rs = rf.new ReadAheadRemoteFileInputStream(16 /*maxUnconfirmedReads*/,0, 3072);
109+
110+
byte[] test = new byte[4097];
111+
int n = 0;
112+
113+
while (n < 2048) {
114+
n += rs.read(test, n, 2048 - n);
115+
}
116+
117+
rf.close();
118+
119+
while (n < 3072) {
120+
n += rs.read(test, n, 3072 - n);
121+
}
122+
123+
assertThat("buffer overrun", test[3072] == 0);
124+
125+
try {
126+
rs.read(test, n, test.length - n);
127+
fail("Content must not be buffered");
128+
} catch (SFTPException e){
129+
// expected
130+
}
131+
}
132+
133+
@Test
134+
public void limitedReadAheadInputStream() throws IOException {
135+
SSHClient ssh = fixture.setupConnectedDefaultClient();
136+
ssh.authPassword("test", "test");
137+
SFTPEngine sftp = new SFTPEngine(ssh).init();
138+
139+
RemoteFile rf;
140+
File file = temp.newFile("SftpReadAheadLimitedTest.bin");
141+
rf = sftp.open(file.getPath(), EnumSet.of(OpenMode.WRITE, OpenMode.CREAT));
142+
byte[] data = new byte[8192];
143+
new Random(53).nextBytes(data);
144+
data[3072] = 1;
145+
rf.write(0, data, 0, data.length);
146+
rf.close();
147+
148+
assertThat("The file should exist", file.exists());
149+
150+
rf = sftp.open(file.getPath());
151+
InputStream rs = rf.new ReadAheadRemoteFileInputStream(16 /*maxUnconfirmedReads*/,0, 3072);
152+
153+
byte[] test = new byte[4097];
154+
int n = 0;
155+
156+
while (n < 2048) {
157+
n += rs.read(test, n, 2048 - n);
158+
}
159+
160+
while (n < 3072) {
161+
n += rs.read(test, n, 3072 - n);
162+
}
163+
164+
assertThat("buffer overrun", test[3072] == 0);
165+
166+
n += rs.read(test, n, test.length - n); // --> ArrayIndexOutOfBoundsException
167+
168+
byte[] test2 = new byte[data.length];
169+
System.arraycopy(test, 0, test2, 0, test.length);
170+
171+
while (n < data.length) {
172+
n += rs.read(test2, n, data.length - n);
173+
}
174+
175+
assertThat("The written and received data should match", data, equalTo(test2));
176+
}
87177
}

0 commit comments

Comments
 (0)