diff options
Diffstat (limited to 'libmproxy/flow.py')
-rw-r--r-- | libmproxy/flow.py | 128 |
1 files changed, 65 insertions, 63 deletions
diff --git a/libmproxy/flow.py b/libmproxy/flow.py index 9238cfbf..6d77fd88 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -23,6 +23,7 @@ import tnetstring, filt, script, utils, encoding, proxy from email.utils import parsedate_tz, formatdate, mktime_tz from netlib import odict, http, certutils import controller, version +import app HDR_FORM_URLENCODED = "application/x-www-form-urlencoded" CONTENT_MISSING = 0 @@ -42,13 +43,13 @@ class ReplaceHooks: def add(self, fpatt, rex, s): """ - Add a replacement hook. + add a replacement hook. - fpatt: A string specifying a filter pattern. - rex: A regular expression. - s: The replacement string + fpatt: a string specifying a filter pattern. + rex: a regular expression. + s: the replacement string - Returns True if hook was added, False if the pattern could not be + returns true if hook was added, false if the pattern could not be parsed. """ cpatt = filt.parse(fpatt) @@ -196,7 +197,15 @@ class decoded(object): self.o.encode(self.ce) -class HTTPMsg(controller.Msg): +class StateObject: + def __eq__(self, other): + try: + return self._get_state() == other._get_state() + except AttributeError: + return False + + +class HTTPMsg(StateObject): def get_decoded_content(self): """ Returns the decoded content based on the current Content-Encoding header. @@ -252,6 +261,7 @@ class HTTPMsg(controller.Msg): return 0 return len(self.content) + class Request(HTTPMsg): """ An HTTP request. @@ -289,7 +299,6 @@ class Request(HTTPMsg): self.timestamp_start = timestamp_start or utils.timestamp() self.timestamp_end = max(timestamp_end or utils.timestamp(), timestamp_start) self.close = False - controller.Msg.__init__(self) # Have this request's cookies been modified by sticky cookies or auth? self.stickycookie = False @@ -388,15 +397,8 @@ class Request(HTTPMsg): def __hash__(self): return id(self) - def __eq__(self, other): - return self._get_state() == other._get_state() - def copy(self): - """ - Returns a copy of this object. - """ c = copy.copy(self) - c.acked = True c.headers = self.headers.copy() return c @@ -458,11 +460,19 @@ class Request(HTTPMsg): query = utils.urlencode(odict.lst) self.set_url(urlparse.urlunparse([scheme, netloc, path, params, query, fragment])) - def get_url(self): + def get_url(self, hostheader=False): """ Returns a URL string, constructed from the Request's URL compnents. + + If hostheader is True, we use the value specified in the request + Host header to construct the URL. """ - return utils.unparse_url(self.scheme, self.host.decode("idna"), self.port, self.path).encode('ascii') + if hostheader: + host = self.headers.get_first("host") or self.host + else: + host = self.host + host = host.encode("idna") + return utils.unparse_url(self.scheme, host, self.port, self.path).encode('ascii') def set_url(self, url): """ @@ -603,7 +613,6 @@ class Response(HTTPMsg): self.cert = cert self.timestamp_start = timestamp_start or utils.timestamp() self.timestamp_end = max(timestamp_end or utils.timestamp(), timestamp_start) - controller.Msg.__init__(self) self.replay = False def _refresh_cookie(self, c, delta): @@ -700,15 +709,8 @@ class Response(HTTPMsg): state["timestamp_end"], ) - def __eq__(self, other): - return self._get_state() == other._get_state() - def copy(self): - """ - Returns a copy of this object. - """ c = copy.copy(self) - c.acked = True c.headers = self.headers.copy() return c @@ -773,7 +775,7 @@ class Response(HTTPMsg): cookies.append((cookie_name, (cookie_value, cookie_parameters))) return dict(cookies) -class ClientDisconnect(controller.Msg): +class ClientDisconnect: """ A client disconnection event. @@ -782,11 +784,10 @@ class ClientDisconnect(controller.Msg): client_conn: ClientConnect object. """ def __init__(self, client_conn): - controller.Msg.__init__(self) self.client_conn = client_conn -class ClientConnect(controller.Msg): +class ClientConnect(StateObject): """ A single client connection. Each connection can result in multiple HTTP Requests. @@ -807,10 +808,6 @@ class ClientConnect(controller.Msg): self.close = False self.requestcount = 0 self.error = None - controller.Msg.__init__(self) - - def __eq__(self, other): - return self._get_state() == other._get_state() def _load_state(self, state): self.close = True @@ -834,15 +831,10 @@ class ClientConnect(controller.Msg): return None def copy(self): - """ - Returns a copy of this object. - """ - c = copy.copy(self) - c.acked = True - return c + return copy.copy(self) -class Error(controller.Msg): +class Error(StateObject): """ An Error. @@ -860,18 +852,13 @@ class Error(controller.Msg): def __init__(self, request, msg, timestamp=None): self.request, self.msg = request, msg self.timestamp = timestamp or utils.timestamp() - controller.Msg.__init__(self) def _load_state(self, state): self.msg = state["msg"] self.timestamp = state["timestamp"] def copy(self): - """ - Returns a copy of this object. - """ c = copy.copy(self) - c.acked = True return c def _get_state(self): @@ -888,9 +875,6 @@ class Error(controller.Msg): state["timestamp"], ) - def __eq__(self, other): - return self._get_state() == other._get_state() - def replace(self, pattern, repl, *args, **kwargs): """ Replaces a regular expression pattern with repl in both the headers @@ -1180,10 +1164,11 @@ class Flow: Kill this request. """ self.error = Error(self.request, "Connection killed") - if self.request and not self.request.acked: - self.request._ack(None) - elif self.response and not self.response.acked: - self.response._ack(None) + self.error.reply = controller.DummyReply() + if self.request and not self.request.reply.acked: + self.request.reply(proxy.KILL) + elif self.response and not self.response.reply.acked: + self.response.reply(proxy.KILL) master.handle_error(self.error) self.intercepting = False @@ -1199,10 +1184,10 @@ class Flow: Continue with the flow - called after an intercept(). """ if self.request: - if not self.request.acked: - self.request._ack() - elif self.response and not self.response.acked: - self.response._ack() + if not self.request.reply.acked: + self.request.reply() + elif self.response and not self.response.reply.acked: + self.response.reply() self.intercepting = False def replace(self, pattern, repl, *args, **kwargs): @@ -1325,7 +1310,7 @@ class State(object): if f.request in self._flow_map: del self._flow_map[f.request] self._flow_list.remove(f) - if f.match(self._limit): + if f in self.view: self.view.remove(f) return True @@ -1368,6 +1353,7 @@ class FlowMaster(controller.Master): self.setheaders = SetHeaders() self.stream = None + app.mapp.config["PMASTER"] = self def add_event(self, e, level="info"): """ @@ -1464,7 +1450,7 @@ class FlowMaster(controller.Master): flow.response = response if self.refresh_server_playback: response.refresh() - flow.request._ack(response) + flow.request.reply(response) if self.server_playback.count() == 0: self.stop_server_playback() return True @@ -1491,10 +1477,13 @@ class FlowMaster(controller.Master): Loads a flow, and returns a new flow object. """ if f.request: + f.request.reply = controller.DummyReply() fr = self.handle_request(f.request) if f.response: + f.response.reply = controller.DummyReply() self.handle_response(f.response) if f.error: + f.error.reply = controller.DummyReply() self.handle_error(f.error) return fr @@ -1522,7 +1511,7 @@ class FlowMaster(controller.Master): if self.kill_nonreplay: f.kill(self) else: - f.request._ack() + f.request.reply() def process_new_response(self, f): if self.stickycookie_state: @@ -1561,11 +1550,11 @@ class FlowMaster(controller.Master): def handle_clientconnect(self, cc): self.run_script_hook("clientconnect", cc) - cc._ack() + cc.reply() def handle_clientdisconnect(self, r): self.run_script_hook("clientdisconnect", r) - r._ack() + r.reply() def handle_error(self, r): f = self.state.add_error(r) @@ -1573,7 +1562,7 @@ class FlowMaster(controller.Master): self.run_script_hook("error", f) if self.client_playback: self.client_playback.clear(f) - r._ack() + r.reply() return f def handle_request(self, r): @@ -1596,7 +1585,7 @@ class FlowMaster(controller.Master): if self.stream: self.stream.add(f) else: - r._ack() + r.reply() return f def shutdown(self): @@ -1609,8 +1598,8 @@ class FlowMaster(controller.Master): self.stream.add(i) self.stop_stream() - def start_stream(self, fp): - self.stream = FlowWriter(fp) + def start_stream(self, fp, filt): + self.stream = FilteredFlowWriter(fp, filt) def stop_stream(self): self.stream.fo.close() @@ -1656,3 +1645,16 @@ class FlowReader: return raise FlowReadError("Invalid data format.") + +class FilteredFlowWriter: + def __init__(self, fo, filt): + self.fo = fo + self.filt = filt + + def add(self, f): + if self.filt and not f.match(self.filt): + return + d = f._get_state() + tnetstring.dump(d, self.fo) + + |