diff --git a/doc/python.rst b/doc/python.rst index a079c590..357e7b3a 100644 --- a/doc/python.rst +++ b/doc/python.rst @@ -448,6 +448,17 @@ Session objects The :py:class:`InvalidArgumentError` will be raised if the *iv* contains duplicate settings ID or invalid value. +.. py:method:: Session.submit_window_update(stream_id, delta_window_size) + + Submits WINDOW_UPDATE frame. The effective range of the + *delta_window_size* is ``[1, (1 << 31)-1]``, inclusive. But the + application must be responsible to keep the resulting window + ``size <= (1 << 31)-1``. + + The :py:class:`InvalidArgumentError` will be raised if the + *delta_window_size* is 0 or negative. The + :py:class:`StreamClosedError` will be raised if the stream is + already closed or does not exist. Frame Types ----------- diff --git a/python/cspdylay.pxd b/python/cspdylay.pxd index 5435f0c5..f993e327 100644 --- a/python/cspdylay.pxd +++ b/python/cspdylay.pxd @@ -128,6 +128,11 @@ cdef extern from 'spdylay/spdylay.h': int32_t last_good_stream_id uint32_t status_code + ctypedef struct spdylay_window_update: + spdylay_ctrl_hd hd + int32_t stream_id + int32_t delta_window_size + ctypedef union spdylay_frame: spdylay_syn_stream syn_stream spdylay_syn_reply syn_reply @@ -136,7 +141,7 @@ cdef extern from 'spdylay/spdylay.h': spdylay_ping ping spdylay_goaway goaway spdylay_headers headers - #spdylay_window_update window_update + spdylay_window_update window_update #spdylay_credential credential ctypedef union spdylay_data_source: @@ -257,3 +262,7 @@ cdef extern from 'spdylay/spdylay.h': int spdylay_submit_settings(spdylay_session *session, uint8_t flags, spdylay_settings_entry *iv, size_t niv) + + int spdylay_submit_window_update(spdylay_session *session, + int32_t stream_id, + int32_t delta_window_size) diff --git a/python/spdylay.pyx b/python/spdylay.pyx index f1ee17a5..45c3a4ac 100644 --- a/python/spdylay.pyx +++ b/python/spdylay.pyx @@ -22,6 +22,9 @@ class ZlibError(Exception): class UnsupportedVersionError(Exception): pass +class StreamClosedError(Exception): + pass + class DataProvider: def __init__(self, source, read_cb): self.source = source @@ -183,6 +186,24 @@ cdef class GoawayFrame(CtrlFrame): def __get__(self): return self.status_code +cdef class WindowUpdateFrame(CtrlFrame): + cdef int32_t stream_id + cdef int32_t delta_window_size + + cdef void fill(self, cspdylay.spdylay_window_update *frame): + self.fillhd(&frame.hd) + + self.stream_id = frame.stream_id + self.delta_window_size = frame.delta_window_size + + property stream_id: + def __get__(self): + return self.stream_id + + property delta_window_size: + def __get__(self): + return self.delta_window_size + cdef object cnv2pynv(char **nv): ''' Convert C-style name/value pairs ``nv`` to Python style pairs. ''' @@ -253,6 +274,7 @@ cdef void on_ctrl_recv_callback(cspdylay.spdylay_session *session, cdef SettingsFrame settings cdef PingFrame ping cdef GoawayFrame goaway + cdef WindowUpdateFrame window_update cdef Session pysession = user_data @@ -288,6 +310,10 @@ cdef void on_ctrl_recv_callback(cspdylay.spdylay_session *session, goaway = GoawayFrame() goaway.fill(&frame.goaway) pyframe = goaway + elif frame_type == cspdylay.SPDYLAY_WINDOW_UPDATE: + window_update = WindowUpdateFrame() + window_update.fill(&frame.window_update) + pyframe = window_update if pyframe: try: @@ -739,6 +765,19 @@ cdef class Session: elif rv == cspdylay.SPDYLAY_ERR_NOMEM: raise MemoryError() + cpdef submit_window_update(self, stream_id, delta_window_size): + cdef int rv + rv = cspdylay.spdylay_submit_window_update(self._c_session, stream_id, + delta_window_size) + if rv == 0: + return + elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT: + raise InvalidArgumentError() + elif rv == cspdylay.SPDYLAY_ERR_STREAM_CLOSED: + raise StreamClosedError() + elif rv == cspdylay.SPDYLAY_ERR_NOMEM: + raise MemoryError() + cpdef submit_settings(self, flags, iv): ''' Submit SETTINGS frame. iv is list of tuple (settings_id, flag, value) diff --git a/python/spdylay_tests.py b/python/spdylay_tests.py index fa4ec632..9e6b1f1c 100644 --- a/python/spdylay_tests.py +++ b/python/spdylay_tests.py @@ -314,5 +314,26 @@ class SpdylayTests(unittest.TestCase): self.assertEqual(spdylay.PING, frame.frame_type) self.assertEqual(1, frame.unique_id) + def test_submit_window_update(self): + self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_NONE, 0, 2, + [(b':path', b'/')], None) + self.client_session.send() + self.server_session.recv() + + self.assertEqual(1, len(self.server_streams.recv_frames)) + frame = self.server_streams.recv_frames[0] + self.assertEqual(spdylay.SYN_STREAM, frame.frame_type) + self.assertEqual(1, frame.stream_id) + + self.server_session.submit_window_update(1, 4096) + self.server_session.send() + self.client_session.recv() + + self.assertEqual(1, len(self.client_streams.recv_frames)) + frame = self.client_streams.recv_frames[0] + self.assertEqual(spdylay.WINDOW_UPDATE, frame.frame_type) + self.assertEqual(1, frame.stream_id) + self.assertEqual(4096, frame.delta_window_size) + if __name__ == '__main__': unittest.main()