M setup.py +27 -27
@@ 7,42 7,42 @@ from wormhole.version import VERSION
 
 
 def readme():
-    with open('README.rst', encoding = 'utf-8') as readme_file:
-        return '\n' + readme_file.read()
+    with open("README.rst", encoding="utf-8") as readme_file:
+        return "\n" + readme_file.read()
 
 
 setup(
-    name='wormhole-proxy',
-    version=VERSION.replace('v',''),  # normalize version from vd.d to d.d
-    author='Chaiwat Suttipongsakul',
-    author_email='cwt@bashell.com',
-    url='https://bitbucket.org/bashell-com/wormhole',
-    license='MIT',
-    description='Asynchronous I/O HTTP and HTTPS Proxy on Python >= 3.5.3',
+    name="wormhole-proxy",
+    version=VERSION.replace("v", ""),  # normalize version from vd.d to d.d
+    author="Chaiwat Suttipongsakul",
+    author_email="cwt@bashell.com",
+    url="https://bitbucket.org/bashell-com/wormhole",
+    license="MIT",
+    description="Asynchronous I/O HTTP and HTTPS Proxy on Python >= 3.5.3",
     long_description=readme(),
-    keywords='wormhole asynchronous web proxy',
+    keywords="wormhole asynchronous web proxy",
     classifiers=[
-        'Development Status :: 5 - Production/Stable',
-        'Environment :: Console',
-        'Intended Audience :: Information Technology',
-        'Intended Audience :: System Administrators',
-        'License :: OSI Approved :: MIT License',
-        'Operating System :: POSIX',
-        'Operating System :: Microsoft :: Windows',
-        'Programming Language :: Python :: 3.5.3',
-        'Programming Language :: Python :: 3.6',
-        'Programming Language :: Python :: 3.7',
-        'Programming Language :: Python :: 3.8',
-        'Programming Language :: Python :: 3.9',
-        'Programming Language :: Python :: 3.10',
-        'Programming Language :: Python :: Implementation :: CPython',
-        'Topic :: Internet :: Proxy Servers',
+        "Development Status :: 5 - Production/Stable",
+        "Environment :: Console",
+        "Intended Audience :: Information Technology",
+        "Intended Audience :: System Administrators",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: POSIX",
+        "Operating System :: Microsoft :: Windows",
+        "Programming Language :: Python :: 3.5.3",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3.10",
+        "Programming Language :: Python :: Implementation :: CPython",
+        "Topic :: Internet :: Proxy Servers",
     ],
     install_requires=[
         'pywin32;platform_system=="Windows"',
         'uvloop;platform_system=="Linux"',
     ],
-    packages=['wormhole'],
+    packages=["wormhole"],
     include_package_data=True,
-    entry_points={'console_scripts': ['wormhole = wormhole.proxy:main']},
+    entry_points={"console_scripts": ["wormhole = wormhole.proxy:main"]},
 )

          
M wormhole/__main__.py +0 -1
@@ 1,4 1,3 @@ 
 from proxy import main
 
 exit(main())
-

          
M wormhole/authentication.py +26 -17
@@ 2,39 2,48 @@ from base64 import decodebytes
 
 
 def get_ident(client_reader, client_writer, user=None):
-    client = client_writer.get_extra_info('peername')[0]
+    client = client_writer.get_extra_info("peername")[0]
     if user:
-        client = '%s@%s' % (user, client)
-    return {'id': hex(id(client_reader))[-6:], 'client':client}
+        client = "%s@%s" % (user, client)
+    return {"id": hex(id(client_reader))[-6:], "client": client}
 
 
 auth_list = list()
+
+
 def get_auth_list(auth):
     global auth_list
     if not auth_list:
-        auth_list = [line.strip()
-                     for line in open(auth, 'r')
-                     if line.strip() and not line.strip().startswith('#')]
+        auth_list = [
+            line.strip()
+            for line in open(auth, "r")
+            if line.strip() and not line.strip().startswith("#")
+        ]
     return auth_list
 
 
 def deny(client_writer):
-    [client_writer.write(message)
-     for message in (
-         b'HTTP/1.1 407 Proxy Authentication Required\r\n',
-         b'Proxy-Authenticate: Basic realm="Wormhole Proxy"\r\n',
-         b'\r\n'
-     )]
+    [
+        client_writer.write(message)
+        for message in (
+            b"HTTP/1.1 407 Proxy Authentication Required\r\n",
+            b'Proxy-Authenticate: Basic realm="Wormhole Proxy"\r\n',
+            b"\r\n",
+        )
+    ]
 
 
 async def verify(client_reader, client_writer, headers, auth):
-    proxy_auth = [header for header in headers
-                  if header.lower().startswith('proxy-authorization:')]
+    proxy_auth = [
+        header
+        for header in headers
+        if header.lower().startswith("proxy-authorization:")
+    ]
     if proxy_auth:
         user_password = decodebytes(
-            proxy_auth[0].split(' ')[2].encode('ascii')
-        ).decode('ascii')
+            proxy_auth[0].split(" ")[2].encode("ascii")
+        ).decode("ascii")
         if user_password in get_auth_list(auth):
-            user = user_password.split(':')[0]
+            user = user_password.split(":")[0]
             return get_ident(client_reader, client_writer, user)
     return deny(client_writer)

          
M wormhole/handler.py +81 -67
@@ 5,8 5,9 @@ from tools import get_content_length
 from tools import get_host_and_port
 
 
-async def relay_stream(stream_reader, stream_writer,
-                       ident, return_first_line=False):
+async def relay_stream(
+    stream_reader, stream_writer, ident, return_first_line=False
+):
     logger = get_logger()
     first_line = None
     while True:

          
@@ 16,22 17,23 @@ async def relay_stream(stream_reader, st
                 break
             stream_writer.write(line)
         except Exception as ex:
-            error_message = '%s: %s' % (
+            error_message = "%s: %s" % (
                 ex.__class__.__name__,
-                ' '.join([str(arg) for arg in ex.args])
+                " ".join([str(arg) for arg in ex.args]),
             )
-            logger.debug((
-                '[{id}][{client}]: %s' % error_message
-            ).format(**ident))
+            logger.debug(
+                ("[{id}][{client}]: %s" % error_message).format(**ident)
+            )
             break
         else:
             if return_first_line and first_line is None:
-                first_line = line[:line.find(b'\r\n')]
+                first_line = line[: line.find(b"\r\n")]
     return first_line
 
 
-async def process_https(client_reader, client_writer, request_method, uri,
-                        ident):
+async def process_https(
+    client_reader, client_writer, request_method, uri, ident
+):
     response_code = 200
     error_message = None
     host, port = get_host_and_port(uri)

          
@@ 40,48 42,53 @@ async def process_https(client_reader, c
         req_reader, req_writer = await asyncio.open_connection(
             host, port, ssl=False
         )
-        client_writer.write(b'HTTP/1.1 200 Connection established\r\n')
-        client_writer.write(b'\r\n')
+        client_writer.write(b"HTTP/1.1 200 Connection established\r\n")
+        client_writer.write(b"\r\n")
         # HTTPS need to log here, as the connection may keep alive for long.
-        logger.info((
-            '[{id}][{client}]: %s %d %s' % (
-                request_method, response_code, uri
-            )
-        ).format(**ident))
+        logger.info(
+            (
+                "[{id}][{client}]: %s %d %s"
+                % (request_method, response_code, uri)
+            ).format(**ident)
+        )
 
         tasks = [
             asyncio.ensure_future(
-                relay_stream(client_reader, req_writer, ident)),
+                relay_stream(client_reader, req_writer, ident)
+            ),
             asyncio.ensure_future(
-                relay_stream(req_reader, client_writer, ident)),
+                relay_stream(req_reader, client_writer, ident)
+            ),
         ]
         await asyncio.wait(tasks)
     except Exception as ex:
         response_code = 502
-        error_message = '%s: %s' % (
+        error_message = "%s: %s" % (
             ex.__class__.__name__,
-            ' '.join([str(arg) for arg in ex.args])
+            " ".join([str(arg) for arg in ex.args]),
         )
     if error_message:
-        logger.error((
-            '[{id}][{client}]: %s %d %s (%s)' % (
-                request_method, response_code, uri, error_message
-            )
-        ).format(**ident))
+        logger.error(
+            (
+                "[{id}][{client}]: %s %d %s (%s)"
+                % (request_method, response_code, uri, error_message)
+            ).format(**ident)
+        )
 
 
-async def process_http(client_writer, request_method, uri, http_version,
-                       headers, payload, ident):
+async def process_http(
+    client_writer, request_method, uri, http_version, headers, payload, ident
+):
     response_status = None
     response_code = None
     error_message = None
-    hostname = '127.0.0.1'  # hostname (with optional port) e.g. example.com:80
+    hostname = "127.0.0.1"  # hostname (with optional port) e.g. example.com:80
     request_headers = []
     request_headers_end_index = 0
     has_connection_header = False
 
     for header in headers:
-        name_and_value = header.split(': ', 1)
+        name_and_value = header.split(": ", 1)
 
         if len(name_and_value) == 2:
             name, value = name_and_value

          
@@ 93,13 100,13 @@ async def process_http(client_writer, re
                 hostname = value
         elif name.lower() == "connection":
             has_connection_header = True
-            if value.lower() in ('keep-alive', 'persist'):
+            if value.lower() in ("keep-alive", "persist"):
                 # current version of this program does not support
                 # the HTTP keep-alive feature
                 request_headers.append("Connection: close")
             else:
                 request_headers.append(header)
-        elif name.lower() != 'proxy-connection':
+        elif name.lower() != "proxy-connection":
             request_headers.append(header)
             if len(header) == 0 and request_headers_end_index == 0:
                 request_headers_end_index = len(request_headers) - 1

          
@@ 110,8 117,8 @@ async def process_http(client_writer, re
     if not has_connection_header:
         request_headers.insert(request_headers_end_index, "Connection: close")
 
-    path = uri[len(hostname) + 7:]  # 7 is len('http://')
-    new_head = ' '.join([request_method, path, http_version])
+    path = uri[len(hostname) + 7 :]  # 7 is len('http://')
+    new_head = " ".join([request_method, path, http_version])
 
     host, port = get_host_and_port(hostname, 80)
 

          
@@ 119,19 126,21 @@ async def process_http(client_writer, re
         req_reader, req_writer = await asyncio.open_connection(
             host, port, flags=TCP_NODELAY
         )
-        req_writer.write(('%s\r\n' % new_head).encode())
+        req_writer.write(("%s\r\n" % new_head).encode())
         await req_writer.drain()
 
-        req_writer.write(b'Host: ' + hostname.encode())
-        req_writer.write(b'\r\n')
+        req_writer.write(b"Host: " + hostname.encode())
+        req_writer.write(b"\r\n")
 
-        [req_writer.write((header + '\r\n').encode())
-         for header in request_headers]
-        req_writer.write(b'\r\n')
+        [
+            req_writer.write((header + "\r\n").encode())
+            for header in request_headers
+        ]
+        req_writer.write(b"\r\n")
 
-        if payload != b'':
+        if payload != b"":
             req_writer.write(payload)
-            req_writer.write(b'\r\n')
+            req_writer.write(b"\r\n")
         await req_writer.drain()
 
         response_status = await relay_stream(

          
@@ 139,34 148,36 @@ async def process_http(client_writer, re
         )
     except Exception as ex:
         response_code = 502
-        error_message = '%s: %s' % (
+        error_message = "%s: %s" % (
             ex.__class__.__name__,
-            ' '.join([str(arg) for arg in ex.args])
+            " ".join([str(arg) for arg in ex.args]),
         )
 
     if response_code is None:
-        response_code = int(response_status.decode('ascii').split(' ')[1])
+        response_code = int(response_status.decode("ascii").split(" ")[1])
     logger = get_logger()
     if error_message is None:
-        logger.info((
-            '[{id}][{client}]: %s %d %s' % (
-                request_method, response_code, uri
-            )
-        ).format(**ident))
+        logger.info(
+            (
+                "[{id}][{client}]: %s %d %s"
+                % (request_method, response_code, uri)
+            ).format(**ident)
+        )
     else:
-        logger.error((
-            '[{id}][{client}]: %s %d %s (%s)' % (
-                request_method, response_code, uri, error_message
-            )
-        ).format(**ident))
+        logger.error(
+            (
+                "[{id}][{client}]: %s %d %s (%s)"
+                % (request_method, response_code, uri, error_message)
+            ).format(**ident)
+        )
 
 
 async def process_request(client_reader, max_retry, ident):
     logger = get_logger()
-    request_line = ''
+    request_line = ""
     headers = []
-    header = ''
-    payload = b''
+    header = ""
+    payload = b""
     try:
         retry = 0
         while True:

          
@@ 180,24 191,27 @@ async def process_request(client_reader,
                     continue
                 else:
                     break
-            if line == b'\r\n':
+            if line == b"\r\n":
                 break
-            if line != b'':
+            if line != b"":
                 header += line.decode()
 
         content_length = get_content_length(header)
         while len(payload) < content_length:
             payload += await client_reader.read(1024)
     except Exception as ex:
-        logger.debug((
-            '[{id}][{client}]: !!! Task reject (%s: %s)' % (
-                ex.__class__.__name__,
-                ' '.join([str(arg) for arg in ex.args])
-            )
-        ).format(**ident))
+        logger.debug(
+            (
+                "[{id}][{client}]: !!! Task reject (%s: %s)"
+                % (
+                    ex.__class__.__name__,
+                    " ".join([str(arg) for arg in ex.args]),
+                )
+            ).format(**ident)
+        )
 
     if header:
-        header_lines = header.split('\r\n')
+        header_lines = header.split("\r\n")
         if len(header_lines) > 1:
             request_line = header_lines[0]
         if len(header_lines) > 2:

          
M wormhole/license.py +1 -1
@@ 26,5 26,5 @@ SOFTWARE.
 """
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     print(LICENSE)

          
M wormhole/logger.py +11 -9
@@ 13,25 13,27 @@ class ContextFilter(logging.Filter):
 
 
 logger = None
+
+
 def get_logger(syslog_host=None, syslog_port=514, verbose=0):
-    unix_format = '%(asctime)s %(name)s[%(process)d]: %(message)s'
-    net_format = '%(asctime)s %(hostname)s %(name)s[%(process)d]: %(message)s'
-    date_format = '%b %d %H:%M:%S'
+    unix_format = "%(asctime)s %(name)s[%(process)d]: %(message)s"
+    net_format = "%(asctime)s %(hostname)s %(name)s[%(process)d]: %(message)s"
+    date_format = "%b %d %H:%M:%S"
 
     global logger
     if logger is None:
         logging.basicConfig(
             level=logging.INFO,
-            format='%(asctime)s %(name)s[%(process)d]: %(message)s'
+            format="%(asctime)s %(name)s[%(process)d]: %(message)s",
         )
-        logging.getLogger('asyncio').setLevel(logging.CRITICAL)
-        logger = logging.getLogger('wormhole')
+        logging.getLogger("asyncio").setLevel(logging.CRITICAL)
+        logger = logging.getLogger("wormhole")
         if verbose >= 1:
             logger.setLevel(logging.DEBUG)
         if verbose >= 2:
-            logging.getLogger('asyncio').setLevel(logging.DEBUG)
-        if syslog_host and syslog_host != 'DISABLED':
-            if syslog_host.startswith('/') and os.path.exists(syslog_host):
+            logging.getLogger("asyncio").setLevel(logging.DEBUG)
+        if syslog_host and syslog_host != "DISABLED":
+            if syslog_host.startswith("/") and os.path.exists(syslog_host):
                 syslog = logging.handlers.SysLogHandler(
                     address=syslog_host,
                 )

          
M wormhole/proxy.py +43 -28
@@ 1,15 1,15 @@ 
 #!/usr/bin/env python3
 
 import sys
+
 if sys.version_info < (3, 5):
-    print('Error: You need python 3.5.0 or above.')
+    print("Error: You need python 3.5.0 or above.")
     exit(1)
 
 import os
 from pathlib import Path
-sys.path.insert(
-    0, Path(os.path.realpath(__file__)).parent.as_posix()
-)
+
+sys.path.insert(0, Path(os.path.realpath(__file__)).parent.as_posix())
 
 import asyncio
 from argparse import ArgumentParser

          
@@ 24,37 24,53 @@ def main():
     port and provides `--help` message.
     """
     parser = ArgumentParser(
-        description='Wormhole(%s): Asynchronous IO HTTP and HTTPS Proxy' %
-        VERSION
+        description="Wormhole(%s): Asynchronous IO HTTP and HTTPS Proxy"
+        % VERSION
     )
     parser.add_argument(
-        '-H', '--host', default='0.0.0.0',
-        help='Host to listen [default: %(default)s]'
+        "-H",
+        "--host",
+        default="0.0.0.0",
+        help="Host to listen [default: %(default)s]",
     )
     parser.add_argument(
-        '-p', '--port', type=int, default=8800,
-        help='Port to listen [default: %(default)d]'
+        "-p",
+        "--port",
+        type=int,
+        default=8800,
+        help="Port to listen [default: %(default)d]",
     )
     parser.add_argument(
-        '-a', '--authentication', default='',
-        help=('File contains username and password list '
-              'for proxy authentication [default: no authentication]')
+        "-a",
+        "--authentication",
+        default="",
+        help=(
+            "File contains username and password list "
+            "for proxy authentication [default: no authentication]"
+        ),
     )
     parser.add_argument(
-        '-S', '--syslog-host', default='DISABLED',
-        help='Syslog Host [default: %(default)s]'
+        "-S",
+        "--syslog-host",
+        default="DISABLED",
+        help="Syslog Host [default: %(default)s]",
     )
     parser.add_argument(
-        '-P', '--syslog-port', type=int, default=514,
-        help='Syslog Port to listen [default: %(default)d]'
+        "-P",
+        "--syslog-port",
+        type=int,
+        default=514,
+        help="Syslog Port to listen [default: %(default)d]",
     )
     parser.add_argument(
-        '-l', '--license', action='store_true', default=False,
-        help='Print LICENSE and exit'
+        "-l",
+        "--license",
+        action="store_true",
+        default=False,
+        help="Print LICENSE and exit",
     )
     parser.add_argument(
-        '-v', '--verbose', action='count', default=0,
-        help='Print verbose'
+        "-v", "--verbose", action="count", default=0, help="Print verbose"
     )
     args = parser.parse_args()
     if args.license:

          
@@ 62,7 78,7 @@ def main():
         print(LICENSE)
         exit()
     if not (1 <= args.port <= 65535):
-        parser.error('port must be 1-65535')
+        parser.error("port must be 1-65535")
 
     logger = get_logger(args.syslog_host, args.syslog_port, args.verbose)
     try:

          
@@ 70,15 86,14 @@ def main():
     except ImportError:
         pass
     else:
-        logger.debug(
-            '[000000][%s]: Using event loop from uvloop.' % args.host
-        )
+        logger.debug("[000000][%s]: Using event loop from uvloop." % args.host)
         asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
     loop = asyncio.new_event_loop()
     try:
         loop.run_until_complete(
             start_wormhole_server(
-                args.host, args.port,
+                args.host,
+                args.port,
                 args.authentication,
             )
         )

          
@@ 86,10 101,10 @@ def main():
     except OSError:
         pass
     except KeyboardInterrupt:
-        print('bye')
+        print("bye")
     finally:
         loop.close()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     exit(main())

          
M wormhole/server.py +51 -42
@@ 11,15 11,19 @@ from logger import get_logger
 
 
 MAX_RETRY = 3
-if sys.platform == 'win32':
+if sys.platform == "win32":
     import win32file
+
     MAX_TASKS = win32file._getmaxstdio()
 else:
     import resource
+
     MAX_TASKS = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
 
 
 wormhole_semaphore = None
+
+
 def get_wormhole_semaphore():
     max_wormholes = int(0.9 * MAX_TASKS)  # Use only 90% of open files limit.
     global wormhole_semaphore

          
@@ 33,12 37,12 @@ def debug_wormhole_semaphore(client_read
     ident = get_ident(client_reader, client_writer)
     available = wormhole_semaphore._value
     logger = get_logger()
-    logger.debug((
-        '[{id}][{client}]: Resource available: %.2f%% (%d/%d)' % (
-            100 * float(available) / MAX_TASKS,
-            available,
-            MAX_TASKS
-    )).format(**ident))
+    logger.debug(
+        (
+            "[{id}][{client}]: Resource available: %.2f%% (%d/%d)"
+            % (100 * float(available) / MAX_TASKS, available, MAX_TASKS)
+        ).format(**ident)
+    )
 
 
 async def process_wormhole(client_reader, client_writer, auth):

          
@@ 49,59 53,68 @@ async def process_wormhole(client_reader
         client_reader, MAX_RETRY, ident
     )
     if not request_line:
-        logger.debug((
-            '[{id}][{client}]: !!! Task reject (empty request)'
-        ).format(**ident))
+        logger.debug(
+            ("[{id}][{client}]: !!! Task reject (empty request)").format(
+                **ident
+            )
+        )
         return
 
-    request_fields = request_line.split(' ')
+    request_fields = request_line.split(" ")
     if len(request_fields) == 2:
         request_method, uri = request_fields
-        http_version = 'HTTP/1.0'
+        http_version = "HTTP/1.0"
     elif len(request_fields) == 3:
         request_method, uri, http_version = request_fields
     else:
-        logger.debug((
-            '[{id}][{client}]: !!! Task reject (invalid request)'
-        ).format(**ident))
+        logger.debug(
+            ("[{id}][{client}]: !!! Task reject (invalid request)").format(
+                **ident
+            )
+        )
         return
 
     if auth:
         user_ident = await verify(client_reader, client_writer, headers, auth)
         if user_ident is None:
-            logger.info((
-                '[{id}][{client}]: %s 407 %s' % (request_method, uri)
-            ).format(**ident))
+            logger.info(
+                ("[{id}][{client}]: %s 407 %s" % (request_method, uri)).format(
+                    **ident
+                )
+            )
             return
         ident = user_ident
 
-    if request_method == 'CONNECT':
+    if request_method == "CONNECT":
         async with get_wormhole_semaphore():
             debug_wormhole_semaphore(client_reader, client_writer)
             return await process_https(
-                client_reader, client_writer, request_method, uri,
-                ident
+                client_reader, client_writer, request_method, uri, ident
             )
     else:
         async with get_wormhole_semaphore():
             debug_wormhole_semaphore(client_reader, client_writer)
             return await process_http(
-                client_writer, request_method, uri, http_version,
-                headers, payload,
-                ident
+                client_writer,
+                request_method,
+                uri,
+                http_version,
+                headers,
+                payload,
+                ident,
             )
 
 
 async def limit_wormhole(client_reader, client_writer, auth):
     async with get_wormhole_semaphore():
         debug_wormhole_semaphore(client_reader, client_writer)
-        await process_wormhole(
-            client_reader, client_writer, auth
-        )
+        await process_wormhole(client_reader, client_writer, auth)
         debug_wormhole_semaphore(client_reader, client_writer)
 
 
 clients = dict()
+
+
 def accept_client(client_reader, client_writer, auth):
     logger = get_logger()
     ident = get_ident(client_reader, client_writer)

          
@@ 115,34 128,30 @@ def accept_client(client_reader, client_
     def client_done(task):
         del clients[task]
         client_writer.close()
-        logger.debug((
-            '[{id}][{client}]: Connection closed (%.5f seconds)' % (
-                time() - started_time
-            )
-        ).format(**ident))
+        logger.debug(
+            (
+                "[{id}][{client}]: Connection closed (%.5f seconds)"
+                % (time() - started_time)
+            ).format(**ident)
+        )
 
-    logger.debug((
-        '[{id}][{client}]: Connection started'
-    ).format(**ident))
+    logger.debug(("[{id}][{client}]: Connection started").format(**ident))
     task.add_done_callback(client_done)
 
 
 async def start_wormhole_server(host, port, auth):
     logger = get_logger()
     try:
-        accept = functools.partial(
-            accept_client, auth=auth
-        )
+        accept = functools.partial(accept_client, auth=auth)
         server = await asyncio.start_server(accept, host, port)
     except OSError as ex:
         logger.critical(
-            '[000000][%s]: !!! Failed to bind server at [%s:%d]: %s' % (
-                host, host, port, ex.args[1]
-            )
+            "[000000][%s]: !!! Failed to bind server at [%s:%d]: %s"
+            % (host, host, port, ex.args[1])
         )
         raise
     else:
         logger.info(
-            '[000000][%s]: wormhole bound at %s:%d' % (host, host, port)
+            "[000000][%s]: wormhole bound at %s:%d" % (host, host, port)
         )
         return server

          
M wormhole/tools.py +2 -5
@@ 1,13 1,10 @@ 
 import re
 
 
-REGEX_HOST = re.compile(
-    r'(.+?):([0-9]{1,5})'
-)
+REGEX_HOST = re.compile(r"(.+?):([0-9]{1,5})")
 
 REGEX_CONTENT_LENGTH = re.compile(
-    r'\r\nContent-Length: ([0-9]+)\r\n',
-    re.IGNORECASE
+    r"\r\nContent-Length: ([0-9]+)\r\n", re.IGNORECASE
 )
 
 

          
M wormhole/version.py +1 -2
@@ 1,2 1,1 @@ 
-VERSION = 'v3.0.0'
-
+VERSION = "v3.0.0"