diff --git a/doc/python.rst b/doc/python.rst index cebe646f..752ec2be 100644 --- a/doc/python.rst +++ b/doc/python.rst @@ -262,10 +262,14 @@ Session objects request message bodies (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9) must be specified with ``:method`` key in nv (e.g. ``POST``). The type - of *data_prd* is expected to be :py:class:`DataProvider`. This - method does not increase reference count of *data_prd*, so the - application must hold the reference to it until the stream is - closed. If *data_prd* is ``None``, SYN_STREAM have FLAG_FIN set. + of *data_prd* is expected to be :py:class:`DataProvider`. If + *data_prd* is ``None``, SYN_STREAM have FLAG_FIN set. + + .. note:: + + This method does not increase reference count of *data_prd*, + so the application must hold the reference to it until the + stream is closed. The *stream_user_data* is data associated to the stream opened by this request and can be an arbitrary object, which can be @@ -309,10 +313,14 @@ Session objects If *data_prd* is not ``None``, it provides data which will be sent in subsequent DATA frames. The type of *data_prd* is expected to - be :py:class:`DataProvider`. This method does not increase - reference count of *data_prd*, so the application must hold the - reference to it until the stream is closed. If *data_prd* is - ``None``, SYN_REPLY have FLAG_FIN set. + be :py:class:`DataProvider`. If *data_prd* is ``None``, SYN_REPLY + have FLAG_FIN set. + + .. note:: + + This method does not increase reference count of *data_prd*, + so the application must hold the reference to it until the + stream is closed. The :py:class:`InvalidArgumentError` will be raised if the *nv* includes empty name or ``None`` value. @@ -376,6 +384,20 @@ Session objects The :py:class:`InvalidArgumentError` will be raised if the *nv* includes empty name or ``None`` value. +.. py:method:: Session.submit_data(stream_id, flags, data_prd) + + Submits one or more DATA frames to the stream *stream_id*. The + data to be sent are provided by *data_prd*. The type of + *data_prd* is expected to be :py:class:`DataProvider`. If *flags* + contains :py:const:`DATA_FLAG_FIN`, the last DATA frame has + FLAG_FIN set. + + .. note:: + + This method does not increase reference count of *data_prd*, + so the application must hold the reference to it until the + stream is closed. + .. py:method:: Session.submit_rst_stream(stream_id, status_code) Submits RST_STREAM frame to cancel/reject the stream *stream_id* diff --git a/python/cspdylay.pxd b/python/cspdylay.pxd index c0579e0b..ef5817d9 100644 --- a/python/cspdylay.pxd +++ b/python/cspdylay.pxd @@ -233,6 +233,9 @@ cdef extern from 'spdylay/spdylay.h': int spdylay_submit_syn_reply(spdylay_session *session, uint8_t flags, int32_t stream_id, char **nv) + int spdylay_submit_data(spdylay_session *session, int32_t stream_id, + uint8_t flags, spdylay_data_provider *data_prd) + int spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id, uint32_t status_code) diff --git a/python/spdylay.pyx b/python/spdylay.pyx index 4258417e..e3efeece 100644 --- a/python/spdylay.pyx +++ b/python/spdylay.pyx @@ -644,6 +644,23 @@ cdef class Session: elif rv == cspdylay.SPDYLAY_ERR_NOMEM: raise MemoryError() + cpdef submit_data(self, stream_id, flags, data_prd): + cdef cspdylay.spdylay_data_provider c_data_prd + cdef cspdylay.spdylay_data_provider *c_data_prd_ptr + cpdef int rv + if data_prd: + create_c_data_prd(&c_data_prd, data_prd) + c_data_prd_ptr = &c_data_prd + else: + c_data_prd_ptr = NULL + + rv = cspdylay.spdylay_submit_data(self._c_session, stream_id, + flags, c_data_prd_ptr) + if rv == 0: + return + elif rv == cspdylay.SPDYLAY_ERR_NOMEM: + raise MemoryError() + cpdef submit_rst_stream(self, stream_id, status_code): cdef int rv rv = cspdylay.spdylay_submit_rst_stream(self._c_session, stream_id, diff --git a/python/spdylay_tests.py b/python/spdylay_tests.py index 1b63248e..11ddf84b 100644 --- a/python/spdylay_tests.py +++ b/python/spdylay_tests.py @@ -121,9 +121,9 @@ class SpdylayTests(unittest.TestCase): def test_submit_syn_stream_and_syn_stream(self): self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_FIN, 0, 2, - [(b':path', b'/')], None); - self.client_session.send(); - self.server_session.recv(); + [(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] @@ -135,8 +135,8 @@ class SpdylayTests(unittest.TestCase): self.server_session.submit_syn_reply(spdylay.CTRL_FLAG_FIN, 1, [(b':version', b'HTTP/1.1')]) - self.server_session.send(); - self.client_session.recv(); + self.server_session.send() + self.client_session.recv() self.assertEqual(1, len(self.client_streams.recv_frames)) frame = self.client_streams.recv_frames[0] @@ -146,13 +146,13 @@ class SpdylayTests(unittest.TestCase): def test_submit_rst_stream(self): self.client_session.submit_syn_stream(spdylay.CTRL_FLAG_FIN, 0, 2, - [(b':path', b'/')], None); - self.client_session.send(); - self.server_session.recv(); + [(b':path', b'/')], None) + self.client_session.send() + self.server_session.recv() self.server_session.submit_rst_stream(1, spdylay.PROTOCOL_ERROR) - self.server_session.send(); - self.client_session.recv(); + self.server_session.send() + self.client_session.recv() self.assertEqual(1, len(self.client_streams.recv_frames)) frame = self.client_streams.recv_frames[0] @@ -162,8 +162,8 @@ class SpdylayTests(unittest.TestCase): def test_submit_goaway(self): self.client_session.submit_goaway(spdylay.GOAWAY_PROTOCOL_ERROR) - self.client_session.send(); - self.server_session.recv(); + self.client_session.send() + self.server_session.recv() self.assertEqual(1, len(self.server_streams.recv_frames)) frame = self.server_streams.recv_frames[0] @@ -179,8 +179,8 @@ class SpdylayTests(unittest.TestCase): def test_fail_session(self): self.client_session.fail_session(spdylay.GOAWAY_PROTOCOL_ERROR) - self.client_session.send(); - self.server_session.recv(); + self.client_session.send() + self.server_session.recv() self.assertEqual(1, len(self.server_streams.recv_frames)) frame = self.server_streams.recv_frames[0] @@ -263,5 +263,24 @@ class SpdylayTests(unittest.TestCase): with self.assertRaises(spdylay.CallbackFailureError): self.client_session.send() + def test_submit_data(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) + + data_prd = spdylay.DataProvider(io.BytesIO(b'Hello World'), read_cb) + self.client_session.submit_data(1, spdylay.DATA_FLAG_FIN, data_prd) + self.client_session.send() + self.server_session.recv() + + self.assertEqual(b'Hello World', + self.server_streams.recv_data.getvalue()) + if __name__ == '__main__': unittest.main()