diff --git a/README.rst b/README.rst index 6eedcf22..edcb702b 100644 --- a/README.rst +++ b/README.rst @@ -1370,7 +1370,7 @@ The extension module is called ``nghttp2``. determined by the ``configure`` script. If the detected Python version is not what you expect, specify a path to Python executable in a ``PYTHON`` variable as an argument to configure script (e.g., ``./configure -PYTHON=/usr/bin/python3.4``). +PYTHON=/usr/bin/python3.5``). The following example code illustrates basic usage of the HPACK compressor and decompressor in Python: diff --git a/doc/sources/python-apiref.rst b/doc/sources/python-apiref.rst index d0a0b331..d64b43cc 100644 --- a/doc/sources/python-apiref.rst +++ b/doc/sources/python-apiref.rst @@ -13,7 +13,7 @@ The extension module is called ``nghttp2``. 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.4``). +PYTHON=/usr/bin/python3.5``). HPACK API --------- @@ -136,15 +136,15 @@ HTTP/2 servers .. note:: - We use :py:mod:`asyncio` for HTTP/2 server classes. Therefore, - Python 3.4 or later is required to use these objects. To - explicitly configure nghttp2 build to use Python 3.4, specify the - ``PYTHON`` variable to the path to Python 3.4 executable when + We use :py:mod:`asyncio` for HTTP/2 server classes, and ALPN. + Therefore, Python 3.5 or later is required to use these objects. + To explicitly configure nghttp2 build to use Python 3.5, specify + the ``PYTHON`` variable to the path to Python 3.5 executable when invoking configure script like this: .. code-block:: text - $ ./configure PYTHON=/usr/bin/python3.4 + $ ./configure PYTHON=/usr/bin/python3.5 .. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None) diff --git a/python/nghttp2.pyx b/python/nghttp2.pyx index 5def868e..7821ff14 100644 --- a/python/nghttp2.pyx +++ b/python/nghttp2.pyx @@ -260,6 +260,7 @@ try: import email.utils import datetime import time + import ssl as tls from urllib.parse import urlparse except ImportError: asyncio = None @@ -294,6 +295,25 @@ def wrap_body(body): # string and flag. return body +def negotiated_protocol(ssl_obj): + protocol = ssl_obj.selected_alpn_protocol() + if protocol: + logging.info('alpn, protocol:%s', protocol) + return protocol + + protocol = ssl_obj.selected_npn_protocol() + if protocol: + logging.info('npn, protocol:%s', protocol) + return protocol + + return None + +def set_application_protocol(ssl_ctx): + app_protos = [cnghttp2.NGHTTP2_PROTO_VERSION_ID.decode('utf-8')] + ssl_ctx.set_npn_protocols(app_protos) + if tls.HAS_ALPN: + ssl_ctx.set_alpn_protocols(app_protos) + cdef _get_stream_user_data(cnghttp2.nghttp2_session *session, int32_t stream_id): cdef void *stream_user_data @@ -902,6 +922,8 @@ cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase): return promised_handler def connection_lost(self): + self._stop_settings_timer() + for handler in self.handlers: handler.on_close(cnghttp2.NGHTTP2_INTERNAL_ERROR) self.handlers = set() @@ -1284,8 +1306,8 @@ if asyncio: logging.info('failed to set tcp-nodelay: %s', str(e)) ssl_ctx = self.transport.get_extra_info('sslcontext') if ssl_ctx: - protocol = sock.selected_npn_protocol() - logging.info('npn, protocol:%s', protocol) + ssl_obj = self.transport.get_extra_info('ssl_object') + protocol = negotiated_protocol(ssl_obj) if protocol is None or protocol.encode('utf-8') != \ cnghttp2.NGHTTP2_PROTO_VERSION_ID: self.transport.abort() @@ -1346,8 +1368,7 @@ if asyncio: self.loop = asyncio.get_event_loop() if ssl: - ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ - .decode('utf-8')]) + set_application_protocol(ssl) coro = self.loop.create_server(session_factory, host=address[0], port=address[1], @@ -1516,8 +1537,8 @@ if asyncio: sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) ssl_ctx = self.transport.get_extra_info('sslcontext') if ssl_ctx: - protocol = sock.selected_npn_protocol() - logging.info('npn, protocol:%s', protocol) + ssl_obj = self.transport.get_extra_info('ssl_object') + protocol = negotiated_protocol(ssl_obj) if protocol is None or protocol.encode('utf-8') != \ cnghttp2.NGHTTP2_PROTO_VERSION_ID: self.transport.abort() @@ -1594,8 +1615,7 @@ if asyncio: return self.session if ssl: - ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ - .decode('utf-8')]) + set_application_protocol(ssl) self.loop = loop if not self.loop: