Improved handling of asynchronous failures.

This commit is contained in:
Simone Bordet 2017-04-12 12:21:01 +02:00
parent 3359db09bb
commit 2dce90c98d
2 changed files with 32 additions and 11 deletions

View File

@ -40,6 +40,7 @@ import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpInput; import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
@ -329,9 +330,17 @@ public class HttpChannelOverHTTP2 extends HttpChannel
{ {
getHttpTransport().onStreamFailure(failure); getHttpTransport().onStreamFailure(failure);
if (onEarlyEOF()) if (onEarlyEOF())
handle(); {
ContextHandler handler = getState().getContextHandler();
if (handler != null)
handler.handle(getRequest(), this);
else
handle();
}
else else
{
getState().asyncError(failure); getState().asyncError(failure);
}
} }
protected void consumeInput() protected void consumeInput()

View File

@ -268,14 +268,17 @@ public class HttpTransportOverHTTP2 implements HttpTransport
{ {
private State state = State.IDLE; private State state = State.IDLE;
private Callback callback; private Callback callback;
private Throwable failure;
private boolean commit; private boolean commit;
public boolean start(Callback callback, boolean commit) public boolean start(Callback callback, boolean commit)
{ {
State state; State state;
Throwable failure;
synchronized (this) synchronized (this)
{ {
state = this.state; state = this.state;
failure = this.failure;
if (state == State.IDLE) if (state == State.IDLE)
{ {
this.state = State.WRITING; this.state = State.WRITING;
@ -284,7 +287,9 @@ public class HttpTransportOverHTTP2 implements HttpTransport
return true; return true;
} }
} }
callback.failed(new IllegalStateException("Invalid transport state: " + state)); if (failure == null)
failure = new IllegalStateException("Invalid transport state: " + state);
callback.failed(failure);
return false; return false;
} }
@ -304,32 +309,36 @@ public class HttpTransportOverHTTP2 implements HttpTransport
} }
} }
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("HTTP2 Response #{}/{} {}", LOG.debug("HTTP2 Response #{}/{} {} {}",
stream.getId(), Integer.toHexString(stream.getSession().hashCode()), stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
commit ? "committed" : "flushed content"); commit ? "commit" : "flush",
callback == null ? "failure" : "success");
if (callback != null) if (callback != null)
callback.succeeded(); callback.succeeded();
} }
@Override @Override
public void failed(Throwable x) public void failed(Throwable failure)
{ {
boolean commit; boolean commit;
Callback callback = null; Callback callback = null;
synchronized (this) synchronized (this)
{ {
commit = this.commit; commit = this.commit;
// Only fail pending writes, as we
// may need to write an error page.
if (state == State.WRITING) if (state == State.WRITING)
{ {
this.state = State.FAILED;
callback = this.callback; callback = this.callback;
this.callback = null; this.callback = null;
this.state = State.FAILED; this.failure = failure;
} }
} }
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("HTTP2 Response #" + stream.getId() + " failed to " + (commit ? "commit" : "flush"), x); LOG.debug(String.format("HTTP2 Response #%d/%h failed to %s", stream.getId(), stream.getSession(), commit ? "commit" : "flush"), failure);
if (callback != null) if (callback != null)
callback.failed(x); callback.failed(failure);
} }
@Override @Override
@ -340,7 +349,7 @@ public class HttpTransportOverHTTP2 implements HttpTransport
{ {
callback = this.callback; callback = this.callback;
} }
return callback.getInvocationType(); return callback != null ? callback.getInvocationType() : Callback.super.getInvocationType();
} }
private boolean onIdleTimeout(Throwable failure) private boolean onIdleTimeout(Throwable failure)
@ -349,16 +358,19 @@ public class HttpTransportOverHTTP2 implements HttpTransport
Callback callback = null; Callback callback = null;
synchronized (this) synchronized (this)
{ {
// Ignore idle timeouts if not writing,
// as the application may be suspended.
result = state == State.WRITING; result = state == State.WRITING;
if (result) if (result)
{ {
this.state = State.TIMEOUT;
callback = this.callback; callback = this.callback;
this.callback = null; this.callback = null;
this.state = State.TIMEOUT; this.failure = failure;
} }
} }
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("HTTP2 Response #" + stream.getId() + " idle timeout", failure); LOG.debug(String.format("HTTP2 Response #%d/%h idle timeout", stream.getId(), stream.getSession()), failure);
if (result) if (result)
callback.failed(failure); callback.failed(failure);
return result; return result;