Skip to content

Commit 4f8904b

Browse files
committed
Better handling of unknown frame types
(cherry picked from commit 563e5c6)
1 parent 7fdf5a4 commit 4f8904b

File tree

2 files changed

+29
-6
lines changed

2 files changed

+29
-6
lines changed

h2/connection.py

+26-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from .exceptions import (
2828
ProtocolError, NoSuchStreamError, FlowControlError, FrameTooLargeError,
2929
TooManyStreamsError, StreamClosedError, StreamIDTooLowError,
30-
NoAvailableStreamIDError, UnsupportedFrameError, RFC1122Error
30+
NoAvailableStreamIDError, RFC1122Error
3131
)
3232
from .frame_buffer import FrameBuffer
3333
from .settings import (
@@ -38,6 +38,15 @@
3838
from .utilities import validate_headers, guard_increment_window
3939

4040

41+
try:
42+
from hyperframe.frame import ExtensionFrame
43+
except ImportError: # Platform-specific: Hyperframe < 5.0.0
44+
# If the frame doesn't exist, that's just fine: we'll define it ourselves
45+
# and the method will just never be called.
46+
class ExtensionFrame(object):
47+
pass
48+
49+
4150
class ConnectionState(Enum):
4251
IDLE = 0
4352
CLIENT_OPEN = 1
@@ -358,6 +367,7 @@ def __init__(self, client_side=True, header_encoding='utf-8'):
358367
GoAwayFrame: self._receive_goaway_frame,
359368
ContinuationFrame: self._receive_naked_continuation,
360369
AltSvcFrame: self._receive_alt_svc_frame,
370+
ExtensionFrame: self._receive_unknown_frame
361371
}
362372

363373
def _prepare_for_sending(self, frames):
@@ -1352,10 +1362,6 @@ def _receive_frame(self, frame):
13521362
if frame.stream_id not in self._reset_streams:
13531363
raise
13541364
events = []
1355-
except KeyError as e: # pragma: no cover
1356-
# We don't have a function for handling this frame. Let's call this
1357-
# a PROTOCOL_ERROR and exit.
1358-
raise UnsupportedFrameError("Unexpected frame: %s" % frame)
13591365
else:
13601366
self._prepare_for_sending(frames)
13611367

@@ -1713,6 +1719,21 @@ def _receive_alt_svc_frame(self, frame):
17131719

17141720
return frames, events
17151721

1722+
def _receive_unknown_frame(self, frame):
1723+
"""
1724+
We have received a frame that we do not understand. This is almost
1725+
certainly an extension frame, though it's impossible to be entirely
1726+
sure.
1727+
1728+
RFC 7540 § 5.5 says that we MUST ignore unknown frame types: so we
1729+
do.
1730+
"""
1731+
# All we do here is log.
1732+
self.config.logger.debug(
1733+
"Received unknown extension frame (ID %d)", frame.stream_id
1734+
)
1735+
return [], []
1736+
17161737
def _local_settings_acked(self):
17171738
"""
17181739
Handle the local settings being ACKed, update internal state.

h2/frame_buffer.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,13 @@ def _parse_frame_header(self, data):
6565
"""
6666
try:
6767
frame, length = Frame.parse_frame_header(data[:9])
68-
except UnknownFrameError as e:
68+
except UnknownFrameError as e: # Platform-specific: Hyperframe < 5.0
6969
# Here we do something a bit odd. We want to consume the frame data
7070
# as consistently as possible, but we also don't ever want to yield
7171
# None. Instead, we make sure that, if there is no frame, we
7272
# recurse into ourselves.
73+
# This can only happen now on older versions of hyperframe.
74+
# TODO: Remove in 3.0
7375
length = e.length
7476
frame = None
7577
except ValueError as e:

0 commit comments

Comments
 (0)