Skip to content

Server Protocol Upgrades #1323

Closed
Closed
@seanmonstar

Description

@seanmonstar

Add support for servers to receive requests that wish to upgrade to a different protocol, such as websockets. A proposed API follows:

Proposal

  • Response
    • pub fn upgrade(proto: header::Upgrade) -> (Response, server::Upgrade)

      This sets the StatusCode::SwitchingProtocols, and the Upgrade header provided. It returns the Response, in case other headers need to be added, and a server::Upgrade type, explained next.

  • server::Upgrade
    • impl Future for server::Upgrade
    • This type is a Future that resolves to the underlying IO object that is being upgraded.

Usage

// inside a `Service`
fn call(&self, req: Request) -> Self::Future {
    if wants_ws_upgrade(&req) {
        let (res, upgrade) = Response::upgrade(Upgrade::ws());
        self.ws_queue.send(res);
        future::ok(res)
    } else {
        self.handle_http(req)
    } 
}

A theoretical websocket server would also exist, and a channel, the ws_queue, would be used to give the socket to the websocket server.

Implementation

The server::Upgrade could wrap a futures::sync::oneshot::Receiver. The purpose for wrapping one is to hide the implementation details.

When upgrading protocols, the response is only supposed to send headers. A blank newline after a header block with a 101 response code is supposed to be for the already upgraded protocol. That means sending a body with an upgrade response is an error.

If it is possible with some Into impls to allow Response::upgrade to return a Response<()>, that would probably be ideal. I imagine it might not be doable, in which case, it will need to be enforced at runtime. This will still work, as the server::Upgrade can be sent a hyper::Error noting that a body was illegal.

It would be nice if the type could be server::Upgrade<T>, where T is the specific IO type being used internally. However, that might not be possible either, as the Service may not have a way to know what type is being used (it might be possible for a user to have encoded the type in their new_service, and thus Service, and be able to statically guarantee the type, though!). If not, then the return type will need to be a Box<AsyncRead + AsyncWrite>.

Any buffered bytes internally associated with the socket should be returned as well, as a Chunk.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-serverArea: server.C-featureCategory: feature. This is adding a new feature.E-mediumEffort: medium. Some knowledge of how hyper internal works would be useful.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions