15 Commits

Author SHA1 Message Date
Pierre Ossman 43c371fc7a Don't fake a close code in close response
If the peer doesn't send us a close code, then don't send any code back
in the response. Sending 1005 is explicitly wrong as the specification
states that code should only be used locally and never be sent over the
network.
2023-09-06 12:25:19 +02:00
Pierre Ossman 354668143f Use Ubuntu 20.04 runner for tests
GitHub has deprecated 18.04, so we can't continue running tests on that
old distribution. Unfortunately, we can't switch to the latest 22.04
since GitHub doesn't package anything older than Python 3.7 for that
runner.

And unfortunately the oldest version in 20.04 is Python 3.5, so we have
to remove the test runs for Python 3.4.
2023-08-23 08:49:17 +02:00
Pierre Ossman e28b7c8c75 Fix usage syntax in regular TCP case
You always need to specify the target on the command line as the system
has no default target address.
2023-08-21 16:28:58 +02:00
Pierre Ossman a1346552fb Merge branch 'token_redis_improvements' of https://github.com/javicacheiro/websockify 2023-01-20 17:01:32 +01:00
Javier Cacheiro 3d2e93aeb0 Allow empty options in redis token source string when using default values 2023-01-19 17:19:30 +01:00
Javier Cacheiro e23d4e337c Token Redis: Support both json and plain text tokens 2023-01-19 17:19:27 +01:00
Javier Cacheiro 8121a5265a Token Redis source: add optional redis port, redis database and redis password 2023-01-19 17:17:07 +01:00
Javier Cacheiro 5dd81a0363 Remove simplejson dependency: use json module from stdlib.
Add missing redis dependency.
2023-01-19 16:35:53 +01:00
don bright 7f53e9c22c Update README.md
adding WSS exceptions for dummies
2023-01-07 16:34:17 -06:00
Pierre Ossman d54020538d Merge branch 'master' of https://github.com/shiomax/websockify 2022-12-22 14:15:46 +01:00
Pierre Ossman ac74ade2ca Test current versions of Python 2022-12-16 14:15:23 +01:00
Pierre Ossman 9ac3272d2b Switch to nose2 for tests
The original nosetests is long abandoned, and doesn't work properly on
newer versions of Python.
2022-12-16 14:14:54 +01:00
Pierre Ossman 5d17281187 Remove redundant test requirements
These should get pulled in via setup.py.
2022-12-16 14:14:54 +01:00
Pierre Ossman 789b80f719 Explicitly install dependencies
It is very buggy if we let setuptools do it for some reason, so have
this as an explicit step instead.
2022-12-16 14:14:23 +01:00
max 63eb24dd8e Add option to listen to unix socket 2022-12-14 20:51:29 +01:00
9 changed files with 347 additions and 65 deletions
+8 -4
View File
@@ -4,16 +4,17 @@ on: [push, pull_request]
jobs: jobs:
test: test:
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
strategy: strategy:
matrix: matrix:
python-version: python-version:
- 3.4
- 3.5 - 3.5
- 3.6 - 3.6
- 3.7 - 3.7
- 3.8 - 3.8
- 3.9 - 3.9
- "3.10"
- 3.11
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -21,10 +22,13 @@ jobs:
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install dependencies - name: Update pip and setuptools
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
python -m pip install setuptools python -m pip install setuptools
- name: Install dependencies
run: |
python -m pip install -e .
python -m pip install -r test-requirements.txt python -m pip install -r test-requirements.txt
- name: Install old numpy - name: Install old numpy
run: | run: |
@@ -32,4 +36,4 @@ jobs:
if: ${{ matrix.python-version >= '3.4' && matrix.python-version < '3.7' }} if: ${{ matrix.python-version >= '3.4' && matrix.python-version < '3.7' }}
- name: Run tests - name: Run tests
run: | run: |
python setup.py nosetests --verbosity=3 python -m nose2 --verbosity=3
+6
View File
@@ -57,6 +57,12 @@ and approve. Browsers generally don't give you the "trust certificate?" prompt
by opening a WSS socket with invalid certificate, hence you need to have it by opening a WSS socket with invalid certificate, hence you need to have it
accept it by either of those two methods. accept it by either of those two methods.
The ports may be considered as distinguishing connections by the browser,
for example, if your website url is https://my.local:8443 and your WebSocket
url is wss://my.local:8001, first browse to https://my.local:8001, add the
exception, then browse to https://my.local:8443 and add another exception.
Then an html page served over :8443 will be able to open WSS to :8001
If you have a commercial/valid SSL certificate with one or more intermediate If you have a commercial/valid SSL certificate with one or more intermediate
certificates, concat them into one file, server certificate first, then the certificates, concat them into one file, server certificate first, then the
intermediate(s) from the CA, etc. Point to this file with the `--cert` option intermediate(s) from the CA, etc. Point to this file with the `--cert` option
+1 -1
View File
@@ -32,7 +32,7 @@ setup(name=name,
install_requires=[ install_requires=[
'numpy', 'requests', 'numpy', 'requests',
'jwcrypto', 'jwcrypto',
'redis', 'simplejson', 'redis',
], ],
zip_safe=False, zip_safe=False,
entry_points={ entry_points={
+1 -3
View File
@@ -1,7 +1,5 @@
mock mock
nose nose2
jwcrypto
six six
redis redis
simplejson
wrapt<=1.12.1;python_version<="3.4" wrapt<=1.12.1;python_version<="3.4"
+150
View File
@@ -203,3 +203,153 @@ class TokenRedisTestCase(unittest.TestCase):
self.assertIsNotNone(result) self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host') self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port') self.assertEqual(result[1], 'remote_port')
@patch('redis.Redis')
def test_json_token_with_spaces(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')
instance = mock_redis.return_value
instance.get.return_value = b' {"host": "remote_host:remote_port"} '
result = plugin.lookup('testhost')
instance.get.assert_called_once_with('testhost')
self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port')
@patch('redis.Redis')
def test_text_token(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')
instance = mock_redis.return_value
instance.get.return_value = b'remote_host:remote_port'
result = plugin.lookup('testhost')
instance.get.assert_called_once_with('testhost')
self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port')
@patch('redis.Redis')
def test_text_token_with_spaces(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')
instance = mock_redis.return_value
instance.get.return_value = b' remote_host:remote_port '
result = plugin.lookup('testhost')
instance.get.assert_called_once_with('testhost')
self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port')
@patch('redis.Redis')
def test_invalid_token(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')
instance = mock_redis.return_value
instance.get.return_value = b'{"host": "remote_host:remote_port" '
result = plugin.lookup('testhost')
instance.get.assert_called_once_with('testhost')
self.assertIsNone(result)
def test_src_only_host(self):
plugin = TokenRedis('127.0.0.1')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)
def test_src_with_host_port(self):
plugin = TokenRedis('127.0.0.1:1234')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)
def test_src_with_host_port_db(self):
plugin = TokenRedis('127.0.0.1:1234:2')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, None)
def test_src_with_host_port_db_pass(self):
plugin = TokenRedis('127.0.0.1:1234:2:verysecret')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, 'verysecret')
def test_src_with_host_empty_port_empty_db_pass(self):
plugin = TokenRedis('127.0.0.1:::verysecret')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, 'verysecret')
def test_src_with_host_empty_port_empty_db_empty_pass(self):
plugin = TokenRedis('127.0.0.1:::')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)
def test_src_with_host_empty_port_empty_db_no_pass(self):
plugin = TokenRedis('127.0.0.1::')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)
def test_src_with_host_empty_port_no_db_no_pass(self):
plugin = TokenRedis('127.0.0.1:')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)
def test_src_with_host_empty_port_db_no_pass(self):
plugin = TokenRedis('127.0.0.1::2')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, None)
def test_src_with_host_port_empty_db_pass(self):
plugin = TokenRedis('127.0.0.1:1234::verysecret')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, 'verysecret')
def test_src_with_host_empty_port_db_pass(self):
plugin = TokenRedis('127.0.0.1::2:verysecret')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, 'verysecret')
def test_src_with_host_empty_port_db_empty_pass(self):
plugin = TokenRedis('127.0.0.1::2:')
self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, None)
+102 -22
View File
@@ -3,6 +3,7 @@ import os
import sys import sys
import time import time
import re import re
import json
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -154,57 +155,136 @@ class JWTTokenApi(BasePlugin):
logger.error("package jwcrypto not found, are you sure you've installed it correctly?") logger.error("package jwcrypto not found, are you sure you've installed it correctly?")
return None return None
class TokenRedis():
""" class TokenRedis(BasePlugin):
The TokenRedis plugin expects the format of the data in a form of json. """Token plugin based on the Redis in-memory data store.
The token source is in the format:
host[:port[:db[:password]]]
where port, db and password are optional. If port or db are left empty
they will take its default value, ie. 6379 and 0 respectively.
If your redis server is using the default port (6379) then you can use:
my-redis-host
In case you need to authenticate with the redis server and you are using
the default database and port you can use:
my-redis-host:::verysecretpass
In the more general case you will use:
my-redis-host:6380:1:verysecretpass
The TokenRedis plugin expects the format of the target in one of these two
formats:
- JSON
{"host": "target-host:target-port"}
- Plain text
target-host:target-port
Prepare data with: Prepare data with:
redis-cli set hello '{"host":"127.0.0.1:5000"}'
redis-cli set my-token '{"host": "127.0.0.1:5000"}'
Verify with: Verify with:
redis-cli --raw get hello
redis-cli --raw get my-token
Spawn a test "server" using netcat Spawn a test "server" using netcat
nc -l 5000 -v nc -l 5000 -v
Note: you have to install also the 'redis' and 'simplejson' modules Note: This Token Plugin depends on the 'redis' module, so you have
pip install redis simplejson to install it before using this plugin:
pip install redis
""" """
def __init__(self, src): def __init__(self, src):
try: try:
# import those ahead of time so we provide error earlier
import redis import redis
import simplejson except ImportError:
self._server, self._port = src.split(":") logger.error("Unable to load redis module")
sys.exit()
# Default values
self._port = 6379
self._db = 0
self._password = None
try:
fields = src.split(":")
if len(fields) == 1:
self._server = fields[0]
elif len(fields) == 2:
self._server, self._port = fields
if not self._port:
self._port = 6379
elif len(fields) == 3:
self._server, self._port, self._db = fields
if not self._port:
self._port = 6379
if not self._db:
self._db = 0
elif len(fields) == 4:
self._server, self._port, self._db, self._password = fields
if not self._port:
self._port = 6379
if not self._db:
self._db = 0
if not self._password:
self._password = None
else:
raise ValueError
self._port = int(self._port)
self._db = int(self._db)
logger.info("TokenRedis backend initilized (%s:%s)" % logger.info("TokenRedis backend initilized (%s:%s)" %
(self._server, self._port)) (self._server, self._port))
except ValueError: except ValueError:
logger.error("The provided --token-source='%s' is not in an expected format <host>:<port>" % logger.error("The provided --token-source='%s' is not in the "
src) "expected format <host>[:<port>[:<db>[:<password>]]]" %
sys.exit() src)
except ImportError:
logger.error("package redis or simplejson not found, are you sure you've installed them correctly?")
sys.exit() sys.exit()
def lookup(self, token): def lookup(self, token):
try: try:
import redis import redis
import simplejson
except ImportError: except ImportError:
logger.error("package redis or simplejson not found, are you sure you've installed them correctly?") logger.error("package redis not found, are you sure you've installed them correctly?")
sys.exit() sys.exit()
logger.info("resolving token '%s'" % token) logger.info("resolving token '%s'" % token)
client = redis.Redis(host=self._server, port=self._port) client = redis.Redis(host=self._server, port=self._port,
db=self._db, password=self._password)
stuff = client.get(token) stuff = client.get(token)
if stuff is None: if stuff is None:
return None return None
else: else:
responseStr = stuff.decode("utf-8") responseStr = stuff.decode("utf-8").strip()
logger.debug("response from redis : %s" % responseStr) logger.debug("response from redis : %s" % responseStr)
combo = simplejson.loads(responseStr) if responseStr.startswith("{"):
(host, port) = combo["host"].split(':') try:
logger.debug("host: %s, port: %s" % (host,port)) combo = json.loads(responseStr)
host, port = combo["host"].split(":")
except ValueError:
logger.error("Unable to decode JSON token: %s" %
responseStr)
return None
except KeyError:
logger.error("Unable to find 'host' key in JSON token: %s" %
responseStr)
return None
elif re.match(r'\S+:\S+', responseStr):
host, port = responseStr.split(":")
else:
logger.error("Unable to parse token: %s" % responseStr)
return None
logger.debug("host: %s, port: %s" % (host, port))
return [host, port] return [host, port]
+1 -1
View File
@@ -666,7 +666,7 @@ class WebSocket(object):
continue continue
if code is None: if code is None:
self.close_code = code = 1005 self.close_code = 1005
self.close_reason = "No close status code specified by peer" self.close_reason = "No close status code specified by peer"
else: else:
self.close_code = code self.close_code = code
+19 -3
View File
@@ -11,7 +11,7 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
''' '''
import signal, socket, optparse, time, os, sys, subprocess, logging, errno, ssl import signal, socket, optparse, time, os, sys, subprocess, logging, errno, ssl, stat
from socketserver import ThreadingMixIn from socketserver import ThreadingMixIn
from http.server import HTTPServer from http.server import HTTPServer
@@ -112,7 +112,9 @@ Traffic Legend:
self.server.target_host, self.server.target_port, e) self.server.target_host, self.server.target_port, e)
raise self.CClose(1011, "Failed to connect to downstream server") raise self.CClose(1011, "Failed to connect to downstream server")
self.request.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) # Option unavailable when listening to unix socket
if not self.server.unix_listen:
self.request.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
if not self.server.wrap_cmd and not self.server.unix_target: if not self.server.wrap_cmd and not self.server.unix_target:
tsock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) tsock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
@@ -448,7 +450,7 @@ def websockify_init():
# Setup optparse. # Setup optparse.
usage = "\n %prog [options]" usage = "\n %prog [options]"
usage += " [source_addr:]source_port [target_addr:target_port]" usage += " [source_addr:]source_port target_addr:target_port"
usage += "\n %prog [options]" usage += "\n %prog [options]"
usage += " --token-plugin=CLASS [source_addr:]source_port" usage += " --token-plugin=CLASS [source_addr:]source_port"
usage += "\n %prog [options]" usage += "\n %prog [options]"
@@ -495,6 +497,10 @@ def websockify_init():
parser.add_option("--ssl-ciphers", action="store", parser.add_option("--ssl-ciphers", action="store",
help="list of ciphers allowed for connection. For a list of " help="list of ciphers allowed for connection. For a list of "
"supported ciphers run `openssl ciphers`") "supported ciphers run `openssl ciphers`")
parser.add_option("--unix-listen",
help="listen to unix socket", metavar="FILE", default=None)
parser.add_option("--unix-listen-mode", default=None,
help="specify mode for unix socket (defaults to 0600)")
parser.add_option("--unix-target", parser.add_option("--unix-target",
help="connect to unix socket target", metavar="FILE") help="connect to unix socket target", metavar="FILE")
parser.add_option("--inetd", parser.add_option("--inetd",
@@ -650,6 +656,16 @@ def websockify_init():
if opts.inetd: if opts.inetd:
opts.listen_fd = sys.stdin.fileno() opts.listen_fd = sys.stdin.fileno()
elif opts.unix_listen:
if opts.unix_listen_mode:
try:
# Parse octal notation (like 750)
opts.unix_listen_mode = int(opts.unix_listen_mode, 8)
except ValueError:
parser.error("Error parsing listen unix socket mode")
else:
# Default to 0600 (Owner Read/Write)
opts.unix_listen_mode = stat.S_IREAD | stat.S_IWRITE
else: else:
if len(args) < 1: if len(args) < 1:
parser.error("Too few arguments") parser.error("Too few arguments")
+58 -30
View File
@@ -325,37 +325,40 @@ class WebSockifyServer():
file_only=False, file_only=False,
run_once=False, timeout=0, idle_timeout=0, traffic=False, run_once=False, timeout=0, idle_timeout=0, traffic=False,
tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None, tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None,
tcp_keepintvl=None, ssl_ciphers=None, ssl_options=0): tcp_keepintvl=None, ssl_ciphers=None, ssl_options=0,
unix_listen=None, unix_listen_mode=None):
# settings # settings
self.RequestHandlerClass = RequestHandlerClass self.RequestHandlerClass = RequestHandlerClass
self.verbose = verbose self.verbose = verbose
self.listen_fd = listen_fd self.listen_fd = listen_fd
self.listen_host = listen_host self.unix_listen = unix_listen
self.listen_port = listen_port self.unix_listen_mode = unix_listen_mode
self.prefer_ipv6 = source_is_ipv6 self.listen_host = listen_host
self.ssl_only = ssl_only self.listen_port = listen_port
self.ssl_ciphers = ssl_ciphers self.prefer_ipv6 = source_is_ipv6
self.ssl_options = ssl_options self.ssl_only = ssl_only
self.verify_client = verify_client self.ssl_ciphers = ssl_ciphers
self.daemon = daemon self.ssl_options = ssl_options
self.run_once = run_once self.verify_client = verify_client
self.timeout = timeout self.daemon = daemon
self.idle_timeout = idle_timeout self.run_once = run_once
self.traffic = traffic self.timeout = timeout
self.file_only = file_only self.idle_timeout = idle_timeout
self.web_auth = web_auth self.traffic = traffic
self.file_only = file_only
self.web_auth = web_auth
self.launch_time = time.time() self.launch_time = time.time()
self.ws_connection = False self.ws_connection = False
self.handler_id = 1 self.handler_id = 1
self.terminating = False self.terminating = False
self.logger = self.get_logger() self.logger = self.get_logger()
self.tcp_keepalive = tcp_keepalive self.tcp_keepalive = tcp_keepalive
self.tcp_keepcnt = tcp_keepcnt self.tcp_keepcnt = tcp_keepcnt
self.tcp_keepidle = tcp_keepidle self.tcp_keepidle = tcp_keepidle
self.tcp_keepintvl = tcp_keepintvl self.tcp_keepintvl = tcp_keepintvl
# keyfile path must be None if not specified # keyfile path must be None if not specified
self.key = None self.key = None
@@ -387,6 +390,8 @@ class WebSockifyServer():
self.msg("WebSocket server settings:") self.msg("WebSocket server settings:")
if self.listen_fd != None: if self.listen_fd != None:
self.msg(" - Listen for inetd connections") self.msg(" - Listen for inetd connections")
elif self.unix_listen != None:
self.msg(" - Listen on unix socket %s", self.unix_listen)
else: else:
self.msg(" - Listen on %s:%s", self.msg(" - Listen on %s:%s",
self.listen_host, self.listen_port) self.listen_host, self.listen_port)
@@ -421,8 +426,9 @@ class WebSockifyServer():
@staticmethod @staticmethod
def socket(host, port=None, connect=False, prefer_ipv6=False, def socket(host, port=None, connect=False, prefer_ipv6=False,
unix_socket=None, use_ssl=False, tcp_keepalive=True, unix_socket=None, unix_socket_mode=None, unix_socket_listen=False,
tcp_keepcnt=None, tcp_keepidle=None, tcp_keepintvl=None): use_ssl=False, tcp_keepalive=True, tcp_keepcnt=None,
tcp_keepidle=None, tcp_keepintvl=None):
""" Resolve a host (and optional port) to an IPv4 or IPv6 """ Resolve a host (and optional port) to an IPv4 or IPv6
address. Create a socket. Bind to it if listen is set, address. Create a socket. Bind to it if listen is set,
otherwise connect to it. Return the socket. otherwise connect to it. Return the socket.
@@ -470,8 +476,22 @@ class WebSockifyServer():
sock.bind(addrs[0][4]) sock.bind(addrs[0][4])
sock.listen(100) sock.listen(100)
else: else:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) if unix_socket_listen:
sock.connect(unix_socket) # Make sure the socket does not already exist
try:
os.unlink(unix_socket)
except FileNotFoundError:
pass
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
oldmask = os.umask(0o777 ^ unix_socket_mode)
try:
sock.bind(unix_socket)
finally:
os.umask(oldmask)
sock.listen(100)
else:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(unix_socket)
return sock return sock
@@ -700,6 +720,11 @@ class WebSockifyServer():
if self.listen_fd != None: if self.listen_fd != None:
lsock = socket.fromfd(self.listen_fd, socket.AF_INET, socket.SOCK_STREAM) lsock = socket.fromfd(self.listen_fd, socket.AF_INET, socket.SOCK_STREAM)
elif self.unix_listen != None:
lsock = self.socket(host=None,
unix_socket=self.unix_listen,
unix_socket_mode=self.unix_listen_mode,
unix_socket_listen=True)
else: else:
lsock = self.socket(self.listen_host, self.listen_port, False, lsock = self.socket(self.listen_host, self.listen_port, False,
self.prefer_ipv6, self.prefer_ipv6,
@@ -766,6 +791,9 @@ class WebSockifyServer():
ready = select.select([lsock], [], [], 1)[0] ready = select.select([lsock], [], [], 1)[0]
if lsock in ready: if lsock in ready:
startsock, address = lsock.accept() startsock, address = lsock.accept()
# Unix Socket will not report address (empty string), but address[0] is logged a bunch
if self.unix_listen != None:
address = [ self.unix_listen ]
else: else:
continue continue
except self.Terminate: except self.Terminate: