Closed ubnt-intrepid closed 5 years ago
An easy solution is to make polling the upgraded I/O rather than retuing a Future
.
trait Upgrade {
type Upgraded: AsyncRead + AsyncWrite;
type Error;
fn poll_upgrade(&mut self) -> Poll<Self::Upgraded, Self::Error>;
/// Converts itself into a `Future` that will be resolved as an `Upgraded`.
fn on_upgrade(self) -> OnUpgrade<Self>
where
Self: Sized,
{
OnUpgrade(self)
}
}
struct OnUpgrade<T>(T);
impl<T> Future for OnUpgrade<T>
where
T: Upgrade,
{
type Item = T::Upgrade;
type Error = T::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0.poll_upgrade()
}
}
Since hyper::Body
cannot directly implement this trait, it is required to prepare the wrapper type with the internal state that monitors whether it is upgrading or not as follows:
struct Body(Inner);
impl<T: Into<hyper::Body>> From<T> for Body {
fn from(body: T) -> Self {
Body(Inner::Raw(body.into()))
}
}
enum Inner {
Raw(hyper::Body),
OnUpgrade(hyper::upgrade::OnUpgrade),
}
impl Upgrade for Body {
type Upgraded = hyper::upgrade::Upgraded;
type Error = hyper::Error;
fn poll_upgrade(&mut self) -> Poll<Self::Upgraded, Self::Error> {
loop {
self.state = match &mut self.state {
Body::Raw(body) => Inner::OnUpgrade(std::mem::replace(body, Body::new())),
Body::OnUpgrade(on_upgrade) => on_upgrade.poll(),
}
}
}
}
It is convenient to be able to store the instance of request bodies as an object, in order not to mention the actual type of the request body in the framework. To achieve this, the trait
OnUpgrade
should be object safe.