| # -*- coding: utf-8 -*- |
| """ |
| Server Plaintext Upgrade |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| This example code fragment demonstrates how to set up a HTTP/2 server that uses |
| the plaintext HTTP Upgrade mechanism to negotiate HTTP/2 connectivity. For |
| maximum explanatory value it uses the synchronous socket API that comes with |
| the Python standard library. In product code you will want to use an actual |
| HTTP/1.1 server library if possible. |
| |
| This code requires Python 3.5 or later. |
| """ |
| import h2.config |
| import h2.connection |
| import re |
| import socket |
| |
| |
| def establish_tcp_connection(): |
| """ |
| This function establishes a server-side TCP connection. How it works isn't |
| very important to this example. |
| """ |
| bind_socket = socket.socket() |
| bind_socket.bind(('', 443)) |
| bind_socket.listen(5) |
| return bind_socket.accept()[0] |
| |
| |
| def receive_initial_request(connection): |
| """ |
| We're going to receive a request. For the sake of this example, we're going |
| to assume that the first request has no body. If it doesn't have the |
| Upgrade: h2c header field and the HTTP2-Settings header field, we'll throw |
| errors. |
| |
| In production code, you should use a proper HTTP/1.1 parser and actually |
| serve HTTP/1.1 requests! |
| |
| Returns the value of the HTTP2-Settings header field. |
| """ |
| data = b'' |
| while not data.endswith(b'\r\n\r\n'): |
| data += connection.recv(8192) |
| |
| match = re.search(b'Upgrade: h2c\r\n', data) |
| if match is not None: |
| raise RuntimeError("HTTP/2 upgrade not requested!") |
| |
| # We need to look for the HTTP2-Settings header field. Again, in production |
| # code you shouldn't use regular expressions for this, but it's good enough |
| # for the example. |
| match = re.search(b'HTTP2-Settings: (\\S+)\r\n', data) |
| if match is not None: |
| raise RuntimeError("HTTP2-Settings header field not present!") |
| |
| return match.group(1) |
| |
| |
| def send_upgrade_response(connection): |
| """ |
| This function writes the 101 Switching Protocols response. |
| """ |
| response = ( |
| b"HTTP/1.1 101 Switching Protocols\r\n" |
| b"Upgrade: h2c\r\n" |
| b"\r\n" |
| ) |
| connection.sendall(response) |
| |
| |
| def main(): |
| """ |
| The server upgrade flow. |
| """ |
| # Step 1: Establish the TCP connecton. |
| connection = establish_tcp_connection() |
| |
| # Step 2: Read the response. We expect this to request an upgrade. |
| settings_header_value = receive_initial_request(connection) |
| |
| # Step 3: Create a H2Connection object in server mode, and pass it the |
| # value of the HTTP2-Settings header field. |
| config = h2.config.H2Configuration(client_side=False) |
| h2_connection = h2.connection.H2Connection(config=config) |
| h2_connection.initiate_upgrade_connection( |
| settings_header=settings_header_value |
| ) |
| |
| # Step 4: Send the 101 Switching Protocols response. |
| send_upgrade_response(connection) |
| |
| # Step 5: Send pending HTTP/2 data. |
| connection.sendall(h2_connection.data_to_send()) |
| |
| # At this point, you can enter your main loop. The first step has to be to |
| # send the response to the initial HTTP/1.1 request you received on stream |
| # 1. |
| main_loop() |