tsukuyomi-rs / izanami

[WIP] A simple Web application interface inspired from ASGI.
https://tsukuyomi-rs.github.io/izanami
Apache License 2.0
3 stars 0 forks source link

Abstraction of protocol upgrade is not object safe #3

Closed ubnt-intrepid closed 5 years ago

ubnt-intrepid commented 5 years ago

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.

ubnt-intrepid commented 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(),
            }
        }
    }
}