mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 00:01:36 +00:00
Add timestamps to flows.
For now, these are only displayed on the connection view screen, with second granularity.
This commit is contained in:
parent
673ff01acc
commit
6c89749f0a
@ -44,13 +44,19 @@ def format_keyvals(lst, key="key", val="text", space=5, indent=0):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def format_flow(f, focus, padding=3):
|
def format_flow(f, focus, extended=False, padding=3):
|
||||||
if not f.request and not f.response:
|
if not f.request and not f.response:
|
||||||
txt = [
|
txt = [
|
||||||
("title", " Connection from %s..."%(f.connection.address)),
|
("title", " Connection from %s..."%(f.connection.address)),
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
|
if extended:
|
||||||
|
ts = ("highlight", utils.format_timestamp(f.request.timestamp))
|
||||||
|
else:
|
||||||
|
ts = ""
|
||||||
|
|
||||||
txt = [
|
txt = [
|
||||||
|
ts,
|
||||||
("ack", "!") if f.intercepting and not f.request.acked else " ",
|
("ack", "!") if f.intercepting and not f.request.acked else " ",
|
||||||
("method", f.request.method),
|
("method", f.request.method),
|
||||||
" ",
|
" ",
|
||||||
@ -60,7 +66,16 @@ def format_flow(f, focus, padding=3):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
if f.response or f.error or f.is_replay():
|
if f.response or f.error or f.is_replay():
|
||||||
txt.append("\n" + " "*(padding+2))
|
|
||||||
|
tsr = f.response or f.error
|
||||||
|
if extended and tsr:
|
||||||
|
ts = ("highlight", utils.format_timestamp(tsr.timestamp))
|
||||||
|
else:
|
||||||
|
ts = ""
|
||||||
|
|
||||||
|
txt.append("\n")
|
||||||
|
txt.append(("text", ts))
|
||||||
|
txt.append(" "*(padding+2))
|
||||||
met = ""
|
met = ""
|
||||||
if f.is_replay():
|
if f.is_replay():
|
||||||
txt.append(("method", "[replay] "))
|
txt.append(("method", "[replay] "))
|
||||||
@ -198,11 +213,11 @@ class ConnectionListView(urwid.ListWalker):
|
|||||||
class ConnectionViewHeader(WWrap):
|
class ConnectionViewHeader(WWrap):
|
||||||
def __init__(self, master, f):
|
def __init__(self, master, f):
|
||||||
self.master, self.flow = master, f
|
self.master, self.flow = master, f
|
||||||
self.w = urwid.Text(format_flow(f, False, padding=0))
|
self.w = urwid.Text(format_flow(f, False, extended=True, padding=0))
|
||||||
|
|
||||||
def refresh_connection(self, f):
|
def refresh_connection(self, f):
|
||||||
if f == self.flow:
|
if f == self.flow:
|
||||||
self.w = urwid.Text(format_flow(f, False, padding=0))
|
self.w = urwid.Text(format_flow(f, False, extended=True, padding=0))
|
||||||
|
|
||||||
|
|
||||||
VIEW_BODY_RAW = 0
|
VIEW_BODY_RAW = 0
|
||||||
@ -810,6 +825,7 @@ class ConsoleMaster(controller.Master):
|
|||||||
('error', 'light red', 'default'),
|
('error', 'light red', 'default'),
|
||||||
('header', 'dark cyan', 'default'),
|
('header', 'dark cyan', 'default'),
|
||||||
('heading', 'white', 'dark blue'),
|
('heading', 'white', 'dark blue'),
|
||||||
|
('highlight', 'white', 'default'),
|
||||||
('inactive', 'dark gray', 'default'),
|
('inactive', 'dark gray', 'default'),
|
||||||
('ack', 'light red', 'default'),
|
('ack', 'light red', 'default'),
|
||||||
|
|
||||||
|
@ -84,10 +84,11 @@ def parse_proxy_request(request):
|
|||||||
|
|
||||||
class Request(controller.Msg):
|
class Request(controller.Msg):
|
||||||
FMT = '%s %s HTTP/1.0\r\n%s\r\n%s'
|
FMT = '%s %s HTTP/1.0\r\n%s\r\n%s'
|
||||||
def __init__(self, connection, host, port, scheme, method, path, headers, content):
|
def __init__(self, connection, host, port, scheme, method, path, headers, content, timestamp=None):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.host, self.port, self.scheme = host, port, scheme
|
self.host, self.port, self.scheme = host, port, scheme
|
||||||
self.method, self.path, self.headers, self.content = method, path, headers, content
|
self.method, self.path, self.headers, self.content = method, path, headers, content
|
||||||
|
self.timestamp = timestamp or time.time()
|
||||||
controller.Msg.__init__(self)
|
controller.Msg.__init__(self)
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
@ -98,7 +99,8 @@ class Request(controller.Msg):
|
|||||||
method = self.method,
|
method = self.method,
|
||||||
path = self.path,
|
path = self.path,
|
||||||
headers = self.headers.get_state(),
|
headers = self.headers.get_state(),
|
||||||
content = self.content
|
content = self.content,
|
||||||
|
timestamp = self.timestamp,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -111,7 +113,8 @@ class Request(controller.Msg):
|
|||||||
state["method"],
|
state["method"],
|
||||||
state["path"],
|
state["path"],
|
||||||
utils.Headers.from_state(state["headers"]),
|
utils.Headers.from_state(state["headers"]),
|
||||||
state["content"]
|
state["content"],
|
||||||
|
state["timestamp"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
@ -159,10 +162,11 @@ class Request(controller.Msg):
|
|||||||
|
|
||||||
class Response(controller.Msg):
|
class Response(controller.Msg):
|
||||||
FMT = '%s\r\n%s\r\n%s'
|
FMT = '%s\r\n%s\r\n%s'
|
||||||
def __init__(self, request, code, proto, msg, headers, content):
|
def __init__(self, request, code, proto, msg, headers, content, timestamp=None):
|
||||||
self.request = request
|
self.request = request
|
||||||
self.code, self.proto, self.msg = code, proto, msg
|
self.code, self.proto, self.msg = code, proto, msg
|
||||||
self.headers, self.content = headers, content
|
self.headers, self.content = headers, content
|
||||||
|
self.timestamp = timestamp or time.time()
|
||||||
controller.Msg.__init__(self)
|
controller.Msg.__init__(self)
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
@ -171,6 +175,7 @@ class Response(controller.Msg):
|
|||||||
proto = self.proto,
|
proto = self.proto,
|
||||||
msg = self.msg,
|
msg = self.msg,
|
||||||
headers = self.headers.get_state(),
|
headers = self.headers.get_state(),
|
||||||
|
timestamp = self.timestamp,
|
||||||
content = self.content
|
content = self.content
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -182,7 +187,8 @@ class Response(controller.Msg):
|
|||||||
state["proto"],
|
state["proto"],
|
||||||
state["msg"],
|
state["msg"],
|
||||||
utils.Headers.from_state(state["headers"]),
|
utils.Headers.from_state(state["headers"]),
|
||||||
state["content"]
|
state["content"],
|
||||||
|
state["timestamp"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
@ -225,8 +231,9 @@ class BrowserConnection(controller.Msg):
|
|||||||
|
|
||||||
|
|
||||||
class Error(controller.Msg):
|
class Error(controller.Msg):
|
||||||
def __init__(self, connection, msg):
|
def __init__(self, connection, msg, timestamp=None):
|
||||||
self.connection, self.msg = connection, msg
|
self.connection, self.msg = connection, msg
|
||||||
|
self.timestamp = timestamp or time.time()
|
||||||
controller.Msg.__init__(self)
|
controller.Msg.__init__(self)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
@ -235,6 +242,7 @@ class Error(controller.Msg):
|
|||||||
def get_state(self):
|
def get_state(self):
|
||||||
return dict(
|
return dict(
|
||||||
msg = self.msg,
|
msg = self.msg,
|
||||||
|
timestamp = self.timestamp,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -242,6 +250,7 @@ class Error(controller.Msg):
|
|||||||
return klass(
|
return klass(
|
||||||
None,
|
None,
|
||||||
state["msg"],
|
state["msg"],
|
||||||
|
state["timestamp"],
|
||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
@ -13,9 +13,15 @@
|
|||||||
# 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
|
import re, os, subprocess, datetime
|
||||||
from contrib import BeautifulSoup
|
from contrib import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
|
def format_timestamp(s):
|
||||||
|
d = datetime.datetime.fromtimestamp(s)
|
||||||
|
return d.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
def isBin(s):
|
def isBin(s):
|
||||||
"""
|
"""
|
||||||
Does this string have any non-ASCII characters?
|
Does this string have any non-ASCII characters?
|
||||||
|
@ -108,13 +108,18 @@ class uformat_flow(libpry.AutoTree):
|
|||||||
assert ('focus', '>> ') not in console.format_flow(f, False)
|
assert ('focus', '>> ') not in console.format_flow(f, False)
|
||||||
assert ('focus', '>> ') in console.format_flow(f, True)
|
assert ('focus', '>> ') in console.format_flow(f, True)
|
||||||
|
|
||||||
|
assert ('focus', '>> ') not in console.format_flow(f, False, True)
|
||||||
|
assert ('focus', '>> ') in console.format_flow(f, True, True)
|
||||||
|
|
||||||
f.response = utils.tresp()
|
f.response = utils.tresp()
|
||||||
f.request = f.response.request
|
f.request = f.response.request
|
||||||
f.backup()
|
f.backup()
|
||||||
|
|
||||||
assert ('method', '[edited] ') in console.format_flow(f, True)
|
assert ('method', '[edited] ') in console.format_flow(f, True)
|
||||||
|
assert ('method', '[edited] ') in console.format_flow(f, True, True)
|
||||||
f.connection = flow.ReplayConnection()
|
f.connection = flow.ReplayConnection()
|
||||||
assert ('method', '[replay] ') in console.format_flow(f, True)
|
assert ('method', '[replay] ') in console.format_flow(f, True)
|
||||||
|
assert ('method', '[replay] ') in console.format_flow(f, True, True)
|
||||||
|
|
||||||
|
|
||||||
class uPathCompleter(libpry.AutoTree):
|
class uPathCompleter(libpry.AutoTree):
|
||||||
|
@ -267,6 +267,8 @@ class uError(libpry.AutoTree):
|
|||||||
state = e.get_state()
|
state = e.get_state()
|
||||||
assert proxy.Error.from_state(state) == e
|
assert proxy.Error.from_state(state) == e
|
||||||
|
|
||||||
|
assert e.copy()
|
||||||
|
|
||||||
|
|
||||||
class uProxyError(libpry.AutoTree):
|
class uProxyError(libpry.AutoTree):
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import textwrap, cStringIO, os
|
import textwrap, cStringIO, os, time
|
||||||
import libpry
|
import libpry
|
||||||
from libmproxy import utils
|
from libmproxy import utils
|
||||||
|
|
||||||
|
|
||||||
|
class uformat_timestamp(libpry.AutoTree):
|
||||||
|
def test_simple(self):
|
||||||
|
assert utils.format_timestamp(time.time())
|
||||||
|
|
||||||
|
|
||||||
class uisBin(libpry.AutoTree):
|
class uisBin(libpry.AutoTree):
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
assert not utils.isBin("testing\n\r")
|
assert not utils.isBin("testing\n\r")
|
||||||
@ -234,6 +239,7 @@ class uprettybody(libpry.AutoTree):
|
|||||||
|
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
|
uformat_timestamp(),
|
||||||
umake_bogus_cert(),
|
umake_bogus_cert(),
|
||||||
uisBin(),
|
uisBin(),
|
||||||
uhexdump(),
|
uhexdump(),
|
||||||
|
Loading…
Reference in New Issue
Block a user