From 7a362e23b3c01097611d1ae349916cd0864a66ef Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 22 Aug 2012 01:41:07 +0900 Subject: [PATCH] python: add Session.submit_headers() --- doc/python.rst | 22 ++++++++++++++++++++++ python/cspdylay.pxd | 10 +++++++++- python/spdylay.pyx | 36 ++++++++++++++++++++++++++++++++++++ python/spdylay_tests.py | 22 ++++++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/doc/python.rst b/doc/python.rst index 752ec2be..a7d62da7 100644 --- a/doc/python.rst +++ b/doc/python.rst @@ -384,6 +384,28 @@ Session objects The :py:class:`InvalidArgumentError` will be raised if the *nv* includes empty name or ``None`` value. +.. py:method:: Session.submit_headers(flags, stream_id, nv) + + Submits HEADERS frame. The *flags* is bitwise OR of the following + values: + + * :py:const:`CTRL_FLAG_FIN` + + If *flags* includes :py:const:`CTRL_FLAG_FIN`, this frame has + FLAG_FIN flag set. + + The stream which this frame belongs to is given in the + *stream_id*. The *nv* is the name/value pairs in this frame. + + The *nv* is a list containing the name/value pairs. The each + element is a tuple of 2 bytestrings: name and value (e.g., + ``(b'host', b'localhost')``). + + The names in *nv* will be lower-cased when they are sent. + + 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 diff --git a/python/cspdylay.pxd b/python/cspdylay.pxd index ef5817d9..d5f71fad 100644 --- a/python/cspdylay.pxd +++ b/python/cspdylay.pxd @@ -99,6 +99,11 @@ cdef extern from 'spdylay/spdylay.h': int32_t stream_id char **nv + ctypedef struct spdylay_headers: + spdylay_ctrl_hd hd + int32_t stream_id + char **nv + ctypedef struct spdylay_rst_stream: spdylay_ctrl_hd hd int32_t stream_id @@ -126,7 +131,7 @@ cdef extern from 'spdylay/spdylay.h': spdylay_settings settings #spdylay_ping ping spdylay_goaway goaway - #spdylay_headers headers + spdylay_headers headers #spdylay_window_update window_update #spdylay_credential credential @@ -233,6 +238,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_headers(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) diff --git a/python/spdylay.pyx b/python/spdylay.pyx index e3efeece..3f9c2886 100644 --- a/python/spdylay.pyx +++ b/python/spdylay.pyx @@ -104,6 +104,24 @@ cdef class SynReplyFrame(CtrlFrame): def __get__(self): return self.nv +cdef class HeadersFrame(CtrlFrame): + cdef int32_t stream_id + cdef object nv + + cdef void fill(self, cspdylay.spdylay_headers *frame): + self.fillhd(&frame.hd) + + self.stream_id = frame.stream_id + self.nv = cnv2pynv(frame.nv) + + property stream_id: + def __get__(self): + return self.stream_id + + property nv: + def __get__(self): + return self.nv + cdef class RstStreamFrame(CtrlFrame): cdef int32_t stream_id cdef uint32_t status_code @@ -218,6 +236,7 @@ cdef void on_ctrl_recv_callback(cspdylay.spdylay_session *session, void *user_data): cdef SynStreamFrame syn_stream cdef SynReplyFrame syn_reply + cdef HeadersFrame headers cdef RstStreamFrame rst_stream cdef SettingsFrame settings cdef GoawayFrame goaway @@ -236,6 +255,10 @@ cdef void on_ctrl_recv_callback(cspdylay.spdylay_session *session, syn_reply = SynReplyFrame() syn_reply.fill(&frame.syn_reply) pyframe = syn_reply + elif frame_type == cspdylay.SPDYLAY_HEADERS: + headers = HeadersFrame() + headers.fill(&frame.headers) + pyframe = headers elif frame_type == cspdylay.SPDYLAY_RST_STREAM: rst_stream = RstStreamFrame() rst_stream.fill(&frame.rst_stream) @@ -644,6 +667,19 @@ cdef class Session: elif rv == cspdylay.SPDYLAY_ERR_NOMEM: raise MemoryError() + cpdef submit_headers(self, flags, stream_id, nv): + cdef char **cnv = pynv2cnv(nv) + cdef int rv + rv = cspdylay.spdylay_submit_headers(self._c_session, + flags, stream_id, cnv) + free(cnv) + if rv == 0: + return + elif rv == cspdylay.SPDYLAY_ERR_INVALID_ARGUMENT: + raise InvalidArgumentError(cspdylay.spdylay_strerror(rv)) + 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 diff --git a/python/spdylay_tests.py b/python/spdylay_tests.py index 11ddf84b..27dbd854 100644 --- a/python/spdylay_tests.py +++ b/python/spdylay_tests.py @@ -282,5 +282,27 @@ class SpdylayTests(unittest.TestCase): self.assertEqual(b'Hello World', self.server_streams.recv_data.getvalue()) + def test_submit_headers(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.client_session.submit_headers(spdylay.CTRL_FLAG_FIN, 1, + [(b':host', b'localhost')]) + self.client_session.send() + self.server_session.recv() + + self.assertEqual(2, len(self.server_streams.recv_frames)) + frame = self.server_streams.recv_frames[1] + self.assertEqual(spdylay.HEADERS, frame.frame_type) + self.assertEqual(1, frame.stream_id) + self.assertEqual((b':host', b'localhost'), frame.nv[0]) + if __name__ == '__main__': unittest.main()