Repository: abhinavsingh/proxy.py
Pull request #213 (2019-12-02 06:23:23)

Refactor

codecov[bot]: # [Codecov](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213?src=pr&el=h1) Report
> Merging [#213](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213?src=pr&el=desc) into [develop](https://codecov.io/gh/abhinavsingh/proxy.py/commit/64192250ee1b8e99526b8866d49703c6503e873a?src=pr&el=desc) will **increase** coverage by `0.32%`.
> The diff coverage is `72.8%`.

[![Impacted file tree graph](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/graphs/tree.svg?width=650&token=Zh9J7b4la2&height=150&src=pr)](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##           develop     #213      +/-   ##
===========================================
+ Coverage    72.89%   73.22%   +0.32%     
===========================================
  Files           50       56       +6     
  Lines         2638     2670      +32     
===========================================
+ Hits          1923     1955      +32     
  Misses         715      715
```


| [Impacted Files](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [proxy/http/exception/base.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9leGNlcHRpb24vYmFzZS5weQ==) | `100% <100%> (ø)` | |
| [proxy/http/parser.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9wYXJzZXIucHk=) | `96.05% <100%> (ø)` | :arrow_up: |
| [proxy/http/exception/proxy\_conn\_failed.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9leGNlcHRpb24vcHJveHlfY29ubl9mYWlsZWQucHk=) | `100% <100%> (ø)` | |
| [proxy/http/exception/http\_request\_rejected.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9leGNlcHRpb24vaHR0cF9yZXF1ZXN0X3JlamVjdGVkLnB5) | `100% <100%> (ø)` | |
| [proxy/http/exception/\_\_init\_\_.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9leGNlcHRpb24vX19pbml0X18ucHk=) | `100% <100%> (ø)` | |
| [proxy/common/constants.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvY29tbW9uL2NvbnN0YW50cy5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [proxy/http/exception/proxy\_auth\_failed.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9leGNlcHRpb24vcHJveHlfYXV0aF9mYWlsZWQucHk=) | `100% <100%> (ø)` | |
| [proxy/http/inspector/\_\_init\_\_.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9pbnNwZWN0b3IvX19pbml0X18ucHk=) | `100% <100%> (ø)` | |
| [proxy/http/inspector/transformer.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9pbnNwZWN0b3IvdHJhbnNmb3JtZXIucHk=) | `60.97% <100%> (ø)` | |
| [proxy/http/inspector/devtools.py](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree#diff-cHJveHkvaHR0cC9pbnNwZWN0b3IvZGV2dG9vbHMucHk=) | `40% <40%> (ø)` | |
| ... and [8 more](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213?src=pr&el=footer). Last update [6419225...d1d8d2a](https://codecov.io/gh/abhinavsingh/proxy.py/pull/213?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/proxy/common/constants.py b/proxy/common/constants.py
index dbd48ee..d2ec48d 100644
--- a/proxy/common/constants.py
+++ b/proxy/common/constants.py
@@ -72,3 +72,4 @@ DEFAULT_STATIC_SERVER_DIR = PROXY_PY_DIR
 DEFAULT_THREADLESS = False
 DEFAULT_TIMEOUT = 10
 DEFAULT_VERSION = False
+DEFAULT_HTTP_PORT = 80
diff --git a/proxy/http/exception.py b/proxy/http/exception.py
deleted file mode 100644
index f568394..0000000
--- a/proxy/http/exception.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    proxy.py
-    ~~~~~~~~
-    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
-    Network monitoring, controls & Application development, testing, debugging.
-
-    :copyright: (c) 2013-present by Abhinav Singh and contributors.
-    :license: BSD, see LICENSE for more details.
-"""
-from typing import Optional, Dict
-
-from .parser import HttpParser
-from .codes import httpStatusCodes
-
-from ..common.constants import PROXY_AGENT_HEADER_VALUE, PROXY_AGENT_HEADER_KEY
-from ..common.utils import build_http_response
-
-
-class HttpProtocolException(Exception):
-    """Top level HttpProtocolException exception class.
-
-    All exceptions raised during execution of Http request lifecycle MUST
-    inherit HttpProtocolException base class. Implement response() method
-    to optionally return custom response to client."""
-
-    def response(self, request: HttpParser) -> Optional[memoryview]:
-        return None  # pragma: no cover
-
-
-class HttpRequestRejected(HttpProtocolException):
-    """Generic exception that can be used to reject the client requests.
-
-    Connections can either be dropped/closed or optionally an
-    HTTP status code can be returned."""
-
-    def __init__(self,
-                 status_code: Optional[int] = None,
-                 reason: Optional[bytes] = None,
-                 headers: Optional[Dict[bytes, bytes]] = None,
-                 body: Optional[bytes] = None):
-        self.status_code: Optional[int] = status_code
-        self.reason: Optional[bytes] = reason
-        self.headers: Optional[Dict[bytes, bytes]] = headers
-        self.body: Optional[bytes] = body
-
-    def response(self, _request: HttpParser) -> Optional[memoryview]:
-        if self.status_code:
-            return memoryview(build_http_response(
-                status_code=self.status_code,
-                reason=self.reason,
-                headers=self.headers,
-                body=self.body
-            ))
-        return None
-
-
-class ProxyConnectionFailed(HttpProtocolException):
-    """Exception raised when HttpProxyPlugin is unable to establish connection to upstream server."""
-
-    RESPONSE_PKT = memoryview(build_http_response(
-        httpStatusCodes.BAD_GATEWAY,
-        reason=b'Bad Gateway',
-        headers={
-            PROXY_AGENT_HEADER_KEY: PROXY_AGENT_HEADER_VALUE,
-            b'Connection': b'close'
-        },
-        body=b'Bad Gateway'
-    ))
-
-    def __init__(self, host: str, port: int, reason: str):
-        self.host: str = host
-        self.port: int = port
-        self.reason: str = reason
-
-    def response(self, _request: HttpParser) -> memoryview:
-        return self.RESPONSE_PKT
-
-
-class ProxyAuthenticationFailed(HttpProtocolException):
-    """Exception raised when Http Proxy auth is enabled and
-    incoming request doesn't present necessary credentials."""
-
-    RESPONSE_PKT = memoryview(build_http_response(
-        httpStatusCodes.PROXY_AUTH_REQUIRED,
-        reason=b'Proxy Authentication Required',
-        headers={
-            PROXY_AGENT_HEADER_KEY: PROXY_AGENT_HEADER_VALUE,
-            b'Proxy-Authenticate': b'Basic',
-            b'Connection': b'close',
-        },
-        body=b'Proxy Authentication Required'))
-
-    def response(self, _request: HttpParser) -> memoryview:
-        return self.RESPONSE_PKT
diff --git a/proxy/http/exception/__init__.py b/proxy/http/exception/__init__.py
new file mode 100644
index 0000000..513d2bd
--- /dev/null
+++ b/proxy/http/exception/__init__.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from .base import HttpProtocolException
+from .http_request_rejected import HttpRequestRejected
+from .proxy_auth_failed import ProxyAuthenticationFailed
+from .proxy_conn_failed import ProxyConnectionFailed
+
+__all__ = [
+    'HttpProtocolException',
+    'HttpRequestRejected',
+    'ProxyAuthenticationFailed',
+    'ProxyConnectionFailed',
+]
diff --git a/proxy/http/exception/base.py b/proxy/http/exception/base.py
new file mode 100644
index 0000000..65138e8
--- /dev/null
+++ b/proxy/http/exception/base.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from typing import Optional
+
+from ..parser import HttpParser
+
+
+class HttpProtocolException(Exception):
+    """Top level HttpProtocolException exception class.
+
+    All exceptions raised during execution of Http request lifecycle MUST
+    inherit HttpProtocolException base class. Implement response() method
+    to optionally return custom response to client."""
+
+    def response(self, request: HttpParser) -> Optional[memoryview]:
+        return None  # pragma: no cover
diff --git a/proxy/http/exception/http_request_rejected.py b/proxy/http/exception/http_request_rejected.py
new file mode 100644
index 0000000..46fd9b0
--- /dev/null
+++ b/proxy/http/exception/http_request_rejected.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from typing import Optional, Dict
+
+from .base import HttpProtocolException
+from ..parser import HttpParser
+from ...common.utils import build_http_response
+
+
+class HttpRequestRejected(HttpProtocolException):
+    """Generic exception that can be used to reject the client requests.
+
+    Connections can either be dropped/closed or optionally an
+    HTTP status code can be returned."""
+
+    def __init__(self,
+                 status_code: Optional[int] = None,
+                 reason: Optional[bytes] = None,
+                 headers: Optional[Dict[bytes, bytes]] = None,
+                 body: Optional[bytes] = None):
+        self.status_code: Optional[int] = status_code
+        self.reason: Optional[bytes] = reason
+        self.headers: Optional[Dict[bytes, bytes]] = headers
+        self.body: Optional[bytes] = body
+
+    def response(self, _request: HttpParser) -> Optional[memoryview]:
+        if self.status_code:
+            return memoryview(build_http_response(
+                status_code=self.status_code,
+                reason=self.reason,
+                headers=self.headers,
+                body=self.body
+            ))
+        return None
diff --git a/proxy/http/exception/proxy_auth_failed.py b/proxy/http/exception/proxy_auth_failed.py
new file mode 100644
index 0000000..ae1c6a4
--- /dev/null
+++ b/proxy/http/exception/proxy_auth_failed.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from .base import HttpProtocolException
+from ..parser import HttpParser
+from ..codes import httpStatusCodes
+
+from ...common.constants import PROXY_AGENT_HEADER_VALUE, PROXY_AGENT_HEADER_KEY
+from ...common.utils import build_http_response
+
+
+class ProxyAuthenticationFailed(HttpProtocolException):
+    """Exception raised when Http Proxy auth is enabled and
+    incoming request doesn't present necessary credentials."""
+
+    RESPONSE_PKT = memoryview(build_http_response(
+        httpStatusCodes.PROXY_AUTH_REQUIRED,
+        reason=b'Proxy Authentication Required',
+        headers={
+            PROXY_AGENT_HEADER_KEY: PROXY_AGENT_HEADER_VALUE,
+            b'Proxy-Authenticate': b'Basic',
+            b'Connection': b'close',
+        },
+        body=b'Proxy Authentication Required'))
+
+    def response(self, _request: HttpParser) -> memoryview:
+        return self.RESPONSE_PKT
diff --git a/proxy/http/exception/proxy_conn_failed.py b/proxy/http/exception/proxy_conn_failed.py
new file mode 100644
index 0000000..0cec224
--- /dev/null
+++ b/proxy/http/exception/proxy_conn_failed.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from .base import HttpProtocolException
+from ..parser import HttpParser
+from ..codes import httpStatusCodes
+
+from ...common.constants import PROXY_AGENT_HEADER_VALUE, PROXY_AGENT_HEADER_KEY
+from ...common.utils import build_http_response
+
+
+class ProxyConnectionFailed(HttpProtocolException):
+    """Exception raised when HttpProxyPlugin is unable to establish connection to upstream server."""
+
+    RESPONSE_PKT = memoryview(build_http_response(
+        httpStatusCodes.BAD_GATEWAY,
+        reason=b'Bad Gateway',
+        headers={
+            PROXY_AGENT_HEADER_KEY: PROXY_AGENT_HEADER_VALUE,
+            b'Connection': b'close'
+        },
+        body=b'Bad Gateway'
+    ))
+
+    def __init__(self, host: str, port: int, reason: str):
+        self.host: str = host
+        self.port: int = port
+        self.reason: str = reason
+
+    def response(self, _request: HttpParser) -> memoryview:
+        return self.RESPONSE_PKT
diff --git a/proxy/http/inspector/__init__.py b/proxy/http/inspector/__init__.py
new file mode 100644
index 0000000..8e5d4aa
--- /dev/null
+++ b/proxy/http/inspector/__init__.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from .devtools import DevtoolsProtocolPlugin
+
+__all__ = [
+    'DevtoolsProtocolPlugin',
+]
diff --git a/proxy/http/inspector/devtools.py b/proxy/http/inspector/devtools.py
new file mode 100644
index 0000000..2e9a983
--- /dev/null
+++ b/proxy/http/inspector/devtools.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+import json
+import logging
+from typing import List, Tuple, Any, Dict
+
+from .transformer import CoreEventsToDevtoolsProtocol
+from ..parser import HttpParser
+from ..websocket import WebsocketFrame, websocketOpcodes
+from ..server import HttpWebServerBasePlugin, httpProtocolTypes
+
+from ...common.utils import bytes_, text_
+from ...core.event import EventSubscriber
+
+logger = logging.getLogger(__name__)
+
+
+class DevtoolsProtocolPlugin(HttpWebServerBasePlugin):
+    """Speaks DevTools protocol with client over websocket.
+
+    - It responds to DevTools client request methods and also
+      relay proxy.py core events to the client.
+    - Core events are transformed into DevTools protocol format before
+      dispatching to client.
+    - Core events unrelated to DevTools protocol are dropped.
+    """
+
+    DOC_URL = 'http://dashboard.proxy.py'
+
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
+        super().__init__(*args, **kwargs)
+        self.subscriber = EventSubscriber(self.event_queue)
+
+    def routes(self) -> List[Tuple[int, str]]:
+        return [
+            (httpProtocolTypes.WEBSOCKET, text_(self.flags.devtools_ws_path))
+        ]
+
+    def handle_request(self, request: HttpParser) -> None:
+        raise NotImplementedError('This should have never been called')
+
+    def on_websocket_open(self) -> None:
+        self.subscriber.subscribe(
+            lambda event: CoreEventsToDevtoolsProtocol.transformer(self.client, event))
+
+    def on_websocket_message(self, frame: WebsocketFrame) -> None:
+        try:
+            assert frame.data
+            message = json.loads(frame.data)
+        except UnicodeDecodeError:
+            logger.error(frame.data)
+            logger.info(frame.opcode)
+            return
+        self.handle_devtools_message(message)
+
+    def on_websocket_close(self) -> None:
+        self.subscriber.unsubscribe()
+
+    def handle_devtools_message(self, message: Dict[str, Any]) -> None:
+        frame = WebsocketFrame()
+        frame.fin = True
+        frame.opcode = websocketOpcodes.TEXT_FRAME
+
+        # logger.info(message)
+        method = message['method']
+        if method in (
+            'Page.canScreencast',
+            'Network.canEmulateNetworkConditions',
+            'Emulation.canEmulate',
+        ):
+            data: Dict[str, Any] = {
+                'result': False
+            }
+        elif method == 'Page.getResourceTree':
+            data = {
+                'result': {
+                    'frameTree': {
+                        'frame': {
+                            'id': 1,
+                            'url': DevtoolsProtocolPlugin.DOC_URL,
+                            'mimeType': 'other',
+                        },
+                        'childFrames': [],
+                        'resources': []
+                    }
+                }
+            }
+        elif method == 'Network.getResponseBody':
+            connection_id = message['params']['requestId']
+            data = {
+                'result': {
+                    'body': text_(CoreEventsToDevtoolsProtocol.RESPONSES[connection_id]),
+                    'base64Encoded': False,
+                }
+            }
+        else:
+            logging.warning('Unhandled devtools method %s', method)
+            data = {}
+
+        data['id'] = message['id']
+        frame.data = bytes_(json.dumps(data))
+        self.client.queue(memoryview(frame.build()))
diff --git a/proxy/http/inspector.py b/proxy/http/inspector/transformer.py
similarity index 55%
rename from proxy/http/inspector.py
rename to proxy/http/inspector/transformer.py
index 398602e..32d6b65 100644
--- a/proxy/http/inspector.py
+++ b/proxy/http/inspector/transformer.py
@@ -9,114 +9,23 @@
     :license: BSD, see LICENSE for more details.
 """
 import json
-import logging
 import secrets
 import time
-from typing import List, Tuple, Any, Dict
+from typing import Any, Dict
 
-from .parser import HttpParser
-from .websocket import WebsocketFrame, websocketOpcodes
-from .server import HttpWebServerBasePlugin, httpProtocolTypes
+from ..websocket import WebsocketFrame
+from ...common.constants import PROXY_PY_START_TIME
+from ...common.utils import bytes_
+from ...core.connection import TcpClientConnection
+from ...core.event import eventNames
 
-from ..common.constants import PROXY_PY_START_TIME
-from ..common.utils import bytes_, text_
-from ..core.connection import TcpClientConnection
-from ..core.event import EventSubscriber, eventNames
 
-logger = logging.getLogger(__name__)
-
-
-class DevtoolsProtocolPlugin(HttpWebServerBasePlugin):
-    """Speaks DevTools protocol with client over websocket.
-
-    - It responds to DevTools client request methods and also
-      relay proxy.py core events to the client.
-    - Core events are transformed into DevTools protocol format before
-      dispatching to client.
-    - Core events unrelated to DevTools protocol are dropped.
-    """
+class CoreEventsToDevtoolsProtocol:
 
     DOC_URL = 'http://dashboard.proxy.py'
     FRAME_ID = secrets.token_hex(8)
     LOADER_ID = secrets.token_hex(8)
 
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
-        self.subscriber = EventSubscriber(self.event_queue)
-
-    def routes(self) -> List[Tuple[int, str]]:
-        return [
-            (httpProtocolTypes.WEBSOCKET, text_(self.flags.devtools_ws_path))
-        ]
-
-    def handle_request(self, request: HttpParser) -> None:
-        raise NotImplementedError('This should have never been called')
-
-    def on_websocket_open(self) -> None:
-        self.subscriber.subscribe(
-            lambda event: CoreEventsToDevtoolsProtocol.transformer(self.client, event))
-
-    def on_websocket_message(self, frame: WebsocketFrame) -> None:
-        try:
-            assert frame.data
-            message = json.loads(frame.data)
-        except UnicodeDecodeError:
-            logger.error(frame.data)
-            logger.info(frame.opcode)
-            return
-        self.handle_devtools_message(message)
-
-    def on_websocket_close(self) -> None:
-        self.subscriber.unsubscribe()
-
-    def handle_devtools_message(self, message: Dict[str, Any]) -> None:
-        frame = WebsocketFrame()
-        frame.fin = True
-        frame.opcode = websocketOpcodes.TEXT_FRAME
-
-        # logger.info(message)
-        method = message['method']
-        if method in (
-            'Page.canScreencast',
-            'Network.canEmulateNetworkConditions',
-            'Emulation.canEmulate',
-        ):
-            data: Dict[str, Any] = {
-                'result': False
-            }
-        elif method == 'Page.getResourceTree':
-            data = {
-                'result': {
-                    'frameTree': {
-                        'frame': {
-                            'id': 1,
-                            'url': DevtoolsProtocolPlugin.DOC_URL,
-                            'mimeType': 'other',
-                        },
-                        'childFrames': [],
-                        'resources': []
-                    }
-                }
-            }
-        elif method == 'Network.getResponseBody':
-            connection_id = message['params']['requestId']
-            data = {
-                'result': {
-                    'body': text_(CoreEventsToDevtoolsProtocol.RESPONSES[connection_id]),
-                    'base64Encoded': False,
-                }
-            }
-        else:
-            logging.warning('Unhandled devtools method %s', method)
-            data = {}
-
-        data['id'] = message['id']
-        frame.data = bytes_(json.dumps(data))
-        self.client.queue(memoryview(frame.build()))
-
-
-class CoreEventsToDevtoolsProtocol:
-
     RESPONSES: Dict[str, bytes] = {}
 
     @staticmethod
@@ -145,9 +54,9 @@ class CoreEventsToDevtoolsProtocol:
         now = time.time()
         return {
             'requestId': event['request_id'],
-            'frameId': DevtoolsProtocolPlugin.FRAME_ID,
-            'loaderId': DevtoolsProtocolPlugin.LOADER_ID,
-            'documentURL': DevtoolsProtocolPlugin.DOC_URL,
+            'frameId': CoreEventsToDevtoolsProtocol.FRAME_ID,
+            'loaderId': CoreEventsToDevtoolsProtocol.LOADER_ID,
+            'documentURL': CoreEventsToDevtoolsProtocol.DOC_URL,
             'timestamp': now - PROXY_PY_START_TIME,
             'wallTime': now,
             'hasUserGesture': False,
@@ -172,8 +81,8 @@ class CoreEventsToDevtoolsProtocol:
     def response_headers_complete(event: Dict[str, Any]) -> Dict[str, Any]:
         return {
             'requestId': event['request_id'],
-            'frameId': DevtoolsProtocolPlugin.FRAME_ID,
-            'loaderId': DevtoolsProtocolPlugin.LOADER_ID,
+            'frameId': CoreEventsToDevtoolsProtocol.FRAME_ID,
+            'loaderId': CoreEventsToDevtoolsProtocol.LOADER_ID,
             'timestamp': time.time(),
             'type': event['event_payload']['headers']['content-type']
             if event['event_payload']['headers'].has_header('content-type')
diff --git a/proxy/http/parser.py b/proxy/http/parser.py
index c30cc8a..80945b5 100644
--- a/proxy/http/parser.py
+++ b/proxy/http/parser.py
@@ -14,7 +14,7 @@ from typing import TypeVar, NamedTuple, Optional, Dict, Type, Tuple, List
 from .methods import httpMethods
 from .chunk_parser import ChunkParser, chunkParserStates
 
-from ..common.constants import DEFAULT_DISABLE_HEADERS, COLON, CRLF, WHITESPACE, HTTP_1_1
+from ..common.constants import DEFAULT_DISABLE_HEADERS, COLON, CRLF, WHITESPACE, HTTP_1_1, DEFAULT_HTTP_PORT
 from ..common.utils import build_http_request, find_http_line, text_
 
 
@@ -115,7 +115,7 @@ class HttpParser:
                 self.host, self.port = u.hostname, u.port
             elif self.url:
                 self.host, self.port = self.url.hostname, self.url.port \
-                    if self.url.port else 80
+                    if self.url.port else DEFAULT_HTTP_PORT
             else:
                 raise KeyError(
                     'Invalid request. Method: %r, Url: %r' %
diff --git a/proxy/http/proxy/__init__.py b/proxy/http/proxy/__init__.py
new file mode 100644
index 0000000..afd3527
--- /dev/null
+++ b/proxy/http/proxy/__init__.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from .plugin import HttpProxyBasePlugin
+from .server import HttpProxyPlugin
+
+__all__ = [
+    'HttpProxyBasePlugin',
+    'HttpProxyPlugin',
+]
diff --git a/proxy/http/proxy/plugin.py b/proxy/http/proxy/plugin.py
new file mode 100644
index 0000000..75b5c9b
--- /dev/null
+++ b/proxy/http/proxy/plugin.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from abc import ABC, abstractmethod
+from typing import Optional
+
+from ..parser import HttpParser
+
+from ...common.flags import Flags
+
+from ...core.event import EventQueue
+from ...core.connection import TcpClientConnection
+
+
+class HttpProxyBasePlugin(ABC):
+    """Base HttpProxyPlugin Plugin class.
+
+    Implement various lifecycle event methods to customize behavior."""
+
+    def __init__(
+            self,
+            uid: str,
+            flags: Flags,
+            client: TcpClientConnection,
+            event_queue: EventQueue) -> None:
+        self.uid = uid                  # pragma: no cover
+        self.flags = flags              # pragma: no cover
+        self.client = client            # pragma: no cover
+        self.event_queue = event_queue  # pragma: no cover
+
+    def name(self) -> str:
+        """A unique name for your plugin.
+
+        Defaults to name of the class. This helps plugin developers to directly
+        access a specific plugin by its name."""
+        return self.__class__.__name__      # pragma: no cover
+
+    @abstractmethod
+    def before_upstream_connection(
+            self, request: HttpParser) -> Optional[HttpParser]:
+        """Handler called just before Proxy upstream connection is established.
+
+        Return optionally modified request object.
+        Raise HttpRequestRejected or HttpProtocolException directly to drop the connection."""
+        return request  # pragma: no cover
+
+    @abstractmethod
+    def handle_client_request(
+            self, request: HttpParser) -> Optional[HttpParser]:
+        """Handler called before dispatching client request to upstream.
+
+        Note: For pipelined (keep-alive) connections, this handler can be
+        called multiple times, for each request sent to upstream.
+
+        Note: If TLS interception is enabled, this handler can
+        be called multiple times if client exchanges multiple
+        requests over same SSL session.
+
+        Return optionally modified request object to dispatch to upstream.
+        Return None to drop the request data, e.g. in case a response has already been queued.
+        Raise HttpRequestRejected or HttpProtocolException directly to
+            teardown the connection with client.
+        """
+        return request  # pragma: no cover
+
+    @abstractmethod
+    def handle_upstream_chunk(self, chunk: memoryview) -> memoryview:
+        """Handler called right after receiving raw response from upstream server.
+
+        For HTTPS connections, chunk will be encrypted unless
+        TLS interception is also enabled."""
+        return chunk  # pragma: no cover
+
+    @abstractmethod
+    def on_upstream_connection_close(self) -> None:
+        """Handler called right after upstream connection has been closed."""
+        pass  # pragma: no cover
diff --git a/proxy/http/proxy.py b/proxy/http/proxy/server.py
similarity index 85%
rename from proxy/http/proxy.py
rename to proxy/http/proxy/server.py
index 1ea74e2..f036ec9 100644
--- a/proxy/http/proxy.py
+++ b/proxy/http/proxy/server.py
@@ -16,91 +16,25 @@ import socket
 import time
 import errno
 import logging
-from abc import ABC, abstractmethod
 from typing import Optional, List, Union, Dict, cast, Any, Tuple
 
-from proxy.core.event import eventNames, EventQueue
-from .handler import HttpProtocolHandlerPlugin
-from .exception import HttpProtocolException, ProxyConnectionFailed, ProxyAuthenticationFailed
-from .codes import httpStatusCodes
-from .parser import HttpParser, httpParserStates, httpParserTypes
-from .methods import httpMethods
+from .plugin import HttpProxyBasePlugin
+from ..handler import HttpProtocolHandlerPlugin
+from ..exception import HttpProtocolException, ProxyConnectionFailed, ProxyAuthenticationFailed
+from ..codes import httpStatusCodes
+from ..parser import HttpParser, httpParserStates, httpParserTypes
+from ..methods import httpMethods
 
-from ..common.types import HasFileno
-from ..common.flags import Flags
-from ..common.constants import PROXY_AGENT_HEADER_VALUE
-from ..common.utils import build_http_response, text_
+from ...common.types import HasFileno
+from ...common.constants import PROXY_AGENT_HEADER_VALUE
+from ...common.utils import build_http_response, text_
 
-from ..core.connection import TcpClientConnection, TcpServerConnection, TcpConnectionUninitializedException
+from ...core.event import eventNames
+from ...core.connection import TcpServerConnection, TcpConnectionUninitializedException
 
 logger = logging.getLogger(__name__)
 
 
-class HttpProxyBasePlugin(ABC):
-    """Base HttpProxyPlugin Plugin class.
-
-    Implement various lifecycle event methods to customize behavior."""
-
-    def __init__(
-            self,
-            uid: str,
-            flags: Flags,
-            client: TcpClientConnection,
-            event_queue: EventQueue) -> None:
-        self.uid = uid                  # pragma: no cover
-        self.flags = flags              # pragma: no cover
-        self.client = client            # pragma: no cover
-        self.event_queue = event_queue  # pragma: no cover
-
-    def name(self) -> str:
-        """A unique name for your plugin.
-
-        Defaults to name of the class. This helps plugin developers to directly
-        access a specific plugin by its name."""
-        return self.__class__.__name__      # pragma: no cover
-
-    @abstractmethod
-    def before_upstream_connection(
-            self, request: HttpParser) -> Optional[HttpParser]:
-        """Handler called just before Proxy upstream connection is established.
-
-        Return optionally modified request object.
-        Raise HttpRequestRejected or HttpProtocolException directly to drop the connection."""
-        return request  # pragma: no cover
-
-    @abstractmethod
-    def handle_client_request(
-            self, request: HttpParser) -> Optional[HttpParser]:
-        """Handler called before dispatching client request to upstream.
-
-        Note: For pipelined (keep-alive) connections, this handler can be
-        called multiple times, for each request sent to upstream.
-
-        Note: If TLS interception is enabled, this handler can
-        be called multiple times if client exchanges multiple
-        requests over same SSL session.
-
-        Return optionally modified request object to dispatch to upstream.
-        Return None to drop the request data, e.g. in case a response has already been queued.
-        Raise HttpRequestRejected or HttpProtocolException directly to
-            teardown the connection with client.
-        """
-        return request  # pragma: no cover
-
-    @abstractmethod
-    def handle_upstream_chunk(self, chunk: memoryview) -> memoryview:
-        """Handler called right after receiving raw response from upstream server.
-
-        For HTTPS connections, chunk will be encrypted unless
-        TLS interception is also enabled."""
-        return chunk  # pragma: no cover
-
-    @abstractmethod
-    def on_upstream_connection_close(self) -> None:
-        """Handler called right after upstream connection has been closed."""
-        pass  # pragma: no cover
-
-
 class HttpProxyPlugin(HttpProtocolHandlerPlugin):
     """HttpProtocolHandler plugin which implements HttpProxy specifications."""
 
diff --git a/proxy/http/server/__init__.py b/proxy/http/server/__init__.py
new file mode 100644
index 0000000..059c2cc
--- /dev/null
+++ b/proxy/http/server/__init__.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from .web import HttpWebServerPlugin
+from .pac_plugin import HttpWebServerPacFilePlugin
+from .plugin import HttpWebServerBasePlugin
+from .protocols import httpProtocolTypes
+
+__all__ = [
+    'HttpWebServerPlugin',
+    'HttpWebServerPacFilePlugin',
+    'HttpWebServerBasePlugin',
+    'httpProtocolTypes',
+]
diff --git a/proxy/http/server/pac_plugin.py b/proxy/http/server/pac_plugin.py
new file mode 100644
index 0000000..0dfa0b4
--- /dev/null
+++ b/proxy/http/server/pac_plugin.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+import gzip
+from typing import List, Tuple, Optional, Any
+
+from .plugin import HttpWebServerBasePlugin
+from .protocols import httpProtocolTypes
+from ..websocket import WebsocketFrame
+from ..parser import HttpParser
+from ...common.utils import bytes_, text_, build_http_response
+
+
+class HttpWebServerPacFilePlugin(HttpWebServerBasePlugin):
+
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
+        super().__init__(*args, **kwargs)
+        self.pac_file_response: Optional[memoryview] = None
+        self.cache_pac_file_response()
+
+    def routes(self) -> List[Tuple[int, str]]:
+        if self.flags.pac_file_url_path:
+            return [
+                (httpProtocolTypes.HTTP, text_(self.flags.pac_file_url_path)),
+                (httpProtocolTypes.HTTPS, text_(self.flags.pac_file_url_path)),
+            ]
+        return []   # pragma: no cover
+
+    def handle_request(self, request: HttpParser) -> None:
+        if self.flags.pac_file and self.pac_file_response:
+            self.client.queue(self.pac_file_response)
+
+    def on_websocket_open(self) -> None:
+        pass    # pragma: no cover
+
+    def on_websocket_message(self, frame: WebsocketFrame) -> None:
+        pass    # pragma: no cover
+
+    def on_websocket_close(self) -> None:
+        pass    # pragma: no cover
+
+    def cache_pac_file_response(self) -> None:
+        if self.flags.pac_file:
+            try:
+                with open(self.flags.pac_file, 'rb') as f:
+                    content = f.read()
+            except IOError:
+                content = bytes_(self.flags.pac_file)
+            self.pac_file_response = memoryview(build_http_response(
+                200, reason=b'OK', headers={
+                    b'Content-Type': b'application/x-ns-proxy-autoconfig',
+                    b'Content-Encoding': b'gzip',
+                }, body=gzip.compress(content)
+            ))
diff --git a/proxy/http/server/plugin.py b/proxy/http/server/plugin.py
new file mode 100644
index 0000000..64491a3
--- /dev/null
+++ b/proxy/http/server/plugin.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from abc import ABC, abstractmethod
+from typing import List, Tuple
+
+from ..websocket import WebsocketFrame
+from ..parser import HttpParser
+
+from ...common.flags import Flags
+from ...core.connection import TcpClientConnection
+from ...core.event import EventQueue
+
+
+class HttpWebServerBasePlugin(ABC):
+    """Web Server Plugin for routing of requests."""
+
+    def __init__(
+            self,
+            uid: str,
+            flags: Flags,
+            client: TcpClientConnection,
+            event_queue: EventQueue):
+        self.uid = uid
+        self.flags = flags
+        self.client = client
+        self.event_queue = event_queue
+
+    @abstractmethod
+    def routes(self) -> List[Tuple[int, str]]:
+        """Return List(protocol, path) that this plugin handles."""
+        raise NotImplementedError()     # pragma: no cover
+
+    @abstractmethod
+    def handle_request(self, request: HttpParser) -> None:
+        """Handle the request and serve response."""
+        raise NotImplementedError()     # pragma: no cover
+
+    @abstractmethod
+    def on_websocket_open(self) -> None:
+        """Called when websocket handshake has finished."""
+        raise NotImplementedError()     # pragma: no cover
+
+    @abstractmethod
+    def on_websocket_message(self, frame: WebsocketFrame) -> None:
+        """Handle websocket frame."""
+        raise NotImplementedError()     # pragma: no cover
+
+    @abstractmethod
+    def on_websocket_close(self) -> None:
+        """Called when websocket connection has been closed."""
+        raise NotImplementedError()     # pragma: no cover
diff --git a/proxy/http/server/protocols.py b/proxy/http/server/protocols.py
new file mode 100644
index 0000000..e2a99ae
--- /dev/null
+++ b/proxy/http/server/protocols.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+"""
+    proxy.py
+    ~~~~~~~~
+    ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
+    Network monitoring, controls & Application development, testing, debugging.
+
+    :copyright: (c) 2013-present by Abhinav Singh and contributors.
+    :license: BSD, see LICENSE for more details.
+"""
+from typing import NamedTuple
+
+HttpProtocolTypes = NamedTuple('HttpProtocolTypes', [
+    ('HTTP', int),
+    ('HTTPS', int),
+    ('WEBSOCKET', int),
+])
+httpProtocolTypes = HttpProtocolTypes(1, 2, 3)
diff --git a/proxy/http/server.py b/proxy/http/server/web.py
similarity index 69%
rename from proxy/http/server.py
rename to proxy/http/server/web.py
index 6651795..b1b9475 100644
--- a/proxy/http/server.py
+++ b/proxy/http/server/web.py
@@ -15,116 +15,23 @@ import logging
 import os
 import mimetypes
 import socket
-from abc import ABC, abstractmethod
-from typing import List, Tuple, Optional, NamedTuple, Dict, Union, Any, Pattern
+from typing import List, Tuple, Optional, Dict, Union, Any, Pattern
 
-from .exception import HttpProtocolException
-from .websocket import WebsocketFrame, websocketOpcodes
-from .codes import httpStatusCodes
-from .parser import HttpParser, httpParserStates, httpParserTypes
-from .handler import HttpProtocolHandlerPlugin
+from .plugin import HttpWebServerBasePlugin
+from .protocols import httpProtocolTypes
+from ..exception import HttpProtocolException
+from ..websocket import WebsocketFrame, websocketOpcodes
+from ..codes import httpStatusCodes
+from ..parser import HttpParser, httpParserStates, httpParserTypes
+from ..handler import HttpProtocolHandlerPlugin
 
-from ..common.utils import bytes_, text_, build_http_response, build_websocket_handshake_response
-from ..common.flags import Flags
-from ..common.constants import PROXY_AGENT_HEADER_VALUE
-from ..common.types import HasFileno
-from ..core.connection import TcpClientConnection
-from ..core.event import EventQueue
+from ...common.utils import bytes_, text_, build_http_response, build_websocket_handshake_response
+from ...common.constants import PROXY_AGENT_HEADER_VALUE
+from ...common.types import HasFileno
 
 logger = logging.getLogger(__name__)
 
 
-HttpProtocolTypes = NamedTuple('HttpProtocolTypes', [
-    ('HTTP', int),
-    ('HTTPS', int),
-    ('WEBSOCKET', int),
-])
-httpProtocolTypes = HttpProtocolTypes(1, 2, 3)
-
-
-class HttpWebServerBasePlugin(ABC):
-    """Web Server Plugin for routing of requests."""
-
-    def __init__(
-            self,
-            uid: str,
-            flags: Flags,
-            client: TcpClientConnection,
-            event_queue: EventQueue):
-        self.uid = uid
-        self.flags = flags
-        self.client = client
-        self.event_queue = event_queue
-
-    @abstractmethod
-    def routes(self) -> List[Tuple[int, str]]:
-        """Return List(protocol, path) that this plugin handles."""
-        raise NotImplementedError()     # pragma: no cover
-
-    @abstractmethod
-    def handle_request(self, request: HttpParser) -> None:
-        """Handle the request and serve response."""
-        raise NotImplementedError()     # pragma: no cover
-
-    @abstractmethod
-    def on_websocket_open(self) -> None:
-        """Called when websocket handshake has finished."""
-        raise NotImplementedError()     # pragma: no cover
-
-    @abstractmethod
-    def on_websocket_message(self, frame: WebsocketFrame) -> None:
-        """Handle websocket frame."""
-        raise NotImplementedError()     # pragma: no cover
-
-    @abstractmethod
-    def on_websocket_close(self) -> None:
-        """Called when websocket connection has been closed."""
-        raise NotImplementedError()     # pragma: no cover
-
-
-class HttpWebServerPacFilePlugin(HttpWebServerBasePlugin):
-
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
-        self.pac_file_response: Optional[memoryview] = None
-        self.cache_pac_file_response()
-
-    def routes(self) -> List[Tuple[int, str]]:
-        if self.flags.pac_file_url_path:
-            return [
-                (httpProtocolTypes.HTTP, text_(self.flags.pac_file_url_path)),
-                (httpProtocolTypes.HTTPS, text_(self.flags.pac_file_url_path)),
-            ]
-        return []   # pragma: no cover
-
-    def handle_request(self, request: HttpParser) -> None:
-        if self.flags.pac_file and self.pac_file_response:
-            self.client.queue(self.pac_file_response)
-
-    def on_websocket_open(self) -> None:
-        pass    # pragma: no cover
-
-    def on_websocket_message(self, frame: WebsocketFrame) -> None:
-        pass    # pragma: no cover
-
-    def on_websocket_close(self) -> None:
-        pass    # pragma: no cover
-
-    def cache_pac_file_response(self) -> None:
-        if self.flags.pac_file:
-            try:
-                with open(self.flags.pac_file, 'rb') as f:
-                    content = f.read()
-            except IOError:
-                content = bytes_(self.flags.pac_file)
-            self.pac_file_response = memoryview(build_http_response(
-                200, reason=b'OK', headers={
-                    b'Content-Type': b'application/x-ns-proxy-autoconfig',
-                    b'Content-Encoding': b'gzip',
-                }, body=gzip.compress(content)
-            ))
-
-
 class HttpWebServerPlugin(HttpProtocolHandlerPlugin):
     """HttpProtocolHandler plugin which handles incoming requests to local web server."""
 
diff --git a/proxy/plugin/reverse_proxy.py b/proxy/plugin/reverse_proxy.py
index 42048d3..3675b9e 100644
--- a/proxy/plugin/reverse_proxy.py
+++ b/proxy/plugin/reverse_proxy.py
@@ -12,7 +12,7 @@ import random
 from typing import List, Tuple
 from urllib import parse as urlparse
 
-from ..common.constants import DEFAULT_BUFFER_SIZE
+from ..common.constants import DEFAULT_BUFFER_SIZE, DEFAULT_HTTP_PORT
 from ..common.utils import socket_connection, text_
 from ..http.parser import HttpParser
 from ..http.websocket import WebsocketFrame
@@ -58,7 +58,7 @@ class ReverseProxyPlugin(HttpWebServerBasePlugin):
         upstream = random.choice(ReverseProxyPlugin.REVERSE_PROXY_PASS)
         url = urlparse.urlsplit(upstream)
         assert url.hostname
-        with socket_connection((text_(url.hostname), url.port if url.port else 80)) as conn:
+        with socket_connection((text_(url.hostname), url.port if url.port else DEFAULT_HTTP_PORT)) as conn:
             conn.send(request.build())
             self.client.queue(memoryview(conn.recv(DEFAULT_BUFFER_SIZE)))
 


Test patch:
diff --git a/tests/common/test_utils.py b/tests/common/test_utils.py
index 27ada7b..b75b2b5 100644
--- a/tests/common/test_utils.py
+++ b/tests/common/test_utils.py
@@ -13,6 +13,7 @@ import unittest
 from unittest import mock
 
 from proxy.common.constants import DEFAULT_IPV6_HOSTNAME, DEFAULT_IPV4_HOSTNAME, DEFAULT_PORT, DEFAULT_TIMEOUT
+from proxy.common.constants import DEFAULT_HTTP_PORT
 from proxy.common.utils import new_socket_connection, socket_connection
 
 
@@ -21,7 +22,7 @@ class TestSocketConnectionUtils(unittest.TestCase):
     def setUp(self) -> None:
         self.addr_ipv4 = (str(DEFAULT_IPV4_HOSTNAME), DEFAULT_PORT)
         self.addr_ipv6 = (str(DEFAULT_IPV6_HOSTNAME), DEFAULT_PORT)
-        self.addr_dual = ('httpbin.org', 80)
+        self.addr_dual = ('httpbin.org', DEFAULT_HTTP_PORT)
 
     @mock.patch('socket.socket')
     def test_new_socket_connection_ipv4(self, mock_socket: mock.Mock) -> None:
diff --git a/tests/http/test_http_proxy.py b/tests/http/test_http_proxy.py
index e3d861a..3bb2648 100644
--- a/tests/http/test_http_proxy.py
+++ b/tests/http/test_http_proxy.py
@@ -12,6 +12,7 @@ import unittest
 import selectors
 from unittest import mock
 
+from proxy.common.constants import DEFAULT_HTTP_PORT
 from proxy.common.flags import Flags
 from proxy.http.proxy import HttpProxyPlugin
 from proxy.http.handler import HttpProtocolHandler
@@ -45,7 +46,7 @@ class TestHttpProxyPlugin(unittest.TestCase):
     def test_proxy_plugin_initialized(self) -> None:
         self.plugin.assert_called()
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_proxy_plugin_on_and_before_upstream_connection(
             self,
             mock_server_conn: mock.Mock) -> None:
@@ -65,11 +66,11 @@ class TestHttpProxyPlugin(unittest.TestCase):
                 data=None), selectors.EVENT_READ)], ]
 
         self.protocol_handler.run_once()
-        mock_server_conn.assert_called_with('upstream.host', 80)
+        mock_server_conn.assert_called_with('upstream.host', DEFAULT_HTTP_PORT)
         self.plugin.return_value.before_upstream_connection.assert_called()
         self.plugin.return_value.handle_client_request.assert_called()
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_proxy_plugin_before_upstream_connection_can_teardown(
             self,
             mock_server_conn: mock.Mock) -> None:
diff --git a/tests/http/test_http_proxy_tls_interception.py b/tests/http/test_http_proxy_tls_interception.py
index 68dc06f..87c3eed 100644
--- a/tests/http/test_http_proxy_tls_interception.py
+++ b/tests/http/test_http_proxy_tls_interception.py
@@ -28,7 +28,7 @@ class TestHttpProxyTlsInterception(unittest.TestCase):
 
     @mock.patch('ssl.wrap_socket')
     @mock.patch('ssl.create_default_context')
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     @mock.patch('subprocess.Popen')
     @mock.patch('selectors.DefaultSelector')
     @mock.patch('socket.fromfd')
diff --git a/tests/http/test_protocol_handler.py b/tests/http/test_protocol_handler.py
index 1356e9c..f4140fe 100644
--- a/tests/http/test_protocol_handler.py
+++ b/tests/http/test_protocol_handler.py
@@ -47,7 +47,7 @@ class TestHttpProtocolHandler(unittest.TestCase):
             self.fileno, self._addr, flags=self.flags)
         self.protocol_handler.initialize()
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_http_get(self, mock_server_connection: mock.Mock) -> None:
         server = mock_server_connection.return_value
         server.connect.return_value = True
@@ -99,7 +99,7 @@ class TestHttpProtocolHandler(unittest.TestCase):
         assert parser.code is not None
         self.assertEqual(int(parser.code), 200)
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_http_tunnel(self, mock_server_connection: mock.Mock) -> None:
         server = mock_server_connection.return_value
         server.connect.return_value = True
@@ -189,7 +189,7 @@ class TestHttpProtocolHandler(unittest.TestCase):
 
     @mock.patch('selectors.DefaultSelector')
     @mock.patch('socket.fromfd')
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_authenticated_proxy_http_get(
             self, mock_server_connection: mock.Mock,
             mock_fromfd: mock.Mock,
@@ -237,7 +237,7 @@ class TestHttpProtocolHandler(unittest.TestCase):
 
     @mock.patch('selectors.DefaultSelector')
     @mock.patch('socket.fromfd')
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_authenticated_proxy_http_tunnel(
             self, mock_server_connection: mock.Mock,
             mock_fromfd: mock.Mock,
diff --git a/tests/plugin/test_http_proxy_plugins.py b/tests/plugin/test_http_proxy_plugins.py
index b17d572..a768c10 100644
--- a/tests/plugin/test_http_proxy_plugins.py
+++ b/tests/plugin/test_http_proxy_plugins.py
@@ -20,7 +20,7 @@ from proxy.common.flags import Flags
 from proxy.http.handler import HttpProtocolHandler
 from proxy.http.proxy import HttpProxyPlugin
 from proxy.common.utils import build_http_request, bytes_, build_http_response
-from proxy.common.constants import PROXY_AGENT_HEADER_VALUE
+from proxy.common.constants import PROXY_AGENT_HEADER_VALUE, DEFAULT_HTTP_PORT
 from proxy.http.codes import httpStatusCodes
 
 from proxy.plugin import ProposedRestApiPlugin, RedirectToCustomServerPlugin
@@ -54,7 +54,7 @@ class TestHttpProxyPluginExamples(unittest.TestCase):
             self.fileno, self._addr, flags=self.flags)
         self.protocol_handler.initialize()
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_modify_post_data_plugin(
             self, mock_server_conn: mock.Mock) -> None:
         original = b'{"key": "value"}'
@@ -77,7 +77,7 @@ class TestHttpProxyPluginExamples(unittest.TestCase):
                 data=None), selectors.EVENT_READ)], ]
 
         self.protocol_handler.run_once()
-        mock_server_conn.assert_called_with('httpbin.org', 80)
+        mock_server_conn.assert_called_with('httpbin.org', DEFAULT_HTTP_PORT)
         mock_server_conn.return_value.queue.assert_called_with(
             build_http_request(
                 b'POST', b'/post',
@@ -91,7 +91,7 @@ class TestHttpProxyPluginExamples(unittest.TestCase):
             )
         )
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_proposed_rest_api_plugin(
             self, mock_server_conn: mock.Mock) -> None:
         path = b'/v1/users/'
@@ -121,7 +121,7 @@ class TestHttpProxyPluginExamples(unittest.TestCase):
                         ProposedRestApiPlugin.REST_API_SPEC[path]))
             ))
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_redirect_to_custom_server_plugin(
             self, mock_server_conn: mock.Mock) -> None:
         request = build_http_request(
@@ -152,7 +152,7 @@ class TestHttpProxyPluginExamples(unittest.TestCase):
             )
         )
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_filter_by_upstream_host_plugin(
             self, mock_server_conn: mock.Mock) -> None:
         request = build_http_request(
@@ -182,7 +182,7 @@ class TestHttpProxyPluginExamples(unittest.TestCase):
             )
         )
 
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     def test_man_in_the_middle_plugin(
             self, mock_server_conn: mock.Mock) -> None:
         request = build_http_request(
@@ -224,7 +224,7 @@ class TestHttpProxyPluginExamples(unittest.TestCase):
 
         # Client read
         self.protocol_handler.run_once()
-        mock_server_conn.assert_called_with('super.secure', 80)
+        mock_server_conn.assert_called_with('super.secure', DEFAULT_HTTP_PORT)
         server.connect.assert_called_once()
         queued_request = \
             build_http_request(
diff --git a/tests/plugin/test_http_proxy_plugins_with_tls_interception.py b/tests/plugin/test_http_proxy_plugins_with_tls_interception.py
index 2ef478c..ad05b2b 100644
--- a/tests/plugin/test_http_proxy_plugins_with_tls_interception.py
+++ b/tests/plugin/test_http_proxy_plugins_with_tls_interception.py
@@ -31,7 +31,7 @@ class TestHttpProxyPluginExamplesWithTlsInterception(unittest.TestCase):
 
     @mock.patch('ssl.wrap_socket')
     @mock.patch('ssl.create_default_context')
-    @mock.patch('proxy.http.proxy.TcpServerConnection')
+    @mock.patch('proxy.http.proxy.server.TcpServerConnection')
     @mock.patch('subprocess.Popen')
     @mock.patch('selectors.DefaultSelector')
     @mock.patch('socket.fromfd')