Skip to content
This repository has been archived by the owner on Jan 13, 2021. It is now read-only.

ALPN Support #125

Merged
merged 3 commits into from
Apr 16, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions hyper/ssl_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ def selected_npn_protocol(self):

return proto if proto else None

def selected_alpn_protocol(self):
proto = self._conn.get_alpn_proto_negotiated()
if isinstance(proto, bytes):
proto = proto.decode('ascii')

return proto if proto else None

def getpeercert(self):
def resolve_alias(alias):
return dict(
Expand Down Expand Up @@ -246,6 +253,10 @@ def cb(conn, protos):

self._ctx.set_npn_select_callback(cb)

def set_alpn_protocols(self, protocols):
protocols = list(map(lambda x: x.encode('ascii'), protocols))
self._ctx.set_alpn_protos(protocols)

def wrap_socket(self, sock, server_side=False, do_handshake_on_connect=True,
suppress_ragged_eofs=True, server_hostname=None):
conn = ossl.Connection(self._ctx, sock)
Expand Down
19 changes: 15 additions & 4 deletions hyper/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


NPN_PROTOCOL = 'h2'
H2_NPN_PROTOCOLS = [NPN_PROTOCOL, 'h2-16', 'h2-15', 'h2-14'] # All h2s we support.
H2_NPN_PROTOCOLS = [NPN_PROTOCOL, 'h2-16', 'h2-15', 'h2-14']
SUPPORTED_NPN_PROTOCOLS = H2_NPN_PROTOCOLS + ['http/1.1']


Expand All @@ -22,6 +22,7 @@
# Work out where our certificates are.
cert_loc = path.join(path.dirname(__file__), 'certs.pem')


def wrap_socket(sock, server_hostname):
"""
A vastly simplified SSL wrapping function. We'll probably extend this to
Expand All @@ -35,17 +36,24 @@ def wrap_socket(sock, server_hostname):
# the spec requires SNI support
ssl_sock = _context.wrap_socket(sock, server_hostname=server_hostname)
# Setting SSLContext.check_hostname to True only verifies that the
# post-handshake servername matches that of the certificate. We also need to
# check that it matches the requested one.
# post-handshake servername matches that of the certificate. We also need
# to check that it matches the requested one.
if _context.check_hostname: # pragma: no cover
try:
ssl.match_hostname(ssl_sock.getpeercert(), server_hostname)
except AttributeError:
ssl.verify_hostname(ssl_sock, server_hostname) # pyopenssl

proto = None

# ALPN is newer, so we prefer it over NPN. The odds of us getting different
# answers is pretty low, but let's be sure.
with ignore_missing():
proto = ssl_sock.selected_alpn_protocol()

with ignore_missing():
proto = ssl_sock.selected_npn_protocol()
if proto is None:
proto = ssl_sock.selected_npn_protocol()

return (ssl_sock, proto)

Expand All @@ -63,6 +71,9 @@ def _init_context():
with ignore_missing():
context.set_npn_protocols(SUPPORTED_NPN_PROTOCOLS)

with ignore_missing():
context.set_alpn_protocols(SUPPORTED_NPN_PROTOCOLS)

# required by the spec
context.options |= ssl.OP_NO_COMPRESSION

Expand Down