Permalink
Browse files

python: Support ALPN, require Python 3.5

This commit also fixes the bug that SETTINGS timer continues after
connection was closed.
  • Loading branch information...
1 parent 0ea4407 commit cd471a989aff2bc06ec5901a02cda02eb52cedf5 @tatsuhiro-t tatsuhiro-t committed Aug 27, 2016
Showing with 35 additions and 15 deletions.
  1. +1 −1 README.rst
  2. +6 −6 doc/sources/python-apiref.rst
  3. +28 −8 python/nghttp2.pyx
View
@@ -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:
@@ -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)
View
@@ -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:

0 comments on commit cd471a9

Please sign in to comment.