1818
1919package io .undertow .conduits ;
2020
21- import java .io .IOException ;
22- import java .nio .ByteBuffer ;
23- import java .nio .channels .FileChannel ;
24- import java .util .concurrent .TimeUnit ;
25-
2621import io .undertow .UndertowLogger ;
2722import io .undertow .UndertowMessages ;
2823import io .undertow .UndertowOptions ;
4136import org .xnio .conduits .ReadReadyHandler ;
4237import org .xnio .conduits .StreamSourceConduit ;
4338
39+ import java .io .IOException ;
40+ import java .nio .ByteBuffer ;
41+ import java .nio .channels .FileChannel ;
42+ import java .util .concurrent .TimeUnit ;
43+
4444/**
4545 * Wrapper for read timeout. This should always be the first wrapper applied to the underlying channel.
4646 *
4949 */
5050public final class ReadTimeoutStreamSourceConduit extends AbstractStreamSourceConduit <StreamSourceConduit > {
5151
52- private XnioExecutor .Key handle ;
52+ private volatile XnioExecutor .Key handle ;
5353 private final StreamConnection connection ;
5454 private volatile long expireTime = -1 ;
5555 private final OpenListener openListener ;
@@ -60,14 +60,21 @@ public final class ReadTimeoutStreamSourceConduit extends AbstractStreamSourceCo
6060 private final Runnable timeoutCommand = new Runnable () {
6161 @ Override
6262 public void run () {
63- handle = null ;
64- if (expireTime == -1 ) {
63+ synchronized (ReadTimeoutStreamSourceConduit .this ) {
64+ handle = null ;
65+ }
66+ if (expireTime == -1 || !connection .isOpen ()) {
6567 return ;
6668 }
6769 long current = System .currentTimeMillis ();
6870 if (current < expireTime ) {
6971 //timeout has been bumped, re-schedule
70- handle = WorkerUtils .executeAfter (connection .getIoThread (),timeoutCommand , (expireTime - current ) + FUZZ_FACTOR , TimeUnit .MILLISECONDS );
72+ if (handle == null ) {
73+ synchronized (ReadTimeoutStreamSourceConduit .this ) {
74+ if (handle == null )
75+ handle = WorkerUtils .executeAfter (connection .getIoThread (), timeoutCommand , (expireTime - current ) + FUZZ_FACTOR , TimeUnit .MILLISECONDS );
76+ }
77+ }
7178 return ;
7279 }
7380 UndertowLogger .REQUEST_LOGGER .tracef ("Timing out channel %s due to inactivity" , connection .getSourceChannel ());
@@ -131,12 +138,16 @@ private void handleReadTimeout(final long ret) throws IOException {
131138 final long expireTimeVar = expireTime ;
132139 if (expireTimeVar != -1 && currentTime > expireTimeVar ) {
133140 IoUtils .safeClose (connection );
134- throw UndertowMessages .MESSAGES .readTimedOut (this .getTimeout ());
141+ throw UndertowMessages .MESSAGES .readTimedOut (currentTime - ( expireTimeVar - this .getTimeout () ));
135142 }
136143 }
137144 expireTime = currentTime + timeout ;
138145 if (handle == null ) {
139- handle = connection .getIoThread ().executeAfter (timeoutCommand , timeout , TimeUnit .MILLISECONDS );
146+ synchronized (this ) {
147+ if (handle == null )
148+ handle = connection .getIoThread ().executeAfter (timeoutCommand , timeout , TimeUnit .MILLISECONDS );
149+ }
150+
140151 }
141152 }
142153
@@ -232,9 +243,13 @@ public void terminateReads() throws IOException {
232243
233244 private void cleanup () {
234245 if (handle != null ) {
235- handle .remove ();
236- handle = null ;
237- expireTime = -1 ;
246+ synchronized (this ) {
247+ if (handle != null ) {
248+ handle .remove ();
249+ handle = null ;
250+ expireTime = -1 ;
251+ }
252+ }
238253 }
239254 }
240255
@@ -247,7 +262,7 @@ public void suspendReads() {
247262 private void checkExpired () throws ReadTimeoutException {
248263 synchronized (this ) {
249264 if (expired ) {
250- throw UndertowMessages .MESSAGES .readTimedOut (System .currentTimeMillis ());
265+ throw UndertowMessages .MESSAGES .readTimedOut (System .currentTimeMillis () - ( expireTime - getTimeout ()) );
251266 }
252267 }
253268 }
0 commit comments