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()