|
1 | | -import asyncio |
2 | 1 | import contextlib |
3 | 2 | import os |
4 | | -import ssl |
5 | 3 | import threading |
6 | 4 | import time |
7 | 5 | import typing |
8 | 6 |
|
9 | 7 | import pytest |
10 | | -import trustme |
11 | 8 | import uvicorn |
12 | | -from mitmproxy import options, proxy |
13 | | -from mitmproxy.tools.dump import DumpMaster |
14 | 9 |
|
15 | 10 | from httpcore._types import URL |
16 | 11 |
|
17 | | -from .utils import Server |
| 12 | +from .utils import Server, http_proxy_server |
18 | 13 |
|
19 | | -PROXY_HOST = "127.0.0.1" |
20 | | -PROXY_PORT = 8080 |
21 | 14 | SERVER_HOST = "example.org" |
22 | 15 | HTTPS_SERVER_URL = "https://example.org" |
23 | 16 |
|
24 | 17 |
|
25 | | -class RunNotify: |
26 | | - """A mitmproxy addon wrapping an event to notify us when the server is running.""" |
27 | | - |
28 | | - def __init__(self) -> None: |
29 | | - self.started = threading.Event() |
30 | | - |
31 | | - def running(self) -> None: |
32 | | - self.started.set() |
33 | | - |
34 | | - |
35 | | -class ProxyWrapper(threading.Thread): |
36 | | - """Runs an mitmproxy in a separate thread.""" |
37 | | - |
38 | | - def __init__(self, host: str, port: int, **kwargs: typing.Any) -> None: |
39 | | - self.host = host |
40 | | - self.port = port |
41 | | - self.options = kwargs |
42 | | - super().__init__() |
43 | | - self.notify = RunNotify() |
44 | | - |
45 | | - def run(self) -> None: |
46 | | - # mitmproxy uses asyncio internally but the default loop policy |
47 | | - # will only create event loops for the main thread, create one |
48 | | - # as part of the thread startup |
49 | | - asyncio.set_event_loop(asyncio.new_event_loop()) |
50 | | - opts = options.Options( |
51 | | - listen_host=self.host, listen_port=self.port, **self.options |
52 | | - ) |
53 | | - pconf = proxy.config.ProxyConfig(opts) |
54 | | - |
55 | | - self.master = DumpMaster(opts) |
56 | | - self.master.server = proxy.server.ProxyServer(pconf) |
57 | | - self.master.addons.add(self.notify) |
58 | | - self.master.run() |
59 | | - |
60 | | - def join(self, timeout: float = None) -> None: |
61 | | - self.master.shutdown() |
62 | | - super().join() |
63 | | - |
64 | | - |
65 | | -@pytest.fixture(scope="session") |
66 | | -def cert_authority() -> trustme.CA: |
67 | | - return trustme.CA() |
68 | | - |
69 | | - |
70 | | -@pytest.fixture() |
71 | | -def ca_ssl_context(cert_authority: trustme.CA) -> ssl.SSLContext: |
72 | | - ctx = ssl.create_default_context() |
73 | | - cert_authority.configure_trust(ctx) |
74 | | - return ctx |
75 | | - |
76 | | - |
77 | | -@pytest.fixture(scope="session") |
78 | | -def example_org_cert(cert_authority: trustme.CA) -> trustme.LeafCert: |
79 | | - return cert_authority.issue_cert("example.org") |
80 | | - |
81 | | - |
82 | 18 | @pytest.fixture(scope="session") |
83 | | -def example_org_cert_path(example_org_cert: trustme.LeafCert) -> typing.Iterator[str]: |
84 | | - with example_org_cert.private_key_and_cert_chain_pem.tempfile() as tmp: |
85 | | - yield tmp |
| 19 | +def proxy_server() -> typing.Iterator[URL]: |
| 20 | + proxy_host = "127.0.0.1" |
| 21 | + proxy_port = 8080 |
86 | 22 |
|
87 | | - |
88 | | -@pytest.fixture() |
89 | | -def proxy_server(example_org_cert_path: str) -> typing.Iterator[URL]: |
90 | | - """Starts a proxy server on a different thread and yields its origin tuple. |
91 | | -
|
92 | | - The server is configured to use a trustme CA and key, this will allow our |
93 | | - test client to make HTTPS requests when using the ca_ssl_context fixture |
94 | | - above. |
95 | | -
|
96 | | - Note this is only required because mitmproxy's main purpose is to analyse |
97 | | - traffic. Other proxy servers do not need this but mitmproxy is easier to |
98 | | - integrate in our tests. |
99 | | - """ |
100 | | - try: |
101 | | - thread = ProxyWrapper(PROXY_HOST, PROXY_PORT, certs=[example_org_cert_path]) |
102 | | - thread.start() |
103 | | - thread.notify.started.wait() |
104 | | - yield (b"http", PROXY_HOST.encode(), PROXY_PORT, b"/") |
105 | | - finally: |
106 | | - thread.join() |
| 23 | + with http_proxy_server(proxy_host, proxy_port) as proxy_url: |
| 24 | + yield proxy_url |
107 | 25 |
|
108 | 26 |
|
109 | 27 | class UvicornServer(uvicorn.Server): |
|
0 commit comments