Fix #4876 Don't do CONNECT on plaintext HTTP replays via upstream (#4882)

* Replays via upstream also need to comply with upstream handling

* Adjusted test for HTTP upstream replay which should NOT do a CONNECT

* Added Changelog

* Test for replay https pver upstream with CONNECT

* Proxy requests use full URL with host & port

* Finally remove some prints

* lint!

Co-authored-by: Maximilian Hils <github@maximilianhils.com>
This commit is contained in:
Peter Hoffmann 2021-10-31 21:23:04 +01:00 committed by GitHub
parent 1c10abef00
commit 260fc68211
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 5 deletions

View File

@ -13,6 +13,7 @@
Includes example addon which applies custom definitions for selected gRPC traffic (@mame82)
* Fix a crash caused when editing string option (#4852, @rbdixon)
* Base container image bumped to Debian 11 Bullseye (@Kriechi)
* Upstream replays don't do CONNECT on plaintext HTTP requests (#4876, @HoffmannP)
## 28 September 2021: mitmproxy 7.0.4

View File

@ -78,7 +78,10 @@ class ReplayHandler(server.ConnectionHandler):
super().__init__(context)
self.layer = layers.HttpLayer(context, HTTPMode.transparent)
if options.mode.startswith("upstream:"):
self.layer = layers.HttpLayer(context, HTTPMode.upstream)
else:
self.layer = layers.HttpLayer(context, HTTPMode.transparent)
self.layer.connections[client] = MockServer(flow, context.fork())
self.flow = flow
self.done = asyncio.Event()

View File

@ -31,13 +31,13 @@ async def test_playback(mode, concurrency):
writer.close()
handler_ok.set()
return
req = await reader.readline()
if mode == "upstream":
conn_req = await reader.readuntil(b"\r\n\r\n")
assert conn_req == b'CONNECT address:22 HTTP/1.1\r\n\r\n'
writer.write(b"HTTP/1.1 200 Connection Established\r\n\r\n")
assert req == b'GET http://address:22/path HTTP/1.1\r\n'
else:
assert req == b'GET /path HTTP/1.1\r\n'
req = await reader.readuntil(b"data")
assert req == (
b'GET /path HTTP/1.1\r\n'
b'header: qvalue\r\n'
b'content-length: 4\r\n'
b'\r\n'
@ -59,6 +59,8 @@ async def test_playback(mode, concurrency):
flow.request.content = b"data"
if mode == "upstream":
tctx.options.mode = f"upstream:http://{addr[0]}:{addr[1]}"
flow.request.authority = f"{addr[0]}:{addr[1]}"
flow.request.host, flow.request.port = 'address', 22
else:
flow.request.host, flow.request.port = addr
cp.start_replay([flow])
@ -70,6 +72,37 @@ async def test_playback(mode, concurrency):
assert flow.response.status_code == 204
@pytest.mark.asyncio
async def test_playback_https_upstream():
handler_ok = asyncio.Event()
async def handler(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
conn_req = await reader.readuntil(b"\r\n\r\n")
assert conn_req == b'CONNECT address:22 HTTP/1.1\r\n\r\n'
writer.write(b"HTTP/1.1 502 Bad Gateway\r\n\r\n")
await writer.drain()
assert not await reader.read()
handler_ok.set()
cp = ClientPlayback()
ps = Proxyserver()
with taddons.context(cp, ps) as tctx:
tctx.configure(cp)
async with tcp_server(handler) as addr:
cp.running()
flow = tflow.tflow()
flow.request.scheme = b"https"
flow.request.content = b"data"
tctx.options.mode = f"upstream:http://{addr[0]}:{addr[1]}"
cp.start_replay([flow])
assert cp.count() == 1
await asyncio.wait_for(cp.queue.join(), 5)
await asyncio.wait_for(handler_ok.wait(), 5)
cp.done()
assert flow.response is None
assert str(flow.error) == f'Upstream proxy {addr[0]}:{addr[1]} refused HTTP CONNECT request: 502 Bad Gateway'
@pytest.mark.asyncio
async def test_playback_crash(monkeypatch):
async def raise_err():