Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit a553396

Browse files
committed
Use HTTP2 error code registry
1 parent c9d352b commit a553396

File tree

2 files changed

+109
-4
lines changed

2 files changed

+109
-4
lines changed

hyper/http20/connection.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from .response import HTTP20Response, HTTP20Push
2020
from .window import FlowControlManager
2121
from .exceptions import ConnectionError
22+
from .error_code_registry import H2_ERROR_CODE_REGISTRY
2223

2324
import errno
2425
import logging
@@ -375,10 +376,23 @@ def receive_frame(self, frame):
375376
# shutdown and all is well. Otherwise, throw an exception.
376377
self.close()
377378

379+
# If an error occured, try to read the error description from
380+
# code registry otherwise use the frame's additional data.
378381
if frame.error_code != 0:
382+
err_no = hex(frame.error_code)
383+
384+
if frame.error_code > 0 and \
385+
frame.error_code < len(H2_ERROR_CODE_REGISTRY):
386+
err_name = H2_ERROR_CODE_REGISTRY[frame.error_code]['Name']
387+
err_description = frame.additional_data or \
388+
H2_ERROR_CODE_REGISTRY[frame.error_code]['Description']
389+
else:
390+
err_name = frame.error_code;
391+
err_description = frame.additional_data
392+
379393
raise ConnectionError(
380-
"Encountered error %d, extra data %s." %
381-
(frame.error_code, frame.additional_data)
394+
"Encountered error %s %s: %s." % (err_name, err_no,
395+
err_description)
382396
)
383397
elif frame.type == BlockedFrame.type:
384398
increment = self.window_manager._blocked()

test/test_hyper.py

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from hyper.packages.hyperframe.frame import (
33
Frame, DataFrame, RstStreamFrame, SettingsFrame,
44
PushPromiseFrame, PingFrame, WindowUpdateFrame, HeadersFrame,
5-
ContinuationFrame, BlockedFrame,
5+
ContinuationFrame, BlockedFrame, GoAwayFrame,
66
)
77
from hyper.packages.hpack.hpack_compat import Encoder, Decoder
88
from hyper.http20.connection import HTTP20Connection
@@ -11,7 +11,7 @@
1111
)
1212
from hyper.http20.response import HTTP20Response, HTTP20Push
1313
from hyper.http20.exceptions import (
14-
HPACKDecodingError, HPACKEncodingError, ProtocolError
14+
HPACKDecodingError, HPACKEncodingError, ProtocolError, ConnectionError,
1515
)
1616
from hyper.http20.window import FlowControlManager
1717
from hyper.http20.util import (
@@ -20,6 +20,7 @@
2020
from hyper.common.headers import HTTPHeaderMap
2121
from hyper.compat import zlib_compressobj
2222
from hyper.contrib import HTTP20Adapter
23+
from hyper.http20.error_code_registry import H2_ERROR_CODE_REGISTRY
2324
import errno
2425
import os
2526
import pytest
@@ -1183,6 +1184,96 @@ def test_stripping_multiple_connection_headers(self):
11831184

11841185
assert h2_safe_headers(headers) == stripped
11851186

1187+
def test_goaway_frame_PROTOCOL_ERROR(self):
1188+
f = GoAwayFrame(0)
1189+
# Set error code to PROTOCOL_ERROR
1190+
f.error_code = 1;
1191+
1192+
c = HTTP20Connection('www.google.com')
1193+
c._sock = DummySocket()
1194+
1195+
# 'Receive' the GOAWAY frame.
1196+
# Validate that the spec error name and description are used to throw
1197+
# the connection exception.
1198+
with pytest.raises(ConnectionError) as conn_err:
1199+
c.receive_frame(f)
1200+
1201+
err_msg = str(conn_err)
1202+
assert H2_ERROR_CODE_REGISTRY[f.error_code]['Name'] in err_msg
1203+
assert H2_ERROR_CODE_REGISTRY[f.error_code]['Description'] in err_msg
1204+
assert hex(f.error_code) in err_msg
1205+
1206+
def test_goaway_frame_HTTP_1_1_REQUIRED(self):
1207+
f = GoAwayFrame(0)
1208+
# Set error code to HTTP_1_1_REQUIRED
1209+
f.error_code = 13;
1210+
1211+
c = HTTP20Connection('www.google.com')
1212+
c._sock = DummySocket()
1213+
1214+
# 'Receive' the GOAWAY frame.
1215+
# Validate that the spec error name and description are used to throw
1216+
# the connection exception.
1217+
with pytest.raises(ConnectionError) as conn_err:
1218+
c.receive_frame(f)
1219+
1220+
err_msg = str(conn_err)
1221+
assert H2_ERROR_CODE_REGISTRY[f.error_code]['Name'] in err_msg
1222+
assert H2_ERROR_CODE_REGISTRY[f.error_code]['Description'] in err_msg
1223+
assert hex(f.error_code) in err_msg
1224+
1225+
def test_goaway_frame_NO_ERROR(self):
1226+
f = GoAwayFrame(0)
1227+
# Set error code to NO_ERROR
1228+
f.error_code = 0;
1229+
1230+
c = HTTP20Connection('www.google.com')
1231+
c._sock = DummySocket()
1232+
1233+
# 'Receive' the GOAWAY frame.
1234+
# Test makes sure no exception is raised; error code 0 means we are
1235+
# dealing with a standard and graceful shutdown.
1236+
c.receive_frame(f)
1237+
1238+
def test_goaway_frame_additional_data(self):
1239+
f = GoAwayFrame(0)
1240+
# Set error code to SETTINGS_TIMEOUT
1241+
f.error_code = 4;
1242+
f.additional_data = 'special additional data';
1243+
1244+
c = HTTP20Connection('www.google.com')
1245+
c._sock = DummySocket()
1246+
1247+
# 'Receive' the GOAWAY frame.
1248+
# The connection error contains the extra data if it's available and
1249+
# the description from the spec if it's not available. This test
1250+
# validates that the additional data replaces the standard description.
1251+
with pytest.raises(ConnectionError) as conn_err:
1252+
c.receive_frame(f)
1253+
1254+
err_msg = str(conn_err)
1255+
assert H2_ERROR_CODE_REGISTRY[f.error_code]['Name'] in err_msg
1256+
assert 'special additional data' in err_msg
1257+
assert hex(f.error_code) in err_msg
1258+
1259+
def test_goaway_frame_invalid_error_code(self):
1260+
f = GoAwayFrame(0)
1261+
# Set error code to non existing error
1262+
f.error_code = 100;
1263+
f.additional_data = 'data about non existing error code';
1264+
1265+
c = HTTP20Connection('www.google.com')
1266+
c._sock = DummySocket()
1267+
1268+
# 'Receive' the GOAWAY frame.
1269+
# If the error code does not exist in the spec then the additional
1270+
# data is used instead.
1271+
with pytest.raises(ConnectionError) as conn_err:
1272+
c.receive_frame(f)
1273+
1274+
err_msg = str(conn_err)
1275+
assert 'data about non existing error code' in err_msg
1276+
assert hex(f.error_code) in err_msg
11861277

11871278
# Some utility classes for the tests.
11881279
class NullEncoder(object):

0 commit comments

Comments
 (0)