Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions java/org/apache/coyote/http2/Http2Protocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ public class Http2Protocol implements UpgradeProtocol {
private boolean discardRequestsAndResponses = false;
private final SynchronizedStack<Request> recycledRequestsAndResponses = new SynchronizedStack<>();

/*
* Additional time in nanoseconds between sending the first graceful GOAWAY (max stream id) and the final GOAWAY
* (last seen stream id). During this time the server will continue to process new streams on the connection.
* This is to mitigate the race of client-buffered/sent packets for new streams and the final GOAWAY (with last
* seen stream id). By default, Tomcat uses the last computed RTT for this interval, but the RTT might have
* fluctuated due to network or server load conditions, or the client (e.g. nghttp2) might have already buffered
* frames for opening new streams on a connection.
*
* The name "drainTimeout" is taken from Envoy proxy's identical HTTP Connection Manager property and means exactly
* the same.
*/
private long drainTimeout;

@Override
public String getHttpUpgradeName(boolean isSSLEnabled) {
if (isSSLEnabled) {
Expand Down Expand Up @@ -409,6 +422,16 @@ public void setDiscardRequestsAndResponses(boolean discardRequestsAndResponses)
}


public long getDrainTimeout() {
return drainTimeout;
}


public void setDrainTimeout(long drainTimeout) {
this.drainTimeout = drainTimeout;
}


Request popRequestAndResponse() {
Request requestAndResponse = null;
if (!discardRequestsAndResponses) {
Expand Down
6 changes: 5 additions & 1 deletion java/org/apache/coyote/http2/Http2UpgradeHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
private volatile int lastNonFinalDataPayload;
private volatile int lastWindowUpdate;

// Time between the "graceful" GOAWAY (max stream id) and the final GOAWAY (last seen stream id)
private long drainTimeout = 0;

Http2UpgradeHandler(Http2Protocol protocol, Adapter adapter, Request coyoteRequest,
SocketWrapperBase<?> socketWrapper) {
Expand Down Expand Up @@ -173,6 +175,8 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH

pingManager.initiateDisabled = protocol.getInitiatePingDisabled();

drainTimeout = protocol.getDrainTimeout();

// Initial HTTP request becomes stream 1.
if (coyoteRequest != null) {
if (log.isTraceEnabled()) {
Expand Down Expand Up @@ -543,7 +547,7 @@ public void destroy() {

void checkPauseState() throws IOException {
if (connectionState.get() == ConnectionState.PAUSING) {
if (pausedNanoTime + pingManager.getRoundTripTimeNano() < System.nanoTime()) {
if (pausedNanoTime + pingManager.getRoundTripTimeNano() + drainTimeout < System.nanoTime()) {
connectionState.compareAndSet(ConnectionState.PAUSING, ConnectionState.PAUSED);
writeGoAwayFrame(maxProcessedStreamId, Http2Error.NO_ERROR.getCode(), null);
}
Expand Down