dumper: don't print connection errors to stderr

stderr is reserved for errors in mitmproxy itself,
not for arbitrary network connection problems.
This commit is contained in:
Maximilian Hils 2021-07-15 15:48:37 +02:00
parent 552e7ca4a0
commit d1d0e39f5d
2 changed files with 24 additions and 56 deletions

View File

@ -30,10 +30,9 @@ def colorful(line, styles):
class Dumper:
def __init__(self, outfile=None, errfile=None):
def __init__(self, outfile=None):
self.filter: Optional[flowfilter.TFilter] = None
self.outfp: Optional[IO] = outfile
self.errfp: Optional[IO] = errfile
def load(self, loader):
loader.add_option(
@ -75,11 +74,6 @@ class Dumper:
if self.outfp:
self.outfp.flush()
def echo_error(self, text: str, **style):
click.secho(text, file=self.errfp, err=True, **style)
if self.errfp:
self.errfp.flush()
def _echo_headers(self, headers: http.Headers):
for k, v in headers.fields:
ks = strutils.bytes_to_escaped_str(k)
@ -300,7 +294,7 @@ class Dumper:
self.echo(f"WebSocket connection closed by {c}: {f.websocket.close_code} {f.websocket.close_reason}")
else:
error = flow.Error(f"WebSocket Error: {self.format_websocket_error(f.websocket)}")
self.echo_error(
self.echo(
f"Error in WebSocket connection to {human.format_address(f.server_conn.address)}: {error}",
fg="red"
)
@ -316,7 +310,7 @@ class Dumper:
def tcp_error(self, f):
if self.match(f):
self.echo_error(
self.echo(
f"Error in TCP connection to {human.format_address(f.server_conn.address)}: {f.error}",
fg="red"
)

View File

@ -32,50 +32,37 @@ def test_configure():
def test_simple():
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d) as ctx:
ctx.configure(d, flow_detail=0)
d.response(tflow.tflow(resp=True))
assert not sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=1)
d.response(tflow.tflow(resp=True))
assert sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=1)
d.error(tflow.tflow(err=True))
assert sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=4)
d.response(tflow.tflow(resp=True))
assert sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=4)
d.response(tflow.tflow(resp=True))
assert "<<" in sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=4)
d.response(tflow.tflow(err=True))
assert "<<" in sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=4)
flow = tflow.tflow()
@ -88,8 +75,6 @@ def test_simple():
d.response(flow)
assert sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=4)
flow = tflow.tflow(resp=tutils.tresp(content=b"{"))
@ -98,8 +83,6 @@ def test_simple():
d.response(flow)
assert sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
ctx.configure(d, flow_detail=4)
flow = tflow.tflow()
@ -108,8 +91,6 @@ def test_simple():
d.response(flow)
assert "content missing" in sio.getvalue()
sio.truncate(0)
assert not sio_err.getvalue()
sio_err.truncate(0)
def test_echo_body():
@ -118,8 +99,7 @@ def test_echo_body():
f.response.content = b"foo bar voing\n" * 100
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d) as ctx:
ctx.configure(d, flow_detail=3)
d._echo_message(f.response, f)
@ -129,8 +109,7 @@ def test_echo_body():
def test_echo_trailer():
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d) as ctx:
ctx.configure(d, flow_detail=3)
f = tflow.tflow(client_conn=True, server_conn=True, resp=True)
@ -158,8 +137,7 @@ def test_echo_trailer():
def test_echo_request_line():
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d) as ctx:
ctx.configure(d, flow_detail=3, showhost=True)
f = tflow.tflow(client_conn=None, server_conn=True, resp=True)
@ -195,8 +173,7 @@ class TestContentView:
with mock.patch("mitmproxy.contentviews.auto.ViewAuto.__call__") as va:
va.side_effect = ValueError("")
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d) as tctx:
tctx.configure(d, flow_detail=4)
d.response(tflow.tflow())
@ -205,8 +182,7 @@ class TestContentView:
def test_tcp():
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d) as ctx:
ctx.configure(d, flow_detail=3, showhost=True)
f = tflow.ttcpflow()
@ -216,13 +192,12 @@ def test_tcp():
f = tflow.ttcpflow(client_conn=True, err=True)
d.tcp_error(f)
assert "Error in TCP" in sio_err.getvalue()
assert "Error in TCP" in sio.getvalue()
def test_websocket():
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d) as ctx:
ctx.configure(d, flow_detail=3, showhost=True)
f = tflow.twebsocketflow()
@ -232,36 +207,35 @@ def test_websocket():
d.websocket_end(f)
assert "WebSocket connection closed by" in sio.getvalue()
sio_err.truncate(0)
sio.truncate(0)
f = tflow.twebsocketflow(err=True)
d.websocket_end(f)
assert "Error in WebSocket" in sio_err.getvalue()
assert "(reason:" not in sio_err.getvalue()
sio_err.truncate(0)
assert "Error in WebSocket" in sio.getvalue()
assert "(reason:" not in sio.getvalue()
sio.truncate(0)
f = tflow.twebsocketflow(err=True, close_reason='Some lame excuse')
d.websocket_end(f)
assert "Error in WebSocket" in sio_err.getvalue()
assert "(reason: Some lame excuse)" in sio_err.getvalue()
sio_err.truncate(0)
assert "Error in WebSocket" in sio.getvalue()
assert "(reason: Some lame excuse)" in sio.getvalue()
sio.truncate(0)
f = tflow.twebsocketflow(close_code=4000)
d.websocket_end(f)
assert "UNKNOWN_ERROR=4000" in sio_err.getvalue()
assert "(reason:" not in sio_err.getvalue()
sio_err.truncate(0)
assert "UNKNOWN_ERROR=4000" in sio.getvalue()
assert "(reason:" not in sio.getvalue()
sio.truncate(0)
f = tflow.twebsocketflow(close_code=4000, close_reason='I swear I had a reason')
d.websocket_end(f)
assert "UNKNOWN_ERROR=4000" in sio_err.getvalue()
assert "(reason: I swear I had a reason)" in sio_err.getvalue()
assert "UNKNOWN_ERROR=4000" in sio.getvalue()
assert "(reason: I swear I had a reason)" in sio.getvalue()
def test_http2():
sio = io.StringIO()
sio_err = io.StringIO()
d = dumper.Dumper(sio, sio_err)
d = dumper.Dumper(sio)
with taddons.context(d):
f = tflow.tflow(resp=True)
f.response.http_version = b"HTTP/2.0"