aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/protocol
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-02-04 05:02:17 +0100
committerMaximilian Hils <git@maximilianhils.com>2014-02-04 05:02:17 +0100
commit6a53ae5fd37b516074f9bf46cffab015f6626b9e (patch)
tree4f20e3b51496900586ad11cd89094c6235facc8f /libmproxy/protocol
parentf6253a80fff2ed3a6f7846e866469c8776f1254d (diff)
downloadmitmproxy-6a53ae5fd37b516074f9bf46cffab015f6626b9e.tar.gz
mitmproxy-6a53ae5fd37b516074f9bf46cffab015f6626b9e.tar.bz2
mitmproxy-6a53ae5fd37b516074f9bf46cffab015f6626b9e.zip
push failing tests down to 43
Diffstat (limited to 'libmproxy/protocol')
-rw-r--r--libmproxy/protocol/http.py17
-rw-r--r--libmproxy/protocol/primitives.py124
2 files changed, 134 insertions, 7 deletions
diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py
index 8c44461e..be60f374 100644
--- a/libmproxy/protocol/http.py
+++ b/libmproxy/protocol/http.py
@@ -6,7 +6,7 @@ from netlib.odict import ODict, ODictCaseless
from . import ProtocolHandler, ConnectionTypeChange, KILL
from .. import encoding, utils, version, filt, controller, stateobject
from ..proxy import ProxyError, AddressPriority
-from ..flow import Flow, Error
+from .primitives import Flow, Error
HDR_FORM_URLENCODED = "application/x-www-form-urlencoded"
@@ -340,7 +340,7 @@ class HTTPRequest(HTTPMessage):
Raises an Exception if the request cannot be assembled.
"""
if self.content == CONTENT_MISSING:
- raise RuntimeError("Cannot assemble flow with CONTENT_MISSING")
+ raise ProxyError(502, "Cannot assemble flow with CONTENT_MISSING")
head = self._assemble_head(form)
if self.content:
return head + self.content
@@ -444,6 +444,8 @@ class HTTPRequest(HTTPMessage):
If hostheader is True, we use the value specified in the request
Host header to construct the URL.
"""
+ raise NotImplementedError
+ # FIXME: Take server_conn into account.
host = None
if hostheader:
host = self.headers.get_first("host")
@@ -462,6 +464,8 @@ class HTTPRequest(HTTPMessage):
Returns False if the URL was invalid, True if the request succeeded.
"""
+ raise NotImplementedError
+ # FIXME: Needs to update server_conn as well.
parts = http.parse_url(url)
if not parts:
return False
@@ -595,7 +599,7 @@ class HTTPResponse(HTTPMessage):
Raises an Exception if the request cannot be assembled.
"""
if self.content == CONTENT_MISSING:
- raise RuntimeError("Cannot assemble flow with CONTENT_MISSING")
+ raise ProxyError(502, "Cannot assemble flow with CONTENT_MISSING")
head = self._assemble_head()
if self.content:
return head + self.content
@@ -711,7 +715,7 @@ class HTTPFlow(Flow):
if self.request:
f.request = self.request.copy()
if self.response:
- f.response = self.request.copy()
+ f.response = self.response.copy()
return f
def match(self, f):
@@ -795,8 +799,7 @@ class HTTPHandler(ProtocolHandler):
for i in range(2):
try:
- self.c.server_conn.wfile.write(request_raw)
- self.c.server_conn.wfile.flush()
+ self.c.server_conn.send(request_raw)
return HTTPResponse.from_stream(self.c.server_conn.rfile, request.method,
body_size_limit=self.c.config.body_size_limit)
except (tcp.NetLibDisconnect, http.HttpErrorConnClosed), v:
@@ -821,6 +824,7 @@ class HTTPHandler(ProtocolHandler):
flow.request = HTTPRequest.from_stream(self.c.client_conn.rfile,
body_size_limit=self.c.config.body_size_limit)
self.c.log("request", [flow.request._assemble_first_line(flow.request.form_in)])
+ self.process_request(flow.request)
request_reply = self.c.channel.ask("request" if LEGACY else "httprequest",
flow.request if LEGACY else flow)
@@ -830,7 +834,6 @@ class HTTPHandler(ProtocolHandler):
if isinstance(request_reply, HTTPResponse):
flow.response = request_reply
else:
- self.process_request(flow.request)
self.c.establish_server_connection()
flow.response = self.get_response_from_server(flow.request)
diff --git a/libmproxy/protocol/primitives.py b/libmproxy/protocol/primitives.py
new file mode 100644
index 00000000..f77e097b
--- /dev/null
+++ b/libmproxy/protocol/primitives.py
@@ -0,0 +1,124 @@
+from .. import stateobject, utils, version
+from ..proxy import ServerConnection, ClientConnection
+import copy
+
+
+class _BackreferenceMixin(object):
+ """
+ If an attribute from the _backrefattr tuple is set,
+ this mixin sets a reference back on the attribute object.
+ Example:
+ e = Error()
+ f = Flow()
+ f.error = e
+ assert f is e.flow
+ """
+ _backrefattr = tuple()
+
+ def __setattr__(self, key, value):
+ super(_BackreferenceMixin, self).__setattr__(key, value)
+ if key in self._backrefattr and value is not None:
+ setattr(value, self._backrefname, self)
+
+
+class Error(stateobject.SimpleStateObject):
+ """
+ An Error.
+
+ This is distinct from an HTTP error response (say, a code 500), which
+ is represented by a normal Response object. This class is responsible
+ for indicating errors that fall outside of normal HTTP communications,
+ like interrupted connections, timeouts, protocol errors.
+
+ Exposes the following attributes:
+
+ flow: Flow object
+ msg: Message describing the error
+ timestamp: Seconds since the epoch
+ """
+ def __init__(self, msg, timestamp=None):
+ """
+ @type msg: str
+ @type timestamp: float
+ """
+ self.msg = msg
+ self.timestamp = timestamp or utils.timestamp()
+
+ _stateobject_attributes = dict(
+ msg=str,
+ timestamp=float
+ )
+
+ @classmethod
+ def _from_state(cls, state):
+ f = cls(None) # the default implementation assumes an empty constructor. Override accordingly.
+ f._load_state(state)
+ return f
+
+ def copy(self):
+ c = copy.copy(self)
+ return c
+
+
+class Flow(stateobject.SimpleStateObject, _BackreferenceMixin):
+ def __init__(self, conntype, client_conn, server_conn):
+ self.conntype = conntype
+ self.client_conn = client_conn
+ self.server_conn = server_conn
+ self.error = None
+
+ _backrefattr = ("error",)
+ _backrefname = "flow"
+
+ _stateobject_attributes = dict(
+ error=Error,
+ client_conn=ClientConnection,
+ server_conn=ServerConnection,
+ conntype=str
+ )
+
+ def _get_state(self):
+ d = super(Flow, self)._get_state()
+ d.update(version=version.IVERSION)
+ return d
+
+ @classmethod
+ def _from_state(cls, state):
+ f = cls(None, None, None)
+ f._load_state(state)
+ return f
+
+ def copy(self):
+ f = copy.copy(self)
+
+ f.client_conn = self.client_conn.copy()
+ f.server_conn = self.server_conn.copy()
+
+ if self.error:
+ f.error = self.error.copy()
+ return f
+
+ def modified(self):
+ """
+ Has this Flow been modified?
+ """
+ if self._backup:
+ return self._backup != self._get_state()
+ else:
+ return False
+
+ def backup(self, force=False):
+ """
+ Save a backup of this Flow, which can be reverted to using a
+ call to .revert().
+ """
+ if not self._backup:
+ self._backup = self._get_state()
+
+ def revert(self):
+ """
+ Revert to the last backed up state.
+ """
+ if self._backup:
+ self._load_state(self._backup)
+ self._backup = None \ No newline at end of file