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