def request(flow: http.HTTPFlow) -> None: address = proxy_address(flow) is_proxy_change = address != flow.server_conn.via.address server_connection_already_open = flow.server_conn.timestamp_start is not None if is_proxy_change and server_connection_already_open: # server_conn already refers to an existing connection (which cannot be modified), # so we need to replace it with a new server connection object. flow.server_conn = Server(flow.server_conn.address) flow.server_conn.via = ServerSpec("http", address)
def test_upstream_proxy(tctx, redirect, scheme): """Test that an upstream HTTP proxy is used.""" server = Placeholder(Server) server2 = Placeholder(Server) flow = Placeholder(HTTPFlow) tctx.options.mode = "upstream:http://proxy:8080" playbook = Playbook(http.HttpLayer(tctx, HTTPMode.upstream), hooks=False) if scheme == "http": assert ( playbook >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") << OpenConnection(server) >> reply(None) << SendData(server, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") ) else: assert ( playbook >> DataReceived(tctx.client, b"CONNECT example.com:443 HTTP/1.1\r\n\r\n") << SendData(tctx.client, b"HTTP/1.1 200 Connection established\r\n\r\n") >> DataReceived(tctx.client, b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") << layer.NextLayerHook(Placeholder()) >> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent)) << OpenConnection(server) >> reply(None) << SendData(server, b"CONNECT example.com:443 HTTP/1.1\r\n\r\n") >> DataReceived(server, b"HTTP/1.1 200 Connection established\r\n\r\n") << SendData(server, b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") ) playbook >> DataReceived(server, b"HTTP/1.1 418 OK\r\nContent-Length: 0\r\n\r\n") playbook << SendData(tctx.client, b"HTTP/1.1 418 OK\r\nContent-Length: 0\r\n\r\n") assert playbook assert server().address == ("proxy", 8080) if scheme == "http": playbook >> DataReceived(tctx.client, b"GET http://example.com/two HTTP/1.1\r\nHost: example.com\r\n\r\n") else: playbook >> DataReceived(tctx.client, b"GET /two HTTP/1.1\r\nHost: example.com\r\n\r\n") assert (playbook << http.HttpRequestHook(flow)) if redirect == "change-destination": flow().request.host = "other-server" flow().request.host_header = "example.com" elif redirect == "change-proxy": flow().server_conn.via = ServerSpec("http", address=("other-proxy", 1234)) playbook >> reply() if redirect: # Protocol-wise we wouldn't need to open a new connection for plain http host redirects, # but we disregard this edge case to simplify implementation. playbook << OpenConnection(server2) playbook >> reply(None) else: server2 = server if scheme == "http": if redirect == "change-destination": playbook << SendData(server2, b"GET http://other-server/two HTTP/1.1\r\nHost: example.com\r\n\r\n") else: playbook << SendData(server2, b"GET http://example.com/two HTTP/1.1\r\nHost: example.com\r\n\r\n") else: if redirect == "change-destination": playbook << SendData(server2, b"CONNECT other-server:443 HTTP/1.1\r\n\r\n") playbook >> DataReceived(server2, b"HTTP/1.1 200 Connection established\r\n\r\n") elif redirect == "change-proxy": playbook << SendData(server2, b"CONNECT example.com:443 HTTP/1.1\r\n\r\n") playbook >> DataReceived(server2, b"HTTP/1.1 200 Connection established\r\n\r\n") playbook << SendData(server2, b"GET /two HTTP/1.1\r\nHost: example.com\r\n\r\n") playbook >> DataReceived(server2, b"HTTP/1.1 418 OK\r\nContent-Length: 0\r\n\r\n") playbook << SendData(tctx.client, b"HTTP/1.1 418 OK\r\nContent-Length: 0\r\n\r\n") assert playbook if redirect == "change-proxy": assert server2().address == ("other-proxy", 1234) else: assert server2().address == ("proxy", 8080) assert ( playbook >> ConnectionClosed(tctx.client) << CloseConnection(tctx.client) )