Skip to content

Commit 563e5c6

Browse files
committed
Better handling of unknown frame types
1 parent d8dd594 commit 563e5c6

File tree

2 files changed

+29
-7
lines changed

2 files changed

+29
-7
lines changed

h2/connection.py

+26-6
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828
from .exceptions import (
2929
ProtocolError, NoSuchStreamError, FlowControlError, FrameTooLargeError,
3030
TooManyStreamsError, StreamClosedError, StreamIDTooLowError,
31-
NoAvailableStreamIDError, UnsupportedFrameError, RFC1122Error,
32-
DenialOfServiceError
31+
NoAvailableStreamIDError, RFC1122Error, DenialOfServiceError
3332
)
3433
from .frame_buffer import FrameBuffer
3534
from .settings import Settings, SettingCodes
@@ -46,6 +45,15 @@ class OversizedHeaderListError(Exception):
4645
pass
4746

4847

48+
try:
49+
from hyperframe.frame import ExtensionFrame
50+
except ImportError: # Platform-specific: Hyperframe < 5.0.0
51+
# If the frame doesn't exist, that's just fine: we'll define it ourselves
52+
# and the method will just never be called.
53+
class ExtensionFrame(object):
54+
pass
55+
56+
4957
class ConnectionState(Enum):
5058
IDLE = 0
5159
CLIENT_OPEN = 1
@@ -404,6 +412,7 @@ def __init__(self, client_side=True, header_encoding='utf-8', config=None):
404412
GoAwayFrame: self._receive_goaway_frame,
405413
ContinuationFrame: self._receive_naked_continuation,
406414
AltSvcFrame: self._receive_alt_svc_frame,
415+
ExtensionFrame: self._receive_unknown_frame
407416
}
408417

409418
def _prepare_for_sending(self, frames):
@@ -1575,10 +1584,6 @@ def _receive_frame(self, frame):
15751584
# Closed implicitly, also a connection error, but of type
15761585
# PROTOCOL_ERROR.
15771586
raise
1578-
except KeyError as e: # pragma: no cover
1579-
# We don't have a function for handling this frame. Let's call this
1580-
# a PROTOCOL_ERROR and exit.
1581-
raise UnsupportedFrameError("Unexpected frame: %s" % frame)
15821587
else:
15831588
self._prepare_for_sending(frames)
15841589

@@ -1922,6 +1927,21 @@ def _receive_alt_svc_frame(self, frame):
19221927

19231928
return frames, events
19241929

1930+
def _receive_unknown_frame(self, frame):
1931+
"""
1932+
We have received a frame that we do not understand. This is almost
1933+
certainly an extension frame, though it's impossible to be entirely
1934+
sure.
1935+
1936+
RFC 7540 § 5.5 says that we MUST ignore unknown frame types: so we
1937+
do.
1938+
"""
1939+
# All we do here is log.
1940+
self.config.logger.debug(
1941+
"Received unknown extension frame (ID %d)", frame.stream_id
1942+
)
1943+
return [], []
1944+
19251945
def _local_settings_acked(self):
19261946
"""
19271947
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)