aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmproxy/dump.py2
-rw-r--r--libmproxy/flow.py6
-rw-r--r--libmproxy/proxy.py29
-rwxr-xr-xmitmdump9
-rw-r--r--test/test_flow.py2
-rw-r--r--test/test_proxy.py11
6 files changed, 55 insertions, 4 deletions
diff --git a/libmproxy/dump.py b/libmproxy/dump.py
index 5cbb7389..9e439aaf 100644
--- a/libmproxy/dump.py
+++ b/libmproxy/dump.py
@@ -10,6 +10,7 @@ class Options(object):
"client_replay",
"keepserving",
"kill",
+ "refresh_server_playback",
"request_script",
"response_script",
"rheaders",
@@ -85,6 +86,7 @@ class DumpMaster(flow.FlowMaster):
)
self.anticache = options.anticache
+ self.refresh_server_playback = options.refresh_server_playback
def _readflow(self, path):
path = os.path.expanduser(path)
diff --git a/libmproxy/flow.py b/libmproxy/flow.py
index 0080f1d4..e5f9c35f 100644
--- a/libmproxy/flow.py
+++ b/libmproxy/flow.py
@@ -140,6 +140,8 @@ class StickyCookieState:
def handle_response(self, f):
for i in f.response.headers.get("set-cookie", []):
+ # FIXME: We now know that Cookie.py screws up some cookies with
+ # valid RFC 822/1123 datetime specifications for expiry. Sigh.
c = Cookie.SimpleCookie(i)
m = c.values()[0]
k = self.ckey(m, f)
@@ -432,7 +434,9 @@ class FlowMaster(controller.Master):
self.scripts = {}
self.kill_nonreplay = False
self.stickycookie_state = False
+
self.anticache = False
+ self.refresh_server_playback = False
def _runscript(self, f, script):
#begin nocover
@@ -480,6 +484,8 @@ class FlowMaster(controller.Master):
response = proxy.Response.from_state(flow.request, rflow.response.get_state())
response.set_replay()
flow.response = response
+ if self.refresh_server_playback:
+ response.refresh()
flow.request.ack(response)
return True
return None
diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py
index 3a3db2e7..caa93f58 100644
--- a/libmproxy/proxy.py
+++ b/libmproxy/proxy.py
@@ -5,7 +5,7 @@
Development started from Neil Schemenauer's munchy.py
"""
-import sys, os, string, socket, urlparse, re, select, copy, base64, time
+import sys, os, string, socket, urlparse, re, select, copy, base64, time, Cookie
from email.utils import parsedate_tz, formatdate, mktime_tz
import shutil, tempfile
import optparse, SocketServer, ssl
@@ -281,6 +281,28 @@ class Response(controller.Msg):
controller.Msg.__init__(self)
self.replay = False
+ def _refresh_cookie(self, c, delta):
+ """
+ Takes a cookie string c and a time delta in seconds, and returns
+ a refreshed cookie string.
+ """
+ c = Cookie.SimpleCookie(str(c))
+ for i in c.values():
+ if "expires" in i:
+ d = parsedate_tz(i["expires"])
+ if d:
+ d = mktime_tz(d) + delta
+ i["expires"] = formatdate(d)
+ else:
+ # This can happen when the expires tag is invalid.
+ # reddit.com sends a an expires tag like this: "Thu, 31 Dec
+ # 2037 23:59:59 GMT", which is valid RFC 1123, but not
+ # strictly correct according tot he cookie spec. Browsers
+ # appear to parse this tolerantly - maybe we should too.
+ # For now, we just ignore this.
+ del i["expires"]
+ return c.output(header="").strip()
+
def refresh(self, now=None):
"""
This fairly complex and heuristic function refreshes a server
@@ -302,8 +324,11 @@ class Response(controller.Msg):
d = parsedate_tz(self.headers[i][0])
new = mktime_tz(d) + delta
self.headers[i] = [formatdate(new)]
+ c = []
for i in self.headers.get("set-cookie", []):
- pass
+ c.append(self._refresh_cookie(i, delta))
+ if c:
+ self.headers["set-cookie"] = c
def set_replay(self):
self.replay = True
diff --git a/mitmdump b/mitmdump
index 972c7666..881f99c2 100755
--- a/mitmdump
+++ b/mitmdump
@@ -107,6 +107,12 @@ if __name__ == '__main__':
help="Request headers to be considered during replay. "
"Can be passed multiple times."
)
+ group.add_option(
+ "--norefresh",
+ action="store_true", dest="norefresh", default=False,
+ help= "Disable response refresh, "
+ "which updates times in cookies and headers for replayed responses."
+ )
parser.add_option_group(group)
proxy.certificate_option_group(parser)
@@ -136,7 +142,8 @@ if __name__ == '__main__':
client_replay = options.client_replay,
keepserving = options.keepserving,
stickycookie = stickycookie,
- anticache = options.anticache
+ anticache = options.anticache,
+ refresh_server_playback = not options.norefresh,
)
if args:
filt = " ".join(args)
diff --git a/test/test_flow.py b/test/test_flow.py
index d72a1894..7ab8c753 100644
--- a/test/test_flow.py
+++ b/test/test_flow.py
@@ -405,7 +405,6 @@ class uFlowMaster(libpry.AutoTree):
fm.handle_error(proxy.Error(f.request, "error"))
-
def test_server_playback(self):
s = flow.State()
@@ -414,6 +413,7 @@ class uFlowMaster(libpry.AutoTree):
pb = [f]
fm = flow.FlowMaster(None, s)
+ fm.refresh_server_playback = True
assert not fm.do_server_playback(tutils.tflow())
fm.start_server_playback(pb, False, [], False)
diff --git a/test/test_proxy.py b/test/test_proxy.py
index a449071c..6bae46fc 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -132,6 +132,17 @@ class uResponse(libpry.AutoTree):
r.headers["set-cookie"] = ["MOO=BAR; Expires=Tue, 08-Mar-2011 00:20:38 GMT; Path=foo.com; Secure"]
r.refresh()
+ def test_refresh_cookie(self):
+ r = tutils.tresp()
+
+ # Invalid expires format, sent to us by Reddit.
+ c = "rfoo=bar; Domain=reddit.com; expires=Thu, 31 Dec 2037 23:59:59 GMT; Path=/"
+ assert r._refresh_cookie(c, 60)
+
+ c = "MOO=BAR; Expires=Tue, 08-Mar-2011 00:20:38 GMT; Path=foo.com; Secure"
+ assert "00:21:38" in r._refresh_cookie(c, 60)
+
+
def test_getset_state(self):
h = utils.Headers()
h["test"] = ["test"]