Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix StreamBufferingEncoder GOAWAY bug #11144

Merged
merged 13 commits into from Apr 19, 2021
Expand Up @@ -69,33 +69,45 @@ public Http2ChannelClosedException() {
}
}

private static final class GoAwayDetail {
private final int lastStreamId;
private final long errorCode;
private final byte[] debugData;

GoAwayDetail(int lastStreamId, long errorCode, byte[] debugData) {
this.lastStreamId = lastStreamId;
this.errorCode = errorCode;
this.debugData = debugData.clone();
}
}

/**
* Thrown by {@link StreamBufferingEncoder} if buffered streams are terminated due to
* receipt of a {@code GOAWAY}.
*/
public static final class Http2GoAwayException extends Http2Exception {
private static final long serialVersionUID = 1326785622777291198L;
private final int lastStreamId;
private final long errorCode;
private final byte[] debugData;
private GoAwayDetail goAwayDetail;
normanmaurer marked this conversation as resolved.
Show resolved Hide resolved

public Http2GoAwayException(int lastStreamId, long errorCode, byte[] debugData) {
this(new GoAwayDetail(lastStreamId, errorCode, debugData));
}

private Http2GoAwayException(GoAwayDetail goAwayDetail) {
ejona86 marked this conversation as resolved.
Show resolved Hide resolved
super(Http2Error.STREAM_CLOSED);
this.lastStreamId = lastStreamId;
this.errorCode = errorCode;
this.debugData = debugData;
this.goAwayDetail = goAwayDetail;
}

public int lastStreamId() {
return lastStreamId;
return goAwayDetail.lastStreamId;
}

public long errorCode() {
return errorCode;
return goAwayDetail.errorCode;
}

public byte[] debugData() {
return debugData;
return goAwayDetail.debugData;
ejona86 marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -106,9 +118,7 @@ public byte[] debugData() {
private final TreeMap<Integer, PendingStream> pendingStreams = new TreeMap<Integer, PendingStream>();
private int maxConcurrentStreams;
private boolean closed;
private Integer goAwayLastStreamId;
private long goAwayErrorCode;
private ByteBuf goAwayDebugData;
private GoAwayDetail goAwayDetail;

public StreamBufferingEncoder(Http2ConnectionEncoder delegate) {
this(delegate, SMALLEST_MAX_CONCURRENT_STREAMS);
Expand All @@ -121,10 +131,9 @@ public StreamBufferingEncoder(Http2ConnectionEncoder delegate, int initialMaxCon

@Override
public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) {
goAwayLastStreamId = lastStreamId;
goAwayErrorCode = errorCode;
goAwayDebugData = debugData;
cancelGoAwayStreams();
goAwayDetail = new GoAwayDetail(
lastStreamId, errorCode, ByteBufUtil.getBytes(debugData));
normanmaurer marked this conversation as resolved.
Show resolved Hide resolved
cancelGoAwayStreams(goAwayDetail);
}

@Override
Expand Down Expand Up @@ -159,9 +168,9 @@ public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2
return super.writeHeaders(ctx, streamId, headers, streamDependency, weight,
exclusive, padding, endOfStream, promise);
}
if (goAwayLastStreamId != null) {
if (goAwayDetail != null) {
promise.setFailure(new Http2GoAwayException(
goAwayLastStreamId, goAwayErrorCode, ByteBufUtil.getBytes(goAwayDebugData)));
goAwayDetail.lastStreamId, goAwayDetail.lastStreamId, goAwayDetail.debugData));
ejona86 marked this conversation as resolved.
Show resolved Hide resolved
return promise;
normanmaurer marked this conversation as resolved.
Show resolved Hide resolved
}
PendingStream pendingStream = pendingStreams.get(streamId);
Expand Down Expand Up @@ -255,13 +264,12 @@ private void tryCreatePendingStreams() {
}
}

private void cancelGoAwayStreams() {
private void cancelGoAwayStreams(GoAwayDetail goAwayDetail) {
Iterator<PendingStream> iter = pendingStreams.values().iterator();
Exception e = new Http2GoAwayException(
goAwayLastStreamId, goAwayErrorCode, ByteBufUtil.getBytes(goAwayDebugData));
Exception e = new Http2GoAwayException(goAwayDetail);
while (iter.hasNext()) {
PendingStream stream = iter.next();
if (stream.streamId > goAwayLastStreamId) {
if (stream.streamId > goAwayDetail.lastStreamId) {
iter.remove();
stream.close(e);
}
Expand Down