aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmproxy/console/__init__.py4
-rw-r--r--libmproxy/console/connlist.py5
-rw-r--r--libmproxy/console/connview.py6
-rw-r--r--libmproxy/flow.py37
-rw-r--r--test/test_flow.py20
5 files changed, 63 insertions, 9 deletions
diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py
index 6fc37a47..ca80d013 100644
--- a/libmproxy/console/__init__.py
+++ b/libmproxy/console/__init__.py
@@ -516,9 +516,7 @@ class ConsoleMaster(flow.FlowMaster):
def focus_current(self):
if self.currentflow:
try:
- ids = [id(i) for i in self.state.view]
- idx = ids.index(id(self.currentflow))
- self.conn_list_view.set_focus(idx)
+ self.conn_list_view.set_focus(self.state.index(self.currentflow))
except (IndexError, ValueError):
pass
diff --git a/libmproxy/console/connlist.py b/libmproxy/console/connlist.py
index 957e797b..f54b8061 100644
--- a/libmproxy/console/connlist.py
+++ b/libmproxy/console/connlist.py
@@ -8,6 +8,7 @@ def _mkhelp():
("a", "accept this intercepted connection"),
("C", "clear connection list or eventlog"),
("d", "delete flow"),
+ ("D", "duplicate flow"),
("e", "toggle eventlog"),
("l", "set limit filter pattern"),
("L", "load saved flows"),
@@ -101,6 +102,10 @@ class ConnectionItem(common.WWrap):
self.flow.kill(self.master)
self.state.delete_flow(self.flow)
self.master.sync_list_view()
+ elif key == "D":
+ f = self.master.duplicate_flow(self.flow)
+ self.master.currentflow = f
+ self.master.focus_current()
elif key == "l":
self.master.prompt("Limit: ", self.state.limit_txt, self.master.set_limit)
self.master.sync_list_view()
diff --git a/libmproxy/console/connview.py b/libmproxy/console/connview.py
index d668bfe2..ed038a11 100644
--- a/libmproxy/console/connview.py
+++ b/libmproxy/console/connview.py
@@ -10,6 +10,7 @@ def _mkhelp():
("a", "accept this intercepted connection"),
("b", "save request/response body"),
("d", "delete flow"),
+ ("D", "duplicate flow"),
("e", "edit request/response"),
("m", "change body display mode"),
(None,
@@ -463,6 +464,11 @@ class ConnectionView(common.WWrap):
f = self.flow
f.kill(self.master)
self.state.delete_flow(f)
+ elif key == "D":
+ f = self.master.duplicate_flow(self.flow)
+ self.master.view_flow(f)
+ self.master.currentflow = f
+ self.master.statusbar.message("Duplicated.")
elif key == "e":
if self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
self.master.prompt_onekey(
diff --git a/libmproxy/flow.py b/libmproxy/flow.py
index 81199035..db4cde5e 100644
--- a/libmproxy/flow.py
+++ b/libmproxy/flow.py
@@ -851,6 +851,17 @@ class Flow:
self.intercepting = False
self._backup = None
+ def copy(self):
+ rc = self.request.copy()
+ f = Flow(rc)
+ if self.response:
+ f.response = self.response.copy()
+ f.response.request = rc
+ if self.error:
+ f.error = self.error.copy()
+ f.error.request = rc
+ return f
+
@classmethod
def _from_state(klass, state):
f = klass(None)
@@ -997,6 +1008,9 @@ class State(object):
def flow_count(self):
return len(self._flow_map)
+ def index(self, f):
+ return self._flow_list.index(f)
+
def active_flow_count(self):
c = 0
for i in self._flow_list:
@@ -1011,6 +1025,7 @@ class State(object):
f = Flow(req)
self._flow_list.append(f)
self._flow_map[req] = f
+ assert len(self._flow_list) == len(self._flow_map)
if f.match(self._limit):
self.view.append(f)
return f
@@ -1235,17 +1250,27 @@ class FlowMaster(controller.Master):
return controller.Master.tick(self, q)
+ def duplicate_flow(self, f):
+ return self.load_flow(f.copy())
+
+ def load_flow(self, f):
+ """
+ Loads a flow, and returns a new flow object.
+ """
+ if f.request:
+ fr = self.handle_request(f.request)
+ if f.response:
+ self.handle_response(f.response)
+ if f.error:
+ self.handle_error(f.error)
+ return fr
+
def load_flows(self, fr):
"""
Load flows from a FlowReader object.
"""
for i in fr.stream():
- if i.request:
- self.handle_request(i.request)
- if i.response:
- self.handle_response(i.response)
- if i.error:
- self.handle_error(i.error)
+ self.load_flow(i)
def process_new_request(self, f):
if self.stickycookie_state:
diff --git a/test/test_flow.py b/test/test_flow.py
index 94c4614d..3e126263 100644
--- a/test/test_flow.py
+++ b/test/test_flow.py
@@ -136,6 +136,14 @@ class uServerPlaybackState(libpry.AutoTree):
class uFlow(libpry.AutoTree):
+ def test_copy(self):
+ f = tutils.tflow_full()
+ f2 = f.copy()
+ assert not f is f2
+ assert not f.request is f2.request
+ assert f.request.headers == f2.request.headers
+ assert not f.request.headers is f2.request.headers
+
def test_match(self):
f = tutils.tflow()
f.response = tutils.tresp()
@@ -485,6 +493,17 @@ class uFlowMaster(libpry.AutoTree):
fm.handle_error(err)
assert fm.script.ns["log"][-1] == "error"
+ def test_duplicate_flow(self):
+ s = flow.State()
+ fm = flow.FlowMaster(None, s)
+ f = tutils.tflow_full()
+ fm.load_flow(f)
+ assert s.flow_count() == 1
+ f2 = fm.duplicate_flow(f)
+ assert f2.response
+ assert s.flow_count() == 2
+ assert s.index(f2)
+
def test_all(self):
s = flow.State()
fm = flow.FlowMaster(None, s)
@@ -572,6 +591,7 @@ class uFlowMaster(libpry.AutoTree):
fm.handle_response(tf.response)
assert fm.stickycookie_state.jar
assert not "cookie" in tf.request.headers
+ tf = tf.copy()
fm.handle_request(tf.request)
assert tf.request.headers["cookie"] == ["foo=bar"]