-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
A proper Body
trait
#1438
Comments
I think I like the idea of merging the data poll method and trailers poll method into one that essentially acts like a |
Good idea about I really like being able to just call one method like |
Why can't you deprecate the variants? |
You can add a deprecated attribute, but you can't ever stop passing the data in that variant and instead pass a new one, or you break code at runtime. |
It seems like there will always need to be a variant for body data and one for trailers though, right? |
That's correct, and they probably wouldn't ever need to change But, the enum might need to grow new generic parameters, which could also be problematic. For instance, say we start with this: #[non_exhaustive]
enum Frame<B> {
Data(B),
Trailers(HeaderMap),
} But then want to add a push promise variant. I don't yet now what the quite looks like. Should it be the #[non_exhaustive]
enum Frame<B, PPF> {
Data(B),
Trailers(HeaderMap),
PushPromise(Request<NoBody>, PPF),
} Adding that generic would be a breaking change. So then, do we not allow passing a future? Or do we require boxing it? Or maybe we require a specific I'm not sure. And, if we later decide that the current design sucks, we're completely stuck with it. If we deprecate the variant and try a |
But wouldn't adding a type parameter break back compat anyway by adding a new associated type? Thinking more about it, should content_length even be a method on Body? It can always just be set explicitly in the headers, right? |
So, I think it might be possible to add those without breaking, thanks to defaults allowed on associated types.
That's what I meant when asked if pulled its weight. The reason I added it was because I wanted to add the ability to hyper to be able to determine length vs chunked automatically. It's especially helpful for HTTP/1.1 clients, since many servers arbitrarily decided that don't love a client sending a chunked body, and so it'd be less surprising for users that did Even in cases where servers don't have client chunked encoding, some endpoints want to validate a content length before accepting a body that is too big... |
This dedicated `Entity` trait replaces the previous `Stream<Item=impl AsRef<[u8]>, Error=hyper::Error>`. This allows for several improvements immediately, and prepares for HTTP2 support. - The `Entity::is_end_stream` makes up for change away from `Option<Body>`, which was previously used to know if the body should be empty. Since `Request` and `Response` now require a body to be set, this method can be used to tell hyper that the body is actually empty. It also provides the possibility of slight optimizations when polling for data, by allowing to check `is_end_stream` before polling again. This can allow a consumer to know that a body stream has ended without polling for `None` afterwards. - The `Entity::content_length` method allows a body to automatically declare a size, in case a user doesn't set a `Content-Length` or `Transfer-Encoding` header. - It's now possible to send and receive trailers, though this will be for HTTP2 connections only. By being a trait owned by hyper, new methods can be added later as new features are wanted (with default implementations). The `hyper::Body` type now implements `Entity` instead of `Stream`, provides a better channel option, and is easier to use with custom streams via `Body::wrap_stream`. BREAKING CHANGE: All code that was assuming the body was a `Stream` must be adjusted to use an `Entity` instead. Using `hyper::Body` as a `Stream` can call `Body::into_stream` to get a stream wrapper. Passing a custom `impl Stream` will need to either implement `Entity`, or as an easier option, switch to `Body::wrap_stream`. `Body::pair` has been replaced with `Body::channel`, which returns a `hyper::body::Sender` instead of a `futures::sync::mpsc::Sender`. Closes #1438
I settled on the trait name being |
This dedicated `Entity` trait replaces the previous `Stream<Item=impl AsRef<[u8]>, Error=hyper::Error>`. This allows for several improvements immediately, and prepares for HTTP2 support. - The `Entity::is_end_stream` makes up for change away from `Option<Body>`, which was previously used to know if the body should be empty. Since `Request` and `Response` now require a body to be set, this method can be used to tell hyper that the body is actually empty. It also provides the possibility of slight optimizations when polling for data, by allowing to check `is_end_stream` before polling again. This can allow a consumer to know that a body stream has ended without polling for `None` afterwards. - The `Entity::content_length` method allows a body to automatically declare a size, in case a user doesn't set a `Content-Length` or `Transfer-Encoding` header. - It's now possible to send and receive trailers, though this will be for HTTP2 connections only. By being a trait owned by hyper, new methods can be added later as new features are wanted (with default implementations). The `hyper::Body` type now implements `Entity` instead of `Stream`, provides a better channel option, and is easier to use with custom streams via `Body::wrap_stream`. BREAKING CHANGE: All code that was assuming the body was a `Stream` must be adjusted to use an `Entity` instead. Using `hyper::Body` as a `Stream` can call `Body::into_stream` to get a stream wrapper. Passing a custom `impl Stream` will need to either implement `Entity`, or as an easier option, switch to `Body::wrap_stream`. `Body::pair` has been replaced with `Body::channel`, which returns a `hyper::body::Sender` instead of a `futures::sync::mpsc::Sender`. Closes #1438
Currently (v0.11), hyper allows using any
impl Stream<Item=impl AsRef<[u8]>, Error=hyper::Error>
as the send body for requests and response. That should likely be changed, since it currently prevents using new features from HTTP2. While we can upgrade the current receive stream to have inherent methods to do new things, there is no way for hyper to use them with send streams. Here's some new features we'd like to support:To accomplish this, hyper probably needs to define it's own trait, so that new features can be added later.
The
Body
traitSome questions about the design that I still have:
Does the signature of
poll_trailers()
look like it is a stream of trailers? It's supposed to be like a future, only yielding once, not multipleHeaderMap
s. It's probably fine, sinceIterator::next
returnsOption<T>
, and that doesn't mean that anything else returningOption<T>
is iterator-like. Documentation should be enough.Does
content_length
pull its weight?Should there a trait method
into_stream
? Being there does mean less imports are needed, but does it conflict withBody
trait objects? It could just beIntoStream::new(body)
...Does
on_error
make sense to have? Additionally, should it also be used to allow sending a reset error on a receive stream? For instance, if you've received the default body in aRequest
in the server, and after polling a little while, some other part of your app is on fire, should you be able to callbody.on_error(User::internal_error())
and hyper should try to send that reset error? Currently, all you can do is drop the body, and hyper will send aCANCEL
reset.An alternative to separate
poll_*
methods is to use a single method, and return a non-exhaustive enum of the frames that a user can receive. This might reduce complication around knowing what order to call methods in, especially as new events are added, like push promises.Then, when hyper adds push promise support, a user just needs to update their match block to handle them. If they are added in a new method, it might be confusing when to call
poll_data
vspoll_push_promise
, etc.The default body type
Since the trait name is
Body
, the v0.11hyper::Body
needs a new name. So far, I haven't thought of any that I feel great about. In Java, you often find interfaces or abstract classes with the good name, andDefaultFoo
as the implementation. 🤷♂️Some things I'd like to add to the (naming, bleck)
DefaultBodySender
are:The ability to send new frames as hyper gains support for them, like push promises.
The ability to make closing the stream an explicit action. Right now, with an mpsc channel, if the sender is in another thread and that that thread panics, the sender is dropped, and the receiver just assumes the stream is finished. hyper understands that as "all good, end the writing". However, this can be bad! If the stream had a
Content-Length
, the remote will realize the body was interrupted. But if it didn't, the remote will just assume that the ended stream is the full response. This can result in the remote treating partial content as the full content.I'd like for the default sender to have an option to mark that being dropped without having called
close()
should signal an error has occurred. This could either be a boolean flag you enable on the sender, or it could be a a new method that returns a guard type to be used in places where panics may happen.The text was updated successfully, but these errors were encountered: