Merge pull request #1362 from cortesi/errors

Some work on errors and logs
This commit is contained in:
Aldo Cortesi 2016-07-16 11:46:01 +12:00 committed by GitHub
commit 50c76ac4ff
18 changed files with 78 additions and 60 deletions

View File

@ -131,6 +131,7 @@ class Script:
self.should_reload.clear() self.should_reload.clear()
ctx.log.info("Reloading script: %s" % self.name) ctx.log.info("Reloading script: %s" % self.name)
self.ns = load_script(self.path, self.args) self.ns = load_script(self.path, self.args)
self.start()
self.configure(self.last_options) self.configure(self.last_options)
else: else:
self.run("tick") self.run("tick")

View File

@ -284,8 +284,8 @@ def basic_options(parser):
) )
parser.add_argument( parser.add_argument(
"-v", "--verbose", "-v", "--verbose",
action="store_const", dest="verbose", default=1, const=2, action="store_const", dest="verbose", default=2, const=3,
help="Increase event log verbosity." help="Increase log verbosity."
) )
outfile = parser.add_mutually_exclusive_group() outfile = parser.add_mutually_exclusive_group()
outfile.add_argument( outfile.add_argument(
@ -384,7 +384,7 @@ def proxy_options(parser):
help=""" help="""
Generic TCP SSL proxy mode for all hosts that match the pattern. Generic TCP SSL proxy mode for all hosts that match the pattern.
Similar to --ignore, but SSL connections are intercepted. The Similar to --ignore, but SSL connections are intercepted. The
communication contents are printed to the event log in verbose mode. communication contents are printed to the log in verbose mode.
""" """
) )
group.add_argument( group.add_argument(

View File

@ -44,11 +44,11 @@ footer = [
] ]
class EventListBox(urwid.ListBox): class LogBufferBox(urwid.ListBox):
def __init__(self, master): def __init__(self, master):
self.master = master self.master = master
urwid.ListBox.__init__(self, master.eventlist) urwid.ListBox.__init__(self, master.logbuffer)
def keypress(self, size, key): def keypress(self, size, key):
key = common.shortcuts(key) key = common.shortcuts(key)
@ -56,7 +56,7 @@ class EventListBox(urwid.ListBox):
self.master.clear_events() self.master.clear_events()
key = None key = None
elif key == "G": elif key == "G":
self.set_focus(len(self.master.eventlist) - 1) self.set_focus(len(self.master.logbuffer) - 1)
elif key == "g": elif key == "g":
self.set_focus(0) self.set_focus(0)
return urwid.ListBox.keypress(self, size, key) return urwid.ListBox.keypress(self, size, key)
@ -76,7 +76,7 @@ class BodyPile(urwid.Pile):
[ [
FlowListBox(master), FlowListBox(master),
urwid.Frame( urwid.Frame(
EventListBox(master), LogBufferBox(master),
header = self.inactive_header header = self.inactive_header
) )
] ]

View File

@ -208,7 +208,7 @@ class FlowView(tabs.Tabs):
) )
except exceptions.ContentViewException: except exceptions.ContentViewException:
s = "Content viewer failed: \n" + traceback.format_exc() s = "Content viewer failed: \n" + traceback.format_exc()
signals.add_event(s, "error") signals.add_log(s, "error")
description, lines = contentviews.get_content_view( description, lines = contentviews.get_content_view(
contentviews.get("Raw"), message.content, headers=message.headers contentviews.get("Raw"), message.content, headers=message.headers
) )

View File

@ -22,6 +22,7 @@ from mitmproxy import controller
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import flow from mitmproxy import flow
from mitmproxy import script from mitmproxy import script
from mitmproxy import utils
from mitmproxy.console import flowlist from mitmproxy.console import flowlist
from mitmproxy.console import flowview from mitmproxy.console import flowview
from mitmproxy.console import grideditor from mitmproxy.console import grideditor
@ -204,8 +205,6 @@ class ConsoleMaster(flow.FlowMaster):
def __init__(self, server, options): def __init__(self, server, options):
flow.FlowMaster.__init__(self, options, server, ConsoleState()) flow.FlowMaster.__init__(self, options, server, ConsoleState())
self.addons.add(*builtins.default_addons())
self.stream_path = None self.stream_path = None
# This line is just for type hinting # This line is just for type hinting
self.options = self.options # type: Options self.options = self.options # type: Options
@ -238,8 +237,7 @@ class ConsoleMaster(flow.FlowMaster):
self.palette = options.palette self.palette = options.palette
self.palette_transparent = options.palette_transparent self.palette_transparent = options.palette_transparent
self.eventlog = options.eventlog self.logbuffer = urwid.SimpleListWalker([])
self.eventlist = urwid.SimpleListWalker([])
self.follow = options.follow self.follow = options.follow
if options.client_replay: if options.client_replay:
@ -252,10 +250,12 @@ class ConsoleMaster(flow.FlowMaster):
if options.app: if options.app:
self.start_app(self.options.app_host, self.options.app_port) self.start_app(self.options.app_host, self.options.app_port)
signals.call_in.connect(self.sig_call_in) signals.call_in.connect(self.sig_call_in)
signals.pop_view_state.connect(self.sig_pop_view_state) signals.pop_view_state.connect(self.sig_pop_view_state)
signals.push_view_state.connect(self.sig_push_view_state) signals.push_view_state.connect(self.sig_push_view_state)
signals.sig_add_event.connect(self.sig_add_event) signals.sig_add_log.connect(self.sig_add_log)
self.addons.add(*builtins.default_addons())
def __setattr__(self, name, value): def __setattr__(self, name, value):
self.__dict__[name] = value self.__dict__[name] = value
@ -271,22 +271,24 @@ class ConsoleMaster(flow.FlowMaster):
# We default to using the reloader in the console ui. # We default to using the reloader in the console ui.
return super(ConsoleMaster, self).load_script(command, use_reloader) return super(ConsoleMaster, self).load_script(command, use_reloader)
def sig_add_event(self, sender, e, level): def sig_add_log(self, sender, e, level):
needed = dict(error=0, info=1, debug=2).get(level, 1) if self.options.verbosity < utils.log_tier(level):
if self.options.verbosity < needed:
return return
if level == "error": if level == "error":
signals.status_message.send(
message = "Error: %s" % str(e)
)
e = urwid.Text(("error", str(e))) e = urwid.Text(("error", str(e)))
else: else:
e = urwid.Text(str(e)) e = urwid.Text(str(e))
self.eventlist.append(e) self.logbuffer.append(e)
if len(self.eventlist) > EVENTLOG_SIZE: if len(self.logbuffer) > EVENTLOG_SIZE:
self.eventlist.pop(0) self.logbuffer.pop(0)
self.eventlist.set_focus(len(self.eventlist) - 1) self.logbuffer.set_focus(len(self.logbuffer) - 1)
def add_event(self, e, level): def add_log(self, e, level):
signals.add_event(e, level) signals.add_log(e, level)
def sig_call_in(self, sender, seconds, callback, args=()): def sig_call_in(self, sender, seconds, callback, args=()):
def cb(*_): def cb(*_):
@ -317,16 +319,16 @@ class ConsoleMaster(flow.FlowMaster):
status, val = s.run(method, f) status, val = s.run(method, f)
if val: if val:
if status: if status:
signals.add_event("Method %s return: %s" % (method, val), "debug") signals.add_log("Method %s return: %s" % (method, val), "debug")
else: else:
signals.add_event( signals.add_log(
"Method %s error: %s" % "Method %s error: %s" %
(method, val[1]), "error") (method, val[1]), "error")
def run_script_once(self, command, f): def run_script_once(self, command, f):
if not command: if not command:
return return
signals.add_event("Running script on flow: %s" % command, "debug") signals.add_log("Running script on flow: %s" % command, "debug")
try: try:
s = script.Script(command) s = script.Script(command)
@ -335,7 +337,7 @@ class ConsoleMaster(flow.FlowMaster):
signals.status_message.send( signals.status_message.send(
message='Error loading "{}".'.format(command) message='Error loading "{}".'.format(command)
) )
signals.add_event('Error loading "{}":\n{}'.format(command, e), "error") signals.add_log('Error loading "{}":\n{}'.format(command, e), "error")
return return
if f.request: if f.request:
@ -348,7 +350,7 @@ class ConsoleMaster(flow.FlowMaster):
signals.flow_change.send(self, flow = f) signals.flow_change.send(self, flow = f)
def toggle_eventlog(self): def toggle_eventlog(self):
self.eventlog = not self.eventlog self.options.eventlog = not self.options.eventlog
signals.pop_view_state.send(self) signals.pop_view_state.send(self)
self.view_flowlist() self.view_flowlist()
@ -475,7 +477,7 @@ class ConsoleMaster(flow.FlowMaster):
if self.options.rfile: if self.options.rfile:
ret = self.load_flows_path(self.options.rfile) ret = self.load_flows_path(self.options.rfile)
if ret and self.state.flow_count(): if ret and self.state.flow_count():
signals.add_event( signals.add_log(
"File truncated or corrupted. " "File truncated or corrupted. "
"Loaded as many flows as possible.", "Loaded as many flows as possible.",
"error" "error"
@ -578,7 +580,7 @@ class ConsoleMaster(flow.FlowMaster):
if self.state.follow_focus: if self.state.follow_focus:
self.state.set_focus(self.state.flow_count()) self.state.set_focus(self.state.flow_count())
if self.eventlog: if self.options.eventlog:
body = flowlist.BodyPile(self) body = flowlist.BodyPile(self)
else: else:
body = flowlist.FlowListBox(self) body = flowlist.FlowListBox(self)
@ -723,7 +725,7 @@ class ConsoleMaster(flow.FlowMaster):
signals.flow_change.send(self, flow = f) signals.flow_change.send(self, flow = f)
def clear_events(self): def clear_events(self):
self.eventlist[:] = [] self.logbuffer[:] = []
# Handlers # Handlers
@controller.handler @controller.handler
@ -752,12 +754,12 @@ class ConsoleMaster(flow.FlowMaster):
super(ConsoleMaster, self).tcp_message(f) super(ConsoleMaster, self).tcp_message(f)
message = f.messages[-1] message = f.messages[-1]
direction = "->" if message.from_client else "<-" direction = "->" if message.from_client else "<-"
self.add_event("{client} {direction} tcp {direction} {server}".format( self.add_log("{client} {direction} tcp {direction} {server}".format(
client=repr(f.client_conn.address), client=repr(f.client_conn.address),
server=repr(f.server_conn.address), server=repr(f.server_conn.address),
direction=direction, direction=direction,
), "info") ), "info")
self.add_event(strutils.bytes_to_escaped_str(message.content), "debug") self.add_log(strutils.bytes_to_escaped_str(message.content), "debug")
@controller.handler @controller.handler
def script_change(self, script): def script_change(self, script):

View File

@ -24,7 +24,7 @@ class Palette:
# List and Connections # List and Connections
'method', 'focus', 'method', 'focus',
'code_200', 'code_300', 'code_400', 'code_500', 'code_other', 'code_200', 'code_300', 'code_400', 'code_500', 'code_other',
'error', 'error', "warn",
'header', 'highlight', 'intercept', 'replay', 'mark', 'header', 'highlight', 'intercept', 'replay', 'mark',
# Hex view # Hex view
@ -100,6 +100,7 @@ class LowDark(Palette):
code_500 = ('light red', 'default'), code_500 = ('light red', 'default'),
code_other = ('dark red', 'default'), code_other = ('dark red', 'default'),
warn = ('brown', 'default'),
error = ('light red', 'default'), error = ('light red', 'default'),
header = ('dark cyan', 'default'), header = ('dark cyan', 'default'),
@ -166,6 +167,7 @@ class LowLight(Palette):
code_other = ('light red', 'default'), code_other = ('light red', 'default'),
error = ('light red', 'default'), error = ('light red', 'default'),
warn = ('brown', 'default'),
header = ('dark blue', 'default'), header = ('dark blue', 'default'),
highlight = ('black,bold', 'default'), highlight = ('black,bold', 'default'),
@ -250,6 +252,7 @@ class SolarizedLight(LowLight):
code_other = (sol_magenta, 'default'), code_other = (sol_magenta, 'default'),
error = (sol_red, 'default'), error = (sol_red, 'default'),
warn = (sol_orange, 'default'),
header = (sol_blue, 'default'), header = (sol_blue, 'default'),
highlight = (sol_base01, 'default'), highlight = (sol_base01, 'default'),
@ -299,6 +302,7 @@ class SolarizedDark(LowDark):
code_other = (sol_magenta, 'default'), code_other = (sol_magenta, 'default'),
error = (sol_red, 'default'), error = (sol_red, 'default'),
warn = (sol_orange, 'default'),
header = (sol_blue, 'default'), header = (sol_blue, 'default'),
highlight = (sol_base01, 'default'), highlight = (sol_base01, 'default'),

View File

@ -3,11 +3,11 @@ from __future__ import absolute_import, print_function, division
import blinker import blinker
# Show a status message in the action bar # Show a status message in the action bar
sig_add_event = blinker.Signal() sig_add_log = blinker.Signal()
def add_event(e, level): def add_log(e, level):
sig_add_event.send( sig_add_log.send(
None, None,
e=e, e=e,
level=level level=level

View File

@ -47,7 +47,7 @@ class Log(object):
self.master = master self.master = master
def __call__(self, text, level="info"): def __call__(self, text, level="info"):
self.master.add_event(text, level) self.master.add_log(text, level)
def debug(self, txt): def debug(self, txt):
self(txt, "debug") self(txt, "debug")
@ -89,6 +89,11 @@ class Master(object):
mitmproxy_ctx.master = None mitmproxy_ctx.master = None
mitmproxy_ctx.log = None mitmproxy_ctx.log = None
def add_log(self, e, level="info"):
"""
level: debug, info, warn, error
"""
def add_server(self, server): def add_server(self, server):
# We give a Channel to the server which can be used to communicate with the master # We give a Channel to the server which can be used to communicate with the master
channel = Channel(self.event_queue, self.should_exit) channel = Channel(self.event_queue, self.should_exit)

View File

@ -15,6 +15,7 @@ from mitmproxy import exceptions
from mitmproxy import filt from mitmproxy import filt
from mitmproxy import flow from mitmproxy import flow
from mitmproxy import builtins from mitmproxy import builtins
from mitmproxy import utils
from netlib import human from netlib import human
from netlib import tcp from netlib import tcp
from netlib import strutils from netlib import strutils
@ -44,6 +45,7 @@ class DumpMaster(flow.FlowMaster):
def __init__(self, server, options): def __init__(self, server, options):
flow.FlowMaster.__init__(self, options, server, flow.State()) flow.FlowMaster.__init__(self, options, server, flow.State())
self.has_errored = False
self.addons.add(*builtins.default_addons()) self.addons.add(*builtins.default_addons())
# This line is just for type hinting # This line is just for type hinting
self.options = self.options # type: Options self.options = self.options # type: Options
@ -97,7 +99,7 @@ class DumpMaster(flow.FlowMaster):
try: try:
self.load_flows_file(options.rfile) self.load_flows_file(options.rfile)
except exceptions.FlowReadException as v: except exceptions.FlowReadException as v:
self.add_event("Flow file corrupted.", "error") self.add_log("Flow file corrupted.", "error")
raise DumpError(v) raise DumpError(v)
if self.options.app: if self.options.app:
@ -113,9 +115,10 @@ class DumpMaster(flow.FlowMaster):
except exceptions.FlowReadException as e: except exceptions.FlowReadException as e:
raise DumpError(str(e)) raise DumpError(str(e))
def add_event(self, e, level="info"): def add_log(self, e, level="info"):
needed = dict(error=0, info=1, debug=2).get(level, 1) if level == "error":
if self.options.verbosity >= needed: self.has_errored = True
if self.options.verbosity >= utils.log_tier(level):
self.echo( self.echo(
e, e,
fg="red" if level == "error" else None, fg="red" if level == "error" else None,
@ -157,7 +160,7 @@ class DumpMaster(flow.FlowMaster):
) )
except exceptions.ContentViewException: except exceptions.ContentViewException:
s = "Content viewer failed: \n" + traceback.format_exc() s = "Content viewer failed: \n" + traceback.format_exc()
self.add_event(s, "debug") self.add_log(s, "debug")
type, lines = contentviews.get_content_view( type, lines = contentviews.get_content_view(
contentviews.get("Raw"), contentviews.get("Raw"),
message.content, message.content,

View File

@ -52,11 +52,6 @@ class FlowMaster(controller.Master):
port port
) )
def add_event(self, e, level="info"):
"""
level: debug, info, error
"""
def get_ignore_filter(self): def get_ignore_filter(self):
return self.server.config.check_ignore.patterns return self.server.config.check_ignore.patterns
@ -287,7 +282,7 @@ class FlowMaster(controller.Master):
@controller.handler @controller.handler
def log(self, l): def log(self, l):
self.add_event(l.msg, l.level) self.add_log(l.msg, l.level)
@controller.handler @controller.handler
def clientconnect(self, root_layer): def clientconnect(self, root_layer):
@ -327,7 +322,7 @@ class FlowMaster(controller.Master):
**{"mitmproxy.master": self} **{"mitmproxy.master": self}
) )
if err: if err:
self.add_event("Error in wsgi app. %s" % err, "error") self.add_log("Error in wsgi app. %s" % err, "error")
f.reply.kill() f.reply.kill()
return return
if f not in self.state.flows: # don't add again on replay if f not in self.state.flows: # don't add again on replay
@ -382,7 +377,7 @@ class FlowMaster(controller.Master):
@controller.handler @controller.handler
def tcp_error(self, flow): def tcp_error(self, flow):
self.add_event("Error in TCP connection to {}: {}".format( self.add_log("Error in TCP connection to {}: {}".format(
repr(flow.server_conn.address), repr(flow.server_conn.address),
flow.error flow.error
), "info") ), "info")

View File

@ -30,7 +30,7 @@ class Options(options.Options):
stickycookie=None, # type: Optional[str] stickycookie=None, # type: Optional[str]
stickyauth=None, # type: Optional[str] stickyauth=None, # type: Optional[str]
stream_large_bodies=None, # type: Optional[str] stream_large_bodies=None, # type: Optional[str]
verbosity=1, # type: int verbosity=2, # type: int
outfile=None, # type: Tuple[str, str] outfile=None, # type: Tuple[str, str]
replay_ignore_content=False, # type: bool replay_ignore_content=False, # type: bool
replay_ignore_params=(), # type: Sequence[str] replay_ignore_params=(), # type: Sequence[str]

View File

@ -118,6 +118,9 @@ def mitmdump(args=None): # pragma: no cover
sys.exit(1) sys.exit(1)
except (KeyboardInterrupt, _thread.error): except (KeyboardInterrupt, _thread.error):
pass pass
if master.has_errored:
print("mitmdump: errors occurred during run", file=sys.stderr)
sys.exit(1)
def mitmweb(args=None): # pragma: no cover def mitmweb(args=None): # pragma: no cover

View File

@ -36,3 +36,7 @@ class LRUCache:
d = self.cacheList.pop() d = self.cacheList.pop()
self.cache.pop(d) self.cache.pop(d)
return ret return ret
def log_tier(level):
return dict(error=0, warn=1, info=2, debug=3).get(level)

View File

@ -67,7 +67,7 @@ class WebState(flow.State):
self._last_event_id = 0 self._last_event_id = 0
self.events = collections.deque(maxlen=1000) self.events = collections.deque(maxlen=1000)
def add_event(self, e, level): def add_log(self, e, level):
self._last_event_id += 1 self._last_event_id += 1
entry = { entry = {
"id": self._last_event_id, "id": self._last_event_id,
@ -145,7 +145,7 @@ class WebMaster(flow.FlowMaster):
try: try:
self.load_flows_file(options.rfile) self.load_flows_file(options.rfile)
except exceptions.FlowReadException as v: except exceptions.FlowReadException as v:
self.add_event( self.add_log(
"Could not read flow file: %s" % v, "Could not read flow file: %s" % v,
"error" "error"
) )
@ -200,6 +200,6 @@ class WebMaster(flow.FlowMaster):
super(WebMaster, self).error(f) super(WebMaster, self).error(f)
return self._process_flow(f) return self._process_flow(f)
def add_event(self, e, level="info"): def add_log(self, e, level="info"):
super(WebMaster, self).add_event(e, level) super(WebMaster, self).add_log(e, level)
return self.state.add_event(e, level) return self.state.add_log(e, level)

View File

@ -47,5 +47,5 @@ class RecordingMaster(master.FlowMaster):
master.FlowMaster.__init__(self, *args, **kwargs) master.FlowMaster.__init__(self, *args, **kwargs)
self.event_log = [] self.event_log = []
def add_event(self, e, level): def add_log(self, e, level):
self.event_log.append((level, e)) self.event_log.append((level, e))

View File

@ -235,7 +235,8 @@ class TestDumpMaster(mastertest.MasterTest):
ret = self.dummy_cycle( ret = self.dummy_cycle(
self.mkmaster( self.mkmaster(
None, None,
scripts=[tutils.test_data.path("data/scripts/all.py")], verbosity=1 scripts=[tutils.test_data.path("data/scripts/all.py")],
verbosity=2
), ),
1, b"", 1, b"",
) )

View File

@ -21,7 +21,7 @@ class ScriptError(Exception):
class RaiseMaster(master.FlowMaster): class RaiseMaster(master.FlowMaster):
def add_event(self, e, level): def add_log(self, e, level):
if level in ("warn", "error"): if level in ("warn", "error"):
raise ScriptError(e) raise ScriptError(e)

View File

@ -42,7 +42,7 @@ class TestMaster(flow.FlowMaster):
def clear_log(self): def clear_log(self):
self.tlog = [] self.tlog = []
def add_event(self, message, level=None): def add_log(self, message, level=None):
self.tlog.append(message) self.tlog.append(message)