Add "text" subprotocol, and make it the default.

Websockify previously forced you to specify, "binary" or "base64"
when constructing a WebSocket, forcing you to either handle Blobs
or start all your processing in btoa().

This patch gives a mode to websockify to pass data unprocessed without
marking it with the 0x2 opcode that makes browsers interpret websocket
frames as Blobs. This lets client code that works against other WebSocket
implementations, such as AutobahnPython, to also work unchanged.

In code, the difference is, previously you would have to do:
```
var ws = new WebSocket("ws://localhost:8080", "binary")
ws.onmessage = function(e) {
  var F = new FileReader();
  var payload = null;
  F.readAsText(e.data);
  F.onloadend = function() {
    payload = F.result;
  }
}
```

or

```
var ws = new WebSocket("ws://localhost:8080", "base64")
ws.onmessage = function(e) {
  var payload = btoa(e.data);
}
```

Now you can just do

```
var ws = new WebSocket("ws://localhost:8080")
ws.onmessage = function(e) {
  var payload = e.data;
}
```

This patch still *allows* the old way to work (in particular, when pushing
lots of data, careful Blob processing of should help performance.. I think.)
This commit is contained in:
kousu 2014-09-14 22:11:26 -04:00
parent 63209f84ca
commit f345e4e86a
1 changed files with 25 additions and 11 deletions

View File

@ -147,13 +147,16 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
@staticmethod
def encode_hybi(buf, opcode, base64=False):
""" Encode a HyBi style WebSocket frame.
Optional opcode:
Opcode, defined by the HyBi spec:
0x0 - continuation
0x1 - text frame (base64 encode buf)
0x2 - binary frame (use raw buf)
0x1 - text frame
0x2 - binary frame
0x8 - connection close
0x9 - ping
0xA - pong
If base64 is set, the buffer will be base64 encoded before being sent.
This is probably only useful in conjunction with opcode=0x1.
"""
if base64:
buf = b64encode(buf)
@ -297,10 +300,12 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
if bufs:
for buf in bufs:
if self.base64:
if self.mode == 'base64':
encbuf, lenhead, lentail = self.encode_hybi(buf, opcode=1, base64=True)
else:
elif self.mode == 'binary':
encbuf, lenhead, lentail = self.encode_hybi(buf, opcode=2, base64=False)
elif self.mode == 'text':
encbuf, lenhead, lentail = self.encode_hybi(buf, opcode=1, base64=False)
if self.rec:
self.rec.write("%s,\n" %
@ -415,12 +420,13 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
# Choose binary if client supports it
if 'binary' in protocols:
self.base64 = False
self.mode = 'binary'
elif 'base64' in protocols:
self.base64 = True
self.mode = 'base64'
elif 'text' in protocols:
self.mode = 'text'
else:
self.send_error(400, "Client must support 'binary' or 'base64' protocol")
return False
self.mode = 'text'
# Generate the hash value for the accept header
accept = b64encode(sha1(s2b(key + self.GUID)).digest())
@ -431,15 +437,23 @@ class WebSocketRequestHandler(SimpleHTTPRequestHandler):
self.send_header("Sec-WebSocket-Accept", b2s(accept))
if self.base64:
self.send_header("Sec-WebSocket-Protocol", "base64")
else:
elif self.binary:
self.send_header("Sec-WebSocket-Protocol", "binary")
# no header for "text" mode; it is the default websocket mode.
self.end_headers()
return True
else:
self.send_error(400, "Missing Sec-WebSocket-Version header. Hixie protocols not supported.")
return False
@property
def base64(self): return self.mode == 'base64'
@property
def binary(self): return self.mode == 'binary'
def handle_websocket(self):
"""Upgrade a connection to Websocket, if requested. If this succeeds,
new_websocket_client() will be called. Otherwise, False is returned.