From 9a03f4d7453f69cf6e05d9037170ef27d528e2ae Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 30 Apr 2021 15:14:40 +1000 Subject: [PATCH] Fix #6227 Async timeout dispatch race Only allow the thread calling onTimeout to call dispatch and complete once timeout has expired. Signed-off-by: Greg Wilkins --- .../jetty/server/HttpChannelState.java | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java index d323049e9910..e96b2e97987c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java @@ -155,6 +155,7 @@ public enum Action private boolean _asyncWritePossible; private long _timeoutMs = DEFAULT_TIMEOUT; private AsyncContextEvent _event; + private Thread _onTimeoutThread; protected HttpChannelState(HttpChannel channel) { @@ -582,7 +583,10 @@ public void dispatch(ServletContext context, String path) switch (_requestState) { case ASYNC: + break; case EXPIRING: + if (Thread.currentThread() != _onTimeoutThread) + throw new IllegalStateException(this.getStatusStringLocked()); break; default: throw new IllegalStateException(this.getStatusStringLocked()); @@ -646,36 +650,47 @@ protected void onTimeout() throw new IllegalStateException(toStringLocked()); event = _event; listeners = _asyncListeners; + _onTimeoutThread = Thread.currentThread(); } - if (listeners != null) + try { - Runnable task = new Runnable() + if (listeners != null) { - @Override - public void run() + Runnable task = new Runnable() { - for (AsyncListener listener : listeners) + @Override + public void run() { - try - { - listener.onTimeout(event); - } - catch (Throwable x) + for (AsyncListener listener : listeners) { - LOG.warn("{} while invoking onTimeout listener {}", x, listener, x); + try + { + listener.onTimeout(event); + } + catch (Throwable x) + { + LOG.warn("{} while invoking onTimeout listener {}", x, listener, x); + } } } - } - @Override - public String toString() - { - return "onTimeout"; - } - }; + @Override + public String toString() + { + return "onTimeout"; + } + }; - runInContext(event, task); + runInContext(event, task); + } + } + finally + { + synchronized (this) + { + _onTimeoutThread = null; + } } } @@ -692,6 +707,11 @@ public void complete() switch (_requestState) { case EXPIRING: + if (Thread.currentThread() != _onTimeoutThread) + throw new IllegalStateException(this.getStatusStringLocked()); + _requestState = _sendError ? RequestState.BLOCKING : RequestState.COMPLETE; + break; + case ASYNC: _requestState = _sendError ? RequestState.BLOCKING : RequestState.COMPLETE; break;