19 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
Joel Martin db6a7e3e28 Update to version 0.4.1
*** IMPORTANT NOTE ***

0.4.X is the last minor version to support the Hixie protocol. 0.5.0
will drop the Hixie protocol support. The latest released version of
all major browsers (and web-socket-js) support the IETF 6455 protocol.

- Other changes: add 'include/' sub-dir to source distribution and to
  installed packaged.
2013-03-12 13:46:45 -05:00
Joel Martin d9aedfe7d3 Update to version 0.4.0
*** IMPORTANT NOTE ***

This is the last minor version to support the Hixie protocol. I may do
bug fixes (e.g. 0.4.1) but 0.5.0 will drop the Hixie protocol support.
The latest released version of all major browsers (and web-socket-js)
support the IETF 6455 protocol.

- Other changes: updating TODO and release process notes.
2013-03-12 13:10:53 -05: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
Joel Martin 805026360e Use Buffer base64 support instead of pkrumins/node-base64 2013-02-19 13:12:10 -06:00
20 changed files with 173 additions and 186 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
+23
View File
@@ -1,6 +1,29 @@
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
--------------------
* ***NOTE*** : 0.5.0 will drop Hixie protocol support
* add include/ directory and remove some dev files from source
distribution.
0.4.0 - Mar 12, 2013
--------------------
* ***NOTE*** : 0.5.0 will drop Hixie protocol support
* use Buffer base64 support in Node.js implementation
0.3.0 - Jan 15, 2013 0.3.0 - Jan 15, 2013
-------------------- --------------------
+2 -1
View File
@@ -1 +1,2 @@
include CHANGES.txt *.py README.md LICENSE.txt include CHANGES.txt README.md LICENSE.txt
graft include
+10 -17
View File
@@ -10,24 +10,17 @@ the target in both directions.
### WebSockets binary data ### WebSockets binary data
Websockify supports all versions of the WebSockets protocol (Hixie and Starting with websockify 0.5.0, only the HyBi / IETF
HyBi). The older Hixie versions of the protocol only support UTF-8 6455 WebSocket protocol is supported.
text payloads. In order to transport binary data over UTF-8 an
encoding must used to encapsulate the data within UTF-8.
With Hixie clients, Websockify uses base64 to encode all traffic to Websockify negotiates whether to base64 encode traffic to and from the
and from the client. This does not affect the data between websockify client via the subprotocol header (Sec-WebSocket-Protocol). The valid
and the server. subprotocol values are 'binary' and 'base64' and if the client sends
both then the server (the python implementation) will prefer 'binary'.
With HyBi clients, websockify negotiates whether to base64 encode The 'binary' subprotocol indicates that the data will be sent raw
traffic to and from the client via the subprotocol header using binary WebSocket frames. Some HyBi clients (such as the Flash
(Sec-WebSocket-Protocol). The valid subprotocol values are 'binary' fallback and older Chrome and iOS versions) do not support binary data
and 'base64' and if the client sends both then the server (the python which is why the negotiation is necessary.
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://)
+3 -6
View File
@@ -1,9 +1,6 @@
- Go implementation - Go implementation
- Rust implementation
- Support multiple targets that are selected by the path line. Some - Add sub-protocol support to upstream einaros/ws module and use that
sort of wildcarding of ports too. instead of the patched module.
- Support SSL targets too.
- wstelnet: support CSI L and CSI M - wstelnet: support CSI L and CSI M
+9 -2
View File
@@ -13,5 +13,12 @@ browser.
Building web-socket-js emulator: Building web-socket-js emulator:
cd include/web-socket-js/flash-src cd include/web-socket-js/flash-src
mxmlc -static-link-runtime-shared-libraries WebSocketMain.as mxmlc -static-link-runtime-shared-libraries WebSocketMain.as
Building release tarball:
- not really necessary since tagged revision can be downloaded
from github as tarballs
git archive --format=tar --prefix=websockify-${WVER}/ v${WVER} > websockify-${WVER}.tar
gzip websockify-${WVER}.tar
+4 -3
View File
@@ -1,12 +1,13 @@
- 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}
git push origin master git push origin master
git push origin v${WVER} git push origin v${WVER}
git archive --format=tar --prefix=websockify-${WVER}/ v${WVER} > websockify-${WVER}.tar
gzip websockify-${WVER}.tar
- Register with pypi.python.org (once): - Register with pypi.python.org (once):
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"
}
}
+13 -17
View File
@@ -5,16 +5,8 @@
// Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) // Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
// Known to work with node 0.8.9 // Known to work with node 0.8.9
// Requires node modules: ws, base64, optimist and policyfile // Requires node modules: ws, optimist and policyfile
// npm install ws base64 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,
@@ -26,7 +18,6 @@ var argv = require('optimist').argv,
fs = require('fs'), fs = require('fs'),
policyfile = require('policyfile'), policyfile = require('policyfile'),
base64 = require('base64/build/Release/base64'),
Buffer = require('buffer').Buffer, Buffer = require('buffer').Buffer,
WebSocketServer = require('ws').Server, WebSocketServer = require('ws').Server,
@@ -38,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);
}; };
@@ -51,7 +43,7 @@ new_client = function(client) {
//log("sending message: " + data); //log("sending message: " + data);
try { try {
if (client.protocol === 'base64') { if (client.protocol === 'base64') {
client.send(base64.encode(new Buffer(data))); client.send(new Buffer(data).toString('base64'));
} else { } else {
client.send(data,{binary: true}); client.send(data,{binary: true});
} }
@@ -62,12 +54,18 @@ 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) {
//log('got message: ' + msg); //log('got message: ' + msg);
if (client.protocol === 'base64') { if (client.protocol === 'base64') {
target.write(base64.decode(msg),'binary'); target.write(new Buffer(msg, 'base64'));
} else { } else {
target.write(msg,'binary'); target.write(msg,'binary');
} }
@@ -127,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 \
+9 -1
View File
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
version = '0.3.0' 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"
@@ -12,6 +12,14 @@ setup(name=name,
classifiers=[ classifiers=[
"Programming Language :: Python", "Programming Language :: Python",
], ],
data_files=[('share/websockify/include',
['include/util.js',
'include/base64.js',
'include/websock.js']),
('share/websockify/include/web-socket-js',
['include/web-socket-js/WebSocketMain.swf',
'include/web-socket-js/swfobject.js',
'include/web-socket-js/web_socket.js'])],
keywords='noVNC websockify', keywords='noVNC websockify',
license='LGPLv3', license='LGPLv3',
url="https://github.com/kanaka/websockify", url="https://github.com/kanaka/websockify",
+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)