TypeError: exceptions must be old-style classes or derived from
BaseException, not str
Thus, we are not allowed to raise a string. Raise Exception instead.
refactoring. Basically, we are dividing WebSocketServer into two
classes: One request handler following the SocketServer Requesthandler
API, and one optional server engine. The standard Python SocketServer
engine can also be used.
websocketproxy.py has been updated to match the API change. I've also
added a new option --libserver in order to use the Python built in
server instead.
I've done a lot of testing with the new code. This includes: verbose,
daemon, run-once, timeout, idle-timeout, ssl, web, libserver. I've
tested both Python 2 and 3. I've also tested websocket.py in another
external service.
Code details follows:
* The new request handler class is called WebSocketRequestHandler,
inheriting SimpleHTTPRequestHandler.
* The service engine is called WebSocketServer, just like before.
* do_websocket_handshake: Using send_header() etc, instead of manually
sending HTTP response.
* A new method called handle_websocket() upgrades the connection to
WebSocket, if requested. Otherwise, it returns False. A typical
application use is:
def do_GET(self):
if not self.handle_websocket():
# handle normal requests
* new_client has been renamed to new_websocket_client, in order to
have a better name in the SocketServer/HTTPServer request handler
hierarchy.
* Note that in the request handler, configuration variables must be
provided by the "server" object, ie self.server.target_host.
Move around functions and methods, so that connection-related and
server-related stuff are close together.
This patch just moves things around - it does not change anything at
all. This can be verified with:
git diff websocket.py | grep ^- | cut -c 2- | sort > removed
git diff websocket.py | grep ^+ | cut -c 2- | sort > added
diff -u removed added
If WebSocketServer is used as a library with run_once or timeout, then
cleanup the socket listener socket so that when start_server returns
(due to run_once or timeout) then port is freed up.
Should at least mostly address this issue:
https://github.com/kanaka/websockify/issues/63
The problem is that the target in the test case often immediately
closed the target after sending the data. In this case the last data
received from the target is never sent to the client because the
client is not in the list of sockets being selected against (because
it is only added if their is pending data). By moving the client
conditionals first, we give the client socket a chance to be sent data
before the target is detected as close and we terminate the loop.
Fix recording so that it records the actual payload bytes sent to the
client. This means that if the client and server and using base64
encoding then the captured data will still be base64 encoded. However,
data received from the client is unmasked when recorded. Note that
this is not done efficiently; when recording, client data is unmasked
twice (once for sending on to the target and once for recording). This
could be made more efficient but that would require a refactor of how
frame reception and unmasking works and recording is not considered
a performance sensitive mode.
Old users of the websockify library used websockify.WebSocketProxy
The refactor into a module unneccessarily broke this import. The
current imports in __init__.py don't actually do anything so this
patch changes the import to import everything so that the old import
still works. This appears to be the original intention of the existing
import statements.
Python ignores SIGPIPE on startup, because it prefers to check every
write and raise an IOError exception rather than taking the signal. Most
Unix subprocesses don't expect to work this way. This patch (adapted
from Colin Watson's post at http://tinyurl.com/2a7mzh5) sets SIGPIPE
back to the default action.
Make websockify subdirectory and move websocket.py ->
websockify/websocket.py and websockify ->
websockify/websocketproxy.py. Create a ./run script that launches
websockify as before (unfortunately can't have a websockify script at
the same level since this is now a directory). Make websockify.py
a symlink to ./run. Once the package is installed, the main launch
script will be /usr/bin/websockify.
This makes it easier to package up websockify as a python module.
setup.py should now properly install websockify as a module.
Note that to include the base websocket module/class you will now do:
import websockify.websocket
#OR
from websockify.websocket import WebSocketServer
To import the full websocket proxy functionality:
import websockify.websocketproxy
#OR
from websockify.websocket import WebSocketProxy
This will also help with startup speed slightly because the code in
websocketproxy will now be byte compiled since it is no longer in the
main invocation script.
Config file syntax is now like this:
----------------------
# Comments and blank lines are allow
token1: host1:port1
token2: host2:port2
----------------------
websocket.py has no concept of target/proxy so any target processing
should happen in websockify itself.
Also:
- remove URL parsing imports from websocket.py since they are not
needed with SimpleHTTPRequestHandler doing the parsing.
- read the absolute path of the target_list file on startup since the
--web option will change directories if set.
The --target-list option is used to pass a configuration file on websockify start.
This file contains entries in the form host:port:token, one per line.
When a new connection is open (from noVNC for example), the url should look like:
ws://websockify_host:websockify_port/?token=ABCD
The token is then extracted and checked against the entries in the configuration file.
When the token matches, the connection is proxied to the host:port indicated in the file.
If the configuration file is a folder, then all the entries from files in it are read.
Add the option "-6, --prefer-ipv6". When set, the 'prefer_ipv6' flag in
websocket.py is used so that 'source_addr' is resolved to IPv6 if
available. If 'source_addr' is not set, binds to [::].
- The --web option changes directory so the wrap mode needs to get an
absolute path to the rebinder.
- Also, use long instead of int in rebind.c so avoid compile warnings.
- Rename unix socket option to '--unix-target' to be consistent with
'--ssl-target' which is an analogous switch.
- Fix normal socket target mode which was broken after merge.
- Normalize/fix output for SSL, unix socket and wrap command modes.
- --run-once will exit after handling a single WebSocket connection
(but not ater flash policy or normal web requests).
- --timeout TIME will stop listening for new connections after exit
after TIME seconds (the master process shuts down). Existing
WebSocket connections will continue but once all connections are
closed all processes will terminate.
Use rsplit(':', 1) instead of rpartition(':') in port argument
processing.
python2.4 may or may not work with HyBi which requires the numpy and
ctypes modules.
WebSocketServer.socket() is a static method takes a host and port and
an optional connect parameter. If connect is not set then it returns
a socket listening on host and port. If connect is set then
a connection will be made host and port and the socket returned. This
has IPv6 support like the addrinfo method it replaces.
Also, prefer IPv4 resolutions if they are in the list. This can be
overriden to prefer IPv6 resolutions for the same host using the
optional prefer_ipv6 parameter.
- fix addrinfo to accept empty host as localhost
- use correct host variable in addrinfo error message
- accept HyBi 7, 8 and 9. No difference for now.
- send close buffer correctly.