예제 #1
0
def test_https_proxy(strategy, tctx):
    """Test a CONNECT request, followed by a HTTP GET /"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))
    tctx.options.connection_strategy = strategy

    (playbook >> DataReceived(tctx.client,
                              b"CONNECT example.proxy:80 HTTP/1.1\r\n\r\n") <<
     http.HttpConnectHook(Placeholder()) >> reply())
    if strategy == "eager":
        (playbook << OpenConnection(server) >> reply(None))
    (playbook << SendData(tctx.client,
                          b'HTTP/1.1 200 Connection established\r\n\r\n') >>
     DataReceived(tctx.client,
                  b"GET /foo?hello=1 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.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow)
     >> reply())
    if strategy == "lazy":
        (playbook << OpenConnection(server) >> reply(None))
    (playbook << SendData(
        server, b"GET /foo?hello=1 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\nHello World!")
     << http.HttpResponseHeadersHook(flow) >> reply() <<
     http.HttpResponseHook(flow) >> reply() << SendData(
         tctx.client,
         b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!"))
    assert playbook
예제 #2
0
 def test_no_data_on_closed_stream(self, tctx):
     frame_factory = FrameFactory()
     req = Request.make("GET", "http://example.com/")
     resp = {":status": 200}
     assert (
         Playbook(Http2Client(tctx)) << SendData(
             tctx.server,
             Placeholder(bytes))  # preamble + initial settings frame
         >> DataReceived(
             tctx.server,
             frame_factory.build_settings_frame({}, ack=True).serialize())
         >> http.RequestHeaders(1, req, end_stream=True) << SendData(
             tctx.server,
             b"\x00\x00\x06\x01\x05\x00\x00\x00\x01\x82\x86\x84\\\x81\x07")
         >> http.RequestEndOfMessage(1) >> DataReceived(
             tctx.server,
             frame_factory.build_headers_frame(resp).serialize()) <<
         http.ReceiveHttp(Placeholder(
             http.ResponseHeaders)) >> http.RequestProtocolError(
                 1, "cancelled", code=status_codes.CLIENT_CLOSED_REQUEST) <<
         SendData(
             tctx.server,
             frame_factory.build_rst_stream_frame(
                 1, ErrorCodes.CANCEL).serialize()) >> DataReceived(
                     tctx.server,
                     frame_factory.build_data_frame(b"foo").serialize()) <<
         SendData(
             tctx.server,
             frame_factory.build_rst_stream_frame(
                 1, ErrorCodes.STREAM_CLOSED).serialize())
     )  # important: no ResponseData event here!
예제 #3
0
def test_pipelining(tctx, transfer_encoding):
    """Test that multiple requests can be processed over the same connection"""

    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN

    req = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
    if transfer_encoding == "identity":
        resp = (b"HTTP/1.1 200 OK\r\n"
                b"Content-Length: 12\r\n"
                b"\r\n"
                b"Hello World!")
    else:
        resp = (b"HTTP/1.1 200 OK\r\n"
                b"Transfer-Encoding: chunked\r\n"
                b"\r\n"
                b"c\r\n"
                b"Hello World!\r\n"
                b"0\r\n"
                b"\r\n")

    assert (Playbook(http.HttpLayer(tctx, HTTPMode.transparent), hooks=False)
            # Roundtrip 1
            >> DataReceived(tctx.client, req) << SendData(tctx.server, req) >>
            DataReceived(tctx.server, resp) << SendData(tctx.client, resp)
            # Roundtrip 2
            >> DataReceived(tctx.client, req) << SendData(tctx.server, req) >>
            DataReceived(tctx.server, resp) << SendData(tctx.client, resp))
예제 #4
0
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()
예제 #5
0
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()
예제 #6
0
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) <<
                http.HttpRequestHook(flow) >> reply() << 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
예제 #7
0
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'
예제 #8
0
def test_proxy_chain(tctx, strategy):
    server = Placeholder(Server)
    tctx.options.connection_strategy = strategy
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)

    playbook >> DataReceived(tctx.client,
                             b"CONNECT proxy:8080 HTTP/1.1\r\n\r\n")
    if strategy == "eager":
        playbook << OpenConnection(server)
        playbook >> reply(None)
    playbook << SendData(tctx.client,
                         b"HTTP/1.1 200 Connection established\r\n\r\n")

    playbook >> DataReceived(tctx.client,
                             b"CONNECT second-proxy:8080 HTTP/1.1\r\n\r\n")
    playbook << layer.NextLayerHook(Placeholder())
    playbook >> reply_next_layer(
        lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
    playbook << SendData(
        tctx.client, b"HTTP/1.1 502 Bad Gateway\r\n"
        b"content-length: 198\r\n"
        b"\r\n"
        b"mitmproxy received an HTTP CONNECT request even though it is not running in regular/upstream mode. "
        b"This usually indicates a misconfiguration, please see the mitmproxy mode documentation for details."
    )

    assert playbook
예제 #9
0
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."""
    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) >>
            DataReceived(tctx.client, b"GET / HTTP/1.1\r\n\r\n") << SendData(
                tctx.client, b"HTTP/1.1 400 Bad Request\r\n"
                b"content-length: 53\r\n"
                b"\r\n"
                b"HTTP request has no host header, destination unknown."))
예제 #10
0
def test_close_during_connect_hook(tctx):
    flow = Placeholder(HTTPFlow)
    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> DataReceived(
        tctx.client, b'CONNECT hi.ls:443 HTTP/1.1\r\n'
        b'Proxy-Connection: keep-alive\r\n'
        b'Connection: keep-alive\r\n'
        b'Host: hi.ls:443\r\n\r\n') << http.HttpConnectHook(flow) >>
            ConnectionClosed(tctx.client) << CloseConnection(
                tctx.client) >> reply(to=-3))
예제 #11
0
def test_http_proxy_relative_request(tctx):
    """Test handling of a relative-form "GET /" in regular proxy mode."""
    server = Placeholder(Server)
    assert (Playbook(http.HttpLayer(
        tctx, HTTPMode.regular), hooks=False) >> DataReceived(
            tctx.client, b"GET / 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 204 No Content\r\n\r\n") <<
            SendData(tctx.client, b"HTTP/1.1 204 No Content\r\n\r\n"))
    assert server().address == ("example.com", 80)
예제 #12
0
def test_transparent_failure(tctx: Context, monkeypatch):
    """Test that we recover from a transparent mode resolve error."""
    def raise_err(sock):
        raise RuntimeError("platform-specific error")

    monkeypatch.setattr(platform, "original_addr", raise_err)
    assert (Playbook(modes.TransparentProxy(tctx), logs=True) << GetSocket(
        tctx.client
    ) >> reply(object()) << Log(
        "Transparent mode failure: RuntimeError('platform-specific error')",
        "info"))
예제 #13
0
def test_no_headers(tctx):
    """Test that we can correctly reassemble requests/responses with no headers."""
    server = Placeholder(Server)
    assert (
        Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) >>
        DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\n\r\n")
        << OpenConnection(server) >> reply(None) << SendData(
            server, b"GET / HTTP/1.1\r\n\r\n") >> DataReceived(
                server, b"HTTP/1.1 204 No Content\r\n\r\n") << SendData(
                    tctx.client, b"HTTP/1.1 204 No Content\r\n\r\n"))
    assert server().address == ("example.com", 80)
예제 #14
0
def test_reverse_eager_connect_failure(tctx: Context):
    """
    Test
        client --TCP-- mitmproxy --TCP over TLS-- server
    reverse proxying.
    """

    tctx.options.mode = "reverse:https://localhost:8000"
    tctx.options.connection_strategy = "eager"
    playbook = Playbook(modes.ReverseProxy(tctx))
    assert (playbook << OpenConnection(tctx.server) >> reply("IPoAC unstable")
            << CloseConnection(tctx.client) >> ConnectionClosed(tctx.client))
예제 #15
0
def start_h2_client(tctx: Context) -> Tuple[Playbook, FrameFactory]:
    tctx.client.alpn = b"h2"
    frame_factory = FrameFactory()

    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))
    assert (
        playbook << SendData(tctx.client,
                             Placeholder())  # initial settings frame
        >> DataReceived(tctx.client, frame_factory.preamble()) >> DataReceived(
            tctx.client,
            frame_factory.build_settings_frame({}, ack=True).serialize()))
    return playbook, frame_factory
예제 #16
0
def test_http_reply_from_proxy(tctx):
    """Test a response served by mitmproxy itself."""
    def reply_from_proxy(flow: HTTPFlow):
        flow.response = HTTPResponse.make(418)

    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")
            << http.HttpRequestHook(Placeholder()) >>
            reply(side_effect=reply_from_proxy) << SendData(
                tctx.client,
                b"HTTP/1.1 418 I'm a teapot\r\ncontent-length: 0\r\n\r\n"))
예제 #17
0
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))
예제 #18
0
def test_redirect(strategy, https_server, https_client, tctx, monkeypatch):
    """Test redirects between http:// and https:// in regular proxy mode."""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    tctx.options.connection_strategy = strategy
    p = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)

    if https_server:
        monkeypatch.setattr(tls, "ServerTLSLayer", tls.MockTLSLayer)

    def redirect(flow: HTTPFlow):
        if https_server:
            flow.request.url = "https://redirected.site/"
        else:
            flow.request.url = "http://redirected.site/"

    if https_client:
        p >> DataReceived(tctx.client,
                          b"CONNECT example.com:80 HTTP/1.1\r\n\r\n")
        if strategy == "eager":
            p << OpenConnection(Placeholder())
            p >> reply(None)
        p << SendData(tctx.client,
                      b'HTTP/1.1 200 Connection established\r\n\r\n')
        p >> DataReceived(tctx.client,
                          b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
        p << layer.NextLayerHook(Placeholder())
        p >> reply_next_layer(
            lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
    else:
        p >> DataReceived(
            tctx.client,
            b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
    p << http.HttpRequestHook(flow)
    p >> reply(side_effect=redirect)
    p << OpenConnection(server)
    p >> reply(None)
    p << SendData(server, b"GET / HTTP/1.1\r\nHost: redirected.site\r\n\r\n")
    p >> DataReceived(
        server, b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!")
    p << SendData(
        tctx.client,
        b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!")

    assert p
    if https_server:
        assert server().address == ("redirected.site", 443)
    else:
        assert server().address == ("redirected.site", 80)
예제 #19
0
def _h2_response(chunks):
    tctx = context.Context(
        context.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329),
        opts)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
    server = Placeholder(context.Server)
    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, side_effect=make_h2) << SendData(server, Placeholder()))
    for chunk in chunks:
        for _ in playbook.layer.handle_event(
                events.DataReceived(server(), chunk)):
            pass
예제 #20
0
def ws_testdata(tctx):
    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    flow = HTTPFlow(tctx.client, tctx.server)
    flow.request = Request.make("GET",
                                "http://example.com/",
                                headers={
                                    "Connection": "upgrade",
                                    "Upgrade": "websocket",
                                    "Sec-WebSocket-Version": "13",
                                })
    flow.response = Response.make(101,
                                  headers={
                                      "Connection": "upgrade",
                                      "Upgrade": "websocket",
                                  })
    return tctx, Playbook(websocket.WebsocketLayer(tctx, flow))
예제 #21
0
def test_connection_close_header(tctx, client_close, server_close):
    """Test that we correctly close connections if we have a `Connection: close` header."""
    if not client_close and not server_close:
        return
    server = Placeholder(Server)
    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular),
                     hooks=False) >> DataReceived(
                         tctx.client, b"GET http://example/ HTTP/1.1\r\n"
                         b"Host: example\r\n" + client_close + b"\r\n") <<
            OpenConnection(server) >> reply(None) << SendData(
                server, b"GET / HTTP/1.1\r\n"
                b"Host: example\r\n" + client_close + b"\r\n") >> DataReceived(
                    server, b"HTTP/1.1 200 OK\r\n"
                    b"Content-Length: 0\r\n" + server_close + b"\r\n") <<
            CloseConnection(server) << SendData(
                tctx.client, b"HTTP/1.1 200 OK\r\n"
                b"Content-Length: 0\r\n" + server_close + b"\r\n") <<
            CloseConnection(tctx.client))
예제 #22
0
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()
예제 #23
0
def h2_client(tctx: Context) -> Tuple[h2.connection.H2Connection, Playbook]:
    tctx.client.alpn = b"h2"

    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))
    conn = h2.connection.H2Connection()
    conn.initiate_connection()

    server_preamble = Placeholder(bytes)
    assert (playbook << SendData(tctx.client, server_preamble))
    assert event_types(conn.receive_data(
        server_preamble())) == [h2.events.RemoteSettingsChanged]

    settings_ack = Placeholder(bytes)
    assert (playbook >> DataReceived(tctx.client, conn.data_to_send()) <<
            SendData(tctx.client, settings_ack))
    assert event_types(conn.receive_data(
        settings_ack())) == [h2.events.SettingsAcknowledged]

    return conn, playbook
예제 #24
0
def test_http_proxy(tctx):
    """Test a simple HTTP GET / request"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> DataReceived(
        tctx.client,
        b"GET http://example.com/foo?hello=1 HTTP/1.1\r\nHost: example.com\r\n\r\n"
    ) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(
        flow) >> reply() << OpenConnection(server) >> reply(None) << SendData(
            server, b"GET /foo?hello=1 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\nHello World") <<
            http.HttpResponseHeadersHook(flow) >> reply() >> DataReceived(
                server, b"!") << http.HttpResponseHook(flow) >> reply() <<
            SendData(
                tctx.client,
                b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!"))
    assert server().address == ("example.com", 80)
예제 #25
0
def test_http_expect(tctx):
    """Test handling of a 'Expect: 100-continue' header."""
    server = Placeholder(Server)
    assert (
        Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) >>
        DataReceived(
            tctx.client, b"PUT http://example.com/large-file HTTP/1.1\r\n"
            b"Host: example.com\r\n"
            b"Content-Length: 15\r\n"
            b"Expect: 100-continue\r\n\r\n") << SendData(
                tctx.client, b"HTTP/1.1 100 Continue\r\n\r\n") >> DataReceived(
                    tctx.client, b"lots of content") << OpenConnection(server)
        >> reply(None) << SendData(
            server, b"PUT /large-file HTTP/1.1\r\n"
            b"Host: example.com\r\n"
            b"Content-Length: 15\r\n\r\n"
            b"lots of content") >> DataReceived(
                server, b"HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n")
        << SendData(tctx.client,
                    b"HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n"))
    assert server().address == ("example.com", 80)
예제 #26
0
def test_upgrade(tctx):
    """Test a HTTP -> WebSocket upgrade"""
    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    http_flow = Placeholder(HTTPFlow)
    flow = Placeholder(WebSocketFlow)
    assert (
        Playbook(http.HttpLayer(tctx, HTTPMode.transparent)) >> DataReceived(
            tctx.client, b"GET / HTTP/1.1\r\n"
            b"Connection: upgrade\r\n"
            b"Upgrade: websocket\r\n"
            b"Sec-WebSocket-Version: 13\r\n"
            b"\r\n") << http.HttpRequestHeadersHook(http_flow) >> reply() <<
        http.HttpRequestHook(http_flow) >> reply() << SendData(
            tctx.server, b"GET / HTTP/1.1\r\n"
            b"Connection: upgrade\r\n"
            b"Upgrade: websocket\r\n"
            b"Sec-WebSocket-Version: 13\r\n"
            b"\r\n") >> DataReceived(
                tctx.server, b"HTTP/1.1 101 Switching Protocols\r\n"
                b"Upgrade: websocket\r\n"
                b"Connection: Upgrade\r\n"
                b"\r\n") << http.HttpResponseHeadersHook(http_flow) >> reply()
        << http.HttpResponseHook(http_flow) >> reply() << SendData(
            tctx.client, b"HTTP/1.1 101 Switching Protocols\r\n"
            b"Upgrade: websocket\r\n"
            b"Connection: Upgrade\r\n"
            b"\r\n") << websocket.WebsocketStartHook(flow) >> reply() >>
        DataReceived(tctx.client, masked_bytes(b"\x81\x0bhello world")) <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.server, masked(b"\x81\x0bhello world")) >> DataReceived(
                tctx.server, b"\x82\nhello back") <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.client, b"\x82\nhello back"))
    assert flow().handshake_flow == http_flow()
    assert len(flow().messages) == 2
    assert flow().messages[0].content == "hello world"
    assert flow().messages[0].from_client
    assert flow().messages[1].content == b"hello back"
    assert flow().messages[1].from_client is False
예제 #27
0
def test_http_server_aborts(tctx, stream):
    """Test handling of the case where a server aborts during response transmission."""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))

    def enable_streaming(flow: HTTPFlow):
        flow.response.stream = True

    assert (playbook >> DataReceived(
        tctx.client, b"GET http://example.com/ HTTP/1.1\r\n"
        b"Host: example.com\r\n\r\n") << http.HttpRequestHeadersHook(flow) >>
            reply() << http.HttpRequestHook(flow) >> reply() <<
            OpenConnection(server) >> reply(None) << SendData(
                server, b"GET / HTTP/1.1\r\n"
                b"Host: example.com\r\n\r\n") >> DataReceived(
                    server, b"HTTP/1.1 200 OK\r\n"
                    b"Content-Length: 6\r\n"
                    b"\r\n"
                    b"abc") << http.HttpResponseHeadersHook(flow))
    if stream:
        assert (playbook >> reply(side_effect=enable_streaming) << SendData(
            tctx.client, b"HTTP/1.1 200 OK\r\n"
            b"Content-Length: 6\r\n"
            b"\r\n"
            b"abc"))
    else:
        assert playbook >> reply()
    assert (playbook >> ConnectionClosed(server) << CloseConnection(server) <<
            http.HttpErrorHook(flow))
    if stream:
        assert (playbook >> reply() << CloseConnection(tctx.client))
    else:
        error_html = Placeholder(bytes)
        assert (playbook >> reply() << SendData(tctx.client, error_html) <<
                CloseConnection(tctx.client))
        assert b"502 Bad Gateway" in error_html()
        assert b"peer closed connection" in error_html()

    assert "peer closed connection" in flow().error.msg
예제 #28
0
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))
예제 #29
0
def test_transparent_tcp(tctx: Context, monkeypatch, connection_strategy):
    monkeypatch.setattr(platform, "original_addr", lambda sock:
                        ("address", 22))

    flow = Placeholder(TCPFlow)
    tctx.options.connection_strategy = connection_strategy

    sock = object()
    playbook = Playbook(modes.TransparentProxy(tctx))
    (playbook << GetSocket(tctx.client) >> reply(sock))
    if connection_strategy == "lazy":
        assert playbook
    else:
        assert (playbook << OpenConnection(tctx.server) >> reply(None) >>
                DataReceived(tctx.server, b"hello") << NextLayerHook(
                    Placeholder(NextLayer)) >> reply_next_layer(tcp.TCPLayer)
                << TcpStartHook(flow) >> reply() << TcpMessageHook(flow) >>
                reply() << SendData(tctx.client, b"hello"))
        assert flow().messages[0].content == b"hello"
        assert not flow().messages[0].from_client

    assert tctx.server.address == ("address", 22)
예제 #30
0
def test_response_streaming(tctx):
    """Test HTTP response streaming"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)

    def enable_streaming(flow: HTTPFlow):
        flow.response.stream = lambda x: x.upper()

    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> DataReceived(
        tctx.client,
        b"GET http://example.com/largefile HTTP/1.1\r\nHost: example.com\r\n\r\n"
    ) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(
        flow) >> reply() << OpenConnection(server) >> reply(None) << SendData(
            server, b"GET /largefile HTTP/1.1\r\nHost: example.com\r\n\r\n")
            >> DataReceived(
                server, b"HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nabc") <<
            http.HttpResponseHeadersHook(flow) >>
            reply(side_effect=enable_streaming) << SendData(
                tctx.client,
                b"HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nABC") >>
            DataReceived(server, b"def") << SendData(
                tctx.client, b"DEF") << http.HttpResponseHook(flow) >> reply())