diff options
-rw-r--r-- | libmproxy/flow.py | 24 | ||||
-rw-r--r-- | libmproxy/proxy.py | 8 | ||||
-rw-r--r-- | test/test_server.py | 28 |
3 files changed, 58 insertions, 2 deletions
diff --git a/libmproxy/flow.py b/libmproxy/flow.py index 6d77fd88..50b30fe1 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -289,8 +289,15 @@ class Request(HTTPMsg): method: HTTP method timestamp_end: Seconds since the epoch signifying request transmission ended + + tcp_setup_timestamp: Seconds since the epoch signifying remote TCP connection setup completion time + (or None, if request didn't results TCP setup) + + ssl_setup_timestamp: Seconds since the epoch signifying remote SSL encryption setup completion time + (or None, if request didn't results SSL setup) + """ - def __init__(self, client_conn, httpversion, host, port, scheme, method, path, headers, content, timestamp_start=None, timestamp_end=None): + def __init__(self, client_conn, httpversion, host, port, scheme, method, path, headers, content, timestamp_start=None, timestamp_end=None, tcp_setup_timestamp=None, ssl_setup_timestamp=None): assert isinstance(headers, ODictCaseless) self.client_conn = client_conn self.httpversion = httpversion @@ -299,6 +306,8 @@ 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 + self.tcp_setup_timestamp = tcp_setup_timestamp + self.ssl_setup_timestamp = ssl_setup_timestamp # Have this request's cookies been modified by sticky cookies or auth? self.stickycookie = False @@ -362,6 +371,8 @@ class Request(HTTPMsg): self.content = state["content"] self.timestamp_start = state["timestamp_start"] self.timestamp_end = state["timestamp_end"] + self.tcp_setup_timestamp = state["tcp_setup_timestamp"] + self.ssl_setup_timestamp = state["ssl_setup_timestamp"] def _get_state(self): return dict( @@ -375,7 +386,9 @@ class Request(HTTPMsg): headers = self.headers._get_state(), content = self.content, timestamp_start = self.timestamp_start, - timestamp_end = self.timestamp_end + timestamp_end = self.timestamp_end, + tcp_setup_timestamp = self.tcp_setup_timestamp, + ssl_setup_timestamp = self.ssl_setup_timestamp ) @classmethod @@ -392,6 +405,8 @@ class Request(HTTPMsg): state["content"], state["timestamp_start"], state["timestamp_end"], + state["tcp_setup_timestamp"], + state["ssl_setup_timestamp"] ) def __hash__(self): @@ -809,6 +824,11 @@ class ClientConnect(StateObject): self.requestcount = 0 self.error = None + def __str__(self): + if self.address: + return "%s:%d"%(self.address[0],self.address[1]) + return "None" + def _load_state(self, state): self.close = True self.error = state["error"] diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 3fac17b8..283072ab 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -58,9 +58,13 @@ class ServerConnection(tcp.TCPClient): self.config = config self.scheme, self.sni = scheme, sni self.requestcount = 0 + self.tcp_setup_timestamp = None + self.ssl_setup_timestamp = None + def connect(self): tcp.TCPClient.connect(self) + self.tcp_setup_timestamp = time.time() if self.scheme == "https": clientcert = None if self.config.clientcerts: @@ -69,6 +73,7 @@ class ServerConnection(tcp.TCPClient): clientcert = path try: self.convert_to_ssl(cert=clientcert, sni=self.sni) + self.ssl_setup_timestamp = time.time() except tcp.NetLibError, v: raise ProxyError(400, str(v)) @@ -228,6 +233,9 @@ class ProxyHandler(tcp.BaseHandler): while 1: sc = self.get_server_connection(cc, scheme, host, port, self.sni) sc.send(request) + if sc.requestcount == 1: # add timestamps only for first request (others are not directly affected) + request.tcp_setup_timestamp = sc.tcp_setup_timestamp + request.ssl_setup_timestamp = sc.ssl_setup_timestamp sc.rfile.reset_timestamps() try: httpversion, code, msg, headers, content = http.read_response( diff --git a/test/test_server.py b/test/test_server.py index c20b0cea..6a88578c 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -277,7 +277,35 @@ class TestProxy(tservers.HTTPProxTest): request = self.master.state.view[1].request assert request.timestamp_end - request.timestamp_start <= 0.1 + def test_request_tcp_setup_timestamp_presence(self): + # tests that the first request in a tcp connection has a tcp_setup_timestamp + # while others do not + connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + connection.connect(("localhost", self.proxy.port)) + connection.send("GET http://localhost:%d/p/304:b@1k HTTP/1.1\r\n"%self.server.port) + connection.send("\r\n"); + connection.recv(5000) + connection.send("GET http://localhost:%d/p/304:b@1k HTTP/1.1\r\n"%self.server.port) + connection.send("\r\n"); + connection.recv(5000) + connection.close() + + first_request = self.master.state.view[0].request + second_request = self.master.state.view[1].request + assert first_request.tcp_setup_timestamp + assert first_request.ssl_setup_timestamp == None + assert second_request.tcp_setup_timestamp == None + assert second_request.ssl_setup_timestamp == None + +class TestProxySSL(tservers.HTTPProxTest): + ssl=True + def test_request_ssl_setup_timestamp_presence(self): + # tests that the ssl timestamp is present when ssl is used + f = self.pathod("304:b@10k") + assert f.status_code == 304 + first_request = self.master.state.view[0].request + assert first_request.ssl_setup_timestamp class MasterFakeResponse(tservers.TestMaster): def handle_request(self, m): |