1# -*- coding: utf-8 -*-
2"""
3hyperframe/frame
4~~~~~~~~~~~~~~~~
5
6Defines framing logic for HTTP/2. Provides both classes to represent framed
7data and logic for aiding the connection when it comes to reading from the
8socket.
9"""
10import struct
11import binascii
12
13from .exceptions import (
14    UnknownFrameError, InvalidPaddingError, InvalidFrameError, InvalidDataError
15)
16from .flags import Flag, Flags
17
18
19# The maximum initial length of a frame. Some frames have shorter maximum
20# lengths.
21FRAME_MAX_LEN = (2 ** 14)
22
23# The maximum allowed length of a frame.
24FRAME_MAX_ALLOWED_LEN = (2 ** 24) - 1
25
26# Stream association enumerations.
27_STREAM_ASSOC_HAS_STREAM = "has-stream"
28_STREAM_ASSOC_NO_STREAM = "no-stream"
29_STREAM_ASSOC_EITHER = "either"
30
31# Structs for packing and unpacking
32_STRUCT_HBBBL = struct.Struct(">HBBBL")
33_STRUCT_LL = struct.Struct(">LL")
34_STRUCT_HL = struct.Struct(">HL")
35_STRUCT_LB = struct.Struct(">LB")
36_STRUCT_L = struct.Struct(">L")
37_STRUCT_H = struct.Struct(">H")
38_STRUCT_B = struct.Struct(">B")
39
40
41class Frame:
42    """
43    The base class for all HTTP/2 frames.
44    """
45    #: The flags defined on this type of frame.
46    defined_flags = []
47
48    #: The byte used to define the type of the frame.
49    type = None
50
51    # If 'has-stream', the frame's stream_id must be non-zero. If 'no-stream',
52    # it must be zero. If 'either', it's not checked.
53    stream_association = None
54
55    def __init__(self, stream_id, flags=()):
56        #: The stream identifier for the stream this frame was received on.
57        #: Set to 0 for frames sent on the connection (stream-id 0).
58        self.stream_id = stream_id
59
60        #: The flags set for this frame.
61        self.flags = Flags(self.defined_flags)
62
63        #: The frame length, excluding the nine-byte header.
64        self.body_len = 0
65
66        for flag in flags:
67            self.flags.add(flag)
68
69        if (not self.stream_id and
70           self.stream_association == _STREAM_ASSOC_HAS_STREAM):
71            raise InvalidDataError(
72                'Stream ID must be non-zero for {}'.format(
73                    type(self).__name__,
74                )
75            )
76        if (self.stream_id and
77           self.stream_association == _STREAM_ASSOC_NO_STREAM):
78            raise InvalidDataError(
79                'Stream ID must be zero for {} with stream_id={}'.format(
80                    type(self).__name__,
81                    self.stream_id,
82                )
83            )
84
85    def __repr__(self):
86        return (
87            "{}(stream_id={}, flags={}): {}"
88        ).format(
89            type(self).__name__,
90            self.stream_id,
91            repr(self.flags),
92            self._body_repr(),
93        )
94
95    def _body_repr(self):
96        # More specific implementation may be provided by subclasses of Frame.
97        # This fallback shows the serialized (and truncated) body content.
98        return _raw_data_repr(self.serialize_body())
99
100    @staticmethod
101    def explain(data):
102        """
103        Takes a bytestring and tries to parse a single frame and print it.
104
105        This function is only provided for debugging purposes.
106
107        :param data: A memoryview object containing the raw data of at least
108                     one complete frame (header and body).
109
110        .. versionadded:: 6.0.0
111        """
112        frame, length = Frame.parse_frame_header(data[:9])
113        frame.parse_body(data[9:9 + length])
114        print(frame)
115        return frame, length
116
117    @staticmethod
118    def parse_frame_header(header, strict=False):
119        """
120        Takes a 9-byte frame header and returns a tuple of the appropriate
121        Frame object and the length that needs to be read from the socket.
122
123        This populates the flags field, and determines how long the body is.
124
125        :param header: A memoryview object containing the 9-byte frame header
126                       data of a frame. Must not contain more or less.
127
128        :param strict: Whether to raise an exception when encountering a frame
129            not defined by spec and implemented by hyperframe.
130
131        :raises hyperframe.exceptions.UnknownFrameError: If a frame of unknown
132            type is received.
133
134        .. versionchanged:: 5.0.0
135            Added :param:`strict` to accommodate :class:`ExtensionFrame`
136        """
137        try:
138            fields = _STRUCT_HBBBL.unpack(header)
139        except struct.error:
140            raise InvalidFrameError("Invalid frame header")
141
142        # First 24 bits are frame length.
143        length = (fields[0] << 8) + fields[1]
144        type = fields[2]
145        flags = fields[3]
146        stream_id = fields[4] & 0x7FFFFFFF
147
148        try:
149            frame = FRAMES[type](stream_id)
150        except KeyError:
151            if strict:
152                raise UnknownFrameError(type, length)
153            frame = ExtensionFrame(type=type, stream_id=stream_id)
154
155        frame.parse_flags(flags)
156        return (frame, length)
157
158    def parse_flags(self, flag_byte):
159        for flag, flag_bit in self.defined_flags:
160            if flag_byte & flag_bit:
161                self.flags.add(flag)
162
163        return self.flags
164
165    def serialize(self):
166        """
167        Convert a frame into a bytestring, representing the serialized form of
168        the frame.
169        """
170        body = self.serialize_body()
171        self.body_len = len(body)
172
173        # Build the common frame header.
174        # First, get the flags.
175        flags = 0
176
177        for flag, flag_bit in self.defined_flags:
178            if flag in self.flags:
179                flags |= flag_bit
180
181        header = _STRUCT_HBBBL.pack(
182            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits
183            self.body_len & 0xFF,
184            self.type,
185            flags,
186            self.stream_id & 0x7FFFFFFF  # Stream ID is 32 bits.
187        )
188
189        return header + body
190
191    def serialize_body(self):
192        raise NotImplementedError()
193
194    def parse_body(self, data):
195        """
196        Given the body of a frame, parses it into frame data. This populates
197        the non-header parts of the frame: that is, it does not populate the
198        stream ID or flags.
199
200        :param data: A memoryview object containing the body data of the frame.
201                     Must not contain *more* data than the length returned by
202                     :meth:`parse_frame_header
203                     <hyperframe.frame.Frame.parse_frame_header>`.
204        """
205        raise NotImplementedError()
206
207
208class Padding:
209    """
210    Mixin for frames that contain padding. Defines extra fields that can be
211    used and set by frames that can be padded.
212    """
213    def __init__(self, stream_id, pad_length=0, **kwargs):
214        super().__init__(stream_id, **kwargs)
215
216        #: The length of the padding to use.
217        self.pad_length = pad_length
218
219    def serialize_padding_data(self):
220        if 'PADDED' in self.flags:
221            return _STRUCT_B.pack(self.pad_length)
222        return b''
223
224    def parse_padding_data(self, data):
225        if 'PADDED' in self.flags:
226            try:
227                self.pad_length = struct.unpack('!B', data[:1])[0]
228            except struct.error:
229                raise InvalidFrameError("Invalid Padding data")
230            return 1
231        return 0
232
233    #: .. deprecated:: 5.2.1
234    #:    Use self.pad_length instead.
235    @property
236    def total_padding(self):  # pragma: no cover
237        import warnings
238        warnings.warn(
239            "total_padding contains the same information as pad_length.",
240            DeprecationWarning
241        )
242        return self.pad_length
243
244
245class Priority:
246    """
247    Mixin for frames that contain priority data. Defines extra fields that can
248    be used and set by frames that contain priority data.
249    """
250    def __init__(self,
251                 stream_id,
252                 depends_on=0x0,
253                 stream_weight=0x0,
254                 exclusive=False,
255                 **kwargs):
256        super().__init__(stream_id, **kwargs)
257
258        #: The stream ID of the stream on which this stream depends.
259        self.depends_on = depends_on
260
261        #: The weight of the stream. This is an integer between 0 and 256.
262        self.stream_weight = stream_weight
263
264        #: Whether the exclusive bit was set.
265        self.exclusive = exclusive
266
267    def serialize_priority_data(self):
268        return _STRUCT_LB.pack(
269            self.depends_on + (0x80000000 if self.exclusive else 0),
270            self.stream_weight
271        )
272
273    def parse_priority_data(self, data):
274        try:
275            self.depends_on, self.stream_weight = _STRUCT_LB.unpack(data[:5])
276        except struct.error:
277            raise InvalidFrameError("Invalid Priority data")
278
279        self.exclusive = True if self.depends_on >> 31 else False
280        self.depends_on &= 0x7FFFFFFF
281        return 5
282
283
284class DataFrame(Padding, Frame):
285    """
286    DATA frames convey arbitrary, variable-length sequences of octets
287    associated with a stream. One or more DATA frames are used, for instance,
288    to carry HTTP request or response payloads.
289    """
290    #: The flags defined for DATA frames.
291    defined_flags = [
292        Flag('END_STREAM', 0x01),
293        Flag('PADDED', 0x08),
294    ]
295
296    #: The type byte for data frames.
297    type = 0x0
298
299    stream_association = _STREAM_ASSOC_HAS_STREAM
300
301    def __init__(self, stream_id, data=b'', **kwargs):
302        super().__init__(stream_id, **kwargs)
303
304        #: The data contained on this frame.
305        self.data = data
306
307    def serialize_body(self):
308        padding_data = self.serialize_padding_data()
309        padding = b'\0' * self.pad_length
310        if isinstance(self.data, memoryview):
311            self.data = self.data.tobytes()
312        return b''.join([padding_data, self.data, padding])
313
314    def parse_body(self, data):
315        padding_data_length = self.parse_padding_data(data)
316        self.data = (
317            data[padding_data_length:len(data)-self.pad_length].tobytes()
318        )
319        self.body_len = len(data)
320
321        if self.pad_length and self.pad_length >= self.body_len:
322            raise InvalidPaddingError("Padding is too long.")
323
324    @property
325    def flow_controlled_length(self):
326        """
327        The length of the frame that needs to be accounted for when considering
328        flow control.
329        """
330        padding_len = 0
331        if 'PADDED' in self.flags:
332            # Account for extra 1-byte padding length field, which is still
333            # present if possibly zero-valued.
334            padding_len = self.pad_length + 1
335        return len(self.data) + padding_len
336
337
338class PriorityFrame(Priority, Frame):
339    """
340    The PRIORITY frame specifies the sender-advised priority of a stream. It
341    can be sent at any time for an existing stream. This enables
342    reprioritisation of existing streams.
343    """
344    #: The flags defined for PRIORITY frames.
345    defined_flags = []
346
347    #: The type byte defined for PRIORITY frames.
348    type = 0x02
349
350    stream_association = _STREAM_ASSOC_HAS_STREAM
351
352    def _body_repr(self):
353        return "exclusive={}, depends_on={}, stream_weight={}".format(
354            self.exclusive,
355            self.depends_on,
356            self.stream_weight
357        )
358
359    def serialize_body(self):
360        return self.serialize_priority_data()
361
362    def parse_body(self, data):
363        if len(data) > 5:
364            raise InvalidFrameError(
365                "PRIORITY must have 5 byte body: actual length %s." %
366                len(data)
367            )
368
369        self.parse_priority_data(data)
370        self.body_len = 5
371
372
373class RstStreamFrame(Frame):
374    """
375    The RST_STREAM frame allows for abnormal termination of a stream. When sent
376    by the initiator of a stream, it indicates that they wish to cancel the
377    stream or that an error condition has occurred. When sent by the receiver
378    of a stream, it indicates that either the receiver is rejecting the stream,
379    requesting that the stream be cancelled or that an error condition has
380    occurred.
381    """
382    #: The flags defined for RST_STREAM frames.
383    defined_flags = []
384
385    #: The type byte defined for RST_STREAM frames.
386    type = 0x03
387
388    stream_association = _STREAM_ASSOC_HAS_STREAM
389
390    def __init__(self, stream_id, error_code=0, **kwargs):
391        super().__init__(stream_id, **kwargs)
392
393        #: The error code used when resetting the stream.
394        self.error_code = error_code
395
396    def _body_repr(self):
397        return "error_code={}".format(
398            self.error_code,
399        )
400
401    def serialize_body(self):
402        return _STRUCT_L.pack(self.error_code)
403
404    def parse_body(self, data):
405        if len(data) != 4:
406            raise InvalidFrameError(
407                "RST_STREAM must have 4 byte body: actual length %s." %
408                len(data)
409            )
410
411        try:
412            self.error_code = _STRUCT_L.unpack(data)[0]
413        except struct.error:  # pragma: no cover
414            raise InvalidFrameError("Invalid RST_STREAM body")
415
416        self.body_len = 4
417
418
419class SettingsFrame(Frame):
420    """
421    The SETTINGS frame conveys configuration parameters that affect how
422    endpoints communicate. The parameters are either constraints on peer
423    behavior or preferences.
424
425    Settings are not negotiated. Settings describe characteristics of the
426    sending peer, which are used by the receiving peer. Different values for
427    the same setting can be advertised by each peer. For example, a client
428    might set a high initial flow control window, whereas a server might set a
429    lower value to conserve resources.
430    """
431    #: The flags defined for SETTINGS frames.
432    defined_flags = [Flag('ACK', 0x01)]
433
434    #: The type byte defined for SETTINGS frames.
435    type = 0x04
436
437    stream_association = _STREAM_ASSOC_NO_STREAM
438
439    # We need to define the known settings, they may as well be class
440    # attributes.
441    #: The byte that signals the SETTINGS_HEADER_TABLE_SIZE setting.
442    HEADER_TABLE_SIZE = 0x01
443    #: The byte that signals the SETTINGS_ENABLE_PUSH setting.
444    ENABLE_PUSH = 0x02
445    #: The byte that signals the SETTINGS_MAX_CONCURRENT_STREAMS setting.
446    MAX_CONCURRENT_STREAMS = 0x03
447    #: The byte that signals the SETTINGS_INITIAL_WINDOW_SIZE setting.
448    INITIAL_WINDOW_SIZE = 0x04
449    #: The byte that signals the SETTINGS_MAX_FRAME_SIZE setting.
450    MAX_FRAME_SIZE = 0x05
451    #: The byte that signals the SETTINGS_MAX_HEADER_LIST_SIZE setting.
452    MAX_HEADER_LIST_SIZE = 0x06
453    #: The byte that signals SETTINGS_ENABLE_CONNECT_PROTOCOL setting.
454    ENABLE_CONNECT_PROTOCOL = 0x08
455
456    def __init__(self, stream_id=0, settings=None, **kwargs):
457        super().__init__(stream_id, **kwargs)
458
459        if settings and "ACK" in kwargs.get("flags", ()):
460            raise InvalidDataError(
461                "Settings must be empty if ACK flag is set."
462            )
463
464        #: A dictionary of the setting type byte to the value of the setting.
465        self.settings = settings or {}
466
467    def _body_repr(self):
468        return "settings={}".format(
469            self.settings,
470        )
471
472    def serialize_body(self):
473        return b''.join([_STRUCT_HL.pack(setting & 0xFF, value)
474                         for setting, value in self.settings.items()])
475
476    def parse_body(self, data):
477        if 'ACK' in self.flags and len(data) > 0:
478            raise InvalidDataError(
479                "SETTINGS ack frame must not have payload: got %s bytes" %
480                len(data)
481            )
482
483        body_len = 0
484        for i in range(0, len(data), 6):
485            try:
486                name, value = _STRUCT_HL.unpack(data[i:i+6])
487            except struct.error:
488                raise InvalidFrameError("Invalid SETTINGS body")
489
490            self.settings[name] = value
491            body_len += 6
492
493        self.body_len = body_len
494
495
496class PushPromiseFrame(Padding, Frame):
497    """
498    The PUSH_PROMISE frame is used to notify the peer endpoint in advance of
499    streams the sender intends to initiate.
500    """
501    #: The flags defined for PUSH_PROMISE frames.
502    defined_flags = [
503        Flag('END_HEADERS', 0x04),
504        Flag('PADDED', 0x08)
505    ]
506
507    #: The type byte defined for PUSH_PROMISE frames.
508    type = 0x05
509
510    stream_association = _STREAM_ASSOC_HAS_STREAM
511
512    def __init__(self, stream_id, promised_stream_id=0, data=b'', **kwargs):
513        super().__init__(stream_id, **kwargs)
514
515        #: The stream ID that is promised by this frame.
516        self.promised_stream_id = promised_stream_id
517
518        #: The HPACK-encoded header block for the simulated request on the new
519        #: stream.
520        self.data = data
521
522    def _body_repr(self):
523        return "promised_stream_id={}, data={}".format(
524            self.promised_stream_id,
525            _raw_data_repr(self.data),
526        )
527
528    def serialize_body(self):
529        padding_data = self.serialize_padding_data()
530        padding = b'\0' * self.pad_length
531        data = _STRUCT_L.pack(self.promised_stream_id)
532        return b''.join([padding_data, data, self.data, padding])
533
534    def parse_body(self, data):
535        padding_data_length = self.parse_padding_data(data)
536
537        try:
538            self.promised_stream_id = _STRUCT_L.unpack(
539                data[padding_data_length:padding_data_length + 4]
540            )[0]
541        except struct.error:
542            raise InvalidFrameError("Invalid PUSH_PROMISE body")
543
544        self.data = (
545            data[padding_data_length + 4:len(data)-self.pad_length].tobytes()
546        )
547        self.body_len = len(data)
548
549        if self.promised_stream_id == 0 or self.promised_stream_id % 2 != 0:
550            raise InvalidDataError(
551                "Invalid PUSH_PROMISE promised stream id: %s" %
552                self.promised_stream_id
553            )
554
555        if self.pad_length and self.pad_length >= self.body_len:
556            raise InvalidPaddingError("Padding is too long.")
557
558
559class PingFrame(Frame):
560    """
561    The PING frame is a mechanism for measuring a minimal round-trip time from
562    the sender, as well as determining whether an idle connection is still
563    functional. PING frames can be sent from any endpoint.
564    """
565    #: The flags defined for PING frames.
566    defined_flags = [Flag('ACK', 0x01)]
567
568    #: The type byte defined for PING frames.
569    type = 0x06
570
571    stream_association = _STREAM_ASSOC_NO_STREAM
572
573    def __init__(self, stream_id=0, opaque_data=b'', **kwargs):
574        super().__init__(stream_id, **kwargs)
575
576        #: The opaque data sent in this PING frame, as a bytestring.
577        self.opaque_data = opaque_data
578
579    def _body_repr(self):
580        return "opaque_data={}".format(
581            self.opaque_data,
582        )
583
584    def serialize_body(self):
585        if len(self.opaque_data) > 8:
586            raise InvalidFrameError(
587                "PING frame may not have more than 8 bytes of data, got %s" %
588                self.opaque_data
589            )
590
591        data = self.opaque_data
592        data += b'\x00' * (8 - len(self.opaque_data))
593        return data
594
595    def parse_body(self, data):
596        if len(data) != 8:
597            raise InvalidFrameError(
598                "PING frame must have 8 byte length: got %s" % len(data)
599            )
600
601        self.opaque_data = data.tobytes()
602        self.body_len = 8
603
604
605class GoAwayFrame(Frame):
606    """
607    The GOAWAY frame informs the remote peer to stop creating streams on this
608    connection. It can be sent from the client or the server. Once sent, the
609    sender will ignore frames sent on new streams for the remainder of the
610    connection.
611    """
612    #: The flags defined for GOAWAY frames.
613    defined_flags = []
614
615    #: The type byte defined for GOAWAY frames.
616    type = 0x07
617
618    stream_association = _STREAM_ASSOC_NO_STREAM
619
620    def __init__(self,
621                 stream_id=0,
622                 last_stream_id=0,
623                 error_code=0,
624                 additional_data=b'',
625                 **kwargs):
626        super().__init__(stream_id, **kwargs)
627
628        #: The last stream ID definitely seen by the remote peer.
629        self.last_stream_id = last_stream_id
630
631        #: The error code for connection teardown.
632        self.error_code = error_code
633
634        #: Any additional data sent in the GOAWAY.
635        self.additional_data = additional_data
636
637    def _body_repr(self):
638        return "last_stream_id={}, error_code={}, additional_data={}".format(
639            self.last_stream_id,
640            self.error_code,
641            self.additional_data,
642        )
643
644    def serialize_body(self):
645        data = _STRUCT_LL.pack(
646            self.last_stream_id & 0x7FFFFFFF,
647            self.error_code
648        )
649        data += self.additional_data
650
651        return data
652
653    def parse_body(self, data):
654        try:
655            self.last_stream_id, self.error_code = _STRUCT_LL.unpack(
656                data[:8]
657            )
658        except struct.error:
659            raise InvalidFrameError("Invalid GOAWAY body.")
660
661        self.body_len = len(data)
662
663        if len(data) > 8:
664            self.additional_data = data[8:].tobytes()
665
666
667class WindowUpdateFrame(Frame):
668    """
669    The WINDOW_UPDATE frame is used to implement flow control.
670
671    Flow control operates at two levels: on each individual stream and on the
672    entire connection.
673
674    Both types of flow control are hop by hop; that is, only between the two
675    endpoints. Intermediaries do not forward WINDOW_UPDATE frames between
676    dependent connections. However, throttling of data transfer by any receiver
677    can indirectly cause the propagation of flow control information toward the
678    original sender.
679    """
680    #: The flags defined for WINDOW_UPDATE frames.
681    defined_flags = []
682
683    #: The type byte defined for WINDOW_UPDATE frames.
684    type = 0x08
685
686    stream_association = _STREAM_ASSOC_EITHER
687
688    def __init__(self, stream_id, window_increment=0, **kwargs):
689        super().__init__(stream_id, **kwargs)
690
691        #: The amount the flow control window is to be incremented.
692        self.window_increment = window_increment
693
694    def _body_repr(self):
695        return "window_increment={}".format(
696            self.window_increment,
697        )
698
699    def serialize_body(self):
700        return _STRUCT_L.pack(self.window_increment & 0x7FFFFFFF)
701
702    def parse_body(self, data):
703        if len(data) > 4:
704            raise InvalidFrameError(
705                "WINDOW_UPDATE frame must have 4 byte length: got %s" %
706                len(data)
707            )
708
709        try:
710            self.window_increment = _STRUCT_L.unpack(data)[0]
711        except struct.error:
712            raise InvalidFrameError("Invalid WINDOW_UPDATE body")
713
714        if not 1 <= self.window_increment <= 2**31-1:
715            raise InvalidDataError(
716                "WINDOW_UPDATE increment must be between 1 to 2^31-1"
717            )
718
719        self.body_len = 4
720
721
722class HeadersFrame(Padding, Priority, Frame):
723    """
724    The HEADERS frame carries name-value pairs. It is used to open a stream.
725    HEADERS frames can be sent on a stream in the "open" or "half closed
726    (remote)" states.
727
728    The HeadersFrame class is actually basically a data frame in this
729    implementation, because of the requirement to control the sizes of frames.
730    A header block fragment that doesn't fit in an entire HEADERS frame needs
731    to be followed with CONTINUATION frames. From the perspective of the frame
732    building code the header block is an opaque data segment.
733    """
734    #: The flags defined for HEADERS frames.
735    defined_flags = [
736        Flag('END_STREAM', 0x01),
737        Flag('END_HEADERS', 0x04),
738        Flag('PADDED', 0x08),
739        Flag('PRIORITY', 0x20),
740    ]
741
742    #: The type byte defined for HEADERS frames.
743    type = 0x01
744
745    stream_association = _STREAM_ASSOC_HAS_STREAM
746
747    def __init__(self, stream_id, data=b'', **kwargs):
748        super().__init__(stream_id, **kwargs)
749
750        #: The HPACK-encoded header block.
751        self.data = data
752
753    def _body_repr(self):
754        return "exclusive={}, depends_on={}, stream_weight={}, data={}".format(
755            self.exclusive,
756            self.depends_on,
757            self.stream_weight,
758            _raw_data_repr(self.data),
759        )
760
761    def serialize_body(self):
762        padding_data = self.serialize_padding_data()
763        padding = b'\0' * self.pad_length
764
765        if 'PRIORITY' in self.flags:
766            priority_data = self.serialize_priority_data()
767        else:
768            priority_data = b''
769
770        return b''.join([padding_data, priority_data, self.data, padding])
771
772    def parse_body(self, data):
773        padding_data_length = self.parse_padding_data(data)
774        data = data[padding_data_length:]
775
776        if 'PRIORITY' in self.flags:
777            priority_data_length = self.parse_priority_data(data)
778        else:
779            priority_data_length = 0
780
781        self.body_len = len(data)
782        self.data = (
783            data[priority_data_length:len(data)-self.pad_length].tobytes()
784        )
785
786        if self.pad_length and self.pad_length >= self.body_len:
787            raise InvalidPaddingError("Padding is too long.")
788
789
790class ContinuationFrame(Frame):
791    """
792    The CONTINUATION frame is used to continue a sequence of header block
793    fragments. Any number of CONTINUATION frames can be sent on an existing
794    stream, as long as the preceding frame on the same stream is one of
795    HEADERS, PUSH_PROMISE or CONTINUATION without the END_HEADERS flag set.
796
797    Much like the HEADERS frame, hyper treats this as an opaque data frame with
798    different flags and a different type.
799    """
800    #: The flags defined for CONTINUATION frames.
801    defined_flags = [Flag('END_HEADERS', 0x04)]
802
803    #: The type byte defined for CONTINUATION frames.
804    type = 0x09
805
806    stream_association = _STREAM_ASSOC_HAS_STREAM
807
808    def __init__(self, stream_id, data=b'', **kwargs):
809        super().__init__(stream_id, **kwargs)
810
811        #: The HPACK-encoded header block.
812        self.data = data
813
814    def _body_repr(self):
815        return "data={}".format(
816            _raw_data_repr(self.data),
817        )
818
819    def serialize_body(self):
820        return self.data
821
822    def parse_body(self, data):
823        self.data = data.tobytes()
824        self.body_len = len(data)
825
826
827class AltSvcFrame(Frame):
828    """
829    The ALTSVC frame is used to advertise alternate services that the current
830    host, or a different one, can understand. This frame is standardised as
831    part of RFC 7838.
832
833    This frame does no work to validate that the ALTSVC field parameter is
834    acceptable per the rules of RFC 7838.
835
836    .. note:: If the ``stream_id`` of this frame is nonzero, the origin field
837              must have zero length. Conversely, if the ``stream_id`` of this
838              frame is zero, the origin field must have nonzero length. Put
839              another way, a valid ALTSVC frame has ``stream_id != 0`` XOR
840              ``len(origin) != 0``.
841    """
842    type = 0xA
843
844    stream_association = _STREAM_ASSOC_EITHER
845
846    def __init__(self, stream_id, origin=b'', field=b'', **kwargs):
847        super().__init__(stream_id, **kwargs)
848
849        if not isinstance(origin, bytes):
850            raise InvalidDataError("AltSvc origin must be bytestring.")
851        if not isinstance(field, bytes):
852            raise InvalidDataError("AltSvc field must be a bytestring.")
853        self.origin = origin
854        self.field = field
855
856    def _body_repr(self):
857        return "origin={}, field={}".format(
858            self.origin,
859            self.field,
860        )
861
862    def serialize_body(self):
863        origin_len = _STRUCT_H.pack(len(self.origin))
864        return b''.join([origin_len, self.origin, self.field])
865
866    def parse_body(self, data):
867        try:
868            origin_len = _STRUCT_H.unpack(data[0:2])[0]
869            self.origin = data[2:2+origin_len].tobytes()
870
871            if len(self.origin) != origin_len:
872                raise InvalidFrameError("Invalid ALTSVC frame body.")
873
874            self.field = data[2+origin_len:].tobytes()
875        except (struct.error, ValueError):
876            raise InvalidFrameError("Invalid ALTSVC frame body.")
877
878        self.body_len = len(data)
879
880
881class ExtensionFrame(Frame):
882    """
883    ExtensionFrame is used to wrap frames which are not natively interpretable
884    by hyperframe.
885
886    Although certain byte prefixes are ordained by specification to have
887    certain contextual meanings, frames with other prefixes are not prohibited,
888    and may be used to communicate arbitrary meaning between HTTP/2 peers.
889
890    Thus, hyperframe, rather than raising an exception when such a frame is
891    encountered, wraps it in a generic frame to be properly acted upon by
892    upstream consumers which might have additional context on how to use it.
893
894    .. versionadded:: 5.0.0
895    """
896
897    stream_association = _STREAM_ASSOC_EITHER
898
899    def __init__(self, type, stream_id, flag_byte=0x0, body=b'', **kwargs):
900        super().__init__(stream_id, **kwargs)
901        self.type = type
902        self.flag_byte = flag_byte
903        self.body = body
904
905    def _body_repr(self):
906        return "type={}, flag_byte={}, body={}".format(
907            self.type,
908            self.flag_byte,
909            _raw_data_repr(self.body),
910        )
911
912    def parse_flags(self, flag_byte):
913        """
914        For extension frames, we parse the flags by just storing a flag byte.
915        """
916        self.flag_byte = flag_byte
917
918    def parse_body(self, data):
919        self.body = data.tobytes()
920        self.body_len = len(data)
921
922    def serialize(self):
923        """
924        A broad override of the serialize method that ensures that the data
925        comes back out exactly as it came in. This should not be used in most
926        user code: it exists only as a helper method if frames need to be
927        reconstituted.
928        """
929        # Build the frame header.
930        # First, get the flags.
931        flags = self.flag_byte
932
933        header = _STRUCT_HBBBL.pack(
934            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits
935            self.body_len & 0xFF,
936            self.type,
937            flags,
938            self.stream_id & 0x7FFFFFFF  # Stream ID is 32 bits.
939        )
940
941        return header + self.body
942
943
944def _raw_data_repr(data):
945    if not data:
946        return "None"
947    r = binascii.hexlify(data).decode('ascii')
948    if len(r) > 20:
949        r = r[:20] + "..."
950    return "<hex:" + r + ">"
951
952
953_FRAME_CLASSES = [
954    DataFrame,
955    HeadersFrame,
956    PriorityFrame,
957    RstStreamFrame,
958    SettingsFrame,
959    PushPromiseFrame,
960    PingFrame,
961    GoAwayFrame,
962    WindowUpdateFrame,
963    ContinuationFrame,
964    AltSvcFrame,
965]
966#: FRAMES maps the type byte for each frame to the class used to represent that
967#: frame.
968FRAMES = {cls.type: cls for cls in _FRAME_CLASSES}
969