mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 08:11:00 +00:00
Merge pull request #2258 from mhils/readfile
Integrate readstdin into readfile
This commit is contained in:
commit
d5ea08db62
@ -8,7 +8,6 @@ from mitmproxy.addons import disable_h2c
|
|||||||
from mitmproxy.addons import onboarding
|
from mitmproxy.addons import onboarding
|
||||||
from mitmproxy.addons import proxyauth
|
from mitmproxy.addons import proxyauth
|
||||||
from mitmproxy.addons import replace
|
from mitmproxy.addons import replace
|
||||||
from mitmproxy.addons import readfile
|
|
||||||
from mitmproxy.addons import script
|
from mitmproxy.addons import script
|
||||||
from mitmproxy.addons import serverplayback
|
from mitmproxy.addons import serverplayback
|
||||||
from mitmproxy.addons import setheaders
|
from mitmproxy.addons import setheaders
|
||||||
@ -38,6 +37,5 @@ def default_addons():
|
|||||||
stickycookie.StickyCookie(),
|
stickycookie.StickyCookie(),
|
||||||
streambodies.StreamBodies(),
|
streambodies.StreamBodies(),
|
||||||
streamfile.StreamFile(),
|
streamfile.StreamFile(),
|
||||||
readfile.ReadFile(),
|
|
||||||
upstream_auth.UpstreamAuth(),
|
upstream_auth.UpstreamAuth(),
|
||||||
]
|
]
|
||||||
|
@ -1,38 +1,56 @@
|
|||||||
import os.path
|
import os.path
|
||||||
|
import sys
|
||||||
|
import typing
|
||||||
|
|
||||||
from mitmproxy import ctx
|
from mitmproxy import ctx
|
||||||
from mitmproxy import io
|
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
|
from mitmproxy import io
|
||||||
|
|
||||||
|
|
||||||
class ReadFile:
|
class ReadFile:
|
||||||
"""
|
"""
|
||||||
An addon that handles reading from file on startup.
|
An addon that handles reading from file on startup.
|
||||||
"""
|
"""
|
||||||
def load_flows_file(self, path: str) -> int:
|
|
||||||
path = os.path.expanduser(path)
|
def load_flows(self, fo: typing.IO[bytes]) -> int:
|
||||||
cnt = 0
|
cnt = 0
|
||||||
|
freader = io.FlowReader(fo)
|
||||||
try:
|
try:
|
||||||
with open(path, "rb") as f:
|
for flow in freader.stream():
|
||||||
freader = io.FlowReader(f)
|
ctx.master.load_flow(flow)
|
||||||
for i in freader.stream():
|
cnt += 1
|
||||||
cnt += 1
|
except (IOError, exceptions.FlowReadException) as e:
|
||||||
ctx.master.load_flow(i)
|
|
||||||
return cnt
|
|
||||||
except (IOError, exceptions.FlowReadException) as v:
|
|
||||||
if cnt:
|
if cnt:
|
||||||
ctx.log.warn(
|
ctx.log.warn("Flow file corrupted - loaded %i flows." % cnt)
|
||||||
"Flow file corrupted - loaded %i flows." % cnt,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
ctx.log.error("Flow file corrupted.")
|
ctx.log.error("Flow file corrupted.")
|
||||||
raise exceptions.FlowReadException(v)
|
raise exceptions.FlowReadException(str(e)) from e
|
||||||
|
else:
|
||||||
|
return cnt
|
||||||
|
|
||||||
|
def load_flows_from_path(self, path: str) -> int:
|
||||||
|
path = os.path.expanduser(path)
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
return self.load_flows(f)
|
||||||
|
except IOError as e:
|
||||||
|
ctx.log.error("Cannot load flows: {}".format(e))
|
||||||
|
raise exceptions.FlowReadException(str(e)) from e
|
||||||
|
|
||||||
def running(self):
|
def running(self):
|
||||||
if ctx.options.rfile:
|
if ctx.options.rfile:
|
||||||
try:
|
try:
|
||||||
self.load_flows_file(ctx.options.rfile)
|
self.load_flows_from_path(ctx.options.rfile)
|
||||||
except exceptions.FlowReadException as v:
|
except exceptions.FlowReadException as e:
|
||||||
raise exceptions.OptionsError(v)
|
raise exceptions.OptionsError(e) from e
|
||||||
finally:
|
finally:
|
||||||
ctx.master.addons.trigger("processing_complete")
|
ctx.master.addons.trigger("processing_complete")
|
||||||
|
|
||||||
|
|
||||||
|
class ReadFileStdin(ReadFile):
|
||||||
|
"""Support the special case of "-" for reading from stdin"""
|
||||||
|
def load_flows_from_path(self, path: str) -> int:
|
||||||
|
if path == "-":
|
||||||
|
return self.load_flows(sys.stdin.buffer)
|
||||||
|
else:
|
||||||
|
return super().load_flows_from_path(path)
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
from mitmproxy import ctx
|
|
||||||
from mitmproxy import io
|
|
||||||
from mitmproxy import exceptions
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
class ReadStdin:
|
|
||||||
"""
|
|
||||||
An addon that reads from stdin if we're not attached to (someting like)
|
|
||||||
a tty.
|
|
||||||
"""
|
|
||||||
def running(self, stdin = sys.stdin):
|
|
||||||
if not stdin.isatty():
|
|
||||||
ctx.log.info("Reading from stdin")
|
|
||||||
try:
|
|
||||||
stdin.buffer.read(0)
|
|
||||||
except Exception as e:
|
|
||||||
ctx.log.warn("Cannot read from stdin: {}".format(e))
|
|
||||||
return
|
|
||||||
freader = io.FlowReader(stdin.buffer)
|
|
||||||
try:
|
|
||||||
for i in freader.stream():
|
|
||||||
ctx.master.load_flow(i)
|
|
||||||
except exceptions.FlowReadException as e:
|
|
||||||
ctx.log.error("Error reading from stdin: %s" % e)
|
|
||||||
ctx.master.addons.trigger("processing_complete")
|
|
@ -17,8 +17,9 @@ from mitmproxy import exceptions
|
|||||||
from mitmproxy import master
|
from mitmproxy import master
|
||||||
from mitmproxy import io
|
from mitmproxy import io
|
||||||
from mitmproxy import log
|
from mitmproxy import log
|
||||||
from mitmproxy.addons import view
|
|
||||||
from mitmproxy.addons import intercept
|
from mitmproxy.addons import intercept
|
||||||
|
from mitmproxy.addons import readfile
|
||||||
|
from mitmproxy.addons import view
|
||||||
from mitmproxy.tools.console import flowlist
|
from mitmproxy.tools.console import flowlist
|
||||||
from mitmproxy.tools.console import flowview
|
from mitmproxy.tools.console import flowview
|
||||||
from mitmproxy.tools.console import grideditor
|
from mitmproxy.tools.console import grideditor
|
||||||
@ -91,7 +92,12 @@ class ConsoleMaster(master.Master):
|
|||||||
signals.sig_add_log.connect(self.sig_add_log)
|
signals.sig_add_log.connect(self.sig_add_log)
|
||||||
self.addons.add(Logger())
|
self.addons.add(Logger())
|
||||||
self.addons.add(*addons.default_addons())
|
self.addons.add(*addons.default_addons())
|
||||||
self.addons.add(intercept.Intercept(), self.view, UnsupportedLog())
|
self.addons.add(
|
||||||
|
intercept.Intercept(),
|
||||||
|
self.view,
|
||||||
|
UnsupportedLog(),
|
||||||
|
readfile.ReadFile(),
|
||||||
|
)
|
||||||
|
|
||||||
def sigint_handler(*args, **kwargs):
|
def sigint_handler(*args, **kwargs):
|
||||||
self.prompt_for_exit()
|
self.prompt_for_exit()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from mitmproxy import addons
|
from mitmproxy import addons
|
||||||
from mitmproxy import options
|
from mitmproxy import options
|
||||||
from mitmproxy import master
|
from mitmproxy import master
|
||||||
from mitmproxy.addons import dumper, termlog, termstatus, readstdin, keepserving
|
from mitmproxy.addons import dumper, termlog, termstatus, keepserving, readfile
|
||||||
|
|
||||||
|
|
||||||
class ErrorCheck:
|
class ErrorCheck:
|
||||||
@ -30,7 +30,7 @@ class DumpMaster(master.Master):
|
|||||||
if with_dumper:
|
if with_dumper:
|
||||||
self.addons.add(dumper.Dumper())
|
self.addons.add(dumper.Dumper())
|
||||||
self.addons.add(
|
self.addons.add(
|
||||||
readstdin.ReadStdin(),
|
|
||||||
keepserving.KeepServing(),
|
keepserving.KeepServing(),
|
||||||
|
readfile.ReadFileStdin(),
|
||||||
self.errorcheck
|
self.errorcheck
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,7 @@ from mitmproxy import log
|
|||||||
from mitmproxy import master
|
from mitmproxy import master
|
||||||
from mitmproxy.addons import eventstore
|
from mitmproxy.addons import eventstore
|
||||||
from mitmproxy.addons import intercept
|
from mitmproxy.addons import intercept
|
||||||
|
from mitmproxy.addons import readfile
|
||||||
from mitmproxy.addons import termlog
|
from mitmproxy.addons import termlog
|
||||||
from mitmproxy.addons import view
|
from mitmproxy.addons import view
|
||||||
from mitmproxy.addons import termstatus
|
from mitmproxy.addons import termstatus
|
||||||
@ -32,6 +33,7 @@ class WebMaster(master.Master):
|
|||||||
self.addons.add(*addons.default_addons())
|
self.addons.add(*addons.default_addons())
|
||||||
self.addons.add(
|
self.addons.add(
|
||||||
intercept.Intercept(),
|
intercept.Intercept(),
|
||||||
|
readfile.ReadFile(),
|
||||||
self.view,
|
self.view,
|
||||||
self.events,
|
self.events,
|
||||||
)
|
)
|
||||||
|
@ -68,5 +68,7 @@ def check_type(name: str, value: typing.Any, typeinfo: typing.Any) -> None:
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
elif typename.startswith("typing.Any"):
|
||||||
|
return
|
||||||
elif not isinstance(value, typeinfo):
|
elif not isinstance(value, typeinfo):
|
||||||
raise e
|
raise e
|
||||||
|
@ -1,62 +1,104 @@
|
|||||||
from mitmproxy.addons import readfile
|
import io
|
||||||
from mitmproxy.test import taddons
|
|
||||||
from mitmproxy.test import tflow
|
|
||||||
from mitmproxy import io
|
|
||||||
from mitmproxy import exceptions
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import mitmproxy.io
|
||||||
def write_data(path, corrupt=False):
|
from mitmproxy import exceptions
|
||||||
with open(path, "wb") as tf:
|
from mitmproxy.addons import readfile
|
||||||
w = io.FlowWriter(tf)
|
from mitmproxy.test import taddons
|
||||||
for i in range(3):
|
from mitmproxy.test import tflow
|
||||||
f = tflow.tflow(resp=True)
|
|
||||||
w.add(f)
|
|
||||||
for i in range(3):
|
|
||||||
f = tflow.tflow(err=True)
|
|
||||||
w.add(f)
|
|
||||||
f = tflow.ttcpflow()
|
|
||||||
w.add(f)
|
|
||||||
f = tflow.ttcpflow(err=True)
|
|
||||||
w.add(f)
|
|
||||||
if corrupt:
|
|
||||||
tf.write(b"flibble")
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('mitmproxy.master.Master.load_flow')
|
@pytest.fixture
|
||||||
def test_configure(mck, tmpdir):
|
def data():
|
||||||
|
f = io.BytesIO()
|
||||||
|
|
||||||
rf = readfile.ReadFile()
|
w = mitmproxy.io.FlowWriter(f)
|
||||||
with taddons.context() as tctx:
|
flows = [
|
||||||
tf = str(tmpdir.join("tfile"))
|
tflow.tflow(resp=True),
|
||||||
write_data(tf)
|
tflow.tflow(err=True),
|
||||||
tctx.configure(rf, rfile=str(tf))
|
tflow.ttcpflow(),
|
||||||
assert not mck.called
|
tflow.ttcpflow(err=True)
|
||||||
rf.running()
|
]
|
||||||
assert mck.called
|
for flow in flows:
|
||||||
|
w.add(flow)
|
||||||
|
|
||||||
write_data(tf, corrupt=True)
|
f.seek(0)
|
||||||
tctx.configure(rf, rfile=str(tf))
|
return f
|
||||||
with pytest.raises(exceptions.OptionsError):
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def corrupt_data():
|
||||||
|
f = data()
|
||||||
|
f.seek(0, io.SEEK_END)
|
||||||
|
f.write(b"qibble")
|
||||||
|
f.seek(0)
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
class TestReadFile:
|
||||||
|
@mock.patch('mitmproxy.master.Master.load_flow')
|
||||||
|
def test_configure(self, mck, tmpdir, data, corrupt_data):
|
||||||
|
rf = readfile.ReadFile()
|
||||||
|
with taddons.context() as tctx:
|
||||||
|
tf = tmpdir.join("tfile")
|
||||||
|
|
||||||
|
tf.write(data.getvalue())
|
||||||
|
tctx.configure(rf, rfile=str(tf))
|
||||||
|
assert not mck.called
|
||||||
rf.running()
|
rf.running()
|
||||||
|
assert mck.called
|
||||||
|
|
||||||
|
tf.write(corrupt_data.getvalue())
|
||||||
|
tctx.configure(rf, rfile=str(tf))
|
||||||
|
with pytest.raises(exceptions.OptionsError):
|
||||||
|
rf.running()
|
||||||
|
|
||||||
|
@mock.patch('mitmproxy.master.Master.load_flow')
|
||||||
|
def test_corrupt(self, mck, corrupt_data):
|
||||||
|
rf = readfile.ReadFile()
|
||||||
|
with taddons.context() as tctx:
|
||||||
|
with pytest.raises(exceptions.FlowReadException):
|
||||||
|
rf.load_flows(io.BytesIO(b"qibble"))
|
||||||
|
assert not mck.called
|
||||||
|
assert len(tctx.master.logs) == 1
|
||||||
|
|
||||||
|
with pytest.raises(exceptions.FlowReadException):
|
||||||
|
rf.load_flows(corrupt_data)
|
||||||
|
assert mck.called
|
||||||
|
assert len(tctx.master.logs) == 2
|
||||||
|
|
||||||
|
def test_nonexisting_file(self):
|
||||||
|
rf = readfile.ReadFile()
|
||||||
|
with taddons.context() as tctx:
|
||||||
|
with pytest.raises(exceptions.FlowReadException):
|
||||||
|
rf.load_flows_from_path("nonexistent")
|
||||||
|
assert len(tctx.master.logs) == 1
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('mitmproxy.master.Master.load_flow')
|
class TestReadFileStdin:
|
||||||
def test_corruption(mck, tmpdir):
|
@mock.patch('mitmproxy.master.Master.load_flow')
|
||||||
|
@mock.patch('sys.stdin')
|
||||||
|
def test_stdin(self, stdin, load_flow, data, corrupt_data):
|
||||||
|
rf = readfile.ReadFileStdin()
|
||||||
|
with taddons.context() as tctx:
|
||||||
|
stdin.buffer = data
|
||||||
|
tctx.configure(rf, rfile="-")
|
||||||
|
assert not load_flow.called
|
||||||
|
rf.running()
|
||||||
|
assert load_flow.called
|
||||||
|
|
||||||
rf = readfile.ReadFile()
|
stdin.buffer = corrupt_data
|
||||||
with taddons.context() as tctx:
|
tctx.configure(rf, rfile="-")
|
||||||
with pytest.raises(exceptions.FlowReadException):
|
with pytest.raises(exceptions.OptionsError):
|
||||||
rf.load_flows_file("nonexistent")
|
rf.running()
|
||||||
assert not mck.called
|
|
||||||
assert len(tctx.master.logs) == 1
|
|
||||||
|
|
||||||
tfc = str(tmpdir.join("tfile"))
|
@mock.patch('mitmproxy.master.Master.load_flow')
|
||||||
write_data(tfc, corrupt=True)
|
def test_normal(self, load_flow, tmpdir, data):
|
||||||
|
rf = readfile.ReadFileStdin()
|
||||||
with pytest.raises(exceptions.FlowReadException):
|
with taddons.context():
|
||||||
rf.load_flows_file(tfc)
|
tfile = tmpdir.join("tfile")
|
||||||
assert mck.called
|
tfile.write(data.getvalue())
|
||||||
assert len(tctx.master.logs) == 2
|
rf.load_flows_from_path(str(tfile))
|
||||||
|
assert load_flow.called
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
import io
|
|
||||||
from mitmproxy.addons import readstdin
|
|
||||||
from mitmproxy.test import taddons
|
|
||||||
from mitmproxy.test import tflow
|
|
||||||
import mitmproxy.io
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
|
|
||||||
def gen_data(corrupt=False):
|
|
||||||
tf = io.BytesIO()
|
|
||||||
w = mitmproxy.io.FlowWriter(tf)
|
|
||||||
for i in range(3):
|
|
||||||
f = tflow.tflow(resp=True)
|
|
||||||
w.add(f)
|
|
||||||
for i in range(3):
|
|
||||||
f = tflow.tflow(err=True)
|
|
||||||
w.add(f)
|
|
||||||
f = tflow.ttcpflow()
|
|
||||||
w.add(f)
|
|
||||||
f = tflow.ttcpflow(err=True)
|
|
||||||
w.add(f)
|
|
||||||
if corrupt:
|
|
||||||
tf.write(b"flibble")
|
|
||||||
tf.seek(0)
|
|
||||||
return tf
|
|
||||||
|
|
||||||
|
|
||||||
class mStdin:
|
|
||||||
def __init__(self, d):
|
|
||||||
self.buffer = d
|
|
||||||
|
|
||||||
def isatty(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('mitmproxy.master.Master.load_flow')
|
|
||||||
def test_read(m, tmpdir):
|
|
||||||
rf = readstdin.ReadStdin()
|
|
||||||
with taddons.context() as tctx:
|
|
||||||
assert not m.called
|
|
||||||
rf.running(stdin=mStdin(gen_data()))
|
|
||||||
assert m.called
|
|
||||||
|
|
||||||
rf.running(stdin=mStdin(None))
|
|
||||||
assert tctx.master.logs
|
|
||||||
tctx.master.clear()
|
|
||||||
|
|
||||||
m.reset_mock()
|
|
||||||
assert not m.called
|
|
||||||
rf.running(stdin=mStdin(gen_data(corrupt=True)))
|
|
||||||
assert m.called
|
|
||||||
assert tctx.master.logs
|
|
@ -79,3 +79,9 @@ def test_check_io():
|
|||||||
typecheck.check_type("foo", io.StringIO(), typing.IO[str])
|
typecheck.check_type("foo", io.StringIO(), typing.IO[str])
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
typecheck.check_type("foo", "foo", typing.IO[str])
|
typecheck.check_type("foo", "foo", typing.IO[str])
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_any():
|
||||||
|
typecheck.check_type("foo", 42, typing.Any)
|
||||||
|
typecheck.check_type("foo", object(), typing.Any)
|
||||||
|
typecheck.check_type("foo", None, typing.Any)
|
||||||
|
Loading…
Reference in New Issue
Block a user