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

feat(http1): add support for receiving trailer fields #3637

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

hjr3
Copy link
Contributor

@hjr3 hjr3 commented Apr 17, 2024

Closes #2703

In #3375 we were strict about sending trailers per RFC 7230. In this PR, I have been much more accepting of trailers. Is this the behavior we want?

Some questions:

  • If the client does not send TE: trailers but the server sends trailer headers, should trailers be parsed and passed along?
  • If the server does not include a trailers: ... header indicating which trailer headers are being returned, should trailers be parsed and passed along? I think this one is almost certainly "yes".
  • If the server includes trailers with invalid header names, such as Content-Length: 5, should that still be parsed and passed along?

tests/server.rs Outdated Show resolved Hide resolved
src/proto/h1/decode.rs Outdated Show resolved Hide resolved
Copy link
Member

@dswij dswij left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! It looks great overall.

If the server does not include a trailers: ... header indicating which trailer headers are being returned, should trailers be parsed and passed along? I think this one is almost certainly "yes".

Yes. I think hyper should pass this along and let users decide what to do with the extra trailers.

If the server includes trailers with invalid header names, such as Content-Length: 5, should that still be parsed and passed along?

I'm not too sure about this one. I see two possible options:

  1. Pass it along but mark it as unexpected
  2. Err it

I'm interested to see what the others think about this

trace!("found possible trailers");
match decode_trailers(
&mut trailers_buf.take().expect("Trailer is None"),
// decoder enforces that trailers count will not exceed usize::MAX
Copy link
Member

Choose a reason for hiding this comment

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

where are we enforcing that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i also added a check in 545f8c4

src/proto/h1/decode.rs Outdated Show resolved Hide resolved
match byte {
b'\n' => {
if *trailers_cnt == usize::MAX as u64 {
return Poll::Ready(Err(io::Error::new(
Copy link
Member

Choose a reason for hiding this comment

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

Can we write some tests covering this as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added in 545f8c4

i also lowered the trailer limit to 1024 as i cannot imagine a use case for sending a million trailers.

Copy link
Member

Choose a reason for hiding this comment

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

Yea, limits are needed, both on number of field pairs, and bytes itself, or else we expose servers to OOM attacks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a size limit in f194be1

The trailer limit is now 1024 instead of usize::MAX. There is also a
test proving that the trailer limit is respected.
The size of the trailer fields is now limited. The limit accounts for a
single, very large trailer field or many trailer fields that exceed the
limit in aggregate.
Comment on lines 22 to 26
/// Maximum number of trailer fields allowed in a chunked body.
const TRAILERS_FIELD_LIMIT: u64 = 1024;

/// Maximum number of bytes allowed for all trailer fields.
const TRAILER_LIMIT: u64 = TRAILERS_FIELD_LIMIT * 64;
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we want to expose an option to the user for this.

Copy link
Contributor Author

@hjr3 hjr3 Apr 27, 2024

Choose a reason for hiding this comment

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

sure. when i think about the use cases for this:

  • the client sends TE: trailers and wants to limit the amount of trailers to read in the server's response
  • the client sends a chunked body with trailer fields and the server wants to limit the amount of trailers to read in the client's request

Looking at prior art:

  • We can include h1_max_trailer_size and h1_max_trailers in client::conn::http1::Builder
  • We can include h1_max_trailer_size and h1_max_trailers in server::conn::http1::Builder

Copy link
Member

Choose a reason for hiding this comment

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

Let's add this in this PR too, I think it's simple enough to include this

Copy link
Member

Choose a reason for hiding this comment

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

I've been thinking of adding a max_header_size() for the HTTP/1 builder, to mirror the HTTP/2 side. Does it make sense for that to be used by both headers and trailers? Or do people need to allow different sizes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most uses of trailer fields, that I have observed, add 1-2 headers that are relatively short. I don't think people would want more capacity than what was for max_header_size(). People may want to set a lower capacity for trailers than what max_header_size() allows, but I am unsure how common this would be.

I think we should use max_header_size() to enforce both for now. If there is a real use case that people bring up, we can always add max_trailers() and max_trailers_size() in the future.

If we want to go this route, then I can update the trailer parsing logic to check against max_headers(). In a future PR, I can:

  • Add max_header_size() to the client and server builders.
  • Update the trailer parsing logic to check against max_headers_size()
  • Update the header parsing logic src/proto/h1/role.rs to enforce max_headers_size()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added 6c01c87 to resolve this

hjr3 added 2 commits May 2, 2024 06:21
Trailer parsing now honors h1_max_headers option. It also supports a
future h1_max_header_size option.
Copy link
Member

@dswij dswij left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for the PR!

Anything you want to add? @seanmonstar

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.

Receiving HTTP/1.1 trailers
3 participants