aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy')
-rw-r--r--libmproxy/flow.py3
-rw-r--r--libmproxy/protocol.py69
-rw-r--r--libmproxy/proxy.py28
3 files changed, 71 insertions, 29 deletions
diff --git a/libmproxy/flow.py b/libmproxy/flow.py
index ee9031ba..acebb71d 100644
--- a/libmproxy/flow.py
+++ b/libmproxy/flow.py
@@ -835,7 +835,7 @@ class ClientConnect(StateObject):
def _get_state(self):
return dict(
address = list(self.address),
- requestcount = self.requestcount,
+ requestcount = -1, # FIXME self.requestcount,
error = self.error,
)
@@ -1599,6 +1599,7 @@ class FlowMaster(controller.Master):
if r.is_live():
app = self.apps.get(r)
if app:
+ # FIXME: for the tcp proxy, use flow.client_conn.wfile
err = app.serve(r, r.wfile, **{"mitmproxy.master": self})
if err:
self.add_event("Error in wsgi app. %s"%err, "error")
diff --git a/libmproxy/protocol.py b/libmproxy/protocol.py
index 279ff015..9d3f805c 100644
--- a/libmproxy/protocol.py
+++ b/libmproxy/protocol.py
@@ -22,6 +22,10 @@ def handle_messages(conntype, connection_handler):
_handle("messages", conntype, connection_handler)
+def handle_error(conntype, connection_handler, error):
+ _handle("error", conntype, connection_handler, error)
+
+
class ConnectionTypeChange(Exception):
pass
@@ -29,6 +33,17 @@ class ConnectionTypeChange(Exception):
class ProtocolHandler(object):
def __init__(self, c):
self.c = c
+ def handle_messages(self):
+ """
+ This method gets called if the connection has been established.
+ """
+ raise NotImplementedError
+ def handle_error(self, error):
+ """
+ This method gets called should there be an uncaught exception during the connection.
+ This might happen outside of handle_messages, e.g. if the initial SSL handshake fails in transparent mode.
+ """
+ raise NotImplementedError
"""
@@ -131,9 +146,13 @@ class HTTPRequest(HTTPMessage):
self.form_out = form_out or self.form_in
assert isinstance(headers, ODictCaseless)
- #FIXME: Compatibility Fix
+ #FIXME: Compatibility Fixes
def is_live(self):
return True
+ @property
+ def wfile(self):
+ import mock
+ return mock.Mock(side_effect=tcp.NetLibDisconnect)
def _assemble_request_line(self, form=None):
form = form or self.form_out
@@ -239,19 +258,19 @@ 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_request_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)
if request_reply is None or request_reply == KILL:
return False
- if isinstance(request_reply, HTTPResponse):
+ if isinstance(request_reply, HTTPResponse) or (LEGACY and isinstance(request_reply, libmproxy.flow.Response)):
flow.response = request_reply
else:
+ self.process_request(flow.request)
flow.response = self.get_response_from_server(flow.request)
- self.c.log("response", [flow.response._assemble_response_line()])
+ self.c.log("response", [flow.response._assemble_response_line() if not LEGACY else flow.response._assemble().splitlines()[0]])
response_reply = self.c.channel.ask("response" if LEGACY else "httpresponse",
flow.response if LEGACY else flow)
if response_reply is None or response_reply == KILL:
@@ -269,25 +288,39 @@ class HTTPHandler(ProtocolHandler):
if flow.request.form_in == "authority":
self.ssl_upgrade(flow.request)
return True
- except HttpAuthenticationError, e:
- self.process_error(flow, code=407, message="Proxy Authentication Required", headers=e.auth_headers)
- except (http.HttpError, ProxyError), e:
- self.process_error(flow, code=e.code, message=e.msg)
- except tcp.NetLibError, e:
- self.process_error(flow, message=e.message or e.__class__)
+ except (HttpAuthenticationError, http.HttpError, ProxyError, tcp.NetLibError), e:
+ self.handle_error(e, flow)
return False
- def process_error(self, flow, code=None, message=None, headers=None):
- try:
- err = ("%s: %s" % (code, message)) if code else message
+ def handle_error(self, error, flow=None):
+ code, message, headers = None, None, None
+ if isinstance(error, HttpAuthenticationError):
+ code, message, headers = 407, "Proxy Authentication Required", error.auth_headers
+ elif isinstance(error, (http.HttpError, ProxyError)):
+ code, message = error.code, error.msg
+ elif isinstance(error, tcp.NetLibError):
+ code = 502
+ message = error.message or error.__class__
+
+ if code:
+ err = "%s: %s" % (code, message)
+ else:
+ err = message
+
+ self.c.log("error: %s" %err)
+
+ if flow:
flow.error = libmproxy.flow.Error(False, err)
- self.c.log("error: %s" % err)
self.c.channel.ask("error" if LEGACY else "httperror",
flow.error if LEGACY else flow)
- if code:
+ else:
+ pass # FIXME: Is there any use case for persisting errors that occur outside of flows?
+
+ if code:
+ try:
self.send_error(code, message, headers)
- except:
- pass
+ except:
+ pass
def send_error(self, code, message, headers):
response = http_status.RESPONSES.get(code, "Unknown")
@@ -364,6 +397,8 @@ class HTTPHandler(ProtocolHandler):
if request.form_in == "authority":
pass
elif request.form_in == "absolute":
+ if request.scheme != "http":
+ raise http.HttpError(400, "Invalid Request")
if not self.c.config.forward_proxy:
request.form_out = "origin"
if ((not self.c.server_conn) or
diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py
index 9e3e317b..a7ee9a7b 100644
--- a/libmproxy/proxy.py
+++ b/libmproxy/proxy.py
@@ -40,8 +40,8 @@ class ProxyConfig:
class ClientConnection(tcp.BaseHandler):
- def __init__(self, client_connection, address):
- tcp.BaseHandler.__init__(self, client_connection, address)
+ def __init__(self, client_connection, address, server):
+ tcp.BaseHandler.__init__(self, client_connection, address, server)
self.timestamp_start = utils.timestamp()
self.timestamp_end = None
@@ -72,10 +72,14 @@ class ServerConnection(tcp.TCPClient):
self.peername = self.connection.getpeername()
self.timestamp_tcp_setup = utils.timestamp()
+ def send(self, message):
+ self.wfile.write(message)
+ self.wfile.flush()
+
def establish_ssl(self, clientcerts, sni):
clientcert = None
if clientcerts:
- path = os.path.join(clientcerts, self.host.encode("idna")) + ".pem"
+ path = os.path.join(clientcerts, self.address.host.encode("idna")) + ".pem"
if os.path.exists(path):
clientcert = path
try:
@@ -118,7 +122,7 @@ class RequestReplayThread(threading.Thread):
class ConnectionHandler:
def __init__(self, config, client_connection, client_address, server, channel, server_version):
self.config = config
- self.client_conn = ClientConnection(client_connection, client_address)
+ self.client_conn = ClientConnection(client_connection, client_address, server)
self.server_conn = None
self.channel, self.server_version = channel, server_version
@@ -144,6 +148,8 @@ class ConnectionHandler:
self.log("clientconnect")
self.channel.ask("clientconnect", self)
+ self.determine_conntype()
+
try:
# Can we already identify the target server and connect to it?
server_address = None
@@ -159,8 +165,6 @@ class ConnectionHandler:
raise ProxyError(502, "Transparent mode failure: could not resolve original destination.")
self.log("transparent to %s:%s" % server_address)
- self.determine_conntype()
-
if server_address:
self.establish_server_connection(server_address)
self._handle_ssl()
@@ -171,11 +175,14 @@ class ConnectionHandler:
except protocol.ConnectionTypeChange:
continue
- self.del_server_connection()
- except ProxyError, e:
+ # FIXME: Do we want to persist errors?
+ except (ProxyError, tcp.NetLibError), e:
+ protocol.handle_error(self.conntype, self, e)
+ except Exception, e:
+ self.log(e.__class__)
self.log(str(e))
- # FIXME: We need to persist errors
+ self.del_server_connection()
self.log("clientdisconnect")
self.channel.tell("clientdisconnect", self)
@@ -279,8 +286,7 @@ class ConnectionHandler:
sn = connection.get_servername()
if sn and sn != self.sni:
self.sni = sn.decode("utf8").encode("idna")
- self.establish_server_connection() # reconnect to upstream server with SNI
- self.establish_ssl(server=True) # establish SSL with upstream
+ self.server_reconnect() # reconnect to upstream server with SNI
# Now, change client context to reflect changed certificate:
new_context = SSL.Context(SSL.TLSv1_METHOD)
new_context.use_privatekey_file(self.config.certfile or self.config.cacert)