frame_buffer.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # -*- coding: utf-8 -*-
  2. """
  3. h2/frame_buffer
  4. ~~~~~~~~~~~~~~~
  5. A data structure that provides a way to iterate over a byte buffer in terms of
  6. frames.
  7. """
  8. from hyperframe.exceptions import UnknownFrameError, InvalidFrameError
  9. from hyperframe.frame import (
  10. Frame, HeadersFrame, ContinuationFrame, PushPromiseFrame
  11. )
  12. from .exceptions import (
  13. ProtocolError, FrameTooLargeError, FrameDataMissingError
  14. )
  15. # To avoid a DOS attack based on sending loads of continuation frames, we limit
  16. # the maximum number we're perpared to receive. In this case, we'll set the
  17. # limit to 64, which means the largest encoded header block we can receive by
  18. # default is 262144 bytes long, and the largest possible *at all* is 1073741760
  19. # bytes long.
  20. #
  21. # This value seems reasonable for now, but in future we may want to evaluate
  22. # making it configurable.
  23. CONTINUATION_BACKLOG = 64
  24. class FrameBuffer(object):
  25. """
  26. This is a data structure that expects to act as a buffer for HTTP/2 data
  27. that allows iteraton in terms of H2 frames.
  28. """
  29. def __init__(self, server=False):
  30. self.data = b''
  31. self.max_frame_size = 0
  32. self._preamble = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n' if server else b''
  33. self._preamble_len = len(self._preamble)
  34. self._headers_buffer = []
  35. def add_data(self, data):
  36. """
  37. Add more data to the frame buffer.
  38. :param data: A bytestring containing the byte buffer.
  39. """
  40. if self._preamble_len:
  41. data_len = len(data)
  42. of_which_preamble = min(self._preamble_len, data_len)
  43. if self._preamble[:of_which_preamble] != data[:of_which_preamble]:
  44. raise ProtocolError("Invalid HTTP/2 preamble.")
  45. data = data[of_which_preamble:]
  46. self._preamble_len -= of_which_preamble
  47. self._preamble = self._preamble[of_which_preamble:]
  48. self.data += data
  49. def _parse_frame_header(self, data):
  50. """
  51. Parses the frame header from the data. Either returns a tuple of
  52. (frame, length), or throws an exception. The returned frame may be None
  53. if the frame is of unknown type.
  54. """
  55. try:
  56. frame, length = Frame.parse_frame_header(data[:9])
  57. except UnknownFrameError as e: # Platform-specific: Hyperframe < 5.0
  58. # Here we do something a bit odd. We want to consume the frame data
  59. # as consistently as possible, but we also don't ever want to yield
  60. # None. Instead, we make sure that, if there is no frame, we
  61. # recurse into ourselves.
  62. # This can only happen now on older versions of hyperframe.
  63. # TODO: Remove in 3.0
  64. length = e.length
  65. frame = None
  66. except ValueError as e:
  67. # The frame header is invalid. This is a ProtocolError
  68. raise ProtocolError("Invalid frame header received: %s" % str(e))
  69. return frame, length
  70. def _validate_frame_length(self, length):
  71. """
  72. Confirm that the frame is an appropriate length.
  73. """
  74. if length > self.max_frame_size:
  75. raise FrameTooLargeError(
  76. "Received overlong frame: length %d, max %d" %
  77. (length, self.max_frame_size)
  78. )
  79. def _update_header_buffer(self, f):
  80. """
  81. Updates the internal header buffer. Returns a frame that should replace
  82. the current one. May throw exceptions if this frame is invalid.
  83. """
  84. # Check if we're in the middle of a headers block. If we are, this
  85. # frame *must* be a CONTINUATION frame with the same stream ID as the
  86. # leading HEADERS or PUSH_PROMISE frame. Anything else is a
  87. # ProtocolError. If the frame *is* valid, append it to the header
  88. # buffer.
  89. if self._headers_buffer:
  90. stream_id = self._headers_buffer[0].stream_id
  91. valid_frame = (
  92. f is not None and
  93. isinstance(f, ContinuationFrame) and
  94. f.stream_id == stream_id
  95. )
  96. if not valid_frame:
  97. raise ProtocolError("Invalid frame during header block.")
  98. # Append the frame to the buffer.
  99. self._headers_buffer.append(f)
  100. if len(self._headers_buffer) > CONTINUATION_BACKLOG:
  101. raise ProtocolError("Too many continuation frames received.")
  102. # If this is the end of the header block, then we want to build a
  103. # mutant HEADERS frame that's massive. Use the original one we got,
  104. # then set END_HEADERS and set its data appopriately. If it's not
  105. # the end of the block, lose the current frame: we can't yield it.
  106. if 'END_HEADERS' in f.flags:
  107. f = self._headers_buffer[0]
  108. f.flags.add('END_HEADERS')
  109. f.data = b''.join(x.data for x in self._headers_buffer)
  110. self._headers_buffer = []
  111. else:
  112. f = None
  113. elif (isinstance(f, (HeadersFrame, PushPromiseFrame)) and
  114. 'END_HEADERS' not in f.flags):
  115. # This is the start of a headers block! Save the frame off and then
  116. # act like we didn't receive one.
  117. self._headers_buffer.append(f)
  118. f = None
  119. return f
  120. # The methods below support the iterator protocol.
  121. def __iter__(self):
  122. return self
  123. def next(self): # Python 2
  124. # First, check that we have enough data to successfully parse the
  125. # next frame header. If not, bail. Otherwise, parse it.
  126. if len(self.data) < 9:
  127. raise StopIteration()
  128. try:
  129. f, length = self._parse_frame_header(self.data)
  130. except InvalidFrameError: # pragma: no cover
  131. raise ProtocolError("Received frame with invalid frame header.")
  132. # Next, check that we have enough length to parse the frame body. If
  133. # not, bail, leaving the frame header data in the buffer for next time.
  134. if len(self.data) < length + 9:
  135. raise StopIteration()
  136. # Confirm the frame has an appropriate length.
  137. self._validate_frame_length(length)
  138. # Don't try to parse the body if we didn't get a frame we know about:
  139. # there's nothing we can do with it anyway.
  140. if f is not None:
  141. try:
  142. f.parse_body(memoryview(self.data[9:9+length]))
  143. except InvalidFrameError:
  144. raise FrameDataMissingError("Frame data missing or invalid")
  145. # At this point, as we know we'll use or discard the entire frame, we
  146. # can update the data.
  147. self.data = self.data[9+length:]
  148. # Pass the frame through the header buffer.
  149. f = self._update_header_buffer(f)
  150. # If we got a frame we didn't understand or shouldn't yield, rather
  151. # than return None it'd be better if we just tried to get the next
  152. # frame in the sequence instead. Recurse back into ourselves to do
  153. # that. This is safe because the amount of work we have to do here is
  154. # strictly bounded by the length of the buffer.
  155. return f if f is not None else self.next()
  156. def __next__(self): # Python 3
  157. return self.next()