aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/console/flowview.py
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy/console/flowview.py')
-rw-r--r--libmproxy/console/flowview.py413
1 files changed, 255 insertions, 158 deletions
diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py
index 5c91512c..1aebb0f0 100644
--- a/libmproxy/console/flowview.py
+++ b/libmproxy/console/flowview.py
@@ -1,7 +1,7 @@
from __future__ import absolute_import
import os, sys, copy
import urwid
-from . import common, grideditor, contentview
+from . import common, grideditor, contentview, signals
from .. import utils, flow, controller
from ..protocol.http import HTTPRequest, HTTPResponse, CONTENT_MISSING, decoded
@@ -19,7 +19,7 @@ def _mkhelp():
("D", "duplicate flow"),
("e", "edit request/response"),
("f", "load full body data"),
- ("g", "copy response(content/headers) to clipboard"),
+ ("g", "copy response(content/headers) to clipboard"),
("m", "change body display mode for this entity"),
(None,
common.highlight_key("automatic", "a") +
@@ -84,32 +84,33 @@ footer = [
]
-class FlowViewHeader(common.WWrap):
+class FlowViewHeader(urwid.WidgetWrap):
def __init__(self, master, f):
self.master, self.flow = master, f
- self.w = common.format_flow(f, False, extended=True, padding=0, hostheader=self.master.showhost)
-
- def refresh_flow(self, f):
- if f == self.flow:
- self.w = common.format_flow(f, False, extended=True, padding=0, hostheader=self.master.showhost)
-
-
-class CallbackCache:
- @utils.LRUCache(200)
- def _callback(self, method, *args, **kwargs):
- return getattr(self.obj, method)(*args, **kwargs)
+ self._w = common.format_flow(
+ f,
+ False,
+ extended=True,
+ padding=0,
+ hostheader=self.master.showhost
+ )
+ signals.flow_change.connect(self.sig_flow_change)
+
+ def sig_flow_change(self, sender, flow):
+ if flow == self.flow:
+ self._w = common.format_flow(
+ flow,
+ False,
+ extended=True,
+ padding=0,
+ hostheader=self.master.showhost
+ )
- def callback(self, obj, method, *args, **kwargs):
- # obj varies!
- self.obj = obj
- return self._callback(method, *args, **kwargs)
-cache = CallbackCache()
+cache = utils.LRUCache(200)
-class FlowView(common.WWrap):
- REQ = 0
- RESP = 1
+class FlowView(urwid.WidgetWrap):
highlight_color = "focusfield"
def __init__(self, master, state, flow):
@@ -119,37 +120,39 @@ class FlowView(common.WWrap):
self.view_response()
else:
self.view_request()
+ signals.flow_change.connect(self.sig_flow_change)
- def _cached_content_view(self, viewmode, hdrItems, content, limit, is_request):
- return contentview.get_content_view(viewmode, hdrItems, content, limit, self.master.add_event, is_request)
+ def sig_flow_change(self, sender, flow):
+ if flow == self.flow:
+ if self.state.view_flow_mode == common.VIEW_FLOW_RESPONSE and self.flow.response:
+ self.view_response()
+ else:
+ self.view_request()
def content_view(self, viewmode, conn):
- full = self.state.get_flow_setting(
- self.flow,
- (self.state.view_flow_mode, "fullcontents"),
- False
- )
- if full:
- limit = sys.maxint
+ if conn.content == CONTENT_MISSING:
+ msg, body = "", [urwid.Text([("error", "[content missing]")])]
+ return (msg, body)
else:
- limit = contentview.VIEW_CUTOFF
- description, text_objects = cache.callback(
- self, "_cached_content_view",
- viewmode,
- tuple(tuple(i) for i in conn.headers.lst),
- conn.content,
- limit,
- isinstance(conn, HTTPRequest)
- )
- return (description, text_objects)
-
- def cont_view_handle_missing(self, conn, viewmode):
- if conn.content == CONTENT_MISSING:
- msg, body = "", [urwid.Text([("error", "[content missing]")])]
+ full = self.state.get_flow_setting(
+ self.flow,
+ (self.state.view_flow_mode, "fullcontents"),
+ False
+ )
+ if full:
+ limit = sys.maxint
else:
- msg, body = self.content_view(viewmode, conn)
-
- return (msg, body)
+ limit = contentview.VIEW_CUTOFF
+ description, text_objects = cache.get(
+ contentview.get_content_view,
+ viewmode,
+ tuple(tuple(i) for i in conn.headers.lst),
+ conn.content,
+ limit,
+ self.master.add_event,
+ isinstance(conn, HTTPRequest)
+ )
+ return (description, text_objects)
def viewmode_get(self, override):
return self.state.default_body_view if override is None else override
@@ -170,7 +173,7 @@ class FlowView(common.WWrap):
)
override = self.override_get()
viewmode = self.viewmode_get(override)
- msg, body = self.cont_view_handle_missing(conn, viewmode)
+ msg, body = self.content_view(viewmode, conn)
return headers, msg, body
def conn_text_merge(self, headers, msg, body):
@@ -207,7 +210,8 @@ class FlowView(common.WWrap):
def conn_text(self, conn):
"""
- Same as conn_text_raw, but returns result wrapped in a listbox ready for usage.
+ Same as conn_text_raw, but returns result wrapped in a listbox ready for
+ usage.
"""
headers, msg, body = self.conn_text_raw(conn)
merged = self.conn_text_merge(headers, msg, body)
@@ -278,14 +282,16 @@ class FlowView(common.WWrap):
"""
runs the previous search again, forwards or backwards.
"""
- last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
+ last_search_string = self.state.get_flow_setting(
+ self.flow, "last_search_string"
+ )
if last_search_string:
message = self.search(last_search_string, backwards)
if message:
- self.master.statusbar.message(message)
+ signals.status_message.send(message=message)
else:
message = "no previous searches have been made"
- self.master.statusbar.message(message)
+ signals.status_message.send(message=message)
return message
@@ -319,7 +325,11 @@ class FlowView(common.WWrap):
# generate the body, highlight the words and get focus
headers, msg, body = self.conn_text_raw(text)
try:
- body, focus_position = self.search_highlight_text(body, search_string, backwards=backwards)
+ body, focus_position = self.search_highlight_text(
+ body,
+ search_string,
+ backwards=backwards
+ )
except SearchError:
return "Search not supported in this view."
@@ -331,12 +341,16 @@ class FlowView(common.WWrap):
merged = self.conn_text_merge(headers, msg, body)
list_box = urwid.ListBox(merged)
list_box.set_focus(focus_position + 2)
- self.w = self.wrap_body(const, list_box)
- self.master.statusbar.redraw()
+ self._w = self.wrap_body(const, list_box)
+ signals.update_settings.send(self)
self.last_displayed_body = list_box
- wrapped, wrapped_message = self.search_wrapped_around(last_find_line, last_search_index, backwards)
+ wrapped, wrapped_message = self.search_wrapped_around(
+ last_find_line,
+ last_search_index,
+ backwards
+ )
if wrapped:
return wrapped_message
@@ -344,9 +358,15 @@ class FlowView(common.WWrap):
def search_get_start(self, search_string):
start_line = 0
start_index = 0
- last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
+ last_search_string = self.state.get_flow_setting(
+ self.flow,
+ "last_search_string"
+ )
if search_string == last_search_string:
- start_line = self.state.get_flow_setting(self.flow, "last_find_line")
+ start_line = self.state.get_flow_setting(
+ self.flow,
+ "last_find_line"
+ )
start_index = self.state.get_flow_setting(self.flow,
"last_search_index")
@@ -391,7 +411,10 @@ class FlowView(common.WWrap):
found = False
text_objects = copy.deepcopy(text_objects)
- loop_range = self.search_get_range(len(text_objects), start_line, backwards)
+ loop_range = self.search_get_range(
+ len(text_objects),
+ start_line, backwards
+ )
for i in loop_range:
text_object = text_objects[i]
@@ -403,10 +426,19 @@ class FlowView(common.WWrap):
if i != start_line:
start_index = 0
- find_index = self.search_find(text, search_string, start_index, backwards)
+ find_index = self.search_find(
+ text,
+ search_string,
+ start_index,
+ backwards
+ )
if find_index != -1:
- new_text = self.search_highlight_object(text, find_index, search_string)
+ new_text = self.search_highlight_object(
+ text,
+ find_index,
+ search_string
+ )
text_objects[i] = new_text
found = True
@@ -424,14 +456,26 @@ class FlowView(common.WWrap):
focus_pos = None
else:
if not backwards:
- self.state.add_flow_setting(self.flow, "last_search_index", 0)
- self.state.add_flow_setting(self.flow, "last_find_line", 0)
+ self.state.add_flow_setting(
+ self.flow, "last_search_index", 0
+ )
+ self.state.add_flow_setting(
+ self.flow, "last_find_line", 0
+ )
else:
- self.state.add_flow_setting(self.flow, "last_search_index", None)
- self.state.add_flow_setting(self.flow, "last_find_line", len(text_objects) - 1)
+ self.state.add_flow_setting(
+ self.flow, "last_search_index", None
+ )
+ self.state.add_flow_setting(
+ self.flow, "last_find_line", len(text_objects) - 1
+ )
- text_objects, focus_pos = self.search_highlight_text(text_objects,
- search_string, looping=True, backwards=backwards)
+ text_objects, focus_pos = self.search_highlight_text(
+ text_objects,
+ search_string,
+ looping=True,
+ backwards=backwards
+ )
return text_objects, focus_pos
@@ -455,8 +499,7 @@ class FlowView(common.WWrap):
def view_request(self):
self.state.view_flow_mode = common.VIEW_FLOW_REQUEST
body = self.conn_text(self.flow.request)
- self.w = self.wrap_body(common.VIEW_FLOW_REQUEST, body)
- self.master.statusbar.redraw()
+ self._w = self.wrap_body(common.VIEW_FLOW_REQUEST, body)
def view_response(self):
self.state.view_flow_mode = common.VIEW_FLOW_RESPONSE
@@ -475,29 +518,25 @@ class FlowView(common.WWrap):
)
]
)
- self.w = self.wrap_body(common.VIEW_FLOW_RESPONSE, body)
- self.master.statusbar.redraw()
-
- def refresh_flow(self, c=None):
- if c == self.flow:
- if self.state.view_flow_mode == common.VIEW_FLOW_RESPONSE and self.flow.response:
- self.view_response()
- else:
- self.view_request()
+ self._w = self.wrap_body(common.VIEW_FLOW_RESPONSE, body)
def set_method_raw(self, m):
if m:
self.flow.request.method = m
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
def edit_method(self, m):
if m == "e":
- self.master.prompt_edit("Method", self.flow.request.method, self.set_method_raw)
+ signals.status_prompt.send(
+ prompt = "Method",
+ text = self.flow.request.method,
+ callback = self.set_method_raw
+ )
else:
for i in common.METHOD_OPTIONS:
if i[1] == m:
self.flow.request.method = i[0].upper()
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
def set_url(self, url):
request = self.flow.request
@@ -505,7 +544,7 @@ class FlowView(common.WWrap):
request.url = str(url)
except ValueError:
return "Invalid URL."
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
def set_resp_code(self, code):
response = self.flow.response
@@ -516,28 +555,37 @@ class FlowView(common.WWrap):
import BaseHTTPServer
if BaseHTTPServer.BaseHTTPRequestHandler.responses.has_key(int(code)):
response.msg = BaseHTTPServer.BaseHTTPRequestHandler.responses[int(code)][0]
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
def set_resp_msg(self, msg):
response = self.flow.response
response.msg = msg
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
def set_headers(self, lst, conn):
conn.headers = flow.ODictCaseless(lst)
+ signals.flow_change.send(self, flow = self.flow)
def set_query(self, lst, conn):
conn.set_query(flow.ODict(lst))
+ signals.flow_change.send(self, flow = self.flow)
def set_path_components(self, lst, conn):
conn.set_path_components([i[0] for i in lst])
+ signals.flow_change.send(self, flow = self.flow)
def set_form(self, lst, conn):
conn.set_form_urlencoded(flow.ODict(lst))
+ signals.flow_change.send(self, flow = self.flow)
def edit_form(self, conn):
self.master.view_grideditor(
- grideditor.URLEncodedFormEditor(self.master, conn.get_form_urlencoded().lst, self.set_form, conn)
+ grideditor.URLEncodedFormEditor(
+ self.master,
+ conn.get_form_urlencoded().lst,
+ self.set_form,
+ conn
+ )
)
def edit_form_confirm(self, key, conn):
@@ -559,42 +607,80 @@ class FlowView(common.WWrap):
self.flow.backup()
if part == "r":
with decoded(message):
- # Fix an issue caused by some editors when editing a request/response body.
- # Many editors make it hard to save a file without a terminating newline on the last
- # line. When editing message bodies, this can cause problems. For now, I just
- # strip the newlines off the end of the body when we return from an editor.
+ # Fix an issue caused by some editors when editing a
+ # request/response body. Many editors make it hard to save a
+ # file without a terminating newline on the last line. When
+ # editing message bodies, this can cause problems. For now, I
+ # just strip the newlines off the end of the body when we return
+ # from an editor.
c = self.master.spawn_editor(message.content or "")
message.content = c.rstrip("\n")
elif part == "f":
if not message.get_form_urlencoded() and message.content:
- self.master.prompt_onekey(
- "Existing body is not a URL-encoded form. Clear and edit?",
- [
+ signals.status_prompt_onekey.send(
+ prompt = "Existing body is not a URL-encoded form. Clear and edit?",
+ keys = [
("yes", "y"),
("no", "n"),
],
- self.edit_form_confirm,
- message
+ callback = self.edit_form_confirm,
+ args = (message,)
)
else:
self.edit_form(message)
elif part == "h":
- self.master.view_grideditor(grideditor.HeaderEditor(self.master, message.headers.lst, self.set_headers, message))
+ self.master.view_grideditor(
+ grideditor.HeaderEditor(
+ self.master,
+ message.headers.lst,
+ self.set_headers,
+ message
+ )
+ )
elif part == "p":
p = message.get_path_components()
p = [[i] for i in p]
- self.master.view_grideditor(grideditor.PathEditor(self.master, p, self.set_path_components, message))
+ self.master.view_grideditor(
+ grideditor.PathEditor(
+ self.master,
+ p,
+ self.set_path_components,
+ message
+ )
+ )
elif part == "q":
- self.master.view_grideditor(grideditor.QueryEditor(self.master, message.get_query().lst, self.set_query, message))
+ self.master.view_grideditor(
+ grideditor.QueryEditor(
+ self.master,
+ message.get_query().lst,
+ self.set_query, message
+ )
+ )
elif part == "u" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
- self.master.prompt_edit("URL", message.url, self.set_url)
+ signals.status_prompt.send(
+ prompt = "URL",
+ text = message.url,
+ callback = self.set_url
+ )
elif part == "m" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
- self.master.prompt_onekey("Method", common.METHOD_OPTIONS, self.edit_method)
+ signals.status_prompt_onekey.send(
+ prompt = "Method",
+ keys = common.METHOD_OPTIONS,
+ callback = self.edit_method
+ )
elif part == "c" and self.state.view_flow_mode == common.VIEW_FLOW_RESPONSE:
- self.master.prompt_edit("Code", str(message.code), self.set_resp_code)
+ signals.status_prompt.send(
+ prompt = "Code",
+ text = str(message.code),
+ callback = self.set_resp_code
+ )
elif part == "m" and self.state.view_flow_mode == common.VIEW_FLOW_RESPONSE:
- self.master.prompt_edit("Message", message.msg, self.set_resp_msg)
- self.master.refresh_flow(self.flow)
+ signals.status_prompt.send(
+ prompt = "Message",
+ text = message.msg,
+ callback = self.set_resp_msg
+ )
+ signals.flow_change.send(self, flow = self.flow)
def _view_nextprev_flow(self, np, flow):
try:
@@ -606,9 +692,10 @@ class FlowView(common.WWrap):
else:
new_flow, new_idx = self.state.get_prev(idx)
if new_flow is None:
- self.master.statusbar.message("No more flows!")
- return
- self.master.view_flow(new_flow)
+ signals.status_message.send(message="No more flows!")
+ else:
+ signals.pop_view_state.send(self)
+ self.master.view_flow(new_flow)
def view_next_flow(self, flow):
return self._view_nextprev_flow("next", flow)
@@ -622,7 +709,7 @@ class FlowView(common.WWrap):
(self.state.view_flow_mode, "prettyview"),
contentview.get_by_shortcut(t)
)
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
def delete_body(self, t):
if t == "m":
@@ -633,7 +720,7 @@ class FlowView(common.WWrap):
self.flow.request.content = val
else:
self.flow.response.content = val
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
def keypress(self, size, key):
if key == " ":
@@ -647,8 +734,8 @@ class FlowView(common.WWrap):
conn = self.flow.response
if key == "q":
- self.master.view_flowlist()
- key = None
+ signals.pop_view_state.send(self)
+ return None
elif key == "tab":
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
self.view_response()
@@ -656,7 +743,7 @@ class FlowView(common.WWrap):
self.view_request()
elif key in ("up", "down", "page up", "page down"):
# Why doesn't this just work??
- self.w.keypress(size, key)
+ self._w.keypress(size, key)
elif key == "a":
self.flow.accept_intercept(self.master)
self.master.view_flow(self.flow)
@@ -681,12 +768,12 @@ class FlowView(common.WWrap):
elif key == "D":
f = self.master.duplicate_flow(self.flow)
self.master.view_flow(f)
- self.master.statusbar.message("Duplicated.")
+ signals.status_message.send(message="Duplicated.")
elif key == "e":
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
- self.master.prompt_onekey(
- "Edit request",
- (
+ signals.status_prompt_onekey.send(
+ prompt = "Edit request",
+ keys = (
("query", "q"),
("path", "p"),
("url", "u"),
@@ -695,29 +782,29 @@ class FlowView(common.WWrap):
("raw body", "r"),
("method", "m"),
),
- self.edit
+ callback = self.edit
)
else:
- self.master.prompt_onekey(
- "Edit response",
- (
+ signals.status_prompt_onekey.send(
+ prompt = "Edit response",
+ keys = (
("code", "c"),
("message", "m"),
("header", "h"),
("raw body", "r"),
),
- self.edit
+ callback = self.edit
)
key = None
elif key == "f":
- self.master.statusbar.message("Loading all body data...")
+ signals.status_message.send(message="Loading all body data...")
self.state.add_flow_setting(
self.flow,
(self.state.view_flow_mode, "fullcontents"),
True
)
- self.master.refresh_flow(self.flow)
- self.master.statusbar.message("")
+ signals.flow_change.send(self, flow = self.flow)
+ signals.status_message.send(message="")
elif key == "g":
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
scope = "q"
@@ -727,10 +814,11 @@ class FlowView(common.WWrap):
elif key == "m":
p = list(contentview.view_prompts)
p.insert(0, ("Clear", "C"))
- self.master.prompt_onekey(
- "Display mode",
- p,
- self.change_this_display_mode
+ signals.status_prompt_onekey.send(
+ self,
+ prompt = "Display mode",
+ keys = p,
+ callback = self.change_this_display_mode
)
key = None
elif key == "p":
@@ -738,21 +826,20 @@ class FlowView(common.WWrap):
elif key == "r":
r = self.master.replay_request(self.flow)
if r:
- self.master.statusbar.message(r)
- self.master.refresh_flow(self.flow)
+ signals.status_message.send(message=r)
+ signals.flow_change.send(self, flow = self.flow)
elif key == "V":
if not self.flow.modified():
- self.master.statusbar.message("Flow not modified.")
+ signals.status_message.send(message="Flow not modified.")
return
self.state.revert(self.flow)
- self.master.refresh_flow(self.flow)
- self.master.statusbar.message("Reverted.")
+ signals.flow_change.send(self, flow = self.flow)
+ signals.status_message.send(message="Reverted.")
elif key == "W":
- self.master.path_prompt(
- "Save this flow: ",
- self.state.last_saveload,
- self.master.save_one_flow,
- self.flow
+ signals.status_prompt_path.send(
+ prompt = "Save this flow",
+ callback = self.master.save_one_flow,
+ args = (self.flow,)
)
elif key == "v":
if conn and conn.content:
@@ -761,20 +848,23 @@ class FlowView(common.WWrap):
if os.environ.has_key("EDITOR") or os.environ.has_key("PAGER"):
self.master.spawn_external_viewer(conn.content, t)
else:
- self.master.statusbar.message("Error! Set $EDITOR or $PAGER.")
+ signals.status_message.send(
+ message = "Error! Set $EDITOR or $PAGER."
+ )
elif key == "|":
- self.master.path_prompt(
- "Send flow to script: ", self.state.last_script,
- self.master.run_script_once, self.flow
+ signals.status_prompt_path.send(
+ prompt = "Send flow to script",
+ callback = self.master.run_script_once,
+ args = (self.flow,)
)
elif key == "x":
- self.master.prompt_onekey(
- "Delete body",
- (
+ signals.status_prompt_onekey.send(
+ prompt = "Delete body",
+ keys = (
("completely", "c"),
("mark as missing", "m"),
),
- self.delete_body
+ callback = self.delete_body
)
key = None
elif key == "X":
@@ -785,24 +875,31 @@ class FlowView(common.WWrap):
e = conn.headers.get_first("content-encoding", "identity")
if e != "identity":
if not conn.decode():
- self.master.statusbar.message("Could not decode - invalid data?")
+ signals.status_message.send(
+ message = "Could not decode - invalid data?"
+ )
else:
- self.master.prompt_onekey(
- "Select encoding: ",
- (
+ signals.status_prompt_onekey.send(
+ prompt = "Select encoding: ",
+ keys = (
("gzip", "z"),
("deflate", "d"),
),
- self.encode_callback,
- conn
+ callback = self.encode_callback,
+ args = (conn,)
)
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)
elif key == "/":
- last_search_string = self.state.get_flow_setting(self.flow, "last_search_string")
- search_prompt = "Search body ["+last_search_string+"]: " if last_search_string else "Search body: "
- self.master.prompt(search_prompt,
- None,
- self.search)
+ last_search_string = self.state.get_flow_setting(
+ self.flow,
+ "last_search_string"
+ )
+ search_prompt = "Search body ["+last_search_string+"]" if last_search_string else "Search body"
+ signals.status_prompt.send(
+ prompt = search_prompt,
+ text = "",
+ callback = self.search
+ )
elif key == "n":
self.search_again(backwards=False)
elif key == "N":
@@ -816,4 +913,4 @@ class FlowView(common.WWrap):
"d": "deflate",
}
conn.encode(encoding_map[key])
- self.master.refresh_flow(self.flow)
+ signals.flow_change.send(self, flow = self.flow)