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

Server writes a response with HTTP/2 even after receiving a RST_STREAM frame #4629

Closed
jrhee17 opened this issue Jan 16, 2023 · 1 comment
Closed
Labels
Milestone

Comments

@jrhee17
Copy link
Contributor

jrhee17 commented Jan 16, 2023

Problem statement

When client-side requests time out, a RST_STREAM is sent and the HTTP/2 stream is closed.
However, server-side may not close the response immediately, which may cause responses to still be written.
This is a violation of the HTTP/2 specification.

The RST_STREAM frame fully terminates the referenced stream and causes it to enter the "closed" state. After receiving a RST_STREAM on a stream, the receiver MUST NOT send additional frames for that stream, with the exception of PRIORITY

https://httpwg.org/specs/rfc7540.html#RST_STREAM

As a result the following logs are printed in client-side.

io.netty.handler.codec.http2.Http2Exception$StreamException: Received HEADERS frame for an unknown stream 3
io.netty.handler.codec.http2.Http2Exception$StreamException: Received DATA frame for an unknown stream 3

Analysis

Running the following test reproduces the issue:

void clientTimeout() throws InterruptedException {

  1. A client-side responseTimeout closes the stream and sends a RST_STREAM

Afterwards, the stream is closed and the response is removed

final HttpResponseWrapper res = removeResponse(streamIdToId(stream.id()));

  1. Server-side receives a RST_STREAM

req.abortResponse(cause, /* cancel */ true);

and calls close on the ctx and response sequentially.

isResponseAborted = true;
// Make sure to invoke the ServiceRequestContext.whenRequestCancelling() and whenRequestCancelled()
// by cancelling a request
if (cancel && ctx != null) {
ctx.cancel(cause);
}
if (response != null && !response.isComplete()) {
response.abort(cause);
}

However, FramedGrpcService registers callbacks on ctx.close

ctx.whenRequestCancelling().handle((cancellationCause, unused) -> {
Status status = Status.CANCELLED.withCause(cancellationCause);
if (cancellationCause instanceof RequestTimeoutException) {
status = status.withDescription("Request timed out");
}
call.close(status, new Metadata());
return null;
});

and writes a response before ctx.close is invoked

@jrhee17 jrhee17 added the defect label Jan 16, 2023
@jrhee17 jrhee17 added this to the 1.22.0 milestone Jan 16, 2023
@jrhee17 jrhee17 changed the title Server writes http/2 after receiving a RST_STREAM frame Server writes a response with HTTP/2 even after receiving a RST_STREAM frame Jan 16, 2023
@ikhoon ikhoon modified the milestones: 1.22.0, 1.23.0 Feb 5, 2023
@ikhoon ikhoon modified the milestones: 1.23.0, 1.24.0 Mar 15, 2023
@jrhee17
Copy link
Contributor Author

jrhee17 commented Apr 18, 2023

I believe this was handled by @trustin in #4789 🙇

@jrhee17 jrhee17 closed this as completed Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants