def test_on_connection_closed(self): client_conn = Connection(h11.CLIENT, self.client_stream, on_response=self.on_response) client_conn.send_request( HttpRequest(method="GET", path="/", headers=[("Host", "localhost"), ("Connection", "close")])) server_conn = Connection(h11.SERVER, self.server_stream, on_request=self.on_request) yield server_conn.read_bytes() server_conn.send_response( HttpResponse(version="HTTP/1.1", code="200", reason="OK", headers=[("Host", "localhost"), ("Content-Length", "4")], body=b"Yaya")) yield client_conn.read_bytes() self.assertTrue(self.client_stream.closed()) self.assertTrue(self.server_stream.closed())
def test_on_info_response(self): client_conn = Connection(h11.CLIENT, self.client_stream, on_info_response=self.on_response) client_conn.send_request( HttpRequest(method="GET", path="/chat", version="HTTP/1.1", headers=[("Host", "localhost"), ("Upgrade", "websocket")])) server_conn = Connection(h11.SERVER, self.server_stream, on_request=self.on_request) yield server_conn.read_bytes() server_conn.send_info_response( HttpResponse(version="HTTP/1.1", code="101", reason="Protocol Upgrade", headers=[("Host", "localhost"), ("Upgrade", "websocket")])) yield client_conn.read_bytes() self.assertIsNotNone(self.response) self.assertEqual( self.response.headers, HttpHeaders([("host", "localhost"), ("upgrade", "websocket")])) self.assertEqual(self.response.code, "101") self.assertEqual(self.response.reason, "Protocol Upgrade") self.assertEqual(self.response.version, "HTTP/1.1")
def test_parse_version(self): self.assertEqual( Connection(h11.CLIENT, None)._parse_version(None), "HTTP/1.1") http_content = mock.Mock() http_content.http_version = "1.1" self.assertEqual( Connection(h11.CLIENT, None)._parse_version(http_content), "HTTP/1.1")
def test_on_request(self): client_conn = Connection(h11.CLIENT, self.client_stream) client_conn.send_request(HttpRequest( method="GET", path="/", headers=[("Host", "localhost")])) server_conn = Connection( h11.SERVER, self.server_stream, on_request=self.on_request) yield server_conn.read_bytes() self.assertIsNotNone(self.request) self.assertEqual(self.request.headers, HttpHeaders([("host", "localhost")])) self.assertEqual(self.request.method, "GET") self.assertEqual(self.request.path, "/") self.assertEqual(self.request.version, "HTTP/1.1")
def __init__(self, server_state, context): super(Http1Layer, self).__init__(server_state, context) self.src_conn = Connection(h11.SERVER, self.src_stream, conn_type="src", readonly=(context.mode == "replay"), on_request=self.on_request) self.dest_conn = Connection(h11.CLIENT, self.dest_stream, conn_type="dest", on_response=self.on_response, on_info_response=self.on_info_response) self.req = None self.resp = None self.switch_protocol = False
def __init__(self, server_state, context): super(Http1Layer, self).__init__(server_state, context) self.src_conn = Connection( h11.SERVER, self.src_stream, conn_type="src", readonly=(context.mode == "replay"), on_request=self.on_request) self.dest_conn = Connection( h11.CLIENT, self.dest_stream, conn_type="dest", on_response=self.on_response, on_info_response=self.on_info_response) self.req = None self.resp = None self.switch_protocol = False
def test_on_response(self): server_conn = Connection(h11.SERVER, self.server_stream) server_conn.send_response(HttpResponse( version="HTTP/1.1", code="200", reason="OK", headers=[("Host", "localhost"), ("Content-Length", "1")], body=b"A")) client_conn = Connection( h11.CLIENT, self.client_stream, on_response=self.on_response) yield client_conn.read_bytes() self.assertIsNotNone(self.response) self.assertEqual(self.response.headers, HttpHeaders([("host", "localhost"), ("content-length", "1")])) self.assertEqual(self.response.code, "200") self.assertEqual(self.response.reason, "OK") self.assertEqual(self.response.version, "HTTP/1.1") self.assertEqual(self.response.body, b"A")
def test_on_connection_closed(self): client_conn = Connection( h11.CLIENT, self.client_stream, on_response=self.on_response) client_conn.send_request(HttpRequest( method="GET", path="/", headers=[("Host", "localhost"), ("Connection", "close")])) server_conn = Connection( h11.SERVER, self.server_stream, on_request=self.on_request) yield server_conn.read_bytes() server_conn.send_response(HttpResponse( version="HTTP/1.1", code="200", reason="OK", headers=[("Host", "localhost"), ("Content-Length", "4")], body=b"Yaya")) yield client_conn.read_bytes() self.assertTrue(self.client_stream.closed()) self.assertTrue(self.server_stream.closed())
def test_on_request(self): client_conn = Connection(h11.CLIENT, self.client_stream) client_conn.send_request( HttpRequest(method="GET", path="/", headers=[("Host", "localhost")])) server_conn = Connection(h11.SERVER, self.server_stream, on_request=self.on_request) yield server_conn.read_bytes() self.assertIsNotNone(self.request) self.assertEqual(self.request.headers, HttpHeaders([("host", "localhost")])) self.assertEqual(self.request.method, "GET") self.assertEqual(self.request.path, "/") self.assertEqual(self.request.version, "HTTP/1.1")
def test_on_info_response(self): client_conn = Connection( h11.CLIENT, self.client_stream, on_info_response=self.on_response) client_conn.send_request(HttpRequest( method="GET", path="/chat", version="HTTP/1.1", headers=[("Host", "localhost"), ("Upgrade", "websocket")])) server_conn = Connection(h11.SERVER, self.server_stream, on_request=self.on_request) yield server_conn.read_bytes() server_conn.send_info_response(HttpResponse( version="HTTP/1.1", code="101", reason="Protocol Upgrade", headers=[("Host", "localhost"), ("Upgrade", "websocket")])) yield client_conn.read_bytes() self.assertIsNotNone(self.response) self.assertEqual(self.response.headers, HttpHeaders([("host", "localhost"), ("upgrade", "websocket")])) self.assertEqual(self.response.code, "101") self.assertEqual(self.response.reason, "Protocol Upgrade") self.assertEqual(self.response.version, "HTTP/1.1")
def test_on_response(self): server_conn = Connection(h11.SERVER, self.server_stream) server_conn.send_response( HttpResponse(version="HTTP/1.1", code="200", reason="OK", headers=[("Host", "localhost"), ("Content-Length", "1")], body=b"A")) client_conn = Connection(h11.CLIENT, self.client_stream, on_response=self.on_response) yield client_conn.read_bytes() self.assertIsNotNone(self.response) self.assertEqual( self.response.headers, HttpHeaders([("host", "localhost"), ("content-length", "1")])) self.assertEqual(self.response.code, "200") self.assertEqual(self.response.reason, "OK") self.assertEqual(self.response.version, "HTTP/1.1") self.assertEqual(self.response.body, b"A")
class Http1Layer(ApplicationLayer, DestStreamCreatorMixin): def __init__(self, server_state, context): super(Http1Layer, self).__init__(server_state, context) self.src_conn = Connection( h11.SERVER, self.src_stream, conn_type="src", readonly=(context.mode == "replay"), on_request=self.on_request) self.dest_conn = Connection( h11.CLIENT, self.dest_stream, conn_type="dest", on_response=self.on_response, on_info_response=self.on_info_response) self.req = None self.resp = None self.switch_protocol = False @gen.coroutine def process_and_return_context(self): while not self.finished(): self.req = None self.resp = None try: yield self.read_request() yield self.handle_http_proxy() self.send_request() yield self.read_response() self.send_response() except SrcStreamClosedError: if self.dest_stream: self.dest_stream.close() self.context.done = True if self.req: raise except DestStreamClosedError: self.src_stream.close() raise except SwitchToTunnelHttpProxy: break if self.switch_protocol: self.context.scheme = self.req.headers["Upgrade"] raise gen.Return(self.context) @gen.coroutine def read_request(self): # NOTE: run first request to handle protocol change logger.debug("{0} wait for request".format(self)) while not self.req: try: data = yield self.src_stream.read_bytes( self.src_stream.max_buffer_size, partial=True) except StreamClosedError: raise SrcStreamClosedError(detail="read request failed") else: self.src_conn.receive(data, raise_exception=True) logger.debug("{0} received request: {1}".format(self, self.req)) @gen.coroutine def read_response(self): logger.debug("{0} wait for response".format(self)) while not self.resp: try: data = yield self.dest_stream.read_bytes( self.dest_stream.max_buffer_size, partial=True) except StreamClosedError: # NOTE: for HTTP protocol, there is some condition that response finish when they didn't send data # It may happen when there is no "Content-Length" or "Content-Encoding: chunked" defined in there header self.dest_conn.receive(b"", raise_exception=False) break else: self.dest_conn.receive(data, raise_exception=True) logger.debug("{0} received response: {1}".format(self, self.resp)) def on_request(self, request): plugin_result = self.interceptor.request( layer_context=self.context, request=request) self.req = plugin_result.request if plugin_result else request def send_request(self): try: self.dest_conn.send_request(self.req) except StreamClosedError: raise DestStreamClosedError(detail="send request failed with {0}".format( _wrap_req_path(self.context, self.req))) def on_response(self, response): plugin_result = self.interceptor.response( layer_context=self.context, request=self.req, response=response) self.resp = plugin_result.response if plugin_result else response def send_response(self): try: if int(self.resp.code) in range(200, 600): self.src_conn.send_response(self.resp) self.finish() else: self.src_conn.send_info_response(self.resp) self.finish(switch_protocol=True) except StreamClosedError: raise SrcStreamClosedError(detail="send response failed {0}".format( _wrap_req_path(self.context, self.req))) def on_info_response(self, response): plugin_result = self.interceptor.response( layer_context=self.context, request=self.req, response=response) self.resp = plugin_result.response if plugin_result else response def finished(self): return (self.switch_protocol or self.src_stream.closed() or (self.dest_stream and self.dest_stream.closed())) def finish(self, switch_protocol=False): self.interceptor.publish( layer_context=self.context, request=self.req, response=self.resp) if (self.context.mode == "replay" or self.src_conn.closed() or self.dest_conn.closed()): self.src_stream.close() self.dest_stream.close() self.context.done = True elif switch_protocol: self.switch_protocol = True else: self.src_conn.start_next_cycle() self.dest_conn.start_next_cycle() @gen.coroutine def handle_http_proxy(self): if self.is_tunnel_http_proxy(): logger.debug("{0} proxy tunnel to {1}".format(self, self.req.path)) scheme, host, port = parse_tunnel_proxy_path(self.req.path) yield self.connect_to_dest(scheme, (host, port)) self.src_conn.send_response(HttpResponse( code="200", reason="OK", version="HTTP/1.1")) raise SwitchToTunnelHttpProxy elif self.is_normal_http_proxy(): logger.debug("{0} proxy to {1}".format(self, self.req.path)) scheme, host, port, path = parse_proxy_path(self.req.path) self.req.path = path yield self.connect_to_dest(scheme, (host, port)) self.dest_conn.io_stream = self.dest_stream else: raise gen.Return(None) def is_tunnel_http_proxy(self): return self.req.method == "CONNECT" def is_normal_http_proxy(self): return (self.req.path.startswith("http://") or self.req.path.startswith("https://")) @gen.coroutine def connect_to_dest(self, scheme, addr): if addr != (self.context.host, self.context.port): logger.debug("{0} proxy to new connection {1}".format(self, addr)) if self.dest_stream: self.dest_stream.close() dest_stream = yield self.create_dest_stream(addr) self.context.dest_stream = dest_stream self.context.scheme = scheme self.context.host = addr[0] self.context.port = addr[1] logger.debug("{0} proxy to new connection success".format(self)) else: logger.debug("{0} proxy to same connection".format(self))
class Http1Layer(ApplicationLayer, DestStreamCreatorMixin): def __init__(self, server_state, context): super(Http1Layer, self).__init__(server_state, context) self.src_conn = Connection(h11.SERVER, self.src_stream, conn_type="src", readonly=(context.mode == "replay"), on_request=self.on_request) self.dest_conn = Connection(h11.CLIENT, self.dest_stream, conn_type="dest", on_response=self.on_response, on_info_response=self.on_info_response) self.req = None self.resp = None self.switch_protocol = False @gen.coroutine def process_and_return_context(self): while not self.finished(): self.req = None self.resp = None try: yield self.read_request() yield self.handle_http_proxy() self.send_request() yield self.read_response() self.send_response() except SrcStreamClosedError: if self.dest_stream: self.dest_stream.close() self.context.done = True if self.req: raise except DestStreamClosedError: self.src_stream.close() raise except SwitchToTunnelHttpProxy: break if self.switch_protocol: self.context.scheme = self.req.headers["Upgrade"] raise gen.Return(self.context) @gen.coroutine def read_request(self): # NOTE: run first request to handle protocol change logger.debug("{0} wait for request".format(self)) while not self.req: try: data = yield self.src_stream.read_bytes( self.src_stream.max_buffer_size, partial=True) except StreamClosedError: raise SrcStreamClosedError(detail="read request failed") else: self.src_conn.receive(data, raise_exception=True) logger.debug("{0} received request: {1}".format(self, self.req)) @gen.coroutine def read_response(self): logger.debug("{0} wait for response".format(self)) while not self.resp: try: data = yield self.dest_stream.read_bytes( self.dest_stream.max_buffer_size, partial=True) except StreamClosedError: # NOTE: for HTTP protocol, there is some condition that response finish when they didn't send data # It may happen when there is no "Content-Length" or "Content-Encoding: chunked" defined in there header self.dest_conn.receive(b"", raise_exception=False) break else: self.dest_conn.receive(data, raise_exception=True) logger.debug("{0} received response: {1}".format(self, self.resp)) def on_request(self, request): plugin_result = self.interceptor.request(layer_context=self.context, request=request) self.req = plugin_result.request if plugin_result else request def send_request(self): try: self.dest_conn.send_request(self.req) except StreamClosedError: raise DestStreamClosedError( detail="send request failed with {0}".format( _wrap_req_path(self.context, self.req))) def on_response(self, response): plugin_result = self.interceptor.response(layer_context=self.context, request=self.req, response=response) self.resp = plugin_result.response if plugin_result else response def send_response(self): try: if int(self.resp.code) in range(200, 600): self.src_conn.send_response(self.resp) self.finish() else: self.src_conn.send_info_response(self.resp) self.finish(switch_protocol=True) except StreamClosedError: raise SrcStreamClosedError( detail="send response failed {0}".format( _wrap_req_path(self.context, self.req))) def on_info_response(self, response): plugin_result = self.interceptor.response(layer_context=self.context, request=self.req, response=response) self.resp = plugin_result.response if plugin_result else response def finished(self): return (self.switch_protocol or self.src_stream.closed() or (self.dest_stream and self.dest_stream.closed())) def finish(self, switch_protocol=False): self.interceptor.publish(layer_context=self.context, request=self.req, response=self.resp) if (self.context.mode == "replay" or self.src_conn.closed() or self.dest_conn.closed()): self.src_stream.close() self.dest_stream.close() self.context.done = True elif switch_protocol: self.switch_protocol = True else: self.src_conn.start_next_cycle() self.dest_conn.start_next_cycle() @gen.coroutine def handle_http_proxy(self): if self.is_tunnel_http_proxy(): logger.debug("{0} proxy tunnel to {1}".format(self, self.req.path)) scheme, host, port = parse_tunnel_proxy_path(self.req.path) yield self.connect_to_dest(scheme, (host, port)) self.src_conn.send_response( HttpResponse(code="200", reason="OK", version="HTTP/1.1")) raise SwitchToTunnelHttpProxy elif self.is_normal_http_proxy(): logger.debug("{0} proxy to {1}".format(self, self.req.path)) scheme, host, port, path = parse_proxy_path(self.req.path) self.req.path = path yield self.connect_to_dest(scheme, (host, port)) self.dest_conn.io_stream = self.dest_stream else: raise gen.Return(None) def is_tunnel_http_proxy(self): return self.req.method == "CONNECT" def is_normal_http_proxy(self): return (self.req.path.startswith("http://") or self.req.path.startswith("https://")) @gen.coroutine def connect_to_dest(self, scheme, addr): if addr != (self.context.host, self.context.port): logger.debug("{0} proxy to new connection {1}".format(self, addr)) if self.dest_stream: self.dest_stream.close() dest_stream = yield self.create_dest_stream(addr) self.context.dest_stream = dest_stream self.context.scheme = scheme self.context.host = addr[0] self.context.port = addr[1] logger.debug("{0} proxy to new connection success".format(self)) else: logger.debug("{0} proxy to same connection".format(self))