snapview / tungstenite-rs

Lightweight stream-based WebSocket implementation for Rust.
Apache License 2.0
1.93k stars 221 forks source link

Allow access to the response headers #6

Closed nox closed 7 years ago

nox commented 7 years ago

For the Web, we need to be able to access the headers of the response to the initial HTTP request.

nox commented 7 years ago

I guess one can access them by implementing HandshakeRole themselves, but that doesn't seem like the most straightforward thing to do.

agalakhov commented 7 years ago

It's also planned and the way to do it is known. Most likely it will be done together with permessage-deflate as permessage-deflate depends on the request headers. There is TODO comment in the code.

nox commented 7 years ago

@agalakhov hyper's author @seanmonstar expects the Headers type from hyper to one day live in its own crate. Would you be willing to use that to represent headers in requests and answers in tungstenite? It would help Servo, and I think it would be nice in any case given it's the exact same concept as in hyper.

agalakhov commented 7 years ago

Why not? everything that is properly designed would fit. Tungstenite is flexible enough to use that. BUt I would prefer single-pass zero-copy approach if possible.

nox commented 7 years ago

Most likely it will be done together with permessage-deflate as permessage-deflate depends on the request headers.

My use case is access to the Set-Cookie headers in the response.

ffablia commented 7 years ago

I think this request may be more general. We need to be able to control each connection over their entire lifetime. Some examples for my use case (server side):

ws-rs provides a nice solution with Traits Handler a Factory allowing library users to implement their own logic.

PS: i am looking for building a mio-tungstenite for replacing my modified ws-rs

bluetech commented 7 years ago

I have some opinions on this :)

I think this library (the pure websocket Read/Write library) should not deal with HTTP at all. It should just deal with abstract ClientHandshake and ServerHandshake structs, which contain the relevant fields corresponding to the headers described in the RFC (key, extensions, protocol, origin, accept, version etc.).

Dealing with HTTP is better done by an HTTP library (client or server). This library should provide helpers and guidance for dealing with the handshake (checking version, choosing subprotocol, choosing extensions, checking origin, etc.), but only on an abstract level, not actually parsing the HTTP headers or writing 101 Switching Protocols to the socket and so on. Once the handshake is done, the user hijacks the socket from the HTTP library and passes it to us.

I am only saying this because the WebSocket type is already nicely isolated from stuff like mio, tokio, etc. It'd be nice to apply the same principles to the handshake part as well (let the user control the library, not the other way around).

nox commented 7 years ago

@bluetech This is more or less what I intend to do in Servo, especially because WebSocket as used on the Web is a more restricted version of what the RFC is capable of.

agalakhov commented 7 years ago

@ffablia Reaction to pong could be done in a more elegant way than making a callback. We can add more items to the enum Message. Such an approach does not add artificial restrictions on the message handler. Should I add these enum variants?

In order to get the handshake information, one could just return it together

@bluetech Correct. Point me at the matching handshake library I could use and I'll use it. My definition of "matching" is: the library must be capable of parsing the information as it comes without making any assumption about the underlying stream. The stream is just Read + Write and may return WouldBlock. The ideal API here is somthing like the one of native_tls. I'm very picky about the performance here since on the server it's the place that may be DDoS-ed and thus it should be able to handle higher loads that the rest of the code.

Please note that, for the client, the RFC 6455 specifies URI format as well. Thus Tungstenite is in charge to parse them.

Alternatively, we can split the handshake portion of Tungstenite into a separate crate and transform it into a generic handshake engine. It is theoretically capable of that.

bluetech commented 7 years ago

I don't know of any library which already does that; it would be great if this library would do it :)

Here is a very rough draft of my general idea of an API for this: https://gist.github.com/bluetech/466415d496d261614a6f47dcfa91d6dc (not valid Rust)

The idea is to help as much as possible with the handshake stuff, but not take over completely, so that the library user still has full control to do whatever they want. In particular, they can add headers, remove headers, validate headers, modify headers, reject requests/responses, and so on.

agalakhov commented 7 years ago

Thank you. It's quite similar to my idea and also similar to Tungstenite internals. Tungstenite just does not expose the public API now since I haven't found an elegant way to do that. For a client, just sending extra data together with the URL will do:

fn connect(url, extra: Option<ExtraHeaders>) -> ...;

For a server, there's a need for a callback or even to split handshake in two stages to allow the caller for inserting own code between them:

fn accept(stream) -> Result<Request, ...>;
impl Request {
    fn response(self, extra: Option<ExtraHeaders>) -> Result<WebSocket, ...>;
}