Closed njsmith closed 5 years ago
This will be supported by Firefox 65, to be released by end of January 2019: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/65#Networking https://bugzilla.mozilla.org/show_bug.cgi?id=1434137
Looks to be in chrome, but behind a flag https://www.chromestatus.com/feature/6251293127475200 https://bugs.chromium.org/p/chromium/issues/detail?id=801564
I think to support this the handshake part needs to be separable from the websocket part, as only the handshake differs between HTTP/1.1 and HTTP/2. Then the WSConnection
class can gain a http_version
argument and either carry out a h11 or h2 handshake on the connection.
I think we should make the websocket part separate as well in order to deal with a shared h2 connection - with h11 the connection is taken over by the websocket, whereas with h2 only a stream is. In this case the handshake parsing is likely done by the h2 parser with only the stream data thereafter passing to wsproto. I propose calling the websocket part Connection
.
I propose then the following classes exist,
class H11Handshake:
def __init__(self, client): ...
def send(self, event): ...
def receive_data(self, data): ...
def events(self): ...
@property
def connection(self): ... # Returns a Connection when established
class H2Handshake:
# Same interface as H11Handshake
class Connection:
def __init__(self, client, extensions): ...
def send(self, event): ...
def receive_data(self, data): ...
def events(self): ...
Then the WSConnection
is repurposed to tie this together on the assumption that the connection will be purely for websocket data,
class WSConnection:
def __init__(self, client, http_version): ... # Will either use the H11 or H2 handshake
def send(self, event): ...
def receive_data(self, data): ...
def events(self): ...
This should work without any additional changes for the default h11 case, but allow h2 cases as well.
@njsmith @Kriechi @mehaase What do you think?
Yes - this sounds like what if've been trying to talk about in https://github.com/python-hyper/wsproto/issues/27 already.
In mitmproxy we would only every use your new WSConnection, because our proxy stack already does all the handshake.
Just to make sure we are on the same page: The first byte ever received or sent by WSConnection is a WebSocket frame. WSConnection never sees, hears, or speaks anything other than WebSocket frames.
For WSConnection.__init__
we could either pass in the HTTP handshake, or "plain" arguments, meaning the user has to parse & prepare the values beforehand (if the user is not using our provided H11Handshake / H2Handshake helper tools)
@Kriechi I've confused you a little. In my proposal WSConnection
is unchanged. Instead the Connection
class is what you describe, in that it only talks WebSocket frames, and nothing else.
I think all that is required for the Connection.__init__
is to know if it is a client or server and to know the agreed upon extensions - I think these are the plain arguments you are referring too.
There is an initial implementation here
There is a proposed solution in #102, however #102 is a solution only if you wish to use wsproto to manage the entire connection, yet this somewhat defeats the purpose of using HTTP/2 (no multiplexing). The current master allows for HTTP/2 websockets as it makes the extension handshake available separately, however you have to manage the connection yourself.
I think #102 is probably the best idea with documentation explaining how to make use of the extension handshake part. Yet I'm unsure what the best API wsproto should provide is.
Thinking with my mitmproxy hat again, we want to handle the h2 connection ourself, and only pass off a single stream (and its events) for a possible WS connection.
I think #109 will allow this to be closed as complete.
RFC 8441 was published a few months ago. It defines a standard way to tunnel a websocket through an HTTP/2 connection.
I think the main relevance for wsproto is that it uses a different handshake – instead of a
GET
withUpgrade:
and a funky sha1-based handshake, it's aCONNECT
with some special headers, and removes a bunch of the funky stuff: