def test_read_raw_frame_failed():
    raw = codecs.decode('485454000000000000', 'hex_codec')
    bio = BytesIO(raw)
    bio.safe_read = bio.read

    with pytest.raises(exceptions.HttpException):
        read_raw_frame(bio)
Example #2
0
    def test_connection_terminated(self):
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile, h2_conn, headers=[
            (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
            (':method', 'GET'),
            (':scheme', 'https'),
            (':path', '/'),
        ])

        done = False
        connection_terminated_event = None
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
                for event in events:
                    if isinstance(event, h2.events.ConnectionTerminated):
                        connection_terminated_event = event
                        done = True
            except:
                break

        assert len(self.master.state.flows) == 1
        assert connection_terminated_event is not None
        assert connection_terminated_event.error_code == 5
        assert connection_terminated_event.last_stream_id == 42
        assert connection_terminated_event.additional_data == b'foobar'
Example #3
0
    def test_response_streaming(self, streaming):
        class Stream:
            def responseheaders(self, f):
                f.response.stream = streaming

        self.master.addons.add(Stream())
        h2_conn = self.setup_connection()
        self._send_request(self.client.wfile,
                           h2_conn,
                           headers=[
                               (':authority', "127.0.0.1:{}".format(
                                   self.server.server.address[1])),
                               (':method', 'GET'),
                               (':scheme', 'https'),
                               (':path', '/'),
                           ])
        done = False
        self.client.rfile.o.settimeout(2)
        data = None
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)

                for event in events:
                    if isinstance(event, h2.events.DataReceived):
                        data = event.data
                        done = True
            except:
                break

        if streaming:
            assert data
        else:
            assert data is None
Example #4
0
    def test_connection_lost(self):
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile,
                           h2_conn,
                           stream_id=1,
                           headers=[(':authority', "127.0.0.1:{}".format(
                               self.server.server.address[1])),
                                    (':method', 'GET'), (':scheme', 'https'),
                                    (':path', '/'), ('foo', 'bar')])

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False
            except:
                break
            try:
                self.client.wfile.write(h2_conn.data_to_send())
                self.client.wfile.flush()
            except:
                break

        if len(self.master.state.flows) == 1:
            assert self.master.state.flows[0].response is None
Example #5
0
    def test_connection_terminated(self):
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile,
                           h2_conn,
                           headers=[
                               (':authority', "127.0.0.1:{}".format(
                                   self.server.server.address[1])),
                               (':method', 'GET'),
                               (':scheme', 'https'),
                               (':path', '/'),
                           ])

        done = False
        connection_terminated_event = None
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
                for event in events:
                    if isinstance(event, h2.events.ConnectionTerminated):
                        connection_terminated_event = event
                        done = True
            except:
                break

        assert len(self.master.state.flows) == 1
        assert connection_terminated_event is not None
        assert connection_terminated_event.error_code == 5
        assert connection_terminated_event.last_stream_id == 42
        assert connection_terminated_event.additional_data == b'foobar'
Example #6
0
    def test_connection_lost(self):
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile, h2_conn, stream_id=1, headers=[
            (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
            (':method', 'GET'),
            (':scheme', 'https'),
            (':path', '/'),
            ('foo', 'bar')
        ])

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False
            except:
                break
            try:
                self.client.wfile.write(h2_conn.data_to_send())
                self.client.wfile.flush()
            except:
                break

        if len(self.master.state.flows) == 1:
            assert self.master.state.flows[0].response is None
Example #7
0
    def test_priority(self, prioritize_before, http2_priority_enabled,
                      priority, expected_priority):
        self.options.http2_priority = http2_priority_enabled
        self.__class__.priority_data = []

        h2_conn = self.setup_connection()

        if prioritize_before:
            h2_conn.prioritize(1,
                               exclusive=priority[0],
                               depends_on=priority[1],
                               weight=priority[2])
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority',
                 "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            end_stream=prioritize_before,
        )

        if not prioritize_before:
            h2_conn.prioritize(1,
                               exclusive=priority[0],
                               depends_on=priority[1],
                               weight=priority[2])
            h2_conn.end_stream(1)
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.priority_data == expected_priority
def test_read_raw_frame():
    raw = codecs.decode('000006000101234567666f6f626172', 'hex_codec')
    bio = BytesIO(raw)
    bio.safe_read = bio.read

    header, body = read_raw_frame(bio)
    assert header
    assert body
Example #9
0
    def test_push_promise(self):
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile,
                           h2_conn,
                           stream_id=1,
                           headers=[(':authority', "127.0.0.1:{}".format(
                               self.server.server.address[1])),
                                    (':method', 'GET'), (':scheme', 'https'),
                                    (':path', '/'), ('foo', 'bar')])

        done = False
        ended_streams = 0
        pushed_streams = 0
        responses = 0
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False
            except:
                break
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    ended_streams += 1
                elif isinstance(event, h2.events.PushedStreamReceived):
                    pushed_streams += 1
                elif isinstance(event, h2.events.ResponseReceived):
                    responses += 1
                if isinstance(event, h2.events.ConnectionTerminated):
                    done = True

            if responses == 3 and ended_streams == 3 and pushed_streams == 2:
                done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert ended_streams == 3
        assert pushed_streams == 2

        bodies = [flow.response.content for flow in self.master.state.flows]
        assert len(bodies) == 3
        assert b'regular_stream' in bodies
        assert b'pushed_stream_foo' in bodies
        assert b'pushed_stream_bar' in bodies

        pushed_flows = [
            flow for flow in self.master.state.flows
            if 'h2-pushed-stream' in flow.metadata
        ]
        assert len(pushed_flows) == 2
Example #10
0
    def test_push_promise(self):
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile, h2_conn, stream_id=1, headers=[
            (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
            (':method', 'GET'),
            (':scheme', 'https'),
            (':path', '/'),
            ('foo', 'bar')
        ])

        done = False
        ended_streams = 0
        pushed_streams = 0
        responses = 0
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False
            except:
                break
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    ended_streams += 1
                elif isinstance(event, h2.events.PushedStreamReceived):
                    pushed_streams += 1
                elif isinstance(event, h2.events.ResponseReceived):
                    responses += 1
                if isinstance(event, h2.events.ConnectionTerminated):
                    done = True

            if responses == 3 and ended_streams == 3 and pushed_streams == 2:
                done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert ended_streams == 3
        assert pushed_streams == 2

        bodies = [flow.response.content for flow in self.master.state.flows]
        assert len(bodies) == 3
        assert b'regular_stream' in bodies
        assert b'pushed_stream_foo' in bodies
        assert b'pushed_stream_bar' in bodies

        pushed_flows = [flow for flow in self.master.state.flows if 'h2-pushed-stream' in flow.metadata]
        assert len(pushed_flows) == 2
Example #11
0
    def test_push_promise_reset(self):
        client, h2_conn = self._setup_connection()

        self._send_request(client.wfile,
                           h2_conn,
                           stream_id=1,
                           headers=[(':authority', "127.0.0.1:{}".format(
                               self.server.server.address.port)),
                                    (':method', 'GET'), (':scheme', 'https'),
                                    (':path', '/'), ('foo', 'bar')])

        done = False
        ended_streams = 0
        pushed_streams = 0
        responses = 0
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            client.wfile.write(h2_conn.data_to_send())
            client.wfile.flush()

            for event in events:
                if isinstance(event,
                              h2.events.StreamEnded) and event.stream_id == 1:
                    ended_streams += 1
                elif isinstance(event, h2.events.PushedStreamReceived):
                    pushed_streams += 1
                    h2_conn.reset_stream(event.pushed_stream_id,
                                         error_code=0x8)
                    client.wfile.write(h2_conn.data_to_send())
                    client.wfile.flush()
                elif isinstance(event, h2.events.ResponseReceived):
                    responses += 1
                if isinstance(event, h2.events.ConnectionTerminated):
                    done = True

            if responses >= 1 and ended_streams >= 1 and pushed_streams == 2:
                done = True

        h2_conn.close_connection()
        client.wfile.write(h2_conn.data_to_send())
        client.wfile.flush()

        bodies = [
            flow.response.content for flow in self.master.state.flows
            if flow.response
        ]
        assert len(bodies) >= 1
        assert b'regular_stream' in bodies
        def handle(self):
            # send magic
            self.wfile.write(codecs.decode('505249202a20485454502f322e300d0a0d0a534d0d0a0d0a', 'hex_codec'))
            self.wfile.flush()

            # send empty settings frame
            self.wfile.write(codecs.decode('000000040000000000', 'hex_codec'))
            self.wfile.flush()

            # check empty settings frame
            raw = http2.read_raw_frame(self.rfile)
            assert raw == codecs.decode('00000c040000000000000200000000000300000001', 'hex_codec')

            # check settings acknowledgement
            raw = http2.read_raw_frame(self.rfile)
            assert raw == codecs.decode('000000040100000000', 'hex_codec')

            # send settings acknowledgement
            self.wfile.write(codecs.decode('000000040100000000', 'hex_codec'))
            self.wfile.flush()
        def handle(self):
            # send magic
            self.wfile.write(codecs.decode('505249202a20485454502f322e300d0a0d0a534d0d0a0d0a', 'hex_codec'))
            self.wfile.flush()

            # send empty settings frame
            self.wfile.write(codecs.decode('000000040000000000', 'hex_codec'))
            self.wfile.flush()

            # check empty settings frame
            raw = http2.read_raw_frame(self.rfile)
            assert raw == codecs.decode('00000c040000000000000200000000000300000001', 'hex_codec')

            # check settings acknowledgement
            raw = http2.read_raw_frame(self.rfile)
            assert raw == codecs.decode('000000040100000000', 'hex_codec')

            # send settings acknowledgement
            self.wfile.write(codecs.decode('000000040100000000', 'hex_codec'))
            self.wfile.flush()
Example #14
0
    def test_push_promise_reset(self):
        client, h2_conn = self._setup_connection()

        self._send_request(client.wfile, h2_conn, stream_id=1, headers=[
            (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
            (':method', 'GET'),
            (':scheme', 'https'),
            (':path', '/'),
            ('foo', 'bar')
        ])

        done = False
        ended_streams = 0
        pushed_streams = 0
        responses = 0
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            client.wfile.write(h2_conn.data_to_send())
            client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded) and event.stream_id == 1:
                    ended_streams += 1
                elif isinstance(event, h2.events.PushedStreamReceived):
                    pushed_streams += 1
                    h2_conn.reset_stream(event.pushed_stream_id, error_code=0x8)
                    client.wfile.write(h2_conn.data_to_send())
                    client.wfile.flush()
                elif isinstance(event, h2.events.ResponseReceived):
                    responses += 1
                if isinstance(event, h2.events.ConnectionTerminated):
                    done = True

            if responses >= 1 and ended_streams >= 1 and pushed_streams == 2:
                done = True

        h2_conn.close_connection()
        client.wfile.write(h2_conn.data_to_send())
        client.wfile.flush()

        bodies = [flow.response.content for flow in self.master.state.flows if flow.response]
        assert len(bodies) >= 1
        assert b'regular_stream' in bodies
        # the other two bodies might not be transmitted before the reset

        pushed_flows = [flow for flow in self.master.state.flows if 'h2-pushed-stream' in flow.metadata]
        assert len(pushed_flows) == 2
Example #15
0
    def test_trailers(self, announce, body):
        h2_conn = self.setup_connection()
        stream_id = 1
        headers = [
            (':authority',
             "127.0.0.1:{}".format(self.server.server.address[1])),
            (':method', 'GET'),
            (':scheme', 'https'),
            (':path', '/'),
        ]
        if announce:
            headers.append(('trailer', 'x-my-trailers'))
        h2_conn.send_headers(
            stream_id=stream_id,
            headers=headers,
        )
        if body:
            h2_conn.send_data(stream_id, body)

        # send trailers
        h2_conn.send_headers(stream_id, [('x-my-trailers', 'foobar')],
                             end_stream=True)

        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].request.trailers[
            'x-my-trailers'] == 'foobar'
        assert self.master.state.flows[0].response.status_code == 200
        assert self.master.state.flows[0].response.headers[
            'x-my-trailer-request-received'] == 'success'
Example #16
0
    def test_priority(self, prioritize_before, http2_priority_enabled, priority, expected_priority):
        self.options.http2_priority = http2_priority_enabled
        self.__class__.priority_data = []

        h2_conn = self.setup_connection()

        if prioritize_before:
            h2_conn.prioritize(1, exclusive=priority[0], depends_on=priority[1], weight=priority[2])
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            end_stream=prioritize_before,
        )

        if not prioritize_before:
            h2_conn.prioritize(1, exclusive=priority[0], depends_on=priority[1], weight=priority[2])
            h2_conn.end_stream(1)
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.priority_data == expected_priority
Example #17
0
    def test_request_with_priority(self, http2_priority_enabled, priority,
                                   expected_priority):
        self.options.http2_priority = http2_priority_enabled

        h2_conn = self.setup_connection()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority',
                 "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            priority_exclusive=priority[0],
            priority_depends_on=priority[1],
            priority_weight=priority[2],
        )

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1

        resp = self.master.state.flows[0].response
        assert resp.headers.get('priority_exclusive',
                                None) == expected_priority[0]
        assert resp.headers.get('priority_depends_on',
                                None) == expected_priority[1]
        assert resp.headers.get('priority_weight',
                                None) == expected_priority[2]
Example #18
0
        def handle(self):
            # send magic
            self.wfile.write(
                bytes.fromhex(
                    "505249202a20485454502f322e300d0a0d0a534d0d0a0d0a"))
            self.wfile.flush()

            # send empty settings frame
            self.wfile.write(bytes.fromhex("000000040000000000"))
            self.wfile.flush()

            # check empty settings frame
            raw = http2.read_raw_frame(self.rfile)
            assert raw == bytes.fromhex(
                "00000c040000000000000200000000000300000001")

            # check settings acknowledgement
            raw = http2.read_raw_frame(self.rfile)
            assert raw == bytes.fromhex("000000040100000000")

            # send settings acknowledgement
            self.wfile.write(bytes.fromhex("000000040100000000"))
            self.wfile.flush()
Example #19
0
    def read_frame(self, hide=False):
        while True:
            frm = http2.parse_frame(*http2.read_raw_frame(self.tcp_handler.rfile))
            if not hide and self.dump_frames:  # pragma no cover
                print("<< " + repr(frm))

            if isinstance(frm, hyperframe.frame.PingFrame):
                raw_bytes = hyperframe.frame.PingFrame(flags=['ACK'], payload=frm.payload).serialize()
                self.tcp_handler.wfile.write(raw_bytes)
                self.tcp_handler.wfile.flush()
                continue
            if isinstance(frm, hyperframe.frame.SettingsFrame) and 'ACK' not in frm.flags:
                self._apply_settings(frm.settings, hide)
            if isinstance(frm, hyperframe.frame.DataFrame) and frm.flow_controlled_length > 0:
                self._update_flow_control_window(frm.stream_id, frm.flow_controlled_length)
            return frm
Example #20
0
    def read_frame(self, hide=False):
        while True:
            frm = http2.parse_frame(*http2.read_raw_frame(self.tcp_handler.rfile))
            if not hide and self.dump_frames:  # pragma no cover
                print("<< " + repr(frm))

            if isinstance(frm, hyperframe.frame.PingFrame):
                raw_bytes = hyperframe.frame.PingFrame(flags=['ACK'], payload=frm.payload).serialize()
                self.tcp_handler.wfile.write(raw_bytes)
                self.tcp_handler.wfile.flush()
                continue
            if isinstance(frm, hyperframe.frame.SettingsFrame) and 'ACK' not in frm.flags:
                self._apply_settings(frm.settings, hide)
            if isinstance(frm, hyperframe.frame.DataFrame) and frm.flow_controlled_length > 0:
                self._update_flow_control_window(frm.stream_id, frm.flow_controlled_length)
            return frm
Example #21
0
    def __call__(self):
        self._initiate_server_conn()
        self._complete_handshake()

        client = self.client_conn.connection
        server = self.server_conn.connection
        conns = [client, server]

        try:
            while True:
                r = tcp.ssl_read_select(conns, 1)
                for conn in r:
                    source_conn = self.client_conn if conn == client else self.server_conn
                    other_conn = self.server_conn if conn == client else self.client_conn
                    is_server = (conn == self.server_conn.connection)

                    with source_conn.h2.lock:
                        try:
                            raw_frame = b''.join(
                                http2.read_raw_frame(source_conn.rfile))
                        except:
                            # read frame failed: connection closed
                            self._kill_all_streams()
                            return

                        if source_conn.h2.state_machine.state == h2.connection.ConnectionState.CLOSED:
                            self.log(
                                "HTTP/2 connection entered closed state already",
                                "debug")
                            return

                        incoming_events = source_conn.h2.receive_data(
                            raw_frame)
                        source_conn.send(source_conn.h2.data_to_send())

                        for event in incoming_events:
                            if not self._handle_event(event, source_conn,
                                                      other_conn, is_server):
                                # connection terminated: GoAway
                                self._kill_all_streams()
                                return

                    self._cleanup_streams()
        except Exception as e:  # pragma: no cover
            self.log(repr(e), "info")
            self.log(traceback.format_exc(), "debug")
            self._kill_all_streams()
Example #22
0
    def test_simple(self):
        response_body_buffer = b''
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile,
                           h2_conn,
                           headers=[
                               (':authority', "127.0.0.1:{}".format(
                                   self.server.server.address[1])),
                               (':method', 'GET'),
                               (':scheme', 'https'),
                               (':path', '/'),
                               ('self.client-FoO', 'self.client-bar-1'),
                               ('self.client-FoO', 'self.client-bar-2'),
                           ],
                           body=b'request body')

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.DataReceived):
                    response_body_buffer += event.data
                elif isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].response.status_code == 200
        assert self.master.state.flows[0].response.headers[
            'server-foo'] == 'server-bar'
        assert self.master.state.flows[0].response.headers['föo'] == 'bär'
        assert self.master.state.flows[0].response.content == b'response body'
        assert self.request_body_buffer == b'request body'
        assert response_body_buffer == b'response body'
Example #23
0
    def test_trailers(self, announce):
        response_body_buffer = b''
        h2_conn = self.setup_connection()

        self._send_request(self.client.wfile,
                           h2_conn,
                           stream_id=(1 if announce else 3),
                           headers=[
                               (':authority', "127.0.0.1:{}".format(
                                   self.server.server.address[1])),
                               (':method', 'GET'),
                               (':scheme', 'https'),
                               (':path', '/'),
                           ])

        trailers_buffer = None
        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.DataReceived):
                    response_body_buffer += event.data
                elif isinstance(event, h2.events.TrailersReceived):
                    trailers_buffer = event.headers
                elif isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].response.status_code == 200
        assert self.master.state.flows[0].response.content == b'response body'
        assert response_body_buffer == b'response body'
        assert self.master.state.flows[0].response.trailers[
            'x-my-trailers'] == 'foobar'
        assert trailers_buffer == [(b'x-my-trailers', b'foobar')]
Example #24
0
    def test_simple(self):
        response_body_buffer = b''
        h2_conn = self.setup_connection()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
                ('self.client-FoO', 'self.client-bar-1'),
                ('self.client-FoO', 'self.client-bar-2'),
            ],
            body=b'request body')

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.DataReceived):
                    response_body_buffer += event.data
                elif isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].response.status_code == 200
        assert self.master.state.flows[0].response.headers['server-foo'] == 'server-bar'
        assert self.master.state.flows[0].response.headers['föo'] == 'bär'
        assert self.master.state.flows[0].response.content == b'response body'
        assert self.request_body_buffer == b'request body'
        assert response_body_buffer == b'response body'
Example #25
0
        def handle(self):
            config = h2.config.H2Configuration(client_side=False,
                                               validate_outbound_headers=False,
                                               validate_inbound_headers=False)
            h2_conn = h2.connection.H2Connection(config)

            preamble = self.rfile.read(24)
            h2_conn.initiate_connection()
            h2_conn.receive_data(preamble)
            self.wfile.write(h2_conn.data_to_send())
            self.wfile.flush()

            if 'h2_server_settings' in self.kwargs:
                h2_conn.update_settings(self.kwargs['h2_server_settings'])
                self.wfile.write(h2_conn.data_to_send())
                self.wfile.flush()

            done = False
            while not done:
                try:
                    raw = b''.join(http2.read_raw_frame(self.rfile))
                    events = h2_conn.receive_data(raw)
                except exceptions.HttpException:
                    print(traceback.format_exc())
                    assert False
                except exceptions.TcpDisconnect:
                    break
                except:
                    print(traceback.format_exc())
                    break
                self.wfile.write(h2_conn.data_to_send())
                self.wfile.flush()

                for event in events:
                    try:
                        if not self.server.handle_server_event(
                                event, h2_conn, self.rfile, self.wfile):
                            done = True
                            break
                    except exceptions.TcpDisconnect:
                        done = True
                    except:
                        done = True
                        print(traceback.format_exc())
                        break
Example #26
0
    def test_request_with_priority(self, http2_priority_enabled, priority, expected_priority):
        self.options.http2_priority = http2_priority_enabled

        h2_conn = self.setup_connection()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            priority_exclusive=priority[0],
            priority_depends_on=priority[1],
            priority_weight=priority[2],
        )

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1

        resp = self.master.state.flows[0].response
        assert resp.headers.get('priority_exclusive', None) == expected_priority[0]
        assert resp.headers.get('priority_depends_on', None) == expected_priority[1]
        assert resp.headers.get('priority_weight', None) == expected_priority[2]
Example #27
0
    def test_request_with_priority(self):
        client, h2_conn = self._setup_connection()

        self._send_request(
            client.wfile,
            h2_conn,
            headers=[
                (':authority',
                 "127.0.0.1:{}".format(self.server.server.address.port)),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            priority_exclusive=True,
            priority_depends_on=42424242,
            priority_weight=42,
        )

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            client.wfile.write(h2_conn.data_to_send())
            client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        client.wfile.write(h2_conn.data_to_send())
        client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].response.headers[
            'priority_exclusive'] == 'True'
        assert self.master.state.flows[0].response.headers[
            'priority_depends_on'] == '42424242'
        assert self.master.state.flows[0].response.headers[
            'priority_weight'] == '42'
Example #28
0
        def handle(self):
            config = h2.config.H2Configuration(
                client_side=False,
                validate_outbound_headers=False,
                validate_inbound_headers=False)
            h2_conn = h2.connection.H2Connection(config)

            preamble = self.rfile.read(24)
            h2_conn.initiate_connection()
            h2_conn.receive_data(preamble)
            self.wfile.write(h2_conn.data_to_send())
            self.wfile.flush()

            if 'h2_server_settings' in self.kwargs:
                h2_conn.update_settings(self.kwargs['h2_server_settings'])
                self.wfile.write(h2_conn.data_to_send())
                self.wfile.flush()

            done = False
            while not done:
                try:
                    raw = b''.join(http2.read_raw_frame(self.rfile))
                    events = h2_conn.receive_data(raw)
                except exceptions.HttpException:
                    print(traceback.format_exc())
                    assert False
                except exceptions.TcpDisconnect:
                    break
                except:
                    print(traceback.format_exc())
                    break
                self.wfile.write(h2_conn.data_to_send())
                self.wfile.flush()

                for event in events:
                    try:
                        if not self.server.handle_server_event(event, h2_conn, self.rfile, self.wfile):
                            done = True
                            break
                    except exceptions.TcpDisconnect:
                        done = True
                    except:
                        done = True
                        print(traceback.format_exc())
                        break
Example #29
0
    def __call__(self):
        self._initiate_server_conn()
        self._complete_handshake()

        client = self.client_conn.connection
        server = self.server_conn.connection
        conns = [client, server]

        try:
            while True:
                r = tcp.ssl_read_select(conns, 1)
                for conn in r:
                    source_conn = self.client_conn if conn == client else self.server_conn
                    other_conn = self.server_conn if conn == client else self.client_conn
                    is_server = (conn == self.server_conn.connection)

                    with source_conn.h2.lock:
                        try:
                            raw_frame = b''.join(http2.read_raw_frame(source_conn.rfile))
                        except:
                            # read frame failed: connection closed
                            self._kill_all_streams()
                            return

                        if source_conn.h2.state_machine.state == h2.connection.ConnectionState.CLOSED:
                            self.log("HTTP/2 connection entered closed state already", "debug")
                            return

                        incoming_events = source_conn.h2.receive_data(raw_frame)
                        source_conn.send(source_conn.h2.data_to_send())

                        for event in incoming_events:
                            if not self._handle_event(event, source_conn, other_conn, is_server):
                                # connection terminated: GoAway
                                self._kill_all_streams()
                                return

                    self._cleanup_streams()
        except Exception as e:  # pragma: no cover
            self.log(repr(e), "info")
            self.log(traceback.format_exc(), "debug")
            self._kill_all_streams()
Example #30
0
    def test_priority_with_existing_stream(self):
        client, h2_conn = self._setup_connection()

        self._send_request(
            client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            end_stream=False,
        )

        h2_conn.prioritize(1, exclusive=True, depends_on=0, weight=42)
        h2_conn.end_stream(1)
        client.wfile.write(h2_conn.data_to_send())
        client.wfile.flush()

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            client.wfile.write(h2_conn.data_to_send())
            client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        client.wfile.write(h2_conn.data_to_send())
        client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.priority_data == [(True, 0, 42)]
Example #31
0
    def test_body_size_limit(self):
        self.options.body_size_limit = "20"

        # FIXME: This should not be required?
        self.options._processed["body_size_limit"] = 20

        h2_conn = self.setup_connection()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            body=b'very long body over 20 characters long',
        )

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamReset):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 0
Example #32
0
    def test_request_streaming(self, streaming):
        class Stream:
            def requestheaders(self, f):
                f.request.stream = streaming

        self.master.addons.add(Stream())
        h2_conn = self.setup_connection()
        body = generators.RandomGenerator("bytes", 100)[:]
        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),

            ],
            body=body,
            streaming=True
        )
        done = False
        connection_terminated_event = None
        self.client.rfile.o.settimeout(2)
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)

                for event in events:
                    if isinstance(event, h2.events.ConnectionTerminated):
                        connection_terminated_event = event
                        done = True
            except:
                break

        if streaming:
            assert connection_terminated_event.additional_data == body
        else:
            assert connection_terminated_event is None
Example #33
0
    def test_request_streaming(self, streaming):
        class Stream:
            def requestheaders(self, f):
                f.request.stream = streaming

        self.master.addons.add(Stream())
        h2_conn = self.setup_connection()
        body = generators.RandomGenerator("bytes", 100)[:]
        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),

            ],
            body=body,
            streaming=True
        )
        done = False
        connection_terminated_event = None
        self.client.rfile.o.settimeout(2)
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)

                for event in events:
                    if isinstance(event, h2.events.ConnectionTerminated):
                        connection_terminated_event = event
                        done = True
            except:
                break

        if streaming:
            assert connection_terminated_event.additional_data == body
        else:
            assert connection_terminated_event is None
Example #34
0
    def test_max_concurrent_streams(self):
        h2_conn = self.setup_connection()
        new_streams = [1, 3, 5, 7, 9, 11]
        for stream_id in new_streams:
            # this will exceed MAX_CONCURRENT_STREAMS on the server connection
            # and cause mitmproxy to throttle stream creation to the server
            self._send_request(self.client.wfile,
                               h2_conn,
                               stream_id=stream_id,
                               headers=[
                                   (':authority', "127.0.0.1:{}".format(
                                       self.server.server.address[1])),
                                   (':method', 'GET'),
                                   (':scheme', 'https'),
                                   (':path', '/'),
                                   ('X-Stream-ID', str(stream_id)),
                               ])

        ended_streams = 0
        while ended_streams != len(new_streams):
            try:
                header, body = http2.read_raw_frame(self.client.rfile)
                events = h2_conn.receive_data(b''.join([header, body]))
            except:
                break
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    ended_streams += 1

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == len(new_streams)
        for flow in self.master.state.flows:
            assert flow.response.status_code == 200
            assert b"Stream-ID " in flow.response.content
Example #35
0
    def test_forbidden_headers(self):
        client, h2_conn = self._setup_connection()

        self._send_request(client.wfile,
                           h2_conn,
                           headers=[
                               (':authority', "127.0.0.1:{}".format(
                                   self.server.server.address[1])),
                               (':method', 'GET'),
                               (':scheme', 'https'),
                               (':path', '/'),
                           ])

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            client.wfile.write(h2_conn.data_to_send())
            client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.ResponseReceived):
                    assert 'keep-alive' not in event.headers
                elif isinstance(event, h2.events.StreamEnded):
                    done = True

        h2_conn.close_connection()
        client.wfile.write(h2_conn.data_to_send())
        client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].response.status_code == 200
        assert self.master.state.flows[0].response.headers[
            'keep-alive'] == 'foobar'
Example #36
0
    def test_body_size_limit(self):
        self.options.body_size_limit = "20"

        h2_conn = self.setup_connection()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
            body=b'very long body over 20 characters long',
        )

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamReset):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 0
Example #37
0
    def test_response_streaming(self, streaming):
        class Stream:
            def responseheaders(self, f):
                f.response.stream = streaming

        self.master.addons.add(Stream())
        h2_conn = self.setup_connection()
        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),

            ]
        )
        done = False
        self.client.rfile.o.settimeout(2)
        data = None
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)

                for event in events:
                    if isinstance(event, h2.events.DataReceived):
                        data = event.data
                        done = True
            except:
                break

        if streaming:
            assert data
        else:
            assert data is None
Example #38
0
    def test_request_with_priority(self):
        h2_conn = self.setup_connection()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority',
                 "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
        )

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamReset):
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].response is None
Example #39
0
    def run_test_for_stream_reset(self):
        h2_conn = self.setup_connection()

        self._send_request(
            self.client.wfile,
            h2_conn,
            headers=[
                (':authority',
                 "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
        )

        self.client.rfile.o.settimeout(1)

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(self.client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamReset):
                    assert event.error_code == int(self.current_error_code)
                    done = True

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()
Example #40
0
    def test_request_with_priority(self):
        client, h2_conn = self._setup_connection()

        self._send_request(
            client.wfile,
            h2_conn,
            headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address.port)),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
            ],
        )

        done = False
        while not done:
            try:
                raw = b''.join(http2.read_raw_frame(client.rfile))
                events = h2_conn.receive_data(raw)
            except exceptions.HttpException:
                print(traceback.format_exc())
                assert False

            client.wfile.write(h2_conn.data_to_send())
            client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamReset):
                    done = True

        h2_conn.close_connection()
        client.wfile.write(h2_conn.data_to_send())
        client.wfile.flush()

        assert len(self.master.state.flows) == 1
        assert self.master.state.flows[0].response is None
Example #41
0
    def test_max_concurrent_streams(self):
        h2_conn = self.setup_connection()
        new_streams = [1, 3, 5, 7, 9, 11]
        for stream_id in new_streams:
            # this will exceed MAX_CONCURRENT_STREAMS on the server connection
            # and cause mitmproxy to throttle stream creation to the server
            self._send_request(self.client.wfile, h2_conn, stream_id=stream_id, headers=[
                (':authority', "127.0.0.1:{}".format(self.server.server.address[1])),
                (':method', 'GET'),
                (':scheme', 'https'),
                (':path', '/'),
                ('X-Stream-ID', str(stream_id)),
            ])

        ended_streams = 0
        while ended_streams != len(new_streams):
            try:
                header, body = http2.read_raw_frame(self.client.rfile)
                events = h2_conn.receive_data(b''.join([header, body]))
            except:
                break
            self.client.wfile.write(h2_conn.data_to_send())
            self.client.wfile.flush()

            for event in events:
                if isinstance(event, h2.events.StreamEnded):
                    ended_streams += 1

        h2_conn.close_connection()
        self.client.wfile.write(h2_conn.data_to_send())
        self.client.wfile.flush()

        assert len(self.master.state.flows) == len(new_streams)
        for flow in self.master.state.flows:
            assert flow.response.status_code == 200
            assert b"Stream-ID " in flow.response.content