mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-12-03 12:23:46 +00:00
Improve responsiveness of request and response viewing.
- Computing the view of a large body is expensive, so we introduce an LRU cache to hold the latest 20 results. - Use ListView more correctly, passing it individual urwid.Text snippets, rather than a single large one. This hugely improves render time.
This commit is contained in:
parent
563d4161f1
commit
fe1e2f16ff
@ -289,71 +289,31 @@ class ConnectionView(WWrap):
|
|||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def _view_normal(self, conn, txt):
|
def _conn_text(self, conn, viewmode):
|
||||||
for i in conn.content.splitlines():
|
|
||||||
txt.append(
|
|
||||||
("text", i),
|
|
||||||
)
|
|
||||||
txt.append(
|
|
||||||
("text", "\n"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _view_binary(self, conn, txt):
|
|
||||||
for offset, hex, s in utils.hexdump(conn.content):
|
|
||||||
txt.extend([
|
|
||||||
("offset", offset),
|
|
||||||
" ",
|
|
||||||
("text", hex),
|
|
||||||
" ",
|
|
||||||
("text", s),
|
|
||||||
"\n"
|
|
||||||
])
|
|
||||||
|
|
||||||
def _view_pretty(self, conn, txt):
|
|
||||||
for i in utils.pretty_xmlish(conn.content):
|
|
||||||
txt.append(
|
|
||||||
("text", i),
|
|
||||||
)
|
|
||||||
txt.append(
|
|
||||||
("text", "\n"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _conn_text(self, conn):
|
|
||||||
if conn:
|
if conn:
|
||||||
txt = []
|
return self.master._cached_conn_text(conn.content, tuple(conn.headers.itemPairs()), viewmode)
|
||||||
txt.extend(
|
|
||||||
format_keyvals(
|
|
||||||
[(h+":", v) for (h, v) in sorted(conn.headers.itemPairs())],
|
|
||||||
key = "header",
|
|
||||||
val = "text"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
txt.append("\n\n")
|
|
||||||
if conn.content:
|
|
||||||
if self.state.view_body_mode == VIEW_BODY_BINARY:
|
|
||||||
self._view_binary(conn, txt)
|
|
||||||
elif self.state.view_body_mode == VIEW_BODY_INDENT:
|
|
||||||
self.master.statusbar.update("Calculating pretty mode...")
|
|
||||||
self._view_pretty(conn, txt)
|
|
||||||
self.master.statusbar.update("")
|
|
||||||
else:
|
|
||||||
if utils.isBin(conn.content):
|
|
||||||
self._view_binary(conn, txt)
|
|
||||||
else:
|
|
||||||
self._view_normal(conn, txt)
|
|
||||||
return urwid.ListBox([urwid.Text(txt)])
|
|
||||||
else:
|
else:
|
||||||
return urwid.ListBox([])
|
return urwid.ListBox([])
|
||||||
|
|
||||||
def view_request(self):
|
def view_request(self):
|
||||||
self.state.view_flow_mode = VIEW_FLOW_REQUEST
|
self.state.view_flow_mode = VIEW_FLOW_REQUEST
|
||||||
body = self._conn_text(self.flow.request)
|
self.master.statusbar.update("Calculating view...")
|
||||||
|
body = self._conn_text(
|
||||||
|
self.flow.request,
|
||||||
|
self.state.view_body_mode
|
||||||
|
)
|
||||||
self.w = self.wrap_body(VIEW_FLOW_REQUEST, body)
|
self.w = self.wrap_body(VIEW_FLOW_REQUEST, body)
|
||||||
|
self.master.statusbar.update("")
|
||||||
|
|
||||||
def view_response(self):
|
def view_response(self):
|
||||||
self.state.view_flow_mode = VIEW_FLOW_RESPONSE
|
self.state.view_flow_mode = VIEW_FLOW_RESPONSE
|
||||||
body = self._conn_text(self.flow.response)
|
self.master.statusbar.update("Calculating view...")
|
||||||
|
body = self._conn_text(
|
||||||
|
self.flow.response,
|
||||||
|
self.state.view_body_mode
|
||||||
|
)
|
||||||
self.w = self.wrap_body(VIEW_FLOW_RESPONSE, body)
|
self.w = self.wrap_body(VIEW_FLOW_RESPONSE, body)
|
||||||
|
self.master.statusbar.update("")
|
||||||
|
|
||||||
def refresh_connection(self, c=None):
|
def refresh_connection(self, c=None):
|
||||||
if c == self.flow:
|
if c == self.flow:
|
||||||
@ -729,11 +689,11 @@ class StatusBar(WWrap):
|
|||||||
),
|
),
|
||||||
]), "statusbar")
|
]), "statusbar")
|
||||||
self.ib.set_w(status)
|
self.ib.set_w(status)
|
||||||
self.master.drawscreen()
|
|
||||||
|
|
||||||
def update(self, text):
|
def update(self, text):
|
||||||
self.helptext = text
|
self.helptext = text
|
||||||
self.redraw()
|
self.redraw()
|
||||||
|
self.master.drawscreen()
|
||||||
|
|
||||||
def selectable(self):
|
def selectable(self):
|
||||||
return True
|
return True
|
||||||
@ -896,6 +856,52 @@ class ConsoleMaster(flow.FlowMaster):
|
|||||||
if options.server_replay:
|
if options.server_replay:
|
||||||
self.server_playback_path(options.server_replay)
|
self.server_playback_path(options.server_replay)
|
||||||
|
|
||||||
|
def _view_conn_normal(self, content, txt):
|
||||||
|
for i in content.splitlines():
|
||||||
|
txt.append(
|
||||||
|
urwid.Text(("text", i))
|
||||||
|
)
|
||||||
|
|
||||||
|
def _view_conn_binary(self, content, txt):
|
||||||
|
for offset, hex, s in utils.hexdump(content):
|
||||||
|
txt.append(urwid.Text([
|
||||||
|
("offset", offset),
|
||||||
|
" ",
|
||||||
|
("text", hex),
|
||||||
|
" ",
|
||||||
|
("text", s),
|
||||||
|
]))
|
||||||
|
|
||||||
|
def _view_conn_pretty(self, content, txt):
|
||||||
|
for i in utils.pretty_xmlish(content):
|
||||||
|
txt.append(
|
||||||
|
urwid.Text(("text", i)),
|
||||||
|
)
|
||||||
|
|
||||||
|
@utils.LRUCache(20)
|
||||||
|
def _cached_conn_text(self, content, hdrItems, viewmode):
|
||||||
|
hdr = []
|
||||||
|
hdr.extend(
|
||||||
|
format_keyvals(
|
||||||
|
[(h+":", v) for (h, v) in sorted(hdrItems)],
|
||||||
|
key = "header",
|
||||||
|
val = "text"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hdr.append("\n")
|
||||||
|
|
||||||
|
txt = [urwid.Text(hdr)]
|
||||||
|
if content:
|
||||||
|
if viewmode == VIEW_BODY_BINARY:
|
||||||
|
self._view_conn_binary(content, txt)
|
||||||
|
elif viewmode == VIEW_BODY_INDENT:
|
||||||
|
self._view_conn_pretty(content, txt)
|
||||||
|
else:
|
||||||
|
if utils.isBin(content):
|
||||||
|
self._view_conn_binary(content, txt)
|
||||||
|
else:
|
||||||
|
self._view_conn_normal(content, txt)
|
||||||
|
return urwid.ListBox(txt)
|
||||||
|
|
||||||
def _readflow(self, path):
|
def _readflow(self, path):
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
@ -1272,8 +1278,8 @@ class ConsoleMaster(flow.FlowMaster):
|
|||||||
slave.start()
|
slave.start()
|
||||||
try:
|
try:
|
||||||
while not self._shutdown:
|
while not self._shutdown:
|
||||||
size = self.drawscreen()
|
|
||||||
self.statusbar.redraw()
|
self.statusbar.redraw()
|
||||||
|
size = self.drawscreen()
|
||||||
self.tick(q)
|
self.tick(q)
|
||||||
self.ui.set_input_timeouts(max_wait=0.1)
|
self.ui.set_input_timeouts(max_wait=0.1)
|
||||||
keys = self.ui.get_input()
|
keys = self.ui.get_input()
|
||||||
|
@ -303,6 +303,9 @@ class Response(controller.Msg):
|
|||||||
del i["expires"]
|
del i["expires"]
|
||||||
return c.output(header="").strip()
|
return c.output(header="").strip()
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return id(self)
|
||||||
|
|
||||||
def refresh(self, now=None):
|
def refresh(self, now=None):
|
||||||
"""
|
"""
|
||||||
This fairly complex and heuristic function refreshes a server
|
This fairly complex and heuristic function refreshes a server
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import re, os, subprocess, datetime, textwrap, errno, sys, time
|
import re, os, subprocess, datetime, textwrap, errno, sys, time, functools
|
||||||
|
|
||||||
|
|
||||||
def timestamp():
|
def timestamp():
|
||||||
@ -444,3 +444,41 @@ def dummy_cert(certdir, ca, commonname):
|
|||||||
)
|
)
|
||||||
if ret: return None
|
if ret: return None
|
||||||
return certpath
|
return certpath
|
||||||
|
|
||||||
|
|
||||||
|
class LRUCache:
|
||||||
|
"""
|
||||||
|
A decorator that implements a self-expiring LRU cache for class
|
||||||
|
methods (not functions!).
|
||||||
|
|
||||||
|
Cache data is tracked as attributes on the object itself. There is
|
||||||
|
therefore a separate cache for each object instance.
|
||||||
|
"""
|
||||||
|
def __init__(self, size=100):
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
def __call__(self, f):
|
||||||
|
cacheName = "_cached_%s"%f.__name__
|
||||||
|
cacheListName = "_cachelist_%s"%f.__name__
|
||||||
|
size = self.size
|
||||||
|
|
||||||
|
@functools.wraps(f)
|
||||||
|
def wrap(self, *args):
|
||||||
|
if not hasattr(self, cacheName):
|
||||||
|
setattr(self, cacheName, {})
|
||||||
|
setattr(self, cacheListName, [])
|
||||||
|
cache = getattr(self, cacheName)
|
||||||
|
cacheList = getattr(self, cacheListName)
|
||||||
|
if cache.has_key(args):
|
||||||
|
cacheList.remove(args)
|
||||||
|
cacheList.insert(0, args)
|
||||||
|
return cache[args]
|
||||||
|
else:
|
||||||
|
ret = f(self, *args)
|
||||||
|
cacheList.insert(0, args)
|
||||||
|
cache[args] = ret
|
||||||
|
if len(cacheList) > size:
|
||||||
|
d = cacheList.pop()
|
||||||
|
cache.pop(d)
|
||||||
|
return ret
|
||||||
|
return wrap
|
||||||
|
@ -310,6 +310,36 @@ class udummy_cert(libpry.AutoTree):
|
|||||||
assert os.path.exists(os.path.join(d, "foo.com.pem"))
|
assert os.path.exists(os.path.join(d, "foo.com.pem"))
|
||||||
|
|
||||||
|
|
||||||
|
class uLRUCache(libpry.AutoTree):
|
||||||
|
def test_one(self):
|
||||||
|
class Foo:
|
||||||
|
ran = False
|
||||||
|
@utils.LRUCache(2)
|
||||||
|
def one(self, x):
|
||||||
|
self.ran = True
|
||||||
|
return x
|
||||||
|
|
||||||
|
f = Foo()
|
||||||
|
assert f.one(1) == 1
|
||||||
|
assert f.ran
|
||||||
|
f.ran = False
|
||||||
|
assert f.one(1) == 1
|
||||||
|
assert not f.ran
|
||||||
|
|
||||||
|
f.ran = False
|
||||||
|
assert f.one(1) == 1
|
||||||
|
assert not f.ran
|
||||||
|
assert f.one(2) == 2
|
||||||
|
assert f.one(3) == 3
|
||||||
|
assert f.ran
|
||||||
|
|
||||||
|
f.ran = False
|
||||||
|
assert f.one(1) == 1
|
||||||
|
assert f.ran
|
||||||
|
|
||||||
|
assert len(f._cached_one) == 2
|
||||||
|
assert len(f._cachelist_one) == 2
|
||||||
|
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
uformat_timestamp(),
|
uformat_timestamp(),
|
||||||
@ -324,4 +354,5 @@ tests = [
|
|||||||
upretty_xmlish(),
|
upretty_xmlish(),
|
||||||
udummy_ca(),
|
udummy_ca(),
|
||||||
udummy_cert(),
|
udummy_cert(),
|
||||||
|
uLRUCache(),
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user