1# -*- coding: utf-8 -*-
2"""
3Server HTTPS Setup
4~~~~~~~~~~~~~~~~~~
5
6This example code fragment demonstrates how to set up a HTTP/2 server that
7negotiates HTTP/2 using NPN and ALPN. For the sake of maximum explanatory value
8this code uses the synchronous, low-level sockets API: however, if you're not
9using sockets directly (e.g. because you're using asyncio), you should focus on
10the set up required for the SSLContext object. For other concurrency libraries
11you may need to use other setup (e.g. for Twisted you'll need to use
12IProtocolNegotiationFactory).
13
14This code requires Python 3.5 or later.
15"""
16import h2.config
17import h2.connection
18import socket
19import ssl
20
21
22def establish_tcp_connection():
23    """
24    This function establishes a server-side TCP connection. How it works isn't
25    very important to this example.
26    """
27    bind_socket = socket.socket()
28    bind_socket.bind(('', 443))
29    bind_socket.listen(5)
30    return bind_socket.accept()[0]
31
32
33def get_http2_ssl_context():
34    """
35    This function creates an SSLContext object that is suitably configured for
36    HTTP/2. If you're working with Python TLS directly, you'll want to do the
37    exact same setup as this function does.
38    """
39    # Get the basic context from the standard library.
40    ctx = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
41
42    # RFC 7540 Section 9.2: Implementations of HTTP/2 MUST use TLS version 1.2
43    # or higher. Disable TLS 1.1 and lower.
44    ctx.options |= (
45        ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
46    )
47
48    # RFC 7540 Section 9.2.1: A deployment of HTTP/2 over TLS 1.2 MUST disable
49    # compression.
50    ctx.options |= ssl.OP_NO_COMPRESSION
51
52    # RFC 7540 Section 9.2.2: "deployments of HTTP/2 that use TLS 1.2 MUST
53    # support TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256". In practice, the
54    # blocklist defined in this section allows only the AES GCM and ChaCha20
55    # cipher suites with ephemeral key negotiation.
56    ctx.set_ciphers("ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20")
57
58    # We want to negotiate using NPN and ALPN. ALPN is mandatory, but NPN may
59    # be absent, so allow that. This setup allows for negotiation of HTTP/1.1.
60    ctx.set_alpn_protocols(["h2", "http/1.1"])
61
62    try:
63        ctx.set_npn_protocols(["h2", "http/1.1"])
64    except NotImplementedError:
65        pass
66
67    return ctx
68
69
70def negotiate_tls(tcp_conn, context):
71    """
72    Given an established TCP connection and a HTTP/2-appropriate TLS context,
73    this function:
74
75    1. wraps TLS around the TCP connection.
76    2. confirms that HTTP/2 was negotiated and, if it was not, throws an error.
77    """
78    tls_conn = context.wrap_socket(tcp_conn, server_side=True)
79
80    # Always prefer the result from ALPN to that from NPN.
81    # You can only check what protocol was negotiated once the handshake is
82    # complete.
83    negotiated_protocol = tls_conn.selected_alpn_protocol()
84    if negotiated_protocol is None:
85        negotiated_protocol = tls_conn.selected_npn_protocol()
86
87    if negotiated_protocol != "h2":
88        raise RuntimeError("Didn't negotiate HTTP/2!")
89
90    return tls_conn
91
92
93def main():
94    # Step 1: Set up your TLS context.
95    context = get_http2_ssl_context()
96
97    # Step 2: Receive a TCP connection.
98    connection = establish_tcp_connection()
99
100    # Step 3: Wrap the connection in TLS and validate that we negotiated HTTP/2
101    tls_connection = negotiate_tls(connection, context)
102
103    # Step 4: Create a server-side H2 connection.
104    config = h2.config.H2Configuration(client_side=False)
105    http2_connection = h2.connection.H2Connection(config=config)
106
107    # Step 5: Initiate the connection
108    http2_connection.initiate_connection()
109    tls_connection.sendall(http2_connection.data_to_send())
110
111    # The TCP, TLS, and HTTP/2 handshakes are now complete. You can enter your
112    # main loop now.
113