diff --git a/hyper/http20/connection.py b/hyper/http20/connection.py index 1806942b..d0b94156 100644 --- a/hyper/http20/connection.py +++ b/hyper/http20/connection.py @@ -10,7 +10,7 @@ from .tls import wrap_socket from .frame import ( DataFrame, HeadersFrame, PushPromiseFrame, RstStreamFrame, SettingsFrame, - Frame, WindowUpdateFrame, GoAwayFrame + Frame, WindowUpdateFrame, GoAwayFrame, PingFrame, ) from .response import HTTP20Response, HTTP20Push from .window import FlowControlManager @@ -321,6 +321,13 @@ def receive_frame(self, frame): """ if isinstance(frame, WindowUpdateFrame): self._out_flow_control_window += frame.window_increment + elif isinstance(frame, PingFrame): + if 'ACK' not in frame.flags: + # The spec requires us to reply with PING+ACK and identical data. + p = PingFrame(0) + p.flags.add('ACK') + p.opaque_data = frame.opaque_data + self._data_cb(p, True) elif isinstance(frame, SettingsFrame): if 'ACK' not in frame.flags: self._update_settings(frame) diff --git a/test/test_hyper.py b/test/test_hyper.py index 2b2fc488..2048ff8a 100644 --- a/test/test_hyper.py +++ b/test/test_hyper.py @@ -1146,6 +1146,34 @@ def test_window_increments_appropriately(self): assert isinstance(queue[2], WindowUpdateFrame) assert queue[2].window_increment == len(b'hi there sir again') + def test_ping_with_ack_ignored(self): + c = HTTP20Connection('www.google.com') + f = PingFrame(0) + f.flags = set(['ACK']) + f.opaque_data = b'12345678' + + def data_cb(frame, tolerate_peer_gone=False): + assert False, 'should not be called' + c._data_cb = data_cb + c.receive_frame(f) + + def test_ping_without_ack_gets_reply(self): + c = HTTP20Connection('www.google.com') + f = PingFrame(0) + f.opaque_data = b'12345678' + + frames = [] + + def data_cb(frame, tolerate_peer_gone=False): + frames.append(frame) + c._data_cb = data_cb + c.receive_frame(f) + + assert len(frames) == 1 + assert frames[0].type == PingFrame.type + assert frames[0].flags == set(['ACK']) + assert frames[0].opaque_data == b'12345678' + class TestServerPush(object): def setup_method(self, method):