364 lines
13 KiB
ReStructuredText
364 lines
13 KiB
ReStructuredText
|
Spdylay Python Extension
|
||
|
========================
|
||
|
|
||
|
.. py:module:: spdylay
|
||
|
|
||
|
This is the Python extension of Spdylay library. The wrapping is made
|
||
|
using Cython. The extension provides mostly same APIs as original C
|
||
|
API. The API is still callback-centric. We use exceptions instead of
|
||
|
error code where they are appropriate.
|
||
|
|
||
|
Build
|
||
|
-----
|
||
|
|
||
|
To generate C source code from ``spdylay.pyx``, run ``cython``::
|
||
|
|
||
|
$ cython spdylay.pyx
|
||
|
|
||
|
To build extension, run ``setup.py``::
|
||
|
|
||
|
$ python setup.py build_ext
|
||
|
|
||
|
Session objects
|
||
|
---------------
|
||
|
|
||
|
.. py:class:: Session(side, version, send_cb=None, recv_cb=None, on_ctrl_recv_cb=None, on_data_chunk_recv_cb=None, on_stream_close_cb=None, on_request_recv_cb=None, user_data=None)
|
||
|
|
||
|
This is the class to hold the resources needed for a SPDY session.
|
||
|
Sending and receiving SPDY frames are done using the methods of
|
||
|
this class.
|
||
|
|
||
|
The *side* specifies server or client. Use one of the following:
|
||
|
|
||
|
.. py:data:: CLIENT
|
||
|
|
||
|
Indicates client.
|
||
|
|
||
|
.. py:data:: SERVER
|
||
|
|
||
|
Indicates server.
|
||
|
|
||
|
The *version* specifies SPDY protocol version. Use of the following:
|
||
|
|
||
|
.. py:data:: PROTO_SPDY2
|
||
|
|
||
|
Indicates SPDY/2.
|
||
|
|
||
|
.. py:data:: PROTO_SPDY3
|
||
|
|
||
|
Indicates SPDY/3.
|
||
|
|
||
|
The *user_data* specifies opaque object tied to this object. It
|
||
|
can be accessed through :py:attr:`user_data` attribute.
|
||
|
|
||
|
The *recv_cb* specifies callback function (callable) invoked when
|
||
|
the object wants to receive data from the remote peer. The
|
||
|
signature of this callback is:
|
||
|
|
||
|
.. py:function:: recv_cb(session, length)
|
||
|
|
||
|
The *session* is the :py:class:`Session` object invoking the
|
||
|
callback. The implementation of this function must read at
|
||
|
most *length* bytes of bytestring and return it. If it cannot
|
||
|
read any single byte without blocking, it must return empty
|
||
|
bytestring or ``None``. If it gets EOF before it reads any
|
||
|
single byte, it must raise :py:class:`EOFError`. For other
|
||
|
errors, it must raise :py:class:`CallbackFailureError`.
|
||
|
|
||
|
The *send_cb* specifies callback function (callable) invoked when
|
||
|
session wants to send data to the remote peer. The signature of
|
||
|
this callback is:
|
||
|
|
||
|
.. py:function:: send_cb(session, data)
|
||
|
|
||
|
The *session* is the :py:class:`Session` object invoking the
|
||
|
callback. The *data* is the bytestring to send. The
|
||
|
implementation of this function will send all or part of
|
||
|
*data*. It must return the number of bytes sent if it
|
||
|
succeeds. If it cannot send any single byte without blocking,
|
||
|
it must return 0 or ``None``. For other errors, it must return
|
||
|
:py:class:`CallbackFailureError`.
|
||
|
|
||
|
The *on_ctrl_recv_cb* specifies callback function (callable)
|
||
|
invoked when a control frame is received.
|
||
|
|
||
|
.. py:function:: on_ctrl_recv_cb(session, frame)
|
||
|
|
||
|
The *session* is the :py:class:`Session` object invoking the
|
||
|
callback. The *frame* is the received control
|
||
|
frame. ``frame.frame_type`` tells the type of frame. See
|
||
|
`Frame Types`_ for the details. Once the frame type is
|
||
|
identified, access attribute of the *frame* to get
|
||
|
information.
|
||
|
|
||
|
The *on_data_chunk_recv_cb* specifies callback function (callable)
|
||
|
invoked when a chunk of data in DATA frame is received.
|
||
|
|
||
|
.. py:function:: on_data_chunk_recv_cb(session, flags, stream_id, data)
|
||
|
|
||
|
The *session* is the :py:class:`Session` object invoking the
|
||
|
callback. The *stream_id* is the stream ID this DATA frame
|
||
|
belongs to. The *flags* is the flags of DATA frame which this
|
||
|
data chunk is contained. ``(flags & DATA_FLAG_FIN) != 0`` does
|
||
|
not necessarily mean this chunk of data is the last one in the
|
||
|
stream. You should use :py:func:`on_data_recv_cb` to know all
|
||
|
data frames are received. The *data* is the bytestring of
|
||
|
received data.
|
||
|
|
||
|
The *on_stream_close_cb* specifies callback function (callable)
|
||
|
invoked when the stream is closed.
|
||
|
|
||
|
.. py:function:: on_stream_close_cb(session, stream_id, status_code)
|
||
|
|
||
|
The *session* is the :py:class:`Session` object invoking the
|
||
|
callback. The *stream_id* indicates the stream ID. The reason
|
||
|
of closure is indicated by the *status_code*. See `Stream
|
||
|
Status Codes`_ for the details. The stream_user_data, which
|
||
|
was specified in :py:meth:`submit_request()` or
|
||
|
:py:meth:`submit_syn_stream()`, is still available in this
|
||
|
function.
|
||
|
|
||
|
The *on_request_recv_cb* specifies callback function (callable)
|
||
|
invoked when the request from the remote peer is received. In
|
||
|
other words, the frame with FIN flag set is received. In HTTP,
|
||
|
this means HTTP request, including request body, is fully
|
||
|
received.
|
||
|
|
||
|
.. py:function:: on_request_recv_cb(session, stream_id)
|
||
|
|
||
|
The *session* is the :py:class:`Session` object invoking the
|
||
|
callback. The *stream_id* indicates the stream ID.
|
||
|
|
||
|
.. py:attribute:: Session.user_data
|
||
|
|
||
|
The object passed in the constructor as *user_data* argument.
|
||
|
This attribute is read-only.
|
||
|
|
||
|
.. py:method:: Session.send()
|
||
|
|
||
|
Sends pending frames to the remote peer. This method retrieves
|
||
|
the highest prioritized frame from the outbound queue and sends it
|
||
|
to the remote peer. It does this as many as possible until the
|
||
|
user callback :py:func:`send_cb` returns 0 or ``None`` or the
|
||
|
outbound queue becomes empty. This method calls several callback
|
||
|
functions which are passed when initializing the session. See
|
||
|
:func:`spdylay_session_send` about the callback functions invoked
|
||
|
from this method.
|
||
|
|
||
|
.. py:method:: Session.recv(data=None)
|
||
|
|
||
|
Receives frames from the remote peer. This method receives as
|
||
|
many frames as possible until the user callback :py:func:`recv_cb`
|
||
|
returns empty bytestring or ``None``. This function calls several
|
||
|
callback functions which are passed when initializing the session.
|
||
|
See :func:`spdylay_session_recv` about the callback functions
|
||
|
invoked from this method. If data is ``None``, this method will
|
||
|
invoke :py:func:`recv_cb` callback function to receive incoming
|
||
|
data. If data is not ``None``, it must be a bytestring and this
|
||
|
method uses it as the incoming data and does not call
|
||
|
:py:func:`recv_cb` callback function.
|
||
|
|
||
|
.. py:method:: Session.resume_data(stream_id)
|
||
|
|
||
|
Puts back previously deferred DATA frame in the stream *stream_id*
|
||
|
to the outbound queue.
|
||
|
|
||
|
The :py:class:`InvalidArgumentError` will be raised if the stream
|
||
|
does not exist or no deferred data exist.
|
||
|
|
||
|
.. py:method:: Session.want_read()
|
||
|
|
||
|
Returns ``True`` if session wants to receive data from the
|
||
|
remote peer.
|
||
|
|
||
|
If both :py:meth:`want_read()` and :py:meth:`want_write()` return
|
||
|
``False``, the application should drop the connection.
|
||
|
|
||
|
.. py:method:: Session.want_write()
|
||
|
|
||
|
Returns ``True`` if session wants to send data to the remote peer.
|
||
|
|
||
|
If both :py:meth:`want_read()` and :py:meth:`want_write()` return
|
||
|
``False``, the application should drop the connection.
|
||
|
|
||
|
.. py:method:: Session.get_stream_user_data(stream_id)
|
||
|
|
||
|
Returns stream_user_data for the stream *stream_id*. The
|
||
|
stream_user_data is provided by :py:meth:`submit_request()` or
|
||
|
:py:meth:`submit_syn_stream()`. If the stream is initiated by the
|
||
|
remote endpoint, stream_user_data is always ``None``. If the
|
||
|
stream is initiated by the local endpoint and ``None`` is given in
|
||
|
:py:meth:`submit_request()` or :py:meth:`submit_syn_stream()`,
|
||
|
then this function returns ``None``. If the stream does not exist,
|
||
|
this function returns ``None``.
|
||
|
|
||
|
.. py:method:: Session.get_outbound_queue_size()
|
||
|
|
||
|
Returns the number of frames in the outbound queue. This does not
|
||
|
include the deferred DATA frames.
|
||
|
|
||
|
.. py:method:: Session.get_pri_lowest()
|
||
|
|
||
|
Returns lowest priority value for the session.
|
||
|
|
||
|
.. py:method:: Session.fail_session(status_code)
|
||
|
|
||
|
Submits GOAWAY frame. The status code *status_code* is ignored if
|
||
|
the protocol version is :py:const:`PROTO_SPDY2`.
|
||
|
|
||
|
This method should be called when the connection should be
|
||
|
terminated after sending GOAWAY. If the remaining streams should
|
||
|
be processed after GOAWAY, use :py:meth:`submit_goaway()` instead.
|
||
|
|
||
|
.. py:method:: Session.submit_request(pri, nv, data_prd=None, stream_user_data=None)
|
||
|
|
||
|
Submits SYN_STREAM frame and optionally one or more DATA frames.
|
||
|
|
||
|
The *pri* is priority of this request. ``0`` is the highest
|
||
|
priority value. Use :py:meth:`get_pri_lowest()` to know the lowest
|
||
|
priority value for this session.
|
||
|
|
||
|
The *nv* contains the name/value pairs. For ``i >= 0``,
|
||
|
``nv[2 * i]`` contains a bytestring indicating name and
|
||
|
``nv[2 * i + 1]`` contains a bytestring indicating value.
|
||
|
|
||
|
The *nv* must include following name/value pairs:
|
||
|
|
||
|
``:method``
|
||
|
HTTP method (e.g., ``GET``, ``POST``, ``HEAD``, etc)
|
||
|
``:scheme``
|
||
|
URI scheme (e.g., ``https``)
|
||
|
``:path``
|
||
|
Absolute path and parameters of this request (e.g., ``/foo``,
|
||
|
``/foo;bar;haz?h=j&y=123``)
|
||
|
``:version``
|
||
|
HTTP version (e.g., ``HTTP/1.1``)
|
||
|
``:host``
|
||
|
The hostport portion of the URI for this request (e.g.,
|
||
|
``example.org:443``). This is the same as the HTTP “Host”
|
||
|
header field.
|
||
|
|
||
|
If the session is initialized with the version
|
||
|
:py:const:`PROTO_SPDY2`, the above names are translated to
|
||
|
``method``, ``scheme``, ``url``, ``version`` and ``host``
|
||
|
respectively.
|
||
|
|
||
|
The names in *nv* will be lower-cased when they are sent.
|
||
|
|
||
|
If *data_prd* is not ``None``, it provides data which will be sent
|
||
|
in subsequent DATA frames. In this case, a method that allows
|
||
|
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.
|
||
|
|
||
|
The *stream_user_data* is data associated to the stream opened by
|
||
|
this request and can be an arbitrary object, which can be
|
||
|
retrieved later by :py:meth:`get_stream_user_data()`.
|
||
|
|
||
|
Since the library reorders the frames and tries to send the
|
||
|
highest prioritized one first and the SPDY specification requires
|
||
|
the stream ID must be strictly increasing, the stream ID of this
|
||
|
request cannot be known until it is about to sent. To know the
|
||
|
stream ID of the request, the application can use
|
||
|
:py:func:`before_ctrl_send_cb`. This callback is called just
|
||
|
before the frame is sent. For SYN_STREAM frame, the argument frame
|
||
|
has the stream ID assigned. Also since the stream is already
|
||
|
opened, :py:meth:`get_stream_user_data()` can be used to get
|
||
|
stream_user_data to identify which SYN_STREAM we are processing.
|
||
|
|
||
|
The :py:class:`InvalidArgumentError` will be raised if the *pri*
|
||
|
is invalid.
|
||
|
|
||
|
.. py:method:: Session.submit_response(stream_id, nv, data_prd=None)
|
||
|
|
||
|
Submits SYN_REPLY frame and optionally one or more DATA frames
|
||
|
against the stream *stream_id*.
|
||
|
|
||
|
The *nv* contains the name/value pairs. For ``i >= 0``,
|
||
|
``nv[2 * i]`` contains a bytestring indicating name and
|
||
|
``nv[2 * i + 1]`` contains a bytestring indicating value.
|
||
|
|
||
|
The *nv* must include following name/value pairs:
|
||
|
|
||
|
``:status``
|
||
|
HTTP status code (e.g., ``200`` or ``200 OK``)
|
||
|
``:version``
|
||
|
HTTP response version (e.g., ``HTTP/1.1``)
|
||
|
|
||
|
If the session is initialized with the version
|
||
|
:py:const:`PROTO_SPDY2`, the above names are translated to
|
||
|
``status`` and ``version`` respectively.
|
||
|
|
||
|
The names in *nv* will be lower-cased when they are sent.
|
||
|
|
||
|
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.
|
||
|
|
||
|
.. py:method:: Session.submit_request()
|
||
|
|
||
|
Frame Types
|
||
|
-----------
|
||
|
|
||
|
.. py:data:: SYN_STREAM
|
||
|
|
||
|
.. py:data:: SYN_REPLY
|
||
|
|
||
|
.. py:data:: RST_STREAM
|
||
|
|
||
|
.. py:data:: SETTINGS
|
||
|
|
||
|
.. py:data:: NOOP
|
||
|
|
||
|
Note that this was deprecated in SPDY/3.
|
||
|
|
||
|
.. py:data:: PING
|
||
|
|
||
|
.. py:data:: GOAWAY
|
||
|
|
||
|
.. py:data:: HEADERS
|
||
|
|
||
|
.. py:data:: WINDOW_UPDATE
|
||
|
|
||
|
This first appeared in SPDY/3.
|
||
|
|
||
|
.. py:data:: CREDENTIAL
|
||
|
|
||
|
This first appeared in SPDY/3.
|
||
|
|
||
|
Stream Status Codes
|
||
|
-------------------
|
||
|
|
||
|
.. py:data:: OK
|
||
|
|
||
|
.. py:data:: PROTOCOL_ERROR
|
||
|
|
||
|
.. py:data:: INVALID_STREAM
|
||
|
|
||
|
.. py:data:: REFUSED_STREAM
|
||
|
|
||
|
.. py:data:: UNSUPPORTED_VERSION
|
||
|
|
||
|
.. py:data:: CANCEL
|
||
|
|
||
|
.. py:data:: INTERNAL_ERROR
|
||
|
|
||
|
.. py:data:: FLOW_CONTROL_ERROR
|
||
|
|
||
|
Following status codes were introduced in SPDY/3.
|
||
|
|
||
|
.. py:data:: STREAM_IN_USE
|
||
|
|
||
|
.. py:data:: STREAM_ALREADY_CLOSED
|
||
|
|
||
|
.. py:data:: INVALID_CREDENTIALS
|
||
|
|
||
|
.. py:data:: FRAME_TOO_LARGE
|