Exemple #1
0
def test_early_server_data(tctx):
    playbook, cff = start_h2_client(tctx)
    sff = FrameFactory()

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

    flow = Placeholder(HTTPFlow)
    server1 = Placeholder(bytes)
    server2 = Placeholder(bytes)
    assert (
            playbook
            >> DataReceived(tctx.client,
                            cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize())
            << http.HttpRequestHeadersHook(flow)
            >> reply()
            << (h := http.HttpRequestHook(flow))
            # Surprise! We get data from the server before the request hook finishes.
            >> DataReceived(tctx.server, sff.build_settings_frame({}).serialize())
            << SendData(tctx.server, server1)
            # Request hook finishes...
            >> reply(to=h)
            << SendData(tctx.server, server2)
    )
    assert [type(x) for x in decode_frames(server1())] == [
        hyperframe.frame.SettingsFrame,
        hyperframe.frame.SettingsFrame,
    ]
    assert [type(x) for x in decode_frames(server2())] == [
        hyperframe.frame.HeadersFrame,
    ]
Exemple #2
0
def test_simple(tctx):
    playbook, cff = start_h2_client(tctx)
    flow = Placeholder(HTTPFlow)
    server = Placeholder(Server)
    initial = Placeholder(bytes)
    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, side_effect=make_h2) << SendData(server, initial))
    frames = decode_frames(initial())
    assert [type(x) for x in frames] == [
        hyperframe.frame.SettingsFrame,
        hyperframe.frame.HeadersFrame,
    ]
    sff = FrameFactory()
    assert (
        playbook
        # a conforming h2 server would send settings first, we disregard this for now.
        >> DataReceived(
            server,
            sff.build_headers_frame(example_response_headers).serialize()) <<
        http.HttpResponseHeadersHook(flow) >> reply() >> DataReceived(
            server,
            sff.build_data_frame(b"Hello, World!", flags=["END_STREAM"
                                                          ]).serialize()) <<
        http.HttpResponseHook(flow) >> reply() << SendData(
            tctx.client,
            cff.build_headers_frame(example_response_headers).serialize() +
            cff.build_data_frame(b"Hello, World!").serialize() +
            cff.build_data_frame(b"", flags=["END_STREAM"]).serialize()))
    assert flow().request.url == "http://example.com/"
    assert flow().response.text == "Hello, World!"
Exemple #3
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
Exemple #4
0
def test_response_trailers(tctx: Context, open_h2_server_conn: Server, stream):
    playbook, cff = start_h2_client(tctx)
    tctx.server = open_h2_server_conn
    sff = FrameFactory()

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

    flow = Placeholder(HTTPFlow)
    (
        playbook
        >> DataReceived(tctx.client,
                        cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize())
        << http.HttpRequestHeadersHook(flow)
        >> reply()
        << http.HttpRequestHook(flow)
        >> reply()
        << SendData(tctx.server, Placeholder(bytes))
        # a conforming h2 server would send settings first, we disregard this for now.
        >> DataReceived(tctx.server, sff.build_headers_frame(example_response_headers).serialize() +
                        sff.build_data_frame(b"Hello, World!").serialize())
        << http.HttpResponseHeadersHook(flow)
        >> reply(side_effect=enable_streaming)
    )
    if stream:
        playbook << SendData(
            tctx.client,
            cff.build_headers_frame(example_response_headers).serialize() +
            cff.build_data_frame(b"Hello, World!").serialize()
        )
    assert (
        playbook
        >> DataReceived(tctx.server, sff.build_headers_frame(example_response_trailers, flags=["END_STREAM"]).serialize())
        << http.HttpResponseHook(flow)
    )
    assert flow().response.trailers
    del flow().response.trailers["resp-trailer-a"]
    if stream:
        assert (
            playbook
            >> reply()
            << SendData(tctx.client,
                        cff.build_headers_frame(example_response_trailers[1:], flags=["END_STREAM"]).serialize())
        )
    else:
        assert (
            playbook
            >> reply()
            << SendData(tctx.client,
                        cff.build_headers_frame(example_response_headers).serialize() +
                        cff.build_data_frame(b"Hello, World!").serialize() +
                        cff.build_headers_frame(example_response_trailers[1:], flags=["END_STREAM"]).serialize()))
Exemple #5
0
def test_max_concurrency(tctx):
    playbook, cff = start_h2_client(tctx)
    server = Placeholder(Server)
    req1_bytes = Placeholder(bytes)
    settings_ack_bytes = Placeholder(bytes)
    req2_bytes = Placeholder(bytes)
    playbook.hooks = False
    sff = FrameFactory()

    assert (playbook >> DataReceived(
        tctx.client,
        cff.build_headers_frame(example_request_headers,
                                flags=["END_STREAM"],
                                stream_id=1).serialize()) <<
            OpenConnection(server) >> reply(None, side_effect=make_h2) <<
            SendData(server, req1_bytes) >> DataReceived(
                server,
                sff.build_settings_frame({
                    h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS:
                    1
                }).serialize()) << SendData(
                    server, settings_ack_bytes) >> DataReceived(
                        tctx.client,
                        cff.build_headers_frame(example_request_headers,
                                                flags=["END_STREAM"],
                                                stream_id=3).serialize())
            # Can't send it upstream yet, all streams in use!
            >> DataReceived(
                server,
                sff.build_headers_frame(example_response_headers,
                                        flags=["END_STREAM"],
                                        stream_id=1).serialize())
            # But now we can!
            << SendData(server, req2_bytes) <<
            SendData(tctx.client, Placeholder(bytes)) >> DataReceived(
                server,
                sff.build_headers_frame(example_response_headers,
                                        flags=["END_STREAM"],
                                        stream_id=3).serialize()) << SendData(
                                            tctx.client, Placeholder(bytes)))
    settings, req1 = decode_frames(req1_bytes())
    settings_ack, = decode_frames(settings_ack_bytes())
    req2, = decode_frames(req2_bytes())

    assert type(settings) == hyperframe.frame.SettingsFrame
    assert type(req1) == hyperframe.frame.HeadersFrame
    assert type(settings_ack) == hyperframe.frame.SettingsFrame
    assert type(req2) == hyperframe.frame.HeadersFrame
    assert req1.stream_id == 1
    assert req2.stream_id == 3
Exemple #6
0
def test_no_normalization(tctx, normalize):
    """Test that we don't normalize headers when we just pass them through."""
    tctx.options.normalize_outbound_headers = normalize
    tctx.options.validate_inbound_headers = False

    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook, cff = start_h2_client(tctx)

    request_headers = list(example_request_headers) + [(b"Should-Not-Be-Capitalized! ", b" :) ")]
    request_headers_lower = [(k.lower(), v) for (k, v) in request_headers]
    response_headers = list(example_response_headers) + [(b"Same", b"Here")]
    response_headers_lower = [(k.lower(), v) for (k, v) in response_headers]

    initial = Placeholder(bytes)
    assert (
            playbook
            >> DataReceived(tctx.client,
                            cff.build_headers_frame(request_headers, flags=["END_STREAM"]).serialize())
            << http.HttpRequestHeadersHook(flow)
            >> reply()
            << http.HttpRequestHook(flow)
            >> reply()
            << OpenConnection(server)
            >> reply(None, side_effect=make_h2)
            << SendData(server, initial)
    )
    frames = decode_frames(initial())
    assert [type(x) for x in frames] == [
        hyperframe.frame.SettingsFrame,
        hyperframe.frame.HeadersFrame,
    ]
    assert hpack.hpack.Decoder().decode(frames[1].data, True) == request_headers_lower if normalize else request_headers

    sff = FrameFactory()
    (
            playbook
            >> DataReceived(server, sff.build_headers_frame(response_headers, flags=["END_STREAM"]).serialize())
            << http.HttpResponseHeadersHook(flow)
            >> reply()
            << http.HttpResponseHook(flow)
            >> reply()
    )
    if normalize:
        playbook << Log("Lowercased 'Same' header as uppercase is not allowed with HTTP/2.")
    hdrs = response_headers_lower if normalize else response_headers
    assert playbook << SendData(tctx.client, cff.build_headers_frame(hdrs, flags=["END_STREAM"]).serialize())

    assert flow().request.headers.fields == ((b"Should-Not-Be-Capitalized! ", b" :) "),)
    assert flow().response.headers.fields == ((b"Same", b"Here"),)
Exemple #7
0
def test_no_normalization(tctx):
    """Test that we don't normalize headers when we just pass them through."""

    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook, cff = start_h2_client(tctx)

    request_headers = example_request_headers + (
        (b"Should-Not-Be-Capitalized! ", b" :) "),
    )
    response_headers = example_response_headers + (
        (b"Same", b"Here"),
    )

    initial = Placeholder(bytes)
    assert (
            playbook
            >> DataReceived(tctx.client,
                            cff.build_headers_frame(request_headers, flags=["END_STREAM"]).serialize())
            << http.HttpRequestHeadersHook(flow)
            >> reply()
            << http.HttpRequestHook(flow)
            >> reply()
            << OpenConnection(server)
            >> reply(None, side_effect=make_h2)
            << SendData(server, initial)
    )
    frames = decode_frames(initial())
    assert [type(x) for x in frames] == [
        hyperframe.frame.SettingsFrame,
        hyperframe.frame.HeadersFrame,
    ]
    assert hpack.hpack.Decoder().decode(frames[1].data, True) == list(request_headers)

    sff = FrameFactory()
    assert (
            playbook
            >> DataReceived(server, sff.build_headers_frame(response_headers, flags=["END_STREAM"]).serialize())
            << http.HttpResponseHeadersHook(flow)
            >> reply()
            << http.HttpResponseHook(flow)
            >> reply()
            << SendData(tctx.client,
                        cff.build_headers_frame(response_headers).serialize() +
                        cff.build_data_frame(b"", flags=["END_STREAM"]).serialize())
    )
    assert flow().request.headers.fields == ((b"Should-Not-Be-Capitalized! ", b" :) "),)
    assert flow().response.headers.fields == ((b"Same", b"Here"),)
Exemple #8
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!
def h2_frames(draw):
    ff = FrameFactory()
    headers1 = ff.build_headers_frame(headers=draw(h2_headers()))
    headers1.flags.clear()
    headers1.flags |= draw(h2_flags)
    headers2 = ff.build_headers_frame(headers=draw(h2_headers()),
                                      depends_on=draw(h2_stream_ids),
                                      stream_weight=draw(integers(0, 255)),
                                      exclusive=draw(booleans()))
    headers2.flags.clear()
    headers2.flags |= draw(h2_flags)
    settings = ff.build_settings_frame(
        settings=draw(dictionaries(
            keys=sampled_from(SettingCodes),
            values=integers(0, 2 ** 32 - 1),
            max_size=5,
        )), ack=draw(booleans())
    )
    continuation = ff.build_continuation_frame(header_block=ff.encoder.encode(draw(h2_headers())), flags=draw(h2_flags))
    goaway = ff.build_goaway_frame(draw(h2_stream_ids))
    push_promise = ff.build_push_promise_frame(
        stream_id=draw(h2_stream_ids_nonzero),
        promised_stream_id=draw(h2_stream_ids),
        headers=draw(h2_headers())
    )
    rst = ff.build_rst_stream_frame(draw(h2_stream_ids_nonzero))
    prio = ff.build_priority_frame(
        stream_id=draw(h2_stream_ids_nonzero),
        weight=draw(integers(0, 255)),
        depends_on=draw(h2_stream_ids),
        exclusive=draw(booleans()),
    )
    data1 = ff.build_data_frame(
        draw(binary()), draw(h2_flags)
    )
    data2 = ff.build_data_frame(
        draw(binary()), draw(h2_flags), stream_id=draw(h2_stream_ids_nonzero)
    )
    window_update = ff.build_window_update_frame(draw(h2_stream_ids), draw(integers(0, 2 ** 32 - 1)))

    frames = draw(lists(sampled_from([
        headers1, headers2, settings, continuation, goaway, push_promise, rst, prio, data1, data2, window_update
    ]), min_size=1, max_size=11))
    return b"".join(x.serialize() for x in frames)
Exemple #10
0
import h2.config
import h2.connection
import h2.events

from mitmproxy.http import HTTPFlow
from mitmproxy.proxy.context import Context
from mitmproxy.proxy.layers.http import HTTPMode
from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData
from mitmproxy.connection import Server
from mitmproxy.proxy.events import DataReceived
from mitmproxy.proxy.layers import http
from test.mitmproxy.proxy.layers.http.hyper_h2_test_helpers import FrameFactory
from test.mitmproxy.proxy.layers.http.test_http2 import example_request_headers, example_response_headers, make_h2
from test.mitmproxy.proxy.tutils import Placeholder, Playbook, reply

h2f = FrameFactory()


def event_types(events):
    return [type(x) for x in events]


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))