Python API Reference
====================

.. py:module:: nghttp2

nghttp2 offers some high level Python API to C library.  The bindings
currently provide HPACK compressor and decompressor classes and HTTP/2
server class.

The extension module is called ``nghttp2``.

``make`` will build the bindings.  The target Python version is
determined by configure script.  If the detected Python version is not
what you expect, specify a path to Python executable in ``PYTHON``
variable as an argument to configure script (e.g., ``./configure
PYTHON=/usr/bin/python3.8``).

HPACK API
---------

.. py:class:: HDDeflater(hd_table_bufsize_max=DEFLATE_MAX_HEADER_TABLE_SIZE)

   This class is used to perform header compression.  The
   *hd_table_bufsize_max* limits the usage of header table in the
   given amount of bytes.  The default value is
   :py:data:`DEFLATE_MAX_HEADER_TABLE_SIZE`.  This is necessary
   because the deflater and inflater share the same amount of header
   table and the inflater decides that number.  The deflater may not
   want to use all header table size because of limited memory
   availability.  In that case, *hd_table_bufsize_max* can be used to
   cap the upper limit of table size whatever the header table size is
   chosen by the inflater.

   .. py:method:: deflate(headers)

      Deflates the *headers*. The *headers* must be sequence of tuple
      of name/value pair, which are byte strings (not unicode string).

      This method returns the deflated header block in byte string.
      Raises the exception if any error occurs.

   .. py:method:: set_no_refset(no_refset)

      Tells the deflater not to use reference set if *no_refset* is
      evaluated to ``True``.  If that happens, on each subsequent
      invocation of :py:meth:`deflate()`, deflater will clear up
      refersent set.

   .. py:method:: change_table_size(hd_table_bufsize_max)

      Changes header table size to *hd_table_bufsize_max* byte.  if
      *hd_table_bufsize_max* is strictly larger than
      ``hd_table_bufsize_max`` given in constructor,
      ``hd_table_bufsize_max`` is used as header table size instead.

      Raises the exception if any error occurs.

   .. py:method:: get_hd_table()

      Returns copy of current dynamic header table.

The following example shows how to deflate header name/value pairs:

.. code-block:: python

   import binascii, nghttp2

   deflater = nghttp2.HDDeflater()

   res = deflater.deflate([(b'foo', b'bar'),
                           (b'baz', b'buz')])

   print(binascii.b2a_hex(res))


.. py:class:: HDInflater()

   This class is used to perform header decompression.

   .. py:method:: inflate(data)

      Inflates the deflated header block *data*. The *data* must be
      byte string.

      Raises the exception if any error occurs.

   .. py:method:: change_table_size(hd_table_bufsize_max)

      Changes header table size to *hd_table_bufsize_max* byte.

      Raises the exception if any error occurs.

   .. py:method:: get_hd_table()

      Returns copy of current dynamic header table.

The following example shows how to inflate deflated header block:

.. code-block:: python

   deflater = nghttp2.HDDeflater()

   data = deflater.deflate([(b'foo', b'bar'),
                            (b'baz', b'buz')])

   inflater = nghttp2.HDInflater()

   hdrs = inflater.inflate(data)

   print(hdrs)


.. py:function:: print_hd_table(hdtable)

   Convenient function to print *hdtable* to the standard output.  The
   *hdtable* is the one retrieved by
   :py:meth:`HDDeflater.get_hd_table()` or
   :py:meth:`HDInflater.get_hd_table()`.  This function does not work
   if header name/value cannot be decoded using UTF-8 encoding.

   In output, ``s=N`` means the entry occupies ``N`` bytes in header
   table.  If ``r=y``, then the entry is in the reference set.

.. py:data:: DEFAULT_HEADER_TABLE_SIZE

   The default header table size, which is 4096 as per HTTP/2
   specification.

.. py:data:: DEFLATE_MAX_HEADER_TABLE_SIZE

   The default header table size for deflater.  The initial value
   is 4096.

HTTP/2 servers
--------------

.. note::

   We use :py:mod:`asyncio` for HTTP/2 server classes, and ALPN.
   Therefore, Python 3.8 or later is required to use these objects.
   To explicitly configure nghttp2 build to use Python 3.8, specify
   the ``PYTHON`` variable to the path to Python 3.8 executable when
   invoking configure script like this:

   .. code-block:: text

       $ ./configure PYTHON=/usr/bin/python3.8

.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None)

   This class builds on top of the :py:mod:`asyncio` event loop.  On
   construction, *RequestHandlerClass* must be given, which must be a
   subclass of :py:class:`BaseRequestHandler` class.

   The *address* must be a tuple of hostname/IP address and port to
   bind.  If hostname/IP address is ``None``, all interfaces are
   assumed.

   To enable SSL/TLS, specify instance of :py:class:`ssl.SSLContext`
   in *ssl*.  Before passing *ssl* to
   :py:func:`BaseEventLoop.create_server`, ALPN protocol identifiers
   are set using :py:meth:`ssl.SSLContext.set_npn_protocols`.

   To disable SSL/TLS, omit *ssl* or specify ``None``.

   .. py:method:: serve_forever()

      Runs server and processes incoming requests forever.

.. py:class:: BaseRequestHandler(http2, stream_id)

   The class is used to handle the single HTTP/2 stream.  By default,
   it does not nothing.  It must be subclassed to handle each event
   callback method.

   The first callback method invoked is :py:meth:`on_headers()`. It is
   called when HEADERS frame, which includes request header fields, is
   arrived.

   If request has request body, :py:meth:`on_data()` is invoked for
   each chunk of received data chunk.

   When whole request is received, :py:meth:`on_request_done()` is
   invoked.

   When stream is closed, :py:meth:`on_close()` is called.

   The application can send response using :py:meth:`send_response()`
   method.  It can be used in :py:meth:`on_headers()`,
   :py:meth:`on_data()` or :py:meth:`on_request_done()`.

   The application can push resource using :py:meth:`push()` method.
   It must be used before :py:meth:`send_response()` call.

   A :py:class:`BaseRequestHandler` has the following instance
   variables:

   .. py:attribute:: client_address

      Contains a tuple of the form ``(host, port)`` referring to the
      client's address.

   .. py:attribute:: stream_id

      Stream ID of this stream

   .. py:attribute:: scheme

      Scheme of the request URI.  This is a value of ``:scheme``
      header field.

   .. py:attribute:: method

      Method of this stream.  This is a value of ``:method`` header
      field.

   .. py:attribute:: host

      This is a value of ``:authority`` or ``host`` header field.

   .. py:attribute:: path

      This is a value of ``:path`` header field.

   .. py:attribute:: headers

      Request header fields.

   A :py:class:`BaseRequestHandler` has the following methods:

   .. py:method:: on_headers()

      Called when request HEADERS is arrived.  By default, this method
      does nothing.

   .. py:method:: on_data(data)

      Called when a chunk of request body *data* is arrived.  This
      method will be called multiple times until all data are
      received.  By default, this method does nothing.

   .. py:method:: on_request_done()

      Called when whole request was received.  By default, this method
      does nothing.

   .. py:method:: on_close(error_code)

      Called when stream is about to close.  The *error_code*
      indicates the reason of closure.  If it is ``0``, the stream is
      going to close without error.

   .. py:method:: send_response(status=200, headers=None, body=None)

      Send response.  The *status* is HTTP status code.  The *headers*
      is additional response headers.  The *:status* header field will
      be appended by the library.  The *body* is the response body.
      It could be ``None`` if response body is empty.  Or it must be
      instance of either ``str``, ``bytes``, :py:class:`io.IOBase` or
      callable, called body generator, which takes one parameter,
      size.  The body generator generates response body.  It can pause
      generation of response so that it can wait for slow backend data
      generation.  When invoked, it should return tuple, byte string
      at most size length and flag.  The flag is either
      :py:data:`DATA_OK`, :py:data:`DATA_EOF` or
      :py:data:`DATA_DEFERRED`.  For non-empty byte string and it is
      not the last chunk of response, :py:data:`DATA_OK` must be
      returned as flag.  If this is the last chunk of the response
      (byte string could be ``None``), :py:data:`DATA_EOF` must be
      returned as flag.  If there is no data available right now, but
      additional data are anticipated, return tuple (``None``,
      :py:data:`DATA_DEFERRED`).  When data arrived, call
      :py:meth:`resume()` and restart response body transmission.

      Only the body generator can pause response body generation;
      instance of :py:class:`io.IOBase` must not block.

      If instance of ``str`` is specified as *body*, it will be
      encoded using UTF-8.

      The *headers* is a list of tuple of the form ``(name,
      value)``. The ``name`` and ``value`` can be either byte string
      or unicode string.  In the latter case, they will be encoded
      using UTF-8.

      Raises the exception if any error occurs.

   .. py:method:: push(path, method='GET', request_headers=None, status=200, headers=None, body=None)

      Push a specified resource.  The *path* is a path portion of
      request URI for this resource.  The *method* is a method to
      access this resource.  The *request_headers* is additional
      request headers to access this resource.  The ``:scheme``,
      ``:method``, ``:authority`` and ``:path`` are appended by the
      library.  The ``:scheme`` and ``:authority`` are inherited from
      request header fields of the associated stream.

      The *status* is HTTP status code.  The *headers* is additional
      response headers.  The ``:status`` header field is appended by
      the library.  The *body* is the response body.  It has the same
      semantics of *body* parameter of :py:meth:`send_response()`.

      The headers and request_headers are a list of tuple of the form
      ``(name, value)``. The ``name`` and ``value`` can be either byte
      string or unicode string.  In the latter case, they will be
      encoded using UTF-8.

      Returns an instance of ``RequestHandlerClass`` specified in
      :py:class:`HTTP2Server` constructor for the pushed resource.

      Raises the exception if any error occurs.

   .. py:method:: resume()

      Signals the restarting of response body transmission paused by
      ``DATA_DEFERRED`` from the body generator (see
      :py:meth:`send_response()` about the body generator).  It is not
      an error calling this method while response body transmission is
      not paused.

.. py:data:: DATA_OK

   ``DATA_OK`` indicates non empty data is generated from body generator.

.. py:data:: DATA_EOF

   ``DATA_EOF`` indicates the end of response body.

.. py:data:: DATA_DEFERRED

   ``DATA_DEFERRED`` indicates that data are not available right now
   and response should be paused.

The following example illustrates :py:class:`HTTP2Server` and
:py:class:`BaseRequestHandler` usage:

.. code-block:: python

    #!/usr/bin/env python3

    import io, ssl

    import nghttp2

    class Handler(nghttp2.BaseRequestHandler):

        def on_headers(self):
            self.push(path='/css/style.css',
                      request_headers = [('content-type', 'text/css')],
                      status=200,
                      body='body{margin:0;}')

            self.send_response(status=200,
                               headers = [('content-type', 'text/plain')],
                               body=io.BytesIO(b'nghttp2-python FTW'))

    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
    ctx.load_cert_chain('server.crt', 'server.key')

    # give None to ssl to make the server non-SSL/TLS
    server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
    server.serve_forever()

The following example illustrates HTTP/2 server using asynchronous
response body generation.  This is simplified reverse proxy:

.. code-block:: python

    #!/usr/bin/env python3

    import ssl
    import os
    import urllib
    import asyncio
    import io

    import nghttp2

    @asyncio.coroutine
    def get_http_header(handler, url):
        url = urllib.parse.urlsplit(url)
        ssl = url.scheme == 'https'
        if url.port == None:
            if url.scheme == 'https':
                port = 443
            else:
                port = 80
        else:
            port = url.port

        connect = asyncio.open_connection(url.hostname, port, ssl=ssl)
        reader, writer = yield from connect
        req = 'GET {path} HTTP/1.0\r\n\r\n'.format(path=url.path or '/')
        writer.write(req.encode('utf-8'))
        # skip response header fields
        while True:
            line = yield from reader.readline()
            line = line.rstrip()
            if not line:
                break
        # read body
        while True:
            b = yield from reader.read(4096)
            if not b:
                break
            handler.buf.write(b)
        writer.close()
        handler.buf.seek(0)
        handler.eof = True
        handler.resume()

    class Body:
        def __init__(self, handler):
            self.handler = handler
            self.handler.eof = False
            self.handler.buf = io.BytesIO()

        def generate(self, n):
            buf = self.handler.buf
            data = buf.read1(n)
            if not data and not self.handler.eof:
                return None, nghttp2.DATA_DEFERRED
            return data, nghttp2.DATA_EOF if self.handler.eof else nghttp2.DATA_OK

    class Handler(nghttp2.BaseRequestHandler):

        def on_headers(self):
            body = Body(self)
            asyncio.async(get_http_header(
                self, 'http://localhost' + self.path.decode('utf-8')))
            self.send_response(status=200, body=body.generate)

    ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    ctx.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
    ctx.load_cert_chain('server.crt', 'server.key')

    server = nghttp2.HTTP2Server(('127.0.0.1', 8443), Handler, ssl=ctx)
    server.serve_forever()
