def test_disconnect_during_handshake_start(tctx: Context, disconnect): server = Server(("proxy", 1234)) server.state = ConnectionState.OPEN tl = TTunnelLayer(tctx, server, tctx.server) tl.child_layer = TChildLayer(tctx) playbook = Playbook(tl, logs=True) assert ( playbook << SendData(server, b"handshake-hello") ) if disconnect == "client": assert ( playbook >> ConnectionClosed(tctx.client) >> ConnectionClosed(server) # proxyserver will cancel all other connections as well. << CloseConnection(server) << Log("Got start. Server state: CLOSED") << Log("Got client close.") << CloseConnection(tctx.client) ) else: assert ( playbook >> ConnectionClosed(server) << CloseConnection(server) << Log("Got start. Server state: CLOSED") )
def test_disconnect_during_handshake_command(tctx: Context, disconnect): server = Server(("proxy", 1234)) tl = TTunnelLayer(tctx, server, tctx.server) tl.child_layer = TChildLayer(tctx) playbook = Playbook(tl, logs=True) assert ( playbook << Log("Got start. Server state: CLOSED") >> DataReceived(tctx.client, b"client-hello") << SendData(tctx.client, b"client-hello-reply") >> DataReceived(tctx.client, b"open") << OpenConnection(server) >> reply(None) << SendData(server, b"handshake-hello") ) if disconnect == "client": assert ( playbook >> ConnectionClosed(tctx.client) >> ConnectionClosed(server) # proxyserver will cancel all other connections as well. << CloseConnection(server) << Log("Opened: err='connection closed without notice'. Server state: CLOSED") << Log("Got client close.") << CloseConnection(tctx.client) ) else: assert ( playbook >> ConnectionClosed(server) << CloseConnection(server) << Log("Opened: err='connection closed without notice'. Server state: CLOSED") )
def test_tunnel_default_impls(tctx: Context): """ Some tunnels don't need certain features, so the default behaviour should be to be transparent. """ server = Server(None) server.state = ConnectionState.OPEN tl = tunnel.TunnelLayer(tctx, server, tctx.server) tl.child_layer = TChildLayer(tctx) playbook = Playbook(tl, logs=True) assert ( playbook << Log("Got start. Server state: OPEN") >> DataReceived(server, b"server-hello") << SendData(server, b"server-hello-reply") ) assert tl.tunnel_state is tunnel.TunnelState.OPEN assert ( playbook >> ConnectionClosed(server) << Log("Got server close.") << CloseConnection(server) ) assert tl.tunnel_state is tunnel.TunnelState.CLOSED assert ( playbook >> DataReceived(tctx.client, b"open") << OpenConnection(server) >> reply(None) << Log("Opened: err=None. Server state: OPEN") >> DataReceived(server, b"half-close") << CloseConnection(server, half_close=True) )
def test_http_client_aborts(tctx, stream): """Test handling of the case where a client aborts during request transmission.""" server = Placeholder(Server) flow = Placeholder(HTTPFlow) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=True) def enable_streaming(flow: HTTPFlow): flow.request.stream = True assert (playbook >> DataReceived( tctx.client, b"POST http://example.com/ HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 6\r\n" b"\r\n" b"abc") << http.HttpRequestHeadersHook(flow)) if stream: assert (playbook >> reply(side_effect=enable_streaming) << OpenConnection(server) >> reply(None) << SendData( server, b"POST / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 6\r\n" b"\r\n" b"abc")) else: assert playbook >> reply() (playbook >> ConnectionClosed(tctx.client) << CloseConnection(tctx.client)) if stream: playbook << CloseConnection(server) assert (playbook << http.HttpErrorHook(flow) >> reply() << None) assert "peer closed connection" in flow().error.msg
def test_tunnel_handshake_command(tctx: Context, success): server = Server(("proxy", 1234)) tl = TTunnelLayer(tctx, server, tctx.server) tl.child_layer = TChildLayer(tctx) playbook = Playbook(tl, logs=True) (playbook << Log("Got start. Server state: CLOSED") >> DataReceived( tctx.client, b"client-hello") << SendData( tctx.client, b"client-hello-reply") >> DataReceived( tctx.client, b"open") << OpenConnection(server) >> reply(None) << SendData(server, b"handshake-hello") >> DataReceived( server, b"handshake-" + success.encode()) << SendData( server, b"handshake-" + success.encode())) if success == "success": assert (playbook << Log(f"Opened: err=None. Server state: OPEN") >> DataReceived(server, b"tunneled-server-hello") << SendData( server, b"tunneled-server-hello-reply") >> ConnectionClosed(tctx.client) << Log("Got client close.") << CloseConnection(tctx.client)) assert tl.tunnel_state is tunnel.TunnelState.OPEN assert (playbook >> ConnectionClosed(server) << Log("Got server close.") << CloseConnection(server)) assert tl.tunnel_state is tunnel.TunnelState.CLOSED else: assert (playbook << CloseConnection(server) << Log("Opened: err='handshake error'. Server state: CLOSED")) assert tl.tunnel_state is tunnel.TunnelState.CLOSED
def test_simple(tctx): """open connection, receive data, send it to peer""" f = Placeholder(TCPFlow) assert ( Playbook(tcp.TCPLayer(tctx)) << tcp.TcpStartHook(f) >> reply() << OpenConnection(tctx.server) >> reply(None) >> DataReceived(tctx.client, b"hello!") << tcp.TcpMessageHook(f) >> reply() << SendData(tctx.server, b"hello!") >> DataReceived(tctx.server, b"hi") << tcp.TcpMessageHook(f) >> reply() << SendData(tctx.client, b"hi") >> ConnectionClosed(tctx.server) << CloseConnection(tctx.client, half_close=True) >> ConnectionClosed(tctx.client) << CloseConnection(tctx.server) << tcp.TcpEndHook(f) >> reply() >> ConnectionClosed(tctx.client) << None ) assert len(f().messages) == 2
def test_server_aborts(tctx, data): """Test the scenario where the server doesn't serve a response""" server = Placeholder(Server) flow = Placeholder(HTTPFlow) err = Placeholder(bytes) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) 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/1.1\r\nHost: example.com\r\n\r\n") ) if data: playbook >> DataReceived(server, data) assert ( playbook >> ConnectionClosed(server) << CloseConnection(server) << http.HttpErrorHook(flow) >> reply() << SendData(tctx.client, err) << CloseConnection(tctx.client) ) assert flow().error assert b"502 Bad Gateway" in err()
def test_close_disconnect(ws_testdata): tctx, playbook, flow = ws_testdata assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >> ConnectionClosed(tctx.server) << CloseConnection(tctx.server) << SendData(tctx.client, b"\x88\x02\x03\xe8") << CloseConnection( tctx.client) << websocket.WebsocketErrorHook(flow) >> reply() >> ConnectionClosed(tctx.client)) assert "ABNORMAL_CLOSURE" in flow.error.msg
def test_close_error(ws_testdata): tctx, playbook, flow = ws_testdata assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >> DataReceived(tctx.server, b"\x88\x02\x0f\xa0") << SendData( tctx.server, masked(b"\x88\x02\x0f\xa0")) << CloseConnection( tctx.server) << SendData(tctx.client, b"\x88\x02\x0f\xa0") << CloseConnection( tctx.client) << websocket.WebsocketErrorHook(flow) >> reply()) assert "UNKNOWN_ERROR=4000" in flow.error.msg
def test_close_code(ws_testdata): tctx, playbook, flow = ws_testdata assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >> DataReceived(tctx.server, b"\x88\x02\x0f\xa0") << SendData( tctx.server, masked(b"\x88\x02\x0f\xa0")) << CloseConnection( tctx.server) << SendData(tctx.client, b"\x88\x02\x0f\xa0") << CloseConnection( tctx.client) << websocket.WebsocketEndHook(flow) >> reply()) assert flow.websocket.close_code == 4000
def test_close_disconnect(ws_testdata): tctx, playbook, flow = ws_testdata assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >> ConnectionClosed(tctx.server) << CloseConnection(tctx.server) << SendData(tctx.client, b"\x88\x02\x03\xe8") << CloseConnection( tctx.client) << websocket.WebsocketEndHook(flow) >> reply() >> ConnectionClosed(tctx.client)) # The \x03\xe8 above is code 1000 (normal closure). # But 1006 (ABNORMAL_CLOSURE) is expected, because the connection was already closed. assert flow.websocket.close_code == 1006
def test_receive_data_after_half_close(tctx): """ data received after the other connection has been half-closed should still be forwarded. """ assert (Playbook(tcp.TCPLayer(tctx), hooks=False) << OpenConnection(tctx.server) >> reply(None) >> DataReceived( tctx.client, b"eof-delimited-request") << SendData( tctx.server, b"eof-delimited-request") >> ConnectionClosed( tctx.client) << CloseConnection(tctx.server, half_close=True) >> DataReceived( tctx.server, b"i'm late") << SendData(tctx.client, b"i'm late") >> ConnectionClosed(tctx.server) << CloseConnection(tctx.client))
def test_response_until_eof(tctx): """Test scenario where the server response body is terminated by EOF.""" server = Placeholder(Server) assert (Playbook(http.HttpLayer( tctx, HTTPMode.regular), hooks=False) >> 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/1.1\r\nHost: example.com\r\n\r\n") >> DataReceived(server, b"HTTP/1.1 200 OK\r\n\r\nfoo") >> ConnectionClosed(server) << CloseConnection(server) << SendData( tctx.client, b"HTTP/1.1 200 OK\r\n\r\nfoo") << CloseConnection( tctx.client))
def test_http_proxy_tcp(tctx, mode, close_first): """Test TCP over HTTP CONNECT.""" server = Placeholder(Server) if mode == "upstream": tctx.options.mode = "upstream:http://proxy:8080" toplayer = http.HttpLayer(tctx, HTTPMode.upstream) else: tctx.options.mode = "regular" toplayer = http.HttpLayer(tctx, HTTPMode.regular) playbook = Playbook(toplayer, hooks=False) assert ( playbook >> DataReceived(tctx.client, b"CONNECT example: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"this is not http") << layer.NextLayerHook(Placeholder()) >> reply_next_layer(lambda ctx: TCPLayer(ctx, ignore=True)) << OpenConnection(server) ) playbook >> reply(None) if mode == "upstream": playbook << SendData(server, b"CONNECT example:443 HTTP/1.1\r\n\r\n") playbook >> DataReceived(server, b"HTTP/1.1 200 Connection established\r\n\r\n") assert ( playbook << SendData(server, b"this is not http") >> DataReceived(server, b"true that") << SendData(tctx.client, b"true that") ) if mode == "regular": assert server().address == ("example", 443) else: assert server().address == ("proxy", 8080) if close_first == "client": a, b = tctx.client, server else: a, b = server, tctx.client assert ( playbook >> ConnectionClosed(a) << CloseConnection(b) >> ConnectionClosed(b) << CloseConnection(a) )
def test_protocol_error(ws_testdata): tctx, playbook, flow = ws_testdata assert ( playbook << websocket.WebsocketStartHook(flow) >> reply() >> DataReceived(tctx.server, b"\x01\x03foo") >> DataReceived(tctx.server, b"\x02\x03bar") << SendData( tctx.server, masked( b"\x88/\x03\xeaexpected CONTINUATION, got <Opcode.BINARY: 2>")) << CloseConnection(tctx.server) << SendData( tctx.client, b"\x88/\x03\xeaexpected CONTINUATION, got <Opcode.BINARY: 2>") << CloseConnection( tctx.client) << websocket.WebsocketErrorHook(flow) >> reply()) assert not flow.websocket.messages
def test_http_proxy_tcp(tctx, mode, close_first): """Test TCP over HTTP CONNECT.""" server = Placeholder(Server) f = Placeholder(TCPFlow) tctx.options.connection_strategy = "lazy" if mode == "upstream": tctx.options.mode = "upstream:http://proxy:8080" toplayer = http.HttpLayer(tctx, HTTPMode.upstream) else: tctx.options.mode = "regular" toplayer = http.HttpLayer(tctx, HTTPMode.regular) playbook = Playbook(toplayer, hooks=False) assert (playbook >> DataReceived( tctx.client, b"CONNECT example: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"this is not http") << layer.NextLayerHook(Placeholder()) >> reply_next_layer(lambda ctx: TCPLayer(ctx, ignore=False)) << TcpStartHook(f) >> reply() << OpenConnection(server)) playbook >> reply(None) if mode == "upstream": playbook << SendData(server, b"CONNECT example:443 HTTP/1.1\r\n\r\n") playbook >> DataReceived( server, b"HTTP/1.1 200 Connection established\r\n\r\n") assert (playbook << SendData(server, b"this is not http") >> DataReceived( server, b"true that") << SendData(tctx.client, b"true that")) if mode == "regular": assert server().address == ("example", 443) else: assert server().address == ("proxy", 8080) assert (playbook >> TcpMessageInjected( f, TCPMessage(False, b"fake news from your friendly man-in-the-middle")) << SendData(tctx.client, b"fake news from your friendly man-in-the-middle")) if close_first == "client": a, b = tctx.client, server else: a, b = server, tctx.client assert (playbook >> ConnectionClosed(a) << CloseConnection(b) >> ConnectionClosed(b) << CloseConnection(a))
def test_close_normal(ws_testdata): tctx, playbook, flow = ws_testdata masked_close = Placeholder(bytes) close = Placeholder(bytes) assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >> DataReceived(tctx.client, masked_bytes(b"\x88\x00")) << SendData( tctx.server, masked_close) << CloseConnection(tctx.server) << SendData(tctx.client, close) << CloseConnection( tctx.client) << websocket.WebsocketEndHook(flow) >> reply()) # wsproto currently handles this inconsistently, see # https://github.com/python-hyper/wsproto/pull/153/files assert masked_close() == masked( b"\x88\x02\x03\xe8") or masked_close() == masked(b"\x88\x00") assert close() == b"\x88\x02\x03\xe8" or close() == b"\x88\x00" assert flow.websocket.close_code == 1005
def test_h2_to_h1(tctx): """Test HTTP/2 -> HTTP/1 request translation""" server = Placeholder(Server) flow = Placeholder(HTTPFlow) conn, playbook = h2_client(tctx) conn.send_headers(1, example_request_headers, end_stream=True) response = Placeholder(bytes) assert ( playbook >> DataReceived(tctx.client, conn.data_to_send()) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow) >> reply() << OpenConnection(server) >> reply(None) << SendData( server, b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") >> DataReceived(server, b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\n") << http.HttpResponseHeadersHook(flow) >> reply() >> DataReceived( server, b"Hello World!") << http.HttpResponseHook(flow) << CloseConnection(server) >> reply(to=-2) << SendData( tctx.client, response)) events = conn.receive_data(response()) assert event_types(events) == [ h2.events.ResponseReceived, h2.events.DataReceived, h2.events.DataReceived, h2.events.StreamEnded ] resp: h2.events.ResponseReceived = events[0] body: h2.events.DataReceived = events[1] assert resp.headers == [(b':status', b'200'), (b'content-length', b'12')] assert body.data == b"Hello World!"
def test_h1_to_h2(tctx): """Test HTTP/1 -> HTTP/2 request translation""" server = Placeholder(Server) flow = Placeholder(HTTPFlow) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular)) conf = h2.config.H2Configuration(client_side=False) conn = h2.connection.H2Connection(conf) conn.initiate_connection() request = Placeholder(bytes) assert (playbook >> DataReceived( tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow) >> reply() << OpenConnection(server) >> reply(None, side_effect=make_h2) << SendData(server, request)) events = conn.receive_data(request()) assert event_types(events) == [ h2.events.RemoteSettingsChanged, h2.events.RequestReceived, h2.events.StreamEnded ] conn.send_headers(1, example_response_headers) conn.send_data(1, b"Hello World!", end_stream=True) settings_ack = Placeholder(bytes) assert ( playbook >> DataReceived(server, conn.data_to_send()) << http.HttpResponseHeadersHook(flow) << SendData(server, settings_ack) >> reply(to=-2) << http.HttpResponseHook(flow) >> reply() << SendData( tctx.client, b"HTTP/1.1 200 OK\r\n\r\nHello World!") << CloseConnection(tctx.client)) assert settings_ack() == b'\x00\x00\x00\x04\x01\x00\x00\x00\x00'
def test_cancel_during_response_hook(tctx): """ Test that we properly handle the case of the following event sequence: - we receive a server response - we trigger the response hook - the client cancels the stream - the response hook completes Given that we have already triggered the response hook, we don't want to trigger the error hook. """ playbook, cff = start_h2_client(tctx) flow = Placeholder(HTTPFlow) server = Placeholder(Server) assert ( playbook >> DataReceived(tctx.client, cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize()) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow) >> reply() << OpenConnection(server) >> reply(None) << SendData(server, b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n') >> DataReceived(server, b"HTTP/1.1 204 No Content\r\n\r\n") << http.HttpResponseHeadersHook(flow) << CloseConnection(server) >> reply(to=-2) << http.HttpResponseHook(flow) >> DataReceived(tctx.client, cff.build_rst_stream_frame(1, ErrorCodes.CANCEL).serialize()) >> reply(to=-2) )
def test_tunnel_handshake_start(tctx: Context, success): server = Server(("proxy", 1234)) server.state = ConnectionState.OPEN tl = TTunnelLayer(tctx, server, tctx.server) tl.child_layer = TChildLayer(tctx) assert repr(tl) playbook = Playbook(tl, logs=True) (playbook << SendData(server, b"handshake-hello") >> DataReceived( tctx.client, b"client-hello") >> DataReceived( server, b"handshake-" + success.encode()) << SendData( server, b"handshake-" + success.encode())) if success == "success": playbook << Log("Got start. Server state: OPEN") else: playbook << CloseConnection(server) playbook << Log("Got start. Server state: CLOSED") playbook << SendData(tctx.client, b"client-hello-reply") if success == "success": playbook >> DataReceived(server, b"tunneled-server-hello") playbook << SendData(server, b"tunneled-server-hello-reply") assert playbook
def test_rst_then_close(tctx): """ Test that we properly handle the case of a client that first causes protocol errors and then disconnects. Adapted from h2spec http2/5.1/5. """ playbook, cff = start_h2_client(tctx) flow = Placeholder(HTTPFlow) server = Placeholder(Server) assert ( playbook >> DataReceived(tctx.client, cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize()) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow) >> reply() << OpenConnection(server) >> DataReceived(tctx.client, cff.build_data_frame(b"unexpected data frame").serialize()) << SendData(tctx.client, cff.build_rst_stream_frame(1, ErrorCodes.STREAM_CLOSED).serialize()) >> ConnectionClosed(tctx.client) << CloseConnection(tctx.client) >> reply("connection cancelled", to=-5) << http.HttpErrorHook(flow) >> reply() ) assert flow().error.msg == "connection cancelled"
def test_cancel_then_server_disconnect(tctx): """ Test that we properly handle the case of the following event sequence: - client cancels a stream - we start an error hook - server disconnects - error hook completes. """ playbook, cff = start_h2_client(tctx) flow = Placeholder(HTTPFlow) server = Placeholder(Server) assert ( playbook >> DataReceived(tctx.client, cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize()) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow) >> reply() << OpenConnection(server) >> reply(None) << SendData(server, b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n') >> DataReceived(tctx.client, cff.build_rst_stream_frame(1, ErrorCodes.CANCEL).serialize()) << CloseConnection(server) << http.HttpErrorHook(flow) >> reply() >> ConnectionClosed(server) << None )
def assert_kill(err_hook: bool = True): playbook >> reply(side_effect=kill) if err_hook: playbook << http.HttpErrorHook(flow) playbook >> reply() playbook << CloseConnection(tctx.client) assert playbook
def test_server_unreachable(tctx, connect): """Test the scenario where the target server is unreachable.""" tctx.options.connection_strategy = "eager" server = Placeholder(Server) flow = Placeholder(HTTPFlow) err = Placeholder(bytes) playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) if connect: playbook >> DataReceived(tctx.client, b"CONNECT example.com:443 HTTP/1.1\r\n\r\n") else: playbook >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\n\r\n") playbook << OpenConnection(server) playbook >> reply("Connection failed") if not connect: # Our API isn't ideal here, there is no error hook for CONNECT requests currently. # We could fix this either by having CONNECT request go through all our regular hooks, # or by adding dedicated ok/error hooks. playbook << http.HttpErrorHook(flow) playbook >> reply() playbook << SendData(tctx.client, err) if not connect: playbook << CloseConnection(tctx.client) assert playbook if not connect: assert flow().error assert b"502 Bad Gateway" in err() assert b"Connection failed" in err()
def test_disconnect_while_intercept(tctx): """Test a server disconnect while a request is intercepted.""" tctx.options.connection_strategy = "eager" server1 = Placeholder(Server) server2 = Placeholder(Server) flow = Placeholder(HTTPFlow) assert ( Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) >> DataReceived(tctx.client, b"CONNECT example.com:80 HTTP/1.1\r\n\r\n") << http.HttpConnectHook(Placeholder(HTTPFlow)) >> reply() << OpenConnection(server1) >> reply(None) << 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)) << http.HttpRequestHook(flow) >> ConnectionClosed(server1) << CloseConnection(server1) >> reply(to=-3) << OpenConnection(server2) >> reply(None) << SendData(server2, b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") >> DataReceived(server2, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") ) assert server1() != server2() assert flow().server_conn == server2()
def test_socks5_err(data: bytes, err: bytes, msg: str, tctx: Context): playbook = (Playbook(modes.Socks5Proxy(tctx), logs=True) >> DataReceived( tctx.client, data)) if err: playbook << SendData(tctx.client, err) playbook << CloseConnection(tctx.client) playbook << Log(msg) assert playbook
def test_http_proxy_relative_request_no_host_header(tctx): """Test handling of a relative-form "GET /" in regular proxy mode, but without a host header.""" err = Placeholder(bytes) assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) >> DataReceived(tctx.client, b"GET / HTTP/1.1\r\n\r\n") << SendData( tctx.client, err) << CloseConnection(tctx.client)) assert b"400 Bad Request" in err() assert b"HTTP request has no host header, destination unknown." in err()
def test_socks5_premature_close(tctx: Context): assert ( Playbook(modes.Socks5Proxy(tctx), logs=True) >> DataReceived(tctx.client, b"\x05") >> ConnectionClosed(tctx.client) << Log(r"Client closed connection before completing SOCKS5 handshake: b'\x05'") << CloseConnection(tctx.client) )
def _handle_event(self, event: Event) -> layer.CommandGenerator[None]: if isinstance(event, Start): yield Log(f"Got start. Server state: {self.context.server.state.name}") elif isinstance(event, DataReceived) and event.data == b"client-hello": yield SendData(self.context.client, b"client-hello-reply") elif isinstance(event, DataReceived) and event.data == b"server-hello": yield SendData(self.context.server, b"server-hello-reply") elif isinstance(event, DataReceived) and event.data == b"open": err = yield OpenConnection(self.context.server) yield Log(f"Opened: {err=}. Server state: {self.context.server.state.name}") elif isinstance(event, DataReceived) and event.data == b"half-close": err = yield CloseConnection(event.connection, half_close=True) elif isinstance(event, ConnectionClosed): yield Log(f"Got {event.connection.__class__.__name__.lower()} close.") yield CloseConnection(event.connection) else: raise AssertionError