Skip to content

Commit 0641864

Browse files
GroutClientBasePlugin and example GroutClientPlugin (#1488)
* `GroutClientBasePlugin` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Silence `S113` false positive * Remove example url --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent c703eda commit 0641864

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ extend-ignore =
8282
S101 # FIXME: assertions are thrown away in optimized mode, needs audit
8383
S104 # FIXME: bind-all interface listen
8484
S105 # FIXME: hardcoded password?
85+
S113 # FIXME: Call to httpx without timeout (false positive)
8586
S303 # FIXME: insecure hash func
8687
S311 # FIXME: `random` needs auditing
8788
S404 # FIXME: `subprocess` use needs auditing

proxy/common/constants.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,12 @@ def _env_threadless_compliant() -> bool:
180180

181181
# Cor plugins enabled by default or via flags
182182
DEFAULT_ABC_PLUGINS = [
183-
'HttpProtocolHandlerPlugin',
184-
'HttpProxyBasePlugin',
185-
'HttpWebServerBasePlugin',
186-
'WebSocketTransportBasePlugin',
187-
'ReverseProxyBasePlugin',
183+
"HttpProtocolHandlerPlugin",
184+
"HttpProxyBasePlugin",
185+
"HttpWebServerBasePlugin",
186+
"WebSocketTransportBasePlugin",
187+
"ReverseProxyBasePlugin",
188+
"GroutClientBasePlugin",
188189
]
189190
PLUGIN_DASHBOARD = 'proxy.dashboard.ProxyDashboard'
190191
PLUGIN_HTTP_PROXY = 'proxy.http.proxy.HttpProxyPlugin'

proxy/plugin/grout_client.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
12+
13+
from proxy.proxy import GroutClientBasePlugin
14+
from proxy.common.types import HostPort
15+
from proxy.http.parser.parser import HttpParser
16+
17+
18+
class GroutClientPlugin(GroutClientBasePlugin):
19+
20+
def resolve_route(
21+
self,
22+
route: str,
23+
request: HttpParser,
24+
origin: HostPort,
25+
server: HostPort,
26+
) -> str:
27+
print(request, origin, server, '->', route)
28+
print(request.header(b'host'), request.path)
29+
# Send to localhost:7001 irrespective of the
30+
# original "route" value provided to the grout client
31+
# OR any custom host:upstream mapping provided through the
32+
# --tunnel-route flags.
33+
return 'http://localhost:7001'

proxy/proxy.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import logging
2121
import argparse
2222
import threading
23+
from abc import ABC, abstractmethod
2324
from typing import TYPE_CHECKING, Any, Dict, List, Type, Tuple, Optional, cast
2425

2526
from .core.ssh import SshTunnelListener, SshHttpProtocolHandler
@@ -28,6 +29,7 @@
2829
from .http.codes import httpStatusCodes
2930
from .common.flag import FlagParser, flags
3031
from .http.client import client
32+
from .common.types import HostPort
3133
from .common.utils import bytes_
3234
from .core.work.fd import RemoteFdExecutor
3335
from .http.methods import httpMethods
@@ -44,6 +46,7 @@
4446
DEFAULT_SSH_LISTENER_KLASS,
4547
)
4648
from .core.event.metrics import MetricsEventSubscriber
49+
from .http.parser.parser import HttpParser
4750

4851

4952
if TYPE_CHECKING: # pragma: no cover
@@ -509,3 +512,28 @@ def _parse() -> Tuple[str, int]:
509512
assert env is not None
510513
print('\r' + ' ' * 70 + '\r', end='', flush=True)
511514
Plugins.from_bytes(env['m'].encode(), name='client').grout(env=env['e']) # type: ignore[attr-defined]
515+
516+
517+
class GroutClientBasePlugin(ABC):
518+
"""Base class for dynamic grout client rules.
519+
520+
Implementation of this class must be stateless because a new instance is created
521+
for every route decision making.
522+
"""
523+
524+
@abstractmethod
525+
def resolve_route(
526+
self,
527+
route: str,
528+
request: HttpParser,
529+
origin: HostPort,
530+
server: HostPort,
531+
) -> str:
532+
"""Returns a valid grout route string.
533+
534+
You MUST override this method. For a simple pass through,
535+
simply return the "route" argument value itself. You can also
536+
return a dynamic value based upon "request" and "origin" information.
537+
E.g. sending to different upstream services based upon request Host header.
538+
"""
539+
raise NotImplementedError()

0 commit comments

Comments
 (0)