diff --git a/websockify/websocket.py b/websockify/websocket.py
index 4f298bc..5a18301 100644
--- a/websockify/websocket.py
+++ b/websockify/websocket.py
@@ -72,165 +72,18 @@ class WebSocketServer(object):
buffer_size = 65536
+ GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
server_handshake_hybi = """HTTP/1.1 101 Switching Protocols\r
Upgrade: websocket\r
Connection: Upgrade\r
Sec-WebSocket-Accept: %s\r
"""
- GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-
- policy_response = """\n"""
-
- # An exception before the WebSocket connection was established
- class EClose(Exception):
- pass
-
# An exception while the WebSocket client was connected
class CClose(Exception):
pass
- def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
- verbose=False, cert='', key='', ssl_only=None,
- daemon=False, record='', web='',
- run_once=False, timeout=0, idle_timeout=0):
-
- # settings
- self.verbose = verbose
- self.listen_host = listen_host
- self.listen_port = listen_port
- self.prefer_ipv6 = source_is_ipv6
- self.ssl_only = ssl_only
- self.daemon = daemon
- self.run_once = run_once
- self.timeout = timeout
- self.idle_timeout = idle_timeout
-
- self.launch_time = time.time()
- self.ws_connection = False
- self.handler_id = 1
-
- # Make paths settings absolute
- self.cert = os.path.abspath(cert)
- self.key = self.web = self.record = ''
- if key:
- self.key = os.path.abspath(key)
- if web:
- self.web = os.path.abspath(web)
- if record:
- self.record = os.path.abspath(record)
-
- if self.web:
- os.chdir(self.web)
-
- # Sanity checks
- if not ssl and self.ssl_only:
- raise Exception("No 'ssl' module and SSL-only specified")
- if self.daemon and not resource:
- raise Exception("Module 'resource' required to daemonize")
-
- # Show configuration
- print("WebSocket server settings:")
- print(" - Listen on %s:%s" % (
- self.listen_host, self.listen_port))
- print(" - Flash security policy server")
- if self.web:
- print(" - Web server. Web root: %s" % self.web)
- if ssl:
- if os.path.exists(self.cert):
- print(" - SSL/TLS support")
- if self.ssl_only:
- print(" - Deny non-SSL/TLS connections")
- else:
- print(" - No SSL/TLS support (no cert file)")
- else:
- print(" - No SSL/TLS support (no 'ssl' module)")
- if self.daemon:
- print(" - Backgrounding (daemon)")
- if self.record:
- print(" - Recording to '%s.*'" % self.record)
-
- #
- # WebSocketServer static methods
- #
-
- @staticmethod
- def socket(host, port=None, connect=False, prefer_ipv6=False, unix_socket=None, use_ssl=False):
- """ Resolve a host (and optional port) to an IPv4 or IPv6
- address. Create a socket. Bind to it if listen is set,
- otherwise connect to it. Return the socket.
- """
- flags = 0
- if host == '':
- host = None
- if connect and not (port or unix_socket):
- raise Exception("Connect mode requires a port")
- if use_ssl and not ssl:
- raise Exception("SSL socket requested but Python SSL module not loaded.");
- if not connect and use_ssl:
- raise Exception("SSL only supported in connect mode (for now)")
- if not connect:
- flags = flags | socket.AI_PASSIVE
-
- if not unix_socket:
- addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM,
- socket.IPPROTO_TCP, flags)
- if not addrs:
- raise Exception("Could not resolve host '%s'" % host)
- addrs.sort(key=lambda x: x[0])
- if prefer_ipv6:
- addrs.reverse()
- sock = socket.socket(addrs[0][0], addrs[0][1])
- if connect:
- sock.connect(addrs[0][4])
- if use_ssl:
- sock = ssl.wrap_socket(sock)
- else:
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- sock.bind(addrs[0][4])
- sock.listen(100)
- else:
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- sock.connect(unix_socket)
-
- return sock
-
- @staticmethod
- def daemonize(keepfd=None, chdir='/'):
- os.umask(0)
- if chdir:
- os.chdir(chdir)
- else:
- os.chdir('/')
- os.setgid(os.getgid()) # relinquish elevations
- os.setuid(os.getuid()) # relinquish elevations
-
- # Double fork to daemonize
- if os.fork() > 0: os._exit(0) # Parent exits
- os.setsid() # Obtain new process group
- if os.fork() > 0: os._exit(0) # Parent exits
-
- # Signal handling
- def terminate(a,b): os._exit(0)
- signal.signal(signal.SIGTERM, terminate)
- signal.signal(signal.SIGINT, signal.SIG_IGN)
-
- # Close open files
- maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
- if maxfd == resource.RLIM_INFINITY: maxfd = 256
- for fd in reversed(range(maxfd)):
- try:
- if fd != keepfd:
- os.close(fd)
- except OSError:
- _, exc, _ = sys.exc_info()
- if exc.errno != errno.EBADF: raise
-
- # Redirect I/O to /dev/null
- os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdin.fileno())
- os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdout.fileno())
- os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
-
@staticmethod
def unmask(buf, hlen, plen):
pstart = hlen + 4
@@ -373,30 +226,10 @@ Sec-WebSocket-Accept: %s\r
return f
-
- #
- # WebSocketServer logging/output functions
- #
-
- def traffic(self, token="."):
- """ Show traffic flow in verbose mode. """
- if self.verbose and not self.daemon:
- sys.stdout.write(token)
- sys.stdout.flush()
-
- def msg(self, msg):
- """ Output message with handler_id prefix. """
- if not self.daemon:
- print("% 3d: %s" % (self.handler_id, msg))
-
- def vmsg(self, msg):
- """ Same as msg() but only if verbose. """
- if self.verbose:
- self.msg(msg)
-
#
# Main WebSocketServer methods
#
+
def send_frames(self, bufs=None):
""" Encode and send WebSocket frames. Any frames already
queued will be sent first. If buf is not set then only queued
@@ -547,6 +380,156 @@ Sec-WebSocket-Accept: %s\r
return response
+ def new_client(self):
+ """ Do something with a WebSockets client connection. """
+ raise("WebSocketServer.new_client() must be overloaded")
+
+ policy_response = """\n"""
+
+ # An exception before the WebSocket connection was established
+ class EClose(Exception):
+ pass
+
+ def __init__(self, listen_host='', listen_port=None, source_is_ipv6=False,
+ verbose=False, cert='', key='', ssl_only=None,
+ daemon=False, record='', web='',
+ run_once=False, timeout=0, idle_timeout=0):
+
+ # settings
+ self.verbose = verbose
+ self.listen_host = listen_host
+ self.listen_port = listen_port
+ self.prefer_ipv6 = source_is_ipv6
+ self.ssl_only = ssl_only
+ self.daemon = daemon
+ self.run_once = run_once
+ self.timeout = timeout
+ self.idle_timeout = idle_timeout
+
+ self.launch_time = time.time()
+ self.ws_connection = False
+ self.handler_id = 1
+
+ # Make paths settings absolute
+ self.cert = os.path.abspath(cert)
+ self.key = self.web = self.record = ''
+ if key:
+ self.key = os.path.abspath(key)
+ if web:
+ self.web = os.path.abspath(web)
+ if record:
+ self.record = os.path.abspath(record)
+
+ if self.web:
+ os.chdir(self.web)
+
+ # Sanity checks
+ if not ssl and self.ssl_only:
+ raise Exception("No 'ssl' module and SSL-only specified")
+ if self.daemon and not resource:
+ raise Exception("Module 'resource' required to daemonize")
+
+ # Show configuration
+ print("WebSocket server settings:")
+ print(" - Listen on %s:%s" % (
+ self.listen_host, self.listen_port))
+ print(" - Flash security policy server")
+ if self.web:
+ print(" - Web server. Web root: %s" % self.web)
+ if ssl:
+ if os.path.exists(self.cert):
+ print(" - SSL/TLS support")
+ if self.ssl_only:
+ print(" - Deny non-SSL/TLS connections")
+ else:
+ print(" - No SSL/TLS support (no cert file)")
+ else:
+ print(" - No SSL/TLS support (no 'ssl' module)")
+ if self.daemon:
+ print(" - Backgrounding (daemon)")
+ if self.record:
+ print(" - Recording to '%s.*'" % self.record)
+
+ #
+ # WebSocketServer static methods
+ #
+
+ @staticmethod
+ def socket(host, port=None, connect=False, prefer_ipv6=False, unix_socket=None, use_ssl=False):
+ """ Resolve a host (and optional port) to an IPv4 or IPv6
+ address. Create a socket. Bind to it if listen is set,
+ otherwise connect to it. Return the socket.
+ """
+ flags = 0
+ if host == '':
+ host = None
+ if connect and not (port or unix_socket):
+ raise Exception("Connect mode requires a port")
+ if use_ssl and not ssl:
+ raise Exception("SSL socket requested but Python SSL module not loaded.");
+ if not connect and use_ssl:
+ raise Exception("SSL only supported in connect mode (for now)")
+ if not connect:
+ flags = flags | socket.AI_PASSIVE
+
+ if not unix_socket:
+ addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP, flags)
+ if not addrs:
+ raise Exception("Could not resolve host '%s'" % host)
+ addrs.sort(key=lambda x: x[0])
+ if prefer_ipv6:
+ addrs.reverse()
+ sock = socket.socket(addrs[0][0], addrs[0][1])
+ if connect:
+ sock.connect(addrs[0][4])
+ if use_ssl:
+ sock = ssl.wrap_socket(sock)
+ else:
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind(addrs[0][4])
+ sock.listen(100)
+ else:
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(unix_socket)
+
+ return sock
+
+ @staticmethod
+ def daemonize(keepfd=None, chdir='/'):
+ os.umask(0)
+ if chdir:
+ os.chdir(chdir)
+ else:
+ os.chdir('/')
+ os.setgid(os.getgid()) # relinquish elevations
+ os.setuid(os.getuid()) # relinquish elevations
+
+ # Double fork to daemonize
+ if os.fork() > 0: os._exit(0) # Parent exits
+ os.setsid() # Obtain new process group
+ if os.fork() > 0: os._exit(0) # Parent exits
+
+ # Signal handling
+ def terminate(a,b): os._exit(0)
+ signal.signal(signal.SIGTERM, terminate)
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+ # Close open files
+ maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
+ if maxfd == resource.RLIM_INFINITY: maxfd = 256
+ for fd in reversed(range(maxfd)):
+ try:
+ if fd != keepfd:
+ os.close(fd)
+ except OSError:
+ _, exc, _ = sys.exc_info()
+ if exc.errno != errno.EBADF: raise
+
+ # Redirect I/O to /dev/null
+ os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdin.fileno())
+ os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdout.fileno())
+ os.dup2(os.open(os.devnull, os.O_RDWR), sys.stderr.fileno())
def do_handshake(self, sock, address):
"""
@@ -650,6 +633,27 @@ Sec-WebSocket-Accept: %s\r
return retsock
+ #
+ # WebSocketServer logging/output functions
+ #
+
+ def traffic(self, token="."):
+ """ Show traffic flow in verbose mode. """
+ if self.verbose and not self.daemon:
+ sys.stdout.write(token)
+ sys.stdout.flush()
+
+ def msg(self, msg):
+ """ Output message with handler_id prefix. """
+ if not self.daemon:
+ print("% 3d: %s" % (self.handler_id, msg))
+
+ def vmsg(self, msg):
+ """ Same as msg() but only if verbose. """
+ if self.verbose:
+ self.msg(msg)
+
+
#
# Events that can/should be overridden in sub-classes
#
@@ -730,10 +734,6 @@ Sec-WebSocket-Accept: %s\r
# Original socket closed by caller
self.request.close()
- def new_client(self):
- """ Do something with a WebSockets client connection. """
- raise("WebSocketServer.new_client() must be overloaded")
-
def start_server(self):
"""
Daemonize if requested. Listen for for connections. Run