Пример #1
0
def test_upgrade_streamed(tctx):
    """If the HTTP response is streamed, we may get early data from the client."""
    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    flow = Placeholder(HTTPFlow)

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

    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(flow)
            >> reply()
            << http.HttpRequestHook(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(flow)
            >> reply(side_effect=enable_streaming)
            << 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")
            << http.HttpResponseHook(flow)
            >> DataReceived(tctx.client, masked_bytes(b"\x81\x0bhello world"))  # early !!
            >> reply(to=-2)
            << websocket.WebsocketStartHook(flow)
            >> reply()
            << 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")
            >> DataReceived(tctx.client, masked_bytes(b"\x81\x0bhello again"))
            << websocket.WebsocketMessageHook(flow)
            >> reply()
            << SendData(tctx.server, masked(b"\x81\x0bhello again"))
    )
Пример #2
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
Пример #3
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))
Пример #4
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)
Пример #5
0
def test_upgrade(tctx):
    """Test a HTTP -> WebSocket upgrade"""
    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    flow = Placeholder(HTTPFlow)
    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(flow)
            >> reply()
            << http.HttpRequestHook(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(flow)
            >> reply()
            << http.HttpResponseHook(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 len(flow().websocket.messages) == 2
    assert flow().websocket.messages[0].content == b"hello world"
    assert flow().websocket.messages[0].from_client
    assert flow().websocket.messages[0].type == Opcode.TEXT
    assert flow().websocket.messages[1].content == b"hello back"
    assert flow().websocket.messages[1].from_client is False
    assert flow().websocket.messages[1].type == Opcode.BINARY
Пример #6
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)
Пример #7
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
Пример #8
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))
Пример #9
0
def h2_layer(opts):
    tctx = context.Context(
        connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329),
        opts)
    tctx.client.alpn = b"h2"

    layer = http.HttpLayer(tctx, HTTPMode.regular)
    for _ in layer.handle_event(Start()):
        pass
    for _ in layer.handle_event(
            DataReceived(tctx.client, b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n')):
        pass
    return tctx, layer
Пример #10
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)
Пример #11
0
def test_http_reply_from_proxy(tctx):
    """Test a response served by mitmproxy itself."""

    def reply_from_proxy(flow: HTTPFlow):
        flow.response = Response.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")
    )
Пример #12
0
def test_upgrade(tctx, proto):
    """Test a HTTP -> WebSocket upgrade with different protocols enabled"""
    if proto != "websocket":
        tctx.options.websocket = False
    if proto != "tcp":
        tctx.options.rawtcp = False

    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    http_flow = Placeholder(HTTPFlow)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.transparent))
    (
            playbook
            >> 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")
    )
    if proto == "websocket":
        assert playbook << WebsocketStartHook(Placeholder(WebSocketFlow))
    elif proto == "tcp":
        assert playbook << TcpStartHook(Placeholder(TCPFlow))
    else:
        assert (
            playbook
            << Log("Sent HTTP 101 response, but no protocol is enabled to upgrade to.", "warn")
            << CloseConnection(tctx.client)
        )
Пример #13
0
def test_body_size_limit(tctx, where, transfer_encoding):
    """Test HTTP request body_size_limit"""
    tctx.options.body_size_limit = "3"
    err = Placeholder(bytes)
    flow = Placeholder(HTTPFlow)

    if transfer_encoding == "identity":
        body = b"Content-Length: 6\r\n\r\nabcdef"
    else:
        body = b"Transfer-Encoding: chunked\r\n\r\n6\r\nabcdef"

    if where == "request":
        assert (
            Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> DataReceived(
                tctx.client, b"POST http://example.com/ HTTP/1.1\r\n"
                b"Host: example.com\r\n" + body) <<
            http.HttpRequestHeadersHook(flow) >> reply()
            << http.HttpErrorHook(flow) >> reply() << SendData(
                tctx.client, err) << CloseConnection(tctx.client))
        assert b"413 Payload Too Large" in err()
        assert b"body_size_limit" in err()
    else:
        server = Placeholder(Server)
        assert (
            Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> 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" + body) <<
            http.HttpResponseHeadersHook(flow) >> reply() <<
            http.HttpErrorHook(flow) >> reply() << SendData(tctx.client, err)
            << CloseConnection(tctx.client) << CloseConnection(server))
        assert b"502 Bad Gateway" in err()
        assert b"body_size_limit" in err()
Пример #14
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)
    )
Пример #15
0
def _h2_response(chunks):
    tctx = context.Context(connection.Client(("client", 1234), ("127.0.0.1", 8080), 1605699329), opts)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
    server = Placeholder(connection.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
Пример #16
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
Пример #17
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
Пример #18
0
def test_transparent_sni(tctx):
    """Test that we keep the SNI in lazy transparent mode."""
    tctx.client.sni = "example.com"
    tctx.server.address = ("192.0.2.42", 443)
    tctx.server.tls = True

    flow = Placeholder(HTTPFlow)

    server = Placeholder(Server)
    assert (Playbook(http.HttpLayer(tctx, HTTPMode.transparent)) >>
            DataReceived(tctx.client, b"GET / HTTP/1.1\r\n\r\n") <<
            http.HttpRequestHeadersHook(flow) >> reply() <<
            http.HttpRequestHook(flow) >> reply() << OpenConnection(server))
    assert server().address == ("192.0.2.42", 443)
    assert server().sni == "example.com"
Пример #19
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()
Пример #20
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))
Пример #21
0
def test_request_stream_modify(tctx):
    """Test HTTP response streaming"""
    server = Placeholder(Server)

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

    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> 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\r\n"
        b"abc") << http.HttpRequestHeadersHook(
            Placeholder(HTTPFlow)) >> 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\r\n"
                b"ABC"))
Пример #22
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
Пример #23
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)
Пример #24
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)
Пример #25
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
Пример #26
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())
Пример #27
0
def test_dont_reuse_closed(tctx):
    """Test that a closed connection is not reused."""
    server = Placeholder(Server)
    server2 = 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\nContent-Length: 0\r\n\r\n") <<
        SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
        >> ConnectionClosed(server) << CloseConnection(server) >> DataReceived(
            tctx.client,
            b"GET http://example.com/two HTTP/1.1\r\nHost: example.com\r\n\r\n"
        ) << OpenConnection(server2) >> reply(None) << SendData(
            server2,
            b"GET /two 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"))
Пример #28
0
def test_reverse_proxy(tctx, keep_host_header):
    """Test mitmproxy in reverse proxy mode.

     - make sure that we connect to the right host
     - make sure that we respect keep_host_header
     - make sure that we include non-standard ports in the host header (#4280)
    """
    server = Placeholder(Server)
    tctx.options.mode = "reverse:http://localhost:8000"
    tctx.options.keep_host_header = keep_host_header
    assert (
        Playbook(modes.ReverseProxy(tctx), hooks=False) >> DataReceived(
            tctx.client, b"GET /foo HTTP/1.1\r\n"
            b"Host: example.com\r\n\r\n") << NextLayerHook(
                Placeholder(NextLayer)) >>
        reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
        << OpenConnection(server) >> reply(None) << SendData(
            server, b"GET /foo HTTP/1.1\r\n"
            b"Host: " +
            (b"example.com" if keep_host_header else b"localhost:8000") +
            b"\r\n\r\n") >> DataReceived(
                server, 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 server().address == ("localhost", 8000)
Пример #29
0
def test_multiple_server_connections(tctx):
    """Test multiple requests being rewritten to different targets."""
    server1 = Placeholder(Server)
    server2 = Placeholder(Server)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)

    def redirect(to: str):
        def side_effect(flow: HTTPFlow):
            flow.request.url = to

        return side_effect

    assert (
            playbook
            >> 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=redirect("http://one.redirect/"))
            << OpenConnection(server1)
            >> reply(None)
            << SendData(server1, b"GET / HTTP/1.1\r\nHost: one.redirect\r\n\r\n")
            >> DataReceived(server1, 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 (
            playbook
            >> 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=redirect("http://two.redirect/"))
            << OpenConnection(server2)
            >> reply(None)
            << SendData(server2, b"GET / HTTP/1.1\r\nHost: two.redirect\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().address == ("one.redirect", 80)
    assert server2().address == ("two.redirect", 80)
Пример #30
0
def test_upstream_https(tctx):
    """
    Test mitmproxy in HTTPS upstream mode with another mitmproxy instance upstream.
    In other words:

    mitmdump --mode upstream:https://localhost:8081 --ssl-insecure
    mitmdump -p 8081
    curl -x localhost:8080 -k http://example.com
    """
    tctx1 = Context(Client(("client", 1234), ("127.0.0.1", 8080), 1605699329),
                    copy.deepcopy(tctx.options))
    tctx1.options.mode = "upstream:https://example.mitmproxy.org:8081"
    tctx2 = Context(Client(("client", 4321), ("127.0.0.1", 8080), 1605699329),
                    copy.deepcopy(tctx.options))
    assert tctx2.options.mode == "regular"
    del tctx

    proxy1 = Playbook(modes.HttpProxy(tctx1), hooks=False)
    proxy2 = Playbook(modes.HttpProxy(tctx2), hooks=False)

    upstream = Placeholder(Server)
    server = Placeholder(Server)
    clienthello = Placeholder(bytes)
    serverhello = Placeholder(bytes)
    request = Placeholder(bytes)
    tls_finished = Placeholder(bytes)
    h2_client_settings_ack = Placeholder(bytes)
    response = Placeholder(bytes)
    h2_server_settings_ack = Placeholder(bytes)

    assert (
        proxy1 >> DataReceived(
            tctx1.client,
            b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
        << NextLayerHook(Placeholder(NextLayer)) >>
        reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.upstream)) <<
        OpenConnection(upstream) >> reply(None) << TlsStartHook(Placeholder())
        >> reply_tls_start(alpn=b"h2") << SendData(upstream, clienthello))
    assert upstream().address == ("example.mitmproxy.org", 8081)
    assert (proxy2 >> DataReceived(tctx2.client, clienthello()) <<
            NextLayerHook(Placeholder(NextLayer)) >>
            reply_next_layer(ClientTLSLayer) << TlsStartHook(Placeholder()) >>
            reply_tls_start(alpn=b"h2") << SendData(tctx2.client, serverhello))
    assert (proxy1 >> DataReceived(upstream, serverhello()) << SendData(
        upstream, request))
    assert (proxy2 >> DataReceived(tctx2.client, request()) << SendData(
        tctx2.client, tls_finished) << NextLayerHook(Placeholder(NextLayer)) >>
            reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.regular))
            << SendData(tctx2.client, h2_client_settings_ack) <<
            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: 0\r\n\r\n") <<
            CloseConnection(server) << SendData(tctx2.client, response))
    assert server().address == ("example.com", 80)

    assert (proxy1 >> DataReceived(
        upstream,
        tls_finished() + h2_client_settings_ack() + response()) << SendData(
            upstream, h2_server_settings_ack) << SendData(
                tctx1.client, b"HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n"))