python: Support ALPN, require Python 3.5

This commit also fixes the bug that SETTINGS timer continues after
connection was closed.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-08-28 00:11:59 +09:00
parent 0ea44072a3
commit cd471a989a
3 changed files with 35 additions and 15 deletions

View File

@ -1370,7 +1370,7 @@ The extension module is called ``nghttp2``.
determined by the ``configure`` script. If the detected Python version is not determined by the ``configure`` script. If the detected Python version is not
what you expect, specify a path to Python executable in a ``PYTHON`` what you expect, specify a path to Python executable in a ``PYTHON``
variable as an argument to configure script (e.g., ``./configure 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 The following example code illustrates basic usage of the HPACK compressor
and decompressor in Python: and decompressor in Python:

View File

@ -13,7 +13,7 @@ The extension module is called ``nghttp2``.
determined by configure script. If the detected Python version is not determined by configure script. If the detected Python version is not
what you expect, specify a path to Python executable in ``PYTHON`` what you expect, specify a path to Python executable in ``PYTHON``
variable as an argument to configure script (e.g., ``./configure variable as an argument to configure script (e.g., ``./configure
PYTHON=/usr/bin/python3.4``). PYTHON=/usr/bin/python3.5``).
HPACK API HPACK API
--------- ---------
@ -136,15 +136,15 @@ HTTP/2 servers
.. note:: .. note::
We use :py:mod:`asyncio` for HTTP/2 server classes. Therefore, We use :py:mod:`asyncio` for HTTP/2 server classes, and ALPN.
Python 3.4 or later is required to use these objects. To Therefore, Python 3.5 or later is required to use these objects.
explicitly configure nghttp2 build to use Python 3.4, specify the To explicitly configure nghttp2 build to use Python 3.5, specify
``PYTHON`` variable to the path to Python 3.4 executable when the ``PYTHON`` variable to the path to Python 3.5 executable when
invoking configure script like this: invoking configure script like this:
.. code-block:: text .. code-block:: text
$ ./configure PYTHON=/usr/bin/python3.4 $ ./configure PYTHON=/usr/bin/python3.5
.. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None) .. py:class:: HTTP2Server(address, RequestHandlerClass, ssl=None)

View File

@ -260,6 +260,7 @@ try:
import email.utils import email.utils
import datetime import datetime
import time import time
import ssl as tls
from urllib.parse import urlparse from urllib.parse import urlparse
except ImportError: except ImportError:
asyncio = None asyncio = None
@ -294,6 +295,25 @@ def wrap_body(body):
# string and flag. # string and flag.
return body 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, cdef _get_stream_user_data(cnghttp2.nghttp2_session *session,
int32_t stream_id): int32_t stream_id):
cdef void *stream_user_data cdef void *stream_user_data
@ -902,6 +922,8 @@ cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase):
return promised_handler return promised_handler
def connection_lost(self): def connection_lost(self):
self._stop_settings_timer()
for handler in self.handlers: for handler in self.handlers:
handler.on_close(cnghttp2.NGHTTP2_INTERNAL_ERROR) handler.on_close(cnghttp2.NGHTTP2_INTERNAL_ERROR)
self.handlers = set() self.handlers = set()
@ -1284,8 +1306,8 @@ if asyncio:
logging.info('failed to set tcp-nodelay: %s', str(e)) logging.info('failed to set tcp-nodelay: %s', str(e))
ssl_ctx = self.transport.get_extra_info('sslcontext') ssl_ctx = self.transport.get_extra_info('sslcontext')
if ssl_ctx: if ssl_ctx:
protocol = sock.selected_npn_protocol() ssl_obj = self.transport.get_extra_info('ssl_object')
logging.info('npn, protocol:%s', protocol) protocol = negotiated_protocol(ssl_obj)
if protocol is None or protocol.encode('utf-8') != \ if protocol is None or protocol.encode('utf-8') != \
cnghttp2.NGHTTP2_PROTO_VERSION_ID: cnghttp2.NGHTTP2_PROTO_VERSION_ID:
self.transport.abort() self.transport.abort()
@ -1346,8 +1368,7 @@ if asyncio:
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
if ssl: if ssl:
ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ set_application_protocol(ssl)
.decode('utf-8')])
coro = self.loop.create_server(session_factory, coro = self.loop.create_server(session_factory,
host=address[0], port=address[1], host=address[0], port=address[1],
@ -1516,8 +1537,8 @@ if asyncio:
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
ssl_ctx = self.transport.get_extra_info('sslcontext') ssl_ctx = self.transport.get_extra_info('sslcontext')
if ssl_ctx: if ssl_ctx:
protocol = sock.selected_npn_protocol() ssl_obj = self.transport.get_extra_info('ssl_object')
logging.info('npn, protocol:%s', protocol) protocol = negotiated_protocol(ssl_obj)
if protocol is None or protocol.encode('utf-8') != \ if protocol is None or protocol.encode('utf-8') != \
cnghttp2.NGHTTP2_PROTO_VERSION_ID: cnghttp2.NGHTTP2_PROTO_VERSION_ID:
self.transport.abort() self.transport.abort()
@ -1594,8 +1615,7 @@ if asyncio:
return self.session return self.session
if ssl: if ssl:
ssl.set_npn_protocols([cnghttp2.NGHTTP2_PROTO_VERSION_ID\ set_application_protocol(ssl)
.decode('utf-8')])
self.loop = loop self.loop = loop
if not self.loop: if not self.loop: