aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy/protocol2/layer.py
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2015-08-06 12:13:23 +0200
committerMaximilian Hils <git@maximilianhils.com>2015-08-11 20:32:12 +0200
commitaac0ab23ebb0e4d88306b12efee1dd31338f7664 (patch)
tree686c95d9c36fc28e69f10e11f3034562a6565fdc /libmproxy/protocol2/layer.py
parentc46e3f90bbc38080a41a278340aaad27d8881fd9 (diff)
downloadmitmproxy-aac0ab23ebb0e4d88306b12efee1dd31338f7664.tar.gz
mitmproxy-aac0ab23ebb0e4d88306b12efee1dd31338f7664.tar.bz2
mitmproxy-aac0ab23ebb0e4d88306b12efee1dd31338f7664.zip
simplify layer code, add yield_from_callback decorator
Diffstat (limited to 'libmproxy/protocol2/layer.py')
-rw-r--r--libmproxy/protocol2/layer.py58
1 files changed, 57 insertions, 1 deletions
diff --git a/libmproxy/protocol2/layer.py b/libmproxy/protocol2/layer.py
index 30aed350..aaa51baf 100644
--- a/libmproxy/protocol2/layer.py
+++ b/libmproxy/protocol2/layer.py
@@ -32,6 +32,8 @@ Further goals:
inline scripts shall have a chance to handle everything locally.
"""
from __future__ import (absolute_import, print_function, division)
+import Queue
+import threading
from netlib import tcp
from ..proxy import ProxyError2, Log
from ..proxy.connection import ServerConnection
@@ -131,7 +133,6 @@ class ServerConnectionMixin(object):
self._server_address = tcp.Address.wrap(address)
self.log("Set new server address: " + repr(self.server_address), "debug")
-
def _disconnect(self):
"""
Deletes (and closes) an existing server connection.
@@ -149,3 +150,58 @@ class ServerConnectionMixin(object):
self.server_conn.connect()
except tcp.NetLibError as e:
raise ProxyError2("Server connection to '%s' failed: %s" % (self.server_address, e), e)
+
+
+def yield_from_callback(fun):
+ """
+ Decorator which makes it possible to yield from callbacks in the original thread.
+ As a use case, take the pyOpenSSL handle_sni callback: If we receive a new SNI from the client,
+ we need to reconnect to the server with the new SNI. Reconnecting would normally be done using "yield Reconnect()",
+ but we're in a pyOpenSSL callback here, outside of the main program flow. With this decorator, it looks as follows:
+
+ def handle_sni(self):
+ # ...
+ self.yield_from_callback(Reconnect())
+
+ @yield_from_callback
+ def establish_ssl_with_client():
+ self.client_conn.convert_to_ssl(...)
+
+ for message in self.establish_ssl_with_client(): # will yield Reconnect at some point
+ yield message
+
+
+ Limitations:
+ - You cannot yield True.
+ """
+ yield_queue = Queue.Queue()
+
+ def do_yield(self, msg):
+ yield_queue.put(msg)
+ yield_queue.get()
+
+ def wrapper(self, *args, **kwargs):
+ self.yield_from_callback = do_yield
+
+ def run():
+ try:
+ fun(self, *args, **kwargs)
+ yield_queue.put(True)
+ except Exception as e:
+ yield_queue.put(e)
+
+ threading.Thread(target=run, name="YieldFromCallbackThread").start()
+ while True:
+ e = yield_queue.get()
+ if e is True:
+ break
+ elif isinstance(e, Exception):
+ # TODO: Include func name?
+ raise ProxyError2("Error from callback: " + repr(e), e)
+ else:
+ yield e
+ yield_queue.put(None)
+
+ self.yield_from_callback = None
+
+ return wrapper