Skip to content

Commit 32acdcb

Browse files
authored
Decouple transport framework from dashboard plugin (#953)
* Decouple transport framework from dashboard plugin * Move `InspectTrafficPlugin` within `http.inspector` module * Avoid exporting plugins within `__init__.py` files * Use `/transport/` prefix to avoid #945 conflict issue * Add todo
1 parent 372a9ed commit 32acdcb

File tree

13 files changed

+121
-109
lines changed

13 files changed

+121
-109
lines changed

dashboard/src/core/ws.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class WebsocketApi {
1414
private hostname: string = window.location.hostname ? window.location.hostname : 'localhost';
1515
private port: number = window.location.port ? Number(window.location.port) : 8899;
1616
// TODO: Must map to route registered by dashboard.py, don't hardcode
17-
private wsPrefix: string = '/dashboard';
17+
private wsPrefix: string = '/transport/';
1818
private wsScheme: string = window.location.protocol === 'http:' ? 'ws' : 'wss';
1919
private ws: WebSocket;
2020
private wsPath: string = this.wsScheme + '://' + this.hostname + ':' + this.port + this.wsPrefix;

proxy/common/constants.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,16 @@ def _env_threadless_compliant() -> bool:
146146
'HttpProtocolHandlerPlugin',
147147
'HttpProxyBasePlugin',
148148
'HttpWebServerBasePlugin',
149-
'ProxyDashboardWebsocketPlugin',
149+
'WebSocketTransportBasePlugin',
150150
]
151+
PLUGIN_PROXY_AUTH = 'proxy.http.proxy.AuthPlugin'
152+
PLUGIN_DASHBOARD = 'proxy.dashboard.ProxyDashboard'
151153
PLUGIN_HTTP_PROXY = 'proxy.http.proxy.HttpProxyPlugin'
152154
PLUGIN_WEB_SERVER = 'proxy.http.server.HttpWebServerPlugin'
153155
PLUGIN_PAC_FILE = 'proxy.http.server.HttpWebServerPacFilePlugin'
154-
PLUGIN_DEVTOOLS_PROTOCOL = 'proxy.http.inspector.DevtoolsProtocolPlugin'
155-
PLUGIN_DASHBOARD = 'proxy.dashboard.ProxyDashboard'
156-
PLUGIN_INSPECT_TRAFFIC = 'proxy.dashboard.InspectTrafficPlugin'
157-
PLUGIN_PROXY_AUTH = 'proxy.http.proxy.AuthPlugin'
156+
PLUGIN_DEVTOOLS_PROTOCOL = 'proxy.http.inspector.devtools.DevtoolsProtocolPlugin'
157+
PLUGIN_INSPECT_TRAFFIC = 'proxy.http.inspector.inspect_traffic.InspectTrafficPlugin'
158+
PLUGIN_WEBSOCKET_TRANSPORT = 'proxy.http.websocket.transport.WebSocketTransport'
158159

159160
PY2_DEPRECATION_MESSAGE = '''DEPRECATION: proxy.py no longer supports Python 2.7. Kindly upgrade to Python 3+. '
160161
'If for some reasons you cannot upgrade, use'

proxy/common/flag.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from .constants import DEFAULT_DEVTOOLS_WS_PATH, DEFAULT_DISABLE_HEADERS, PY2_DEPRECATION_MESSAGE
2727
from .constants import PLUGIN_DASHBOARD, PLUGIN_DEVTOOLS_PROTOCOL, DEFAULT_MIN_COMPRESSION_LIMIT
2828
from .constants import PLUGIN_HTTP_PROXY, PLUGIN_INSPECT_TRAFFIC, PLUGIN_PAC_FILE
29-
from .constants import PLUGIN_WEB_SERVER, PLUGIN_PROXY_AUTH, IS_WINDOWS
29+
from .constants import PLUGIN_WEB_SERVER, PLUGIN_PROXY_AUTH, IS_WINDOWS, PLUGIN_WEBSOCKET_TRANSPORT
3030
from .logger import Logger
3131

3232
from .version import __version__
@@ -395,6 +395,7 @@ def get_default_plugins(
395395
default_plugins.append(PLUGIN_WEB_SERVER)
396396
args.enable_static_server = True
397397
default_plugins.append(PLUGIN_DASHBOARD)
398+
default_plugins.append(PLUGIN_WEBSOCKET_TRANSPORT)
398399
default_plugins.append(PLUGIN_INSPECT_TRAFFIC)
399400
args.enable_events = True
400401
args.enable_devtools = True

proxy/core/event/queue.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222

2323

2424
class EventQueue:
25-
"""Global event queue. Must be a multiprocessing.Manager queue because
26-
subscribers need to dispatch their subscription queue over this global
27-
queue.
25+
"""Global event queue. Must be a multiprocess safe queue capable of
26+
transporting other queues. This is necessary because currently
27+
subscribers use a separate subscription queue to consume events.
28+
Subscription queue is exchanged over the global event queue.
2829
2930
Each published event contains following schema::
3031

proxy/core/event/subscriber.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,16 @@ class EventSubscriber:
3434
can be different from publishers. Publishers can even be processes
3535
outside of the proxy.py core.
3636
37-
Note that, EventSubscriber cannot share the `multiprocessing.Manager`
38-
with the EventManager. Because EventSubscriber can be started
39-
in a different process than EventManager.
40-
41-
`multiprocessing.Manager` is used to initialize
42-
a new Queue which is used for subscriptions. EventDispatcher
43-
might be running in a separate process and hence
44-
subscription queue must be multiprocess safe.
45-
46-
When `subscribe` method is called, EventManager will
47-
start a relay thread which consumes using the multiprocess
48-
safe queue passed to the relay thread.
37+
`multiprocessing.Pipe` is used to initialize a new Queue for
38+
receiving subscribed events from eventing core. Note that,
39+
core EventDispatcher might be running in a separate process
40+
and hence subscription queue must be multiprocess safe.
41+
42+
When `subscribe` method is called, EventManager stars
43+
a relay thread which consumes event out of the subscription queue
44+
and invoke callback.
45+
46+
NOTE: Callback is executed in the context of relay thread.
4947
"""
5048

5149
def __init__(self, event_queue: EventQueue, callback: Callable[[Dict[str, Any]], None]) -> None:

proxy/dashboard/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@
99
:license: BSD, see LICENSE for more details.
1010
"""
1111
from .dashboard import ProxyDashboard
12-
from .inspect_traffic import InspectTrafficPlugin
13-
from .plugin import ProxyDashboardWebsocketPlugin
1412

1513
__all__ = [
1614
'ProxyDashboard',
17-
'InspectTrafficPlugin',
18-
'ProxyDashboardWebsocketPlugin',
1915
]

proxy/dashboard/dashboard.py

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,11 @@
99
:license: BSD, see LICENSE for more details.
1010
"""
1111
import os
12-
import json
1312
import logging
14-
from typing import List, Tuple, Any, Dict
15-
16-
from .plugin import ProxyDashboardWebsocketPlugin
17-
18-
from ..common.utils import bytes_
13+
from typing import List, Tuple
1914

2015
from ..http.responses import permanentRedirectResponse
2116
from ..http.parser import HttpParser
22-
from ..http.websocket import WebsocketFrame
2317
from ..http.server import HttpWebServerPlugin, HttpWebServerBasePlugin, httpProtocolTypes
2418

2519
logger = logging.getLogger(__name__)
@@ -42,24 +36,9 @@ class ProxyDashboard(HttpWebServerBasePlugin):
4236
(httpProtocolTypes.HTTPS, r'/dashboard/$'),
4337
]
4438

45-
# Handles WebsocketAPI requests for dashboard
46-
WS_ROUTES = [
47-
(httpProtocolTypes.WEBSOCKET, r'/dashboard$'),
48-
]
49-
50-
def __init__(self, *args: Any, **kwargs: Any) -> None:
51-
super().__init__(*args, **kwargs)
52-
self.plugins: Dict[str, ProxyDashboardWebsocketPlugin] = {}
53-
if b'ProxyDashboardWebsocketPlugin' in self.flags.plugins:
54-
for klass in self.flags.plugins[b'ProxyDashboardWebsocketPlugin']:
55-
p = klass(self.flags, self.client, self.event_queue)
56-
for method in p.methods():
57-
self.plugins[method] = p
58-
5939
def routes(self) -> List[Tuple[int, str]]:
6040
return ProxyDashboard.REDIRECT_ROUTES + \
61-
ProxyDashboard.INDEX_ROUTES + \
62-
ProxyDashboard.WS_ROUTES
41+
ProxyDashboard.INDEX_ROUTES
6342

6443
def handle_request(self, request: HttpParser) -> None:
6544
if request.path == b'/dashboard/':
@@ -76,40 +55,3 @@ def handle_request(self, request: HttpParser) -> None:
7655
b'/dashboard/proxy.html',
7756
):
7857
self.client.queue(permanentRedirectResponse(b'/dashboard/'))
79-
80-
def on_websocket_open(self) -> None:
81-
logger.info('app ws opened')
82-
83-
def on_websocket_message(self, frame: WebsocketFrame) -> None:
84-
try:
85-
assert frame.data
86-
message = json.loads(frame.data)
87-
except UnicodeDecodeError:
88-
logger.error(frame.data)
89-
logger.info(frame.opcode)
90-
return
91-
92-
method = message['method']
93-
if method == 'ping':
94-
self.reply({'id': message['id'], 'response': 'pong'})
95-
elif method in self.plugins:
96-
self.plugins[method].handle_message(message)
97-
else:
98-
logger.info(frame.data)
99-
logger.info(frame.opcode)
100-
self.reply({'id': message['id'], 'response': 'not_implemented'})
101-
102-
def on_client_connection_close(self) -> None:
103-
logger.info('app ws closed')
104-
# TODO(abhinavsingh): unsubscribe
105-
106-
def reply(self, data: Dict[str, Any]) -> None:
107-
self.client.queue(
108-
memoryview(
109-
WebsocketFrame.text(
110-
bytes_(
111-
json.dumps(data),
112-
),
113-
),
114-
),
115-
)

proxy/http/inspector/__init__.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,4 @@
77
88
:copyright: (c) 2013-present by Abhinav Singh and contributors.
99
:license: BSD, see LICENSE for more details.
10-
11-
.. spelling::
12-
13-
http
14-
Submodules
1510
"""
16-
from .devtools import DevtoolsProtocolPlugin
17-
18-
__all__ = [
19-
'DevtoolsProtocolPlugin',
20-
]

proxy/dashboard/inspect_traffic.py renamed to proxy/http/inspector/inspect_traffic.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,13 @@
1111
import json
1212
from typing import List, Dict, Any
1313

14-
from .plugin import ProxyDashboardWebsocketPlugin
14+
from ...common.utils import bytes_
15+
from ...core.event import EventSubscriber
16+
from ...core.connection import TcpClientConnection
17+
from ..websocket import WebsocketFrame, WebSocketTransportBasePlugin
1518

16-
from ..common.utils import bytes_
17-
from ..core.event import EventSubscriber
18-
from ..core.connection import TcpClientConnection
19-
from ..http.websocket import WebsocketFrame
2019

21-
22-
class InspectTrafficPlugin(ProxyDashboardWebsocketPlugin):
20+
class InspectTrafficPlugin(WebSocketTransportBasePlugin):
2321
"""Websocket API for inspect_traffic.ts frontend plugin."""
2422

2523
def __init__(self, *args: Any, **kwargs: Any) -> None:

proxy/http/websocket/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
"""
1818
from .frame import WebsocketFrame, websocketOpcodes
1919
from .client import WebsocketClient
20+
from .plugin import WebSocketTransportBasePlugin
2021

2122
__all__ = [
2223
'websocketOpcodes',
2324
'WebsocketFrame',
2425
'WebsocketClient',
26+
'WebSocketTransportBasePlugin',
2527
]

0 commit comments

Comments
 (0)