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

grpc-web: suppress content-length header from grpc-web requests #2946

Merged
merged 2 commits into from
Apr 1, 2018
Merged

Conversation

negator
Copy link
Contributor

@negator negator commented Mar 30, 2018

grpc-web: suppress content-length header from web requests

Description:
When using envoy in a tiered deployment, with a grpc-web filter on the downstream envoy, requests to upstream envoys fail over http2 when a content-length header is present, since the underlying nghttp2 library strictly adheres to the http2 spec. This header is usually passed up from browsers, but reflects the total http1.1 payload size, not the total DATA frame sizes.

Risk Level: Low

Testing:
A simple unit test was added to verify that content-length headers are suppressed.

Release Notes:
N/A

Fixes #2942

@negator negator changed the title Suppress Content-Length header for grpc-web requests grpc-web: suppress content-length filter from grpc-web requests Mar 30, 2018
Copy link
Member

@lizan lizan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, modulo style fix.

Please run tools/check_format.py fix.

@alyssawilk
Copy link
Contributor

Curious, can you explain why this is a grpc specific issue and not an H2 issue in general? I just want to make sure we're not doing too narrow of a fix.
@mattklein123 who may know offhand :-P

@negator
Copy link
Contributor Author

negator commented Mar 30, 2018

You’re right, it’s not a grpc specific issue. It could occur anytime an incorrect content-length header is passed to a secondary envoy backend over h2. However this fix is necessary regardless of any other general fix, since browsers (currently) know nothing about h2 data framing and the content-length header is almost always going to be incorrect.

@alyssawilk
Copy link
Contributor

Removing the content length header may be necessary but it'd be great if we can find one place to do it for all of HTTP/2 rather than have each filter do their own clearing.

Offhand I think the best place for this is in the HTTP/2 codec, but I'd like to wait for Matt to weigh in.

@negator
Copy link
Contributor Author

negator commented Mar 30, 2018

I don’t think removing it in all cases is the right thing to do, since it could lead to surprising behavior for clients that expect it to be there. But I do think we’d want to perform validation in the h2 codec, or elsewhere, prior to invoking an h2 backend. But I’d defer to you.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the general topic of this change, this problem is actually not specific to h2. It's really specific to all HTTP versions and any filter which fundamentally alters body during processing. I.e., the same issue would occur over HTTP/1.1.

I also don't think removing in all cases is the right thing to do. It really comes down to filters that do this type of modification have to do one of two things:

  1. Strip content length and switch to chunk encoding. Envoy essentially does this transparently. In the HTTP/1.1 codec if no content-length header is seen, it will start chunk encoding. HTTP/2 is inherently chunk encoding at the binary level and content-length is really a hint.
  2. Write a new content-length that will match the final body size.

We could consider somehow checking/warning/enforcing this at the Envoy level so at least you would know at the local level if you were doing something wrong...

@@ -46,6 +46,7 @@ Http::FilterHeadersStatus GrpcWebFilter::decodeHeaders(Http::HeaderMap& headers,
}
is_grpc_web_request_ = true;

headers.removeContentLength();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment as to why this is being removed.

Copy link

@sayrer sayrer Mar 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to chunked transfer encoding is what nginx does in this situation, fwiw.

#2 always runs into the the problem of buffering the entire body to calculate the length, unless you can emit it as a trailer. Maybe feasible for h2, but not 1.1.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sayrer in grpc-web context the upstream must be http2, there is no chunked encoding, hence removing content length is enough.

@alyssawilk
Copy link
Contributor

Ah see I misunderstood the problem - I thought upstream was choking on content length being present not because the gRPC stack was changing the payload and not updating the header. In that case yeah, it should be a fix to the filter and we can add codec-level validation later on.

@lizan
Copy link
Member

lizan commented Mar 30, 2018

We could consider somehow checking/warning/enforcing this at the Envoy level so at least you would know at the local level if you were doing something wrong...

or at nghttp2 level, since it does enforce (RST_STREAM with PROTOCOL_ERROR) this for incoming request, so it can raise at least a warning for outgoing request too.

negator added 2 commits March 30, 2018 15:21
Ensure the content-length header field is not passed through from web requests, since upstream `grpc/http2` services may reject it if the value does not conform to spec: https://http2.github.io/http2-spec/#malformed.

Namely the value must equal the sum of the DATA frames sizes in bytes.

Signed-off-by: Naveen Gattu <[email protected]>
Signed-off-by: Naveen Gattu <[email protected]>
@negator negator changed the title grpc-web: suppress content-length filter from grpc-web requests grpc-web: suppress content-length header from grpc-web requests Apr 1, 2018
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@mattklein123 mattklein123 merged commit cd4a3df into envoyproxy:master Apr 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants