Skip to content

Commit

Permalink
Pass connection and read timeouts to image data socket.
Browse files Browse the repository at this point in the history
SANE uses separate connections for control and image data. Currently,
the control socket uses connection and read timeouts that can be
specified by the user in the `SaneSession.withRemoteSane` methods. The
image data socket uses no timeouts whatsoever. This can cause infinite
delays in the case of network issues.

This change passes the user-specified connection and read timeouts to
the data socket.
  • Loading branch information
sjamesr committed Jan 3, 2020
1 parent 2547f12 commit b2f15f0
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 12 deletions.
32 changes: 21 additions & 11 deletions src/main/java/au/com/southsky/jfreesane/SaneSession.java
Expand Up @@ -26,12 +26,17 @@ public final class SaneSession implements Closeable {
private final Socket socket;
private final SaneOutputStream outputStream;
private final SaneInputStream inputStream;
private final int connectionTimeoutMillis;
private final int socketTimeoutMillis;
private SanePasswordProvider passwordProvider = SanePasswordProvider.usingDotSanePassFile();

private SaneSession(Socket socket) throws IOException {
private SaneSession(Socket socket, int connectionTimeoutMillis, int socketTimeoutMillis)
throws IOException {
this.socket = socket;
this.outputStream = new SaneOutputStream(socket.getOutputStream());
this.inputStream = new SaneInputStream(this, socket.getInputStream());
this.connectionTimeoutMillis = connectionTimeoutMillis;
this.socketTimeoutMillis = socketTimeoutMillis;
}

/**
Expand Down Expand Up @@ -72,7 +77,7 @@ public static SaneSession withRemoteSane(InetAddress saneAddress) throws IOExcep
* @param timeout the timeout for connections to the SANE server, zero implies no connection
* timeout, must not be greater than {@link Integer#MAX_VALUE} milliseconds.
* @param timeUnit connection timeout unit
* @param soTimeout the timeout for reads from the SANE server
* @param soTimeout the timeout for reads from the SANE server, zero implies no read timeout
* @param soTimeUnit socket timeout unit
* @return a {@code SaneSession} that is connected to the remote SANE server
* @throws IOException if any error occurs while communicating with the SANE server
Expand Down Expand Up @@ -122,7 +127,7 @@ public static SaneSession withRemoteSane(InetAddress saneAddress, int port) thro
* @param timeout the timeout for connections to the SANE server, zero implies no connection
* timeout, must not be greater than {@link Integer#MAX_VALUE} milliseconds.
* @param timeUnit connection timeout unit
* @param soTimeout the timeout for reads from the SANE server
* @param soTimeout the timeout for reads from the SANE server, zero implies no read timeout
* @param soTimeUnit socket timeout unit
* @return a {@code SaneSession} that is connected to the remote SANE server
* @throws IOException if any error occurs while communicating with the SANE server
Expand All @@ -148,7 +153,7 @@ public static SaneSession withRemoteSane(
* @param timeout the timeout for connections to the SANE server, zero implies no connection
* timeout, must not be greater than {@link Integer#MAX_VALUE} milliseconds.
* @param timeUnit connection timeout unit
* @param soTimeout the timeout for reads from the SANE server
* @param soTimeout the timeout for reads from the SANE server, zero implies no read timeout
* @param soTimeUnit socket timeout unit
* @return a {@code SaneSession} that is connected to the remote SANE server
* @throws IOException if any error occurs while communicating with the SANE server
Expand All @@ -160,13 +165,13 @@ public static SaneSession withRemoteSane(
long soTimeout,
TimeUnit soTimeUnit)
throws IOException {
long millis = timeUnit.toMillis(timeout);
long connectTimeoutMillis = timeUnit.toMillis(timeout);
Preconditions.checkArgument(
millis >= 0 && millis <= Integer.MAX_VALUE,
connectTimeoutMillis >= 0 && connectTimeoutMillis <= Integer.MAX_VALUE,
"Timeout must be between 0 and Integer.MAX_VALUE milliseconds");
// If the user specifies a non-zero timeout that rounds to 0 milliseconds,
// set the timeout to 1 millisecond instead.
if (timeout > 0 && millis == 0) {
if (timeout > 0 && connectTimeoutMillis == 0) {
Logger.getLogger(SaneSession.class.getName())
.log(
Level.WARNING,
Expand All @@ -175,16 +180,18 @@ public static SaneSession withRemoteSane(
}
Socket socket = new Socket();
socket.setTcpNoDelay(true);
long soTimeoutMillis = 0;

if (soTimeUnit != null && soTimeout > 0) {
long soTimeoutMillis = soTimeUnit.toMillis(soTimeout);
soTimeoutMillis = soTimeUnit.toMillis(soTimeout);
Preconditions.checkArgument(
soTimeoutMillis >= 0 && soTimeoutMillis <= Integer.MAX_VALUE,
"Socket timeout must be between 0 and Integer.MAX_VALUE milliseconds");
socket.setSoTimeout((int) soTimeoutMillis);
}
socket.connect(saneSocketAddress, (int) millis);
SaneSession session = new SaneSession(socket);
socket.connect(saneSocketAddress, (int) connectTimeoutMillis);
SaneSession session =
new SaneSession(socket, (int) connectTimeoutMillis, (int) soTimeoutMillis);
session.initSane();
return session;
}
Expand Down Expand Up @@ -302,7 +309,10 @@ BufferedImage acquireImage(SaneDevice device, ScanListener listener)
outputStream.write(handle.getHandle());
outputStream.flush();

try (Socket imageSocket = new Socket(socket.getInetAddress(), port)) {
InetSocketAddress dataAddress = new InetSocketAddress(socket.getInetAddress(), port);
try (Socket imageSocket = new Socket()) {
imageSocket.setSoTimeout(socketTimeoutMillis);
imageSocket.connect(dataAddress, connectionTimeoutMillis);
int status = inputStream.readWord().integerValue();

if (status != 0) {
Expand Down
Expand Up @@ -17,6 +17,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;

Expand All @@ -41,7 +42,11 @@ public void initSession() throws Exception {
this.session =
SaneSession.withRemoteSane(
InetAddress.getByName(hostAndPort.getHost()),
hostAndPort.getPort() == -1 ? 6566 : hostAndPort.getPort());
hostAndPort.getPort() == -1 ? 6566 : hostAndPort.getPort(),
20,
TimeUnit.SECONDS,
20,
TimeUnit.SECONDS);
session.setPasswordProvider(correctPasswordProvider);
}

Expand Down

0 comments on commit b2f15f0

Please sign in to comment.