Пример #1
0
    def test_settings_frame_serializes_properly(self):
        f = SettingsFrame(0)
        f.parse_flags(0xFF)
        f.settings = self.settings

        s = f.serialize()
        assert s == self.serialized
Пример #2
0
    def build_settings_frame(self, settings, ack=False):
        """
        Builds a single settings frame.
        """
        f = SettingsFrame(0)
        if ack:
            f.flags.add('ACK')

        f.settings = settings
        return f
Пример #3
0
    def build_settings_frame(self, settings, ack=False):
        """
        Builds a single settings frame.
        """
        f = SettingsFrame(0)
        if ack:
            f.flags.add('ACK')

        f.settings = settings
        return f
Пример #4
0
        def socket_handler(listener):
            sock = listener.accept()[0]

            # Dispose of the first packet.
            sock.recv(65535)

            # Send a Settings frame that reduces the flow-control window to
            # 64 bytes.
            f = SettingsFrame(0)
            f.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 64
            sock.send(f.serialize())

            # Grab three frames, the settings ACK, the initial headers frame,
            # and the first data frame.
            for x in range(0, 3):
                data.append(sock.recv(65535))

            # Send a WindowUpdate giving more window room to the stream.
            f = WindowUpdateFrame(1)
            f.window_increment = 64
            sock.send(f.serialize())

            # Send one that gives more room to the connection.
            f = WindowUpdateFrame(0)
            f.window_increment = 64
            sock.send(f.serialize())

            # Reeive the remaining frame.
            data.append(sock.recv(65535))
            send_event.set()

            # We're done.
            sock.close()
Пример #5
0
 def get_http2_upgrade_header(self):
     # try h2c protocol
     hc = {}
     hc["Upgrade"] = "h2c"
     hc['Connection'] = 'Upgrade, HTTP2-Settings'
     http2_settings = SettingsFrame(0)
     http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535
     settings = base64.urlsafe_b64encode(
         http2_settings.serialize_body()).rstrip(b'=').decode("utf8")
     hc['HTTP2-Settings'] = settings
     return hc
Пример #6
0
    def update_settings(self, new_settings):
        """
        Update the local settings. This will prepare and emit the appropriate
        SETTINGS frame.

        :param new_settings: A dictionary of {setting: new value}
        """
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)
        self.local_settings.update(new_settings)
        s = SettingsFrame(0)
        s.settings = new_settings
        self._prepare_for_sending([s])
Пример #7
0
    def _add_upgrade_headers(self, headers):
        # Add HTTP Upgrade headers.
        headers[b'connection'] = b'Upgrade, HTTP2-Settings'
        headers[b'upgrade'] = H2C_PROTOCOL

        # Encode SETTINGS frame payload in Base64 and put into the HTTP-2
        # Settings header.
        http2_settings = SettingsFrame(0)
        http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535
        encoded_settings = base64.urlsafe_b64encode(
            http2_settings.serialize_body())
        headers[b'HTTP2-Settings'] = encoded_settings.rstrip(b'=')
Пример #8
0
    def update_settings(self, new_settings):
        """
        Update the local settings. This will prepare and emit the appropriate
        SETTINGS frame.

        :param new_settings: A dictionary of {setting: new value}
        """
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)
        self.local_settings.update(new_settings)
        s = SettingsFrame(0)
        s.settings = new_settings
        self._prepare_for_sending([s])
Пример #9
0
    def test_incrementing_window_after_close(self):
        """
        Hyper does not attempt to increment the flow control window once the
        stream is closed.
        """
        # For this test, we want to send a response that has three frames at
        # the default max frame size (16,384 bytes). That will, on the third
        # frame, trigger the processing to increment the flow control window,
        # which should then not happen.
        f = SettingsFrame(0, settings={h2.settings.INITIAL_WINDOW_SIZE: 100})

        c = HTTP20Connection('www.google.com')
        c._sock = DummySocket()
        c._sock.buffer = BytesIO(f.serialize())

        # Open stream 1.
        c.request('GET', '/')

        # Check what data we've sent right now.
        originally_sent_data = c._sock.queue[:]

        # Swap out the buffer to get a GoAway frame.
        length = 16384
        total_length = (3 * 16384) + len(b'some more data')
        e = Encoder()
        h1 = HeadersFrame(1)
        h1.data = e.encode(
            [(':status', 200), ('content-length', '%d' % total_length)]
        )
        h1.flags |= set(['END_HEADERS'])

        d1 = DataFrame(1)
        d1.data = b'\x00' * length
        d2 = d1
        d3 = d1
        d4 = DataFrame(1)
        d4.data = b'some more data'
        d4.flags |= set(['END_STREAM'])

        buffer = BytesIO(
            b''.join(f.serialize() for f in [h1, d1, d2, d3, d4])
        )
        c._sock.buffer = buffer

        # Read the response
        resp = c.get_response(stream_id=1)
        assert resp.status == 200
        assert resp.read() == b''.join(
            [b'\x00' * (3 * length), b'some more data']
        )

        # We should have sent only one extra frame
        assert len(originally_sent_data) + 1 == len(c._sock.queue)
Пример #10
0
    def test_incrementing_window_after_close(self):
        """
        Hyper does not attempt to increment the flow control window once the
        stream is closed.
        """
        # For this test, we want to send a response that has three frames at
        # the default max frame size (16,384 bytes). That will, on the third
        # frame, trigger the processing to increment the flow control window,
        # which should then not happen.
        f = SettingsFrame(0, settings={h2.settings.INITIAL_WINDOW_SIZE: 100})

        c = HTTP20Connection('www.google.com')
        c._sock = DummySocket()
        c._sock.buffer = BytesIO(f.serialize())

        # Open stream 1.
        c.request('GET', '/')

        # Check what data we've sent right now.
        originally_sent_data = c._sock.queue[:]

        # Swap out the buffer to get a GoAway frame.
        length = 16384
        total_length = (3 * 16384) + len(b'some more data')
        e = Encoder()
        h1 = HeadersFrame(1)
        h1.data = e.encode(
            [(':status', 200), ('content-length', '%d' % total_length)]
        )
        h1.flags |= set(['END_HEADERS'])

        d1 = DataFrame(1)
        d1.data = b'\x00' * length
        d2 = d1
        d3 = d1
        d4 = DataFrame(1)
        d4.data = b'some more data'
        d4.flags |= set(['END_STREAM'])

        buffer = BytesIO(
            b''.join(f.serialize() for f in [h1, d1, d2, d3, d4])
        )
        c._sock.buffer = buffer

        # Read the response
        resp = c.get_response(stream_id=1)
        assert resp.status == 200
        assert resp.read() == b''.join(
            [b'\x00' * (3 * length), b'some more data']
        )

        # We should have sent only one extra frame
        assert len(originally_sent_data) + 1 == len(c._sock.queue)
Пример #11
0
    def _add_upgrade_headers(self, headers):
        # Add HTTP Upgrade headers.
        headers[b'connection'] = b'Upgrade, HTTP2-Settings'
        headers[b'upgrade'] = H2C_PROTOCOL

        # Encode SETTINGS frame payload in Base64 and put into the HTTP-2
        # Settings header.
        http2_settings = SettingsFrame(0)
        http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535
        encoded_settings = base64.urlsafe_b64encode(
            http2_settings.serialize_body()
        )
        headers[b'HTTP2-Settings'] = encoded_settings.rstrip(b'=')
Пример #12
0
        def socket_handler(listener):
            sock = listener.accept()[0]

            # We should get one big chunk.
            first = sock.recv(65535)
            data.append(first)

            # We need to send back a SettingsFrame.
            f = SettingsFrame(0)
            sock.send(f.serialize())

            send_event.set()
            sock.close()
Пример #13
0
    def _send_preamble(self):
        self.send_queue.put(RawFrame(b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'))

        f = SettingsFrame(0)
        f.settings[SettingsFrame.ENABLE_PUSH] = 0
        f.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = self.local_settings[SettingsFrame.INITIAL_WINDOW_SIZE]
        f.settings[SettingsFrame.MAX_FRAME_SIZE] = self.local_settings[SettingsFrame.MAX_FRAME_SIZE]
        self._send_cb(f)

        # update local connection windows size
        f = WindowUpdateFrame(0)
        f.window_increment = self.local_connection_initial_windows - DEFAULT_WINDOW_SIZE
        self._send_cb(f)
Пример #14
0
        def socket_handler(listener):
            sock = listener.accept()[0]

            # We should get one big chunk.
            first = sock.recv(65535)
            data.append(first)

            # We need to send back a SettingsFrame.
            f = SettingsFrame(0)
            sock.send(f.serialize())

            send_event.set()
            sock.close()
Пример #15
0
        def socket_handler(listener):
            sock = listener.accept()[0]

            # Dispose of the first packet.
            sock.recv(65535)

            # Send a Settings frame that reduces the flow-control window to
            # 64 bytes.
            f = SettingsFrame(0)
            f.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 64
            sock.send(f.serialize())

            # Grab three frames, the settings ACK, the initial headers frame,
            # and the first data frame.
            for x in range(0, 3):
                data.append(sock.recv(65535))

            # Send a WindowUpdate giving more window room to the stream.
            f = WindowUpdateFrame(1)
            f.window_increment = 64
            sock.send(f.serialize())

            # Send one that gives more room to the connection.
            f = WindowUpdateFrame(0)
            f.window_increment = 64
            sock.send(f.serialize())

            # Reeive the remaining frame.
            data.append(sock.recv(65535))
            send_event.set()

            # We're done.
            sock.close()
Пример #16
0
    def initiate_connection(self):
        """
        Provides any data that needs to be sent at the start of the connection.
        Must be called for both clients and servers.
        """
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)
        if self.client_side:
            preamble = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'
        else:
            preamble = b''

        f = SettingsFrame(0)
        for setting, value in self.local_settings.items():
            f.settings[setting] = value

        self._data_to_send += preamble + f.serialize()
Пример #17
0
    def receive_frame(self, frame):
        #self.logger.debug("h2conn recv:%s", frame)
        if frame.type == WindowUpdateFrame.type:
            # self.logger.debug("WindowUpdateFrame %d", frame.window_increment)
            self.increase_remote_window_size(frame.window_increment)

        elif frame.type == PingFrame.type:
            if 'ACK' in frame.flags:
                ping_time = struct.unpack("!d", frame.opaque_data)[0]
                time_now = time.time()
                rtt = (time_now - ping_time) * 1000
                if rtt < 0:
                    self.logger.error("rtt:%f ping_time:%f now:%f", rtt,
                                      ping_time, time_now)
                self.rtt = rtt
                self.ping_on_way -= 1
                #self.logger.debug("RTT:%d, on_way:%d", self.rtt, self.ping_on_way)
                if self.keep_running and self.ping_on_way == 0:
                    self.accept_task = True
            else:
                # The spec requires us to reply with PING+ACK and identical data.
                p = PingFrame(0)
                p.flags.add('ACK')
                p.opaque_data = frame.opaque_data
                self._send_cb(p)

        elif frame.type == SettingsFrame.type:
            if 'ACK' not in frame.flags:
                # send ACK as soon as possible
                f = SettingsFrame(0)
                f.flags.add('ACK')
                self._send_cb(f)

                # this may trigger send DataFrame blocked by remote window
                self._update_settings(frame)
            else:
                self.accept_task = True
                self.idle_cb()

        elif frame.type == GoAwayFrame.type:
            # If we get GoAway with error code zero, we are doing a graceful
            # shutdown and all is well. Otherwise, throw an exception.

            # If an error occured, try to read the error description from
            # code registry otherwise use the frame's additional data.

            time_cost = time.time() - self.last_recv_time

            self.close("GoAway:%s inactive time:%s" %
                       ("conn close", time_cost))

        elif frame.type in FRAMES:
            # This frame isn't valid at this point.
            #raise ValueError("Unexpected frame %s." % frame)
            self.logger.error("%s Unexpected frame %s.", self.ip_str, frame)
        else:  # pragma: no cover
            # Unexpected frames belong to extensions. Just drop it on the
            # floor, but log so that users know that something happened.
            self.logger.error("%s Received unknown frame, type %d",
                              self.ip_str, frame.type)
Пример #18
0
    def initiate_connection(self):
        """
        Provides any data that needs to be sent at the start of the connection.
        Must be called for both clients and servers.
        """
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)
        if self.client_side:
            preamble = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'
        else:
            preamble = b''

        f = SettingsFrame(0)
        for setting, value in self.local_settings.items():
            f.settings[setting] = value

        self._data_to_send += preamble + f.serialize()
Пример #19
0
def receive_preamble(sock):
    # Receive the HTTP/2 'preamble'.
    first = sock.recv(65535)

    # Work around some bugs: if the first message received was only the PRI
    # string, aim to receive a settings frame as well.
    if len(first) <= len(b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'):
        sock.recv(65535)
    sock.send(SettingsFrame(0).serialize())
    sock.recv(65535)
    return
Пример #20
0
    def test_send_tolerate_peer_gone(self):
        class ErrorSocket(DummySocket):
            def sendall(self, data):
                raise socket.error(errno.EPIPE)

        c = HTTP20Connection('www.google.com')
        c._sock = ErrorSocket()
        f = SettingsFrame(0)
        with pytest.raises(socket.error):
            c._send_cb(f, False)
        c._sock = DummySocket()
        c._send_cb(f, True)  # shouldn't raise an error
Пример #21
0
    def test_settings_frame_serializes_properly(self):
        f = SettingsFrame()
        f.parse_flags(0xFF)
        f.settings = self.settings

        s = f.serialize()
        assert s == self.serialized
Пример #22
0
    def test_resetting_streams_after_close(self):
        """
        Attempts to reset streams when the connection is torn down are
        tolerated.
        """
        f = SettingsFrame(0)

        c = HTTP20Connection('www.google.com')
        c._sock = DummySocket()
        c._sock.buffer = BytesIO(f.serialize())

        # Open stream 1.
        c.request('GET', '/')

        # Swap out the buffer to get a GoAway frame.
        f = GoAwayFrame(0)
        f.error_code = 1
        c._sock.buffer = BytesIO(f.serialize())

        # "Read" the GoAway
        with pytest.raises(ConnectionError):
            c._single_read()
Пример #23
0
    def acknowledge_settings(self, event):
        """
        Acknowledge settings that have been received.

        :param event: The RemoteSettingsChanged event that is being
                      acknowledged.
        :returns: A list of events.
        """
        assert isinstance(event, RemoteSettingsChanged)
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)

        changes = self.remote_settings.acknowledge()

        if SettingsFrame.INITIAL_WINDOW_SIZE in changes:
            setting = changes[SettingsFrame.INITIAL_WINDOW_SIZE]
            self._flow_control_change_from_settings(
                setting.original_value,
                setting.new_value,
            )

        # HEADER_TABLE_SIZE changes by the remote part affect our encoder: cf.
        # RFC 7540 Section 6.5.2.
        if SettingsFrame.HEADER_TABLE_SIZE in changes:
            setting = changes[SettingsFrame.HEADER_TABLE_SIZE]
            self.encoder.header_table_size = setting.new_value

        if SettingsFrame.SETTINGS_MAX_FRAME_SIZE in changes:
            setting = changes[SettingsFrame.SETTINGS_MAX_FRAME_SIZE]
            self.max_outbound_frame_size = setting.new_value
            for stream in self.streams.values():
                stream.max_outbound_frame_size = setting.new_value

        f = SettingsFrame(0)
        f.flags.add('ACK')
        self._prepare_for_sending([f])
        return []
Пример #24
0
    def _acknowledge_settings(self):
        """
        Acknowledge settings that have been received.

        .. versionchanged:: 2.0.0
           Removed from public API, removed useless ``event`` parameter, made
           automatic.

        :returns: Nothing
        """
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)

        changes = self.remote_settings.acknowledge()

        if INITIAL_WINDOW_SIZE in changes:
            setting = changes[INITIAL_WINDOW_SIZE]
            self._flow_control_change_from_settings(
                setting.original_value,
                setting.new_value,
            )

        # HEADER_TABLE_SIZE changes by the remote part affect our encoder: cf.
        # RFC 7540 Section 6.5.2.
        if HEADER_TABLE_SIZE in changes:
            setting = changes[HEADER_TABLE_SIZE]
            self.encoder.header_table_size = setting.new_value

        if MAX_FRAME_SIZE in changes:
            setting = changes[MAX_FRAME_SIZE]
            self.max_outbound_frame_size = setting.new_value
            for stream in self.streams.values():
                stream.max_outbound_frame_size = setting.new_value

        f = SettingsFrame(0)
        f.flags.add('ACK')
        return [f]
Пример #25
0
    def test_resetting_streams_after_close(self):
        """
        Attempts to reset streams when the connection is torn down are
        tolerated.
        """
        f = SettingsFrame(0)

        c = HTTP20Connection('www.google.com')
        c._sock = DummySocket()
        c._sock.buffer = BytesIO(f.serialize())

        # Open stream 1.
        c.request('GET', '/')

        # Swap out the buffer to get a GoAway frame.
        f = GoAwayFrame(0)
        f.error_code = 1
        c._sock.buffer = BytesIO(f.serialize())

        # "Read" the GoAway
        with pytest.raises(ConnectionError):
            c._single_read()
Пример #26
0
 def test_repr(self):
     f = SettingsFrame()
     assert repr(f).endswith("settings={}")
     f.settings[SettingsFrame.MAX_FRAME_SIZE] = 16384
     assert repr(f).endswith("settings={5: 16384}")
Пример #27
0
 def test_settings_frame_has_only_one_flag(self):
     f = SettingsFrame(0)
     flags = f.parse_flags(0xFF)
     assert flags == set(['ACK'])
Пример #28
0
 def test_settings_frames_never_have_streams(self):
     with pytest.raises(ValueError):
         SettingsFrame(stream_id=1)
Пример #29
0
 def test_settings_frame_ack_and_settings(self):
     with pytest.raises(ValueError):
         SettingsFrame(settings=self.settings, flags=('ACK', ))
Пример #30
0
 def test_settings_frame_with_ack(self):
     f = SettingsFrame(flags=('ACK', ))
     assert 'ACK' in f.flags
Пример #31
0
 def test_settings_frame_without_settings(self):
     f = SettingsFrame()
     assert f.settings == {}
Пример #32
0
 def test_settings_frame_has_only_one_flag(self):
     f = SettingsFrame()
     flags = f.parse_flags(0xFF)
     assert flags == set(['ACK'])
# https://github.com/python-hyper/hpack/pull/60

--- test/test_hyper.py.orig	2019-05-17 10:17:07 UTC
+++ test/test_hyper.py
@@ -1,14 +1,13 @@
 # -*- coding: utf-8 -*-
-import h2.settings
-
 from h2.frame_buffer import FrameBuffer
 from h2.connection import ConnectionState
+from h2.settings import SettingCodes
 from hyperframe.frame import (
     Frame, DataFrame, RstStreamFrame, SettingsFrame, PushPromiseFrame,
     WindowUpdateFrame, HeadersFrame, ContinuationFrame, GoAwayFrame,
     PingFrame, FRAME_MAX_ALLOWED_LEN
 )
-from hpack.hpack_compat import Encoder
+from hpack import Encoder
 from hyper.common.connection import HTTPConnection
 from hyper.http20.connection import HTTP20Connection
 from hyper.http20.response import HTTP20Response, HTTP20Push
@@ -766,7 +765,7 @@ class TestHyperConnection(object):
         # the default max frame size (16,384 bytes). That will, on the third
         # frame, trigger the processing to increment the flow control window,
         # which should then not happen.
-        f = SettingsFrame(0, settings={h2.settings.INITIAL_WINDOW_SIZE: 100})
+        f = SettingsFrame(0, settings={SettingCodes.INITIAL_WINDOW_SIZE: 100})
 
         c = HTTP20Connection('www.google.com')
         c._sock = DummySocket()
Пример #34
0
 def test_settings_frames_never_have_streams(self):
     with pytest.raises(InvalidDataError):
         SettingsFrame(1)
Пример #35
0
    def test_settings_frame_ack_and_settings(self):
        with pytest.raises(InvalidDataError):
            SettingsFrame(settings=self.settings, flags=('ACK', ))

        with pytest.raises(InvalidDataError):
            decode_frame(self.serialized)
Пример #36
0
 def test_settings_frame_with_settings(self):
     f = SettingsFrame(settings=self.settings)
     assert f.settings == self.settings