16 Commits

Author SHA1 Message Date
Joel Martin 264f8fdd7f Update to version 0.5.1
*** NOTE ***

This version of websockify no longer supports the old Hixie protocol.
2013-06-27 16:35:54 -05:00
Joel Martin 36cb8f4676 Move javascript websockify files to other/js 2013-06-27 16:29:17 -05:00
Joel Martin 36fcd5784f Add package file for websockify.js 2013-06-27 16:29:17 -05:00
Joel Martin 46450577c2 Use upstream einaros/ws 0.4.27 with sub-protocol support.
Also, some connection error handling.
2013-06-27 16:29:17 -05:00
Joel Martin d3ba23fa64 Update to c0855c6cae of web-socket-js
Update both the submodule and the swf build. The submodule now
contains the unobfuscated source for swfobject.js which should make
websockify more DFSG compliant.
2013-06-27 16:29:17 -05:00
Joel Martin 6d27b5d321 Merge pull request #84 from nttdatainc-openstackers/master
Fix for issue #83
2013-06-27 14:01:07 -07:00
NTT Data OpenStackers 888e75a8fb Add 2 arguments to websockify.WSRequestHandler
This patch adds 2 arguments to websockify.WSRequestHandler for security:

* file_only: returns 404 response if non-file contents are requested.
             Required to disable directory listing.

* no_parent: returns 403 response if contents out of the web root are
             requested. Required to disable directory traversal.
2013-06-24 06:09:01 -07:00
Joel Martin b7f255ce0b Clarify messages when optional modules are not found. 2013-04-19 17:14:20 -05:00
Joel Martin 477947ba96 Remove wsproxy references. Sync launch.sh from noVNC. 2013-04-15 12:22:08 -05:00
Joel Martin 33e9a21ce4 Add gimite/web-socket-js submodule for DFSG compliance. 2013-04-12 08:24:07 -05:00
Joel Martin 2d078e8cd8 Merge pull request #76 from SlapOS/include
correctly include include directory in egg.
2013-04-03 09:44:37 -07:00
Cédric de Saint Martin d6d960dbe5 correctly include include directory in egg. 2013-04-03 12:12:37 +02:00
Joel Martin 903198a724 tests: use new module path and remove Hixie code. 2013-03-14 12:24:58 -05:00
Joel Martin d3865688c8 README: no longer supporting Hixie.
NOTE: Hixie support is in version 0.4.X. If necessary, I will branch
and apply critical fixes release a new 0.4.X tagged version.
2013-03-12 13:54:11 -05:00
Joel Martin 5a726c2e4d Merge pull request #70 from astrand/master
Remove support for old Hixie protocols
2013-03-12 11:52:30 -07:00
Peter Åstrand (astrand) b2fe57c950 Remove support for old Hixie protocols.
Hybi protocols 7 and 8 are still supported, in addition to
protocol 13 -  RFC 6455.
2013-03-04 09:38:29 +01:00
18 changed files with 135 additions and 171 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "include/web-socket-js-project"]
path = include/web-socket-js-project
url = https://github.com/gimite/web-socket-js.git
+10
View File
@@ -1,6 +1,16 @@
Changes Changes
======= =======
0.5.1 - Jun 27, 2013
--------------------
* use upstream einaros/ws (>=0.4.27) with websockify.js
* file_only and no_parent security options for WSRequestHandler
* Update build of web-socket-js (c0855c6cae)
* add include/web-socket-js-project submodule to gimite/web-socket-js
for DSFG compliance.
* drop Hixie protocol support
0.4.1 - Mar 12, 2013 0.4.1 - Mar 12, 2013
-------------------- --------------------
+2 -1
View File
@@ -1 +1,2 @@
include CHANGES.txt websockify include README.md LICENSE.txt include CHANGES.txt README.md LICENSE.txt
graft include
+9 -17
View File
@@ -10,25 +10,17 @@ the target in both directions.
### WebSockets binary data ### WebSockets binary data
Websockify 0.4.X supports all versions of the WebSockets protocol Starting with websockify 0.5.0, only the HyBi / IETF
(Hixie and HyBi). Starting with websockify 0.5.0, only the HyBi / IETF
6455 WebSocket protocol is supported. 6455 WebSocket protocol is supported.
The older Hixie versions of the protocol only support UTF-8 text Websockify negotiates whether to base64 encode traffic to and from the
payloads. In order to transport binary data over UTF-8 an encoding client via the subprotocol header (Sec-WebSocket-Protocol). The valid
must used to encapsulate the data within UTF-8. With Hixie clients, subprotocol values are 'binary' and 'base64' and if the client sends
Websockify uses base64 to encode all traffic to and from the client. both then the server (the python implementation) will prefer 'binary'.
This does not affect the data between websockify and the server. The 'binary' subprotocol indicates that the data will be sent raw
using binary WebSocket frames. Some HyBi clients (such as the Flash
With HyBi clients, websockify negotiates whether to base64 encode fallback and older Chrome and iOS versions) do not support binary data
traffic to and from the client via the subprotocol header which is why the negotiation is necessary.
(Sec-WebSocket-Protocol). The valid subprotocol values are 'binary'
and 'base64' and if the client sends both then the server (the python
implementation) will prefer 'binary'. The 'binary' subprotocol
indicates that the data will be sent raw using binary WebSocket
frames. Some HyBi clients (such as the Flash fallback and older Chrome
and iOS versions) do not support binary data which is why the
negotiation is necessary.
### Encrypted WebSocket connections (wss://) ### Encrypted WebSocket connections (wss://)
+4 -1
View File
@@ -1,4 +1,4 @@
- Update setup.py and CHANGES.txt and commit - Update setup.py, CHANGES.txt and other/package.json and commit
- Create version tag and tarball from tag - Create version tag and tarball from tag
WVER=0.1.0 WVER=0.1.0
git tag v${WVER} git tag v${WVER}
@@ -8,3 +8,6 @@
python setup.py register python setup.py register
- Upload the source distribution to pypi - Upload the source distribution to pypi
python setup.py sdist upload python setup.py sdist upload
- Register with npmjs.org (once)
- Upload websockify.js npmjs.org package
npm publish other/js
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
A JavaScript implementation of the websockify WebSocket-to-TCP bridge/proxy.
Copyright (C) 2013 - Joel Martin (github.com/kanaka)
Licensed under LGPL-3.
See http://github.com/kanaka/websockify for more info.
+24
View File
@@ -0,0 +1,24 @@
{
"author": "Joel Martin <github@martintribe.org> (http://github.com/kanaka)",
"name": "websockify",
"description": "websockify is a WebSocket-to-TCP proxy/bridge",
"license": "LGPL-3",
"version": "0.5.1",
"repository": {
"type": "git",
"url": "git://github.com/kanaka/websockify.git"
},
"files": ["../../docs/LICENSE.LGPL-3"],
"bin": {
"websockify": "./websockify.js"
},
"engines": {
"node": ">=0.8.9"
},
"dependencies": {
"ws": ">=0.4.27",
"base64": "latest",
"optimist": "latest",
"policyfile": "latest"
}
}
+9 -12
View File
@@ -7,14 +7,6 @@
// Known to work with node 0.8.9 // Known to work with node 0.8.9
// Requires node modules: ws, optimist and policyfile // Requires node modules: ws, optimist and policyfile
// npm install ws optimist policyfile // npm install ws optimist policyfile
//
// NOTE:
// This version requires a patched version of einaros/ws that supports
// subprotocol negotiation. You can use the patched version like this:
//
// cd websockify/other
// git clone https://github.com/kanaka/ws
// npm link ./ws
var argv = require('optimist').argv, var argv = require('optimist').argv,
@@ -37,6 +29,7 @@ var argv = require('optimist').argv,
// Handle new WebSocket client // Handle new WebSocket client
new_client = function(client) { new_client = function(client) {
var clientAddr = client._socket.remoteAddress, log; var clientAddr = client._socket.remoteAddress, log;
console.log(client.upgradeReq.url);
log = function (msg) { log = function (msg) {
console.log(' ' + clientAddr + ': '+ msg); console.log(' ' + clientAddr + ': '+ msg);
}; };
@@ -61,6 +54,12 @@ new_client = function(client) {
}); });
target.on('end', function() { target.on('end', function() {
log('target disconnected'); log('target disconnected');
client.close();
});
target.on('error', function() {
log('target connection error');
target.end();
client.close();
}); });
client.on('message', function(msg) { client.on('message', function(msg) {
@@ -126,11 +125,9 @@ http_request = function (request, response) {
// Select 'binary' or 'base64' subprotocol, preferring 'binary' // Select 'binary' or 'base64' subprotocol, preferring 'binary'
selectProtocol = function(protocols, callback) { selectProtocol = function(protocols, callback) {
var plist = protocols ? protocols.split(',') : ""; if (protocols.indexOf('binary') >= 0) {
var plist = protocols.split(',');
if (plist.indexOf('binary') >= 0) {
callback(true, 'binary'); callback(true, 'binary');
} else if (plist.indexOf('base64') >= 0) { } else if (protocols.indexOf('base64') >= 0) {
callback(true, 'base64'); callback(true, 'base64');
} else { } else {
console.log("Client must support 'binary' or 'base64' protocol"); console.log("Client must support 'binary' or 'base64' protocol");
+14 -4
View File
@@ -16,6 +16,8 @@ usage() {
echo " Default: localhost:5900" echo " Default: localhost:5900"
echo " --cert CERT Path to combined cert/key file" echo " --cert CERT Path to combined cert/key file"
echo " Default: self.pem" echo " Default: self.pem"
echo " --web WEB Path to web files (e.g. vnc.html)"
echo " Default: ./"
exit 2 exit 2
} }
@@ -24,6 +26,7 @@ HERE="$(cd "$(dirname "$0")" && pwd)"
PORT="6080" PORT="6080"
VNC_DEST="localhost:5900" VNC_DEST="localhost:5900"
CERT="" CERT=""
WEB=""
proxy_pid="" proxy_pid=""
die() { die() {
@@ -50,6 +53,7 @@ while [ "$*" ]; do
--listen) PORT="${OPTARG}"; shift ;; --listen) PORT="${OPTARG}"; shift ;;
--vnc) VNC_DEST="${OPTARG}"; shift ;; --vnc) VNC_DEST="${OPTARG}"; shift ;;
--cert) CERT="${OPTARG}"; shift ;; --cert) CERT="${OPTARG}"; shift ;;
--web) WEB="${OPTARG}"; shift ;;
-h|--help) usage ;; -h|--help) usage ;;
-*) usage "Unknown chrooter option: ${param}" ;; -*) usage "Unknown chrooter option: ${param}" ;;
*) break ;; *) break ;;
@@ -60,18 +64,24 @@ done
which netstat >/dev/null 2>&1 \ which netstat >/dev/null 2>&1 \
|| die "Must have netstat installed" || die "Must have netstat installed"
netstat -ltn | grep -qs "${PORT}.*LISTEN" \ netstat -ltn | grep -qs "${PORT} .*LISTEN" \
&& die "Port ${PORT} in use. Try --listen PORT" && die "Port ${PORT} in use. Try --listen PORT"
trap "cleanup" TERM QUIT INT EXIT trap "cleanup" TERM QUIT INT EXIT
# Find vnc.html # Find vnc.html
if [ -e "$(pwd)/vnc.html" ]; then if [ -n "${WEB}" ]; then
if [ ! -e "${WEB}/vnc.html" ]; then
die "Could not find ${WEB}/vnc.html"
fi
elif [ -e "$(pwd)/vnc.html" ]; then
WEB=$(pwd) WEB=$(pwd)
elif [ -e "${HERE}/../vnc.html" ]; then elif [ -e "${HERE}/../vnc.html" ]; then
WEB=${HERE}/../ WEB=${HERE}/../
elif [ -e "${HERE}/vnc.html" ]; then elif [ -e "${HERE}/vnc.html" ]; then
WEB=${HERE} WEB=${HERE}
elif [ -e "${HERE}/../share/novnc/vnc.html" ]; then
WEB=${HERE}/../share/novnc/
else else
die "Could not find vnc.html" die "Could not find vnc.html"
fi fi
@@ -92,7 +102,7 @@ else
fi fi
echo "Starting webserver and WebSockets proxy on port ${PORT}" echo "Starting webserver and WebSockets proxy on port ${PORT}"
${HERE}/wsproxy.py --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} & ${HERE}/websockify --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
proxy_pid="$!" proxy_pid="$!"
sleep 1 sleep 1
if ! ps -p ${proxy_pid} >/dev/null; then if ! ps -p ${proxy_pid} >/dev/null; then
@@ -101,7 +111,7 @@ if ! ps -p ${proxy_pid} >/dev/null; then
exit 1 exit 1
fi fi
echo -e "\n\nNavigate to to this URL:\n" echo -e "\n\nNavigate to this URL:\n"
echo -e " http://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n" echo -e " http://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
echo -e "Press Ctrl-C to exit\n\n" echo -e "Press Ctrl-C to exit\n\n"
+1 -1
View File
@@ -795,7 +795,7 @@ void start_server() {
} }
handler_msg("handler exit\n"); handler_msg("handler exit\n");
} else { } else {
handler_msg("wsproxy exit\n"); handler_msg("websockify exit\n");
} }
} }
+2 -2
View File
@@ -7,8 +7,8 @@
* REBIND_PORT_NEW environment variables are set then bind on the new * REBIND_PORT_NEW environment variables are set then bind on the new
* port (of localhost) instead of the old port. * port (of localhost) instead of the old port.
* *
* This allows a proxy (such as wsproxy) to run on the old port and translate * This allows a bridge/proxy (such as websockify) to run on the old port and
* traffic to/from the new port. * translate traffic to/from the new port.
* *
* Usage: * Usage:
* LD_PRELOAD=./rebind.so \ * LD_PRELOAD=./rebind.so \
+1 -1
View File
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
version = '0.4.1' version = '0.5.1'
name = 'websockify' name = 'websockify'
long_description = open("README.md").read() + "\n" + \ long_description = open("README.md").read() + "\n" + \
open("CHANGES.txt").read() + "\n" open("CHANGES.txt").read() + "\n"
+1 -1
View File
@@ -11,7 +11,7 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
''' '''
import os, sys, select, optparse import os, sys, select, optparse
sys.path.insert(0,os.path.dirname(__file__) + "/../") sys.path.insert(0,os.path.dirname(__file__) + "/../websockify")
from websocket import WebSocketServer from websocket import WebSocketServer
class WebSocketEcho(WebSocketServer): class WebSocketEcho(WebSocketServer):
+1 -1
View File
@@ -7,7 +7,7 @@ given a sequence number. Any errors are reported and counted.
''' '''
import sys, os, select, random, time, optparse import sys, os, select, random, time, optparse
sys.path.insert(0,os.path.dirname(__file__) + "/../") sys.path.insert(0,os.path.dirname(__file__) + "/../websockify")
from websocket import WebSocketServer from websocket import WebSocketServer
class WebSocketLoad(WebSocketServer): class WebSocketLoad(WebSocketServer):
+3 -5
View File
@@ -6,17 +6,15 @@ Display UTF-8 encoding for 0-255.'''
import sys, os, socket, ssl, time, traceback import sys, os, socket, ssl, time, traceback
from select import select from select import select
sys.path.insert(0,os.path.dirname(__file__) + "/../") sys.path.insert(0,os.path.dirname(__file__) + "/../websockify")
from websocket import WebSocketServer from websocket import WebSocketServer
if __name__ == '__main__': if __name__ == '__main__':
print "val: hixie | hybi_base64 | hybi_binary" print "val: hybi_base64 | hybi_binary"
for c in range(0, 256): for c in range(0, 256):
hixie = WebSocketServer.encode_hixie(chr(c))
hybi_base64 = WebSocketServer.encode_hybi(chr(c), opcode=1, hybi_base64 = WebSocketServer.encode_hybi(chr(c), opcode=1,
base64=True) base64=True)
hybi_binary = WebSocketServer.encode_hybi(chr(c), opcode=2, hybi_binary = WebSocketServer.encode_hybi(chr(c), opcode=2,
base64=False) base64=False)
print "%d: %s | %s | %s" % (c, repr(hixie), repr(hybi_base64), print "%d: %s | %s" % (c, repr(hybi_base64), repr(hybi_binary))
repr(hybi_binary))
+43 -125
View File
@@ -6,9 +6,9 @@ Copyright 2011 Joel Martin
Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
Supports following protocol versions: Supports following protocol versions:
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 - http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07
- http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
- http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 - http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10
- http://tools.ietf.org/html/rfc6455
You can make a cert/key with openssl using: You can make a cert/key with openssl using:
openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
@@ -37,8 +37,8 @@ try: from http.server import SimpleHTTPRequestHandler
except: from SimpleHTTPServer import SimpleHTTPRequestHandler except: from SimpleHTTPServer import SimpleHTTPRequestHandler
# python 2.6 differences # python 2.6 differences
try: from hashlib import md5, sha1 try: from hashlib import sha1
except: from md5 import md5; from sha import sha as sha1 except: from sha import sha as sha1
# python 2.5 differences # python 2.5 differences
try: try:
@@ -50,15 +50,15 @@ except:
return struct.unpack(fmt, slice) return struct.unpack(fmt, slice)
# Degraded functionality if these imports are missing # Degraded functionality if these imports are missing
for mod, sup in [('numpy', 'HyBi protocol'), ('ssl', 'TLS/SSL/wss'), for mod, msg in [('numpy', 'HyBi protocol will be slower'),
('multiprocessing', 'Multi-Processing'), ('ssl', 'TLS/SSL/wss is disabled'),
('resource', 'daemonizing')]: ('multiprocessing', 'Multi-Processing is disabled'),
('resource', 'daemonizing is disabled')]:
try: try:
globals()[mod] = __import__(mod) globals()[mod] = __import__(mod)
except ImportError: except ImportError:
globals()[mod] = None globals()[mod] = None
print("WARNING: no '%s' module, %s is slower or disabled" % ( print("WARNING: no '%s' module, %s" % (mod, msg))
mod, sup))
if multiprocessing and sys.platform == 'win32': if multiprocessing and sys.platform == 'win32':
# make sockets pickle-able/inheritable # make sockets pickle-able/inheritable
import multiprocessing.reduction import multiprocessing.reduction
@@ -72,14 +72,6 @@ class WebSocketServer(object):
buffer_size = 65536 buffer_size = 65536
server_handshake_hixie = """HTTP/1.1 101 Web Socket Protocol Handshake\r
Upgrade: WebSocket\r
Connection: Upgrade\r
%sWebSocket-Origin: %s\r
%sWebSocket-Location: %s://%s%s\r
"""
server_handshake_hybi = """HTTP/1.1 101 Switching Protocols\r server_handshake_hybi = """HTTP/1.1 101 Switching Protocols\r
Upgrade: websocket\r Upgrade: websocket\r
Connection: Upgrade\r Connection: Upgrade\r
@@ -100,7 +92,7 @@ Sec-WebSocket-Accept: %s\r
def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False, def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
verbose=False, cert='', key='', ssl_only=None, verbose=False, cert='', key='', ssl_only=None,
daemon=False, record='', web='', daemon=False, record='', web='', file_only=False, no_parent=False,
run_once=False, timeout=0, idle_timeout=0): run_once=False, timeout=0, idle_timeout=0):
# settings # settings
@@ -118,6 +110,9 @@ Sec-WebSocket-Accept: %s\r
self.ws_connection = False self.ws_connection = False
self.handler_id = 1 self.handler_id = 1
self.file_only = file_only
self.no_parent = no_parent
# Make paths settings absolute # Make paths settings absolute
self.cert = os.path.abspath(cert) self.cert = os.path.abspath(cert)
self.key = self.web = self.record = '' self.key = self.web = self.record = ''
@@ -381,33 +376,6 @@ Sec-WebSocket-Accept: %s\r
return f return f
@staticmethod
def encode_hixie(buf):
return s2b("\x00" + b2s(b64encode(buf)) + "\xff"), 1, 1
@staticmethod
def decode_hixie(buf):
end = buf.find(s2b('\xff'))
return {'payload': b64decode(buf[1:end]),
'hlen': 1,
'masked': False,
'length': end - 1,
'left': len(buf) - (end + 1)}
@staticmethod
def gen_md5(keys):
""" Generate hash value for WebSockets hixie-76. """
key1 = keys['Sec-WebSocket-Key1']
key2 = keys['Sec-WebSocket-Key2']
key3 = keys['key3']
spaces1 = key1.count(" ")
spaces2 = key2.count(" ")
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
return b2s(md5(pack('>II8s',
int(num1), int(num2), key3)).digest())
# #
# WebSocketServer logging/output functions # WebSocketServer logging/output functions
@@ -444,16 +412,10 @@ Sec-WebSocket-Accept: %s\r
if bufs: if bufs:
for buf in bufs: for buf in bufs:
if self.version.startswith("hybi"): if self.base64:
if self.base64: encbuf, lenhead, lentail = self.encode_hybi(buf, opcode=1, base64=True)
encbuf, lenhead, lentail = self.encode_hybi(
buf, opcode=1, base64=True)
else:
encbuf, lenhead, lentail = self.encode_hybi(
buf, opcode=2, base64=False)
else: else:
encbuf, lenhead, lentail = self.encode_hixie(buf) encbuf, lenhead, lentail = self.encode_hybi(buf, opcode=2, base64=False)
if self.rec: if self.rec:
self.rec.write("%s,\n" % self.rec.write("%s,\n" %
@@ -498,41 +460,21 @@ Sec-WebSocket-Accept: %s\r
self.recv_part = None self.recv_part = None
while buf: while buf:
if self.version.startswith("hybi"): frame = self.decode_hybi(buf, base64=self.base64)
#print("Received buf: %s, frame: %s" % (repr(buf), frame))
frame = self.decode_hybi(buf, base64=self.base64)
#print("Received buf: %s, frame: %s" % (repr(buf), frame))
if frame['payload'] == None:
# Incomplete/partial frame
self.traffic("}.")
if frame['left'] > 0:
self.recv_part = buf[-frame['left']:]
break
else:
if frame['opcode'] == 0x8: # connection close
closed = {'code': frame['close_code'],
'reason': frame['close_reason']}
break
if frame['payload'] == None:
# Incomplete/partial frame
self.traffic("}.")
if frame['left'] > 0:
self.recv_part = buf[-frame['left']:]
break
else: else:
if buf[0:2] == s2b('\xff\x00'): if frame['opcode'] == 0x8: # connection close
closed = {'code': 1000, closed = {'code': frame['close_code'],
'reason': "Client sent orderly close frame"} 'reason': frame['close_reason']}
break break
elif buf[0:2] == s2b('\x00\xff'):
buf = buf[2:]
continue # No-op
elif buf.count(s2b('\xff')) == 0:
# Partial frame
self.traffic("}.")
self.recv_part = buf
break
frame = self.decode_hixie(buf)
self.traffic("}") self.traffic("}")
if self.rec: if self.rec:
@@ -560,17 +502,9 @@ Sec-WebSocket-Accept: %s\r
def send_close(self, code=1000, reason=''): def send_close(self, code=1000, reason=''):
""" Send a WebSocket orderly close frame. """ """ Send a WebSocket orderly close frame. """
if self.version.startswith("hybi"): msg = pack(">H%ds" % len(reason), code, reason)
msg = pack(">H%ds" % len(reason), code, reason) buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
self.client.send(buf)
buf, h, t = self.encode_hybi(msg, opcode=0x08, base64=False)
self.client.send(buf)
elif self.version == "hixie-76":
buf = s2b('\xff\x00')
self.client.send(buf)
# No orderly close for 75
def do_websocket_handshake(self, headers, path): def do_websocket_handshake(self, headers, path):
h = self.headers = headers h = self.headers = headers
@@ -612,28 +546,7 @@ Sec-WebSocket-Accept: %s\r
response += "\r\n" response += "\r\n"
else: else:
# Hixie version of the protocol (75 or 76) raise self.EClose("Missing Sec-WebSocket-Version header. Hixie protocols not supported.")
if h.get('key3'):
trailer = self.gen_md5(h)
pre = "Sec-"
self.version = "hixie-76"
else:
trailer = ""
pre = ""
self.version = "hixie-75"
# We only support base64 in Hixie era
self.base64 = True
response = self.server_handshake_hixie % (pre,
h['Origin'], pre, self.scheme, h['Host'], path)
if 'base64' in protocols:
response += "%sWebSocket-Protocol: base64\r\n" % pre
else:
self.msg("Warning: client does not report 'base64' protocol support")
response += "\r\n" + trailer
return response return response
@@ -710,7 +623,8 @@ Sec-WebSocket-Accept: %s\r
self.scheme = "ws" self.scheme = "ws"
stype = "Plain non-SSL (ws://)" stype = "Plain non-SSL (ws://)"
wsh = WSRequestHandler(retsock, address, not self.web) wsh = WSRequestHandler(retsock, address, not self.web,
self.file_only, self.no_parent)
if wsh.last_code == 101: if wsh.last_code == 101:
# Continue on to handle WebSocket upgrade # Continue on to handle WebSocket upgrade
pass pass
@@ -949,19 +863,19 @@ Sec-WebSocket-Accept: %s\r
# HTTP handler with WebSocket upgrade support # HTTP handler with WebSocket upgrade support
class WSRequestHandler(SimpleHTTPRequestHandler): class WSRequestHandler(SimpleHTTPRequestHandler):
def __init__(self, req, addr, only_upgrade=False): def __init__(self, req, addr, only_upgrade=False, file_only=False,
no_parent=False):
self.only_upgrade = only_upgrade # only allow upgrades self.only_upgrade = only_upgrade # only allow upgrades
self.webroot = os.path.realpath(".")
self.file_only = file_only
self.no_parent = no_parent
SimpleHTTPRequestHandler.__init__(self, req, addr, object()) SimpleHTTPRequestHandler.__init__(self, req, addr, object())
def do_GET(self): def do_GET(self):
abspath = os.path.realpath("." + (self.path.split('?')[0]))
if (self.headers.get('upgrade') and if (self.headers.get('upgrade') and
self.headers.get('upgrade').lower() == 'websocket'): self.headers.get('upgrade').lower() == 'websocket'):
if (self.headers.get('sec-websocket-key1') or
self.headers.get('websocket-key1')):
# For Hixie-76 read out the key hash
self.headers.__setitem__('key3', self.rfile.read(8))
# Just indicate that an WebSocket upgrade is needed # Just indicate that an WebSocket upgrade is needed
self.last_code = 101 self.last_code = 101
self.last_message = "101 Switching Protocols" self.last_message = "101 Switching Protocols"
@@ -969,6 +883,10 @@ class WSRequestHandler(SimpleHTTPRequestHandler):
# Normal web request responses are disabled # Normal web request responses are disabled
self.last_code = 405 self.last_code = 405
self.last_message = "405 Method Not Allowed" self.last_message = "405 Method Not Allowed"
elif self.file_only and not os.path.isfile(abspath):
self.send_response(404, "No such file")
elif self.no_parent and not abspath.startswith(self.webroot):
self.send_response(403, "Hidden resources")
else: else:
SimpleHTTPRequestHandler.do_GET(self) SimpleHTTPRequestHandler.do_GET(self)