# -*- coding: utf-8 -*-
"""
Client Plaintext Upgrade
~~~~~~~~~~~~~~~~~~~~~~~~

This example code fragment demonstrates how to set up a HTTP/2 client 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 client if possible.

This code requires Python 3.5 or later.
"""
import h2.connection
import socket


def establish_tcp_connection():
    """
    This function establishes a client-side TCP connection. How it works isn't
    very important to this example. For the purpose of this example we connect
    to localhost.
    """
    return socket.create_connection(('localhost', 80))


def send_initial_request(connection, settings):
    """
    For the sake of this upgrade demonstration, we're going to issue a GET
    request against the root of the site. In principle the best request to
    issue for an upgrade is actually ``OPTIONS *``, but this is remarkably
    poorly supported and can break in weird ways.
    """
    # Craft our initial request per RFC 7540 Section 3.2. This requires two
    # special header fields: the Upgrade headre, and the HTTP2-Settings header.
    # The value of the HTTP2-Settings header field comes from h2.
    request = (
        b"GET / HTTP/1.1\r\n" +
        b"Host: localhost\r\n" +
        b"Upgrade: h2c\r\n" +
        b"HTTP2-Settings: " + settings + b"\r\n" +
        b"\r\n"
    )
    connection.sendall(request)


def get_upgrade_response(connection):
    """
    This function reads from the socket until the HTTP/1.1 end-of-headers
    sequence (CRLFCRLF) is received. It then checks what the status code of the
    response is.

    This is not a substitute for proper HTTP/1.1 parsing, but it's good enough
    for example purposes.
    """
    data = b''
    while b'\r\n\r\n' not in data:
        data += connection.recv(8192)

    headers, rest = data.split(b'\r\n\r\n', 1)

    # An upgrade response begins HTTP/1.1 101 Switching Protocols. Look for the
    # code. In production code you should also check that the upgrade is to
    # h2c, but here we know we only offered one upgrade so there's only one
    # possible upgrade in use.
    split_headers = headers.split()
    if split_headers[1] != b'101':
        raise RuntimeError("Not upgrading!")

    # We don't care about the HTTP/1.1 data anymore, but we do care about
    # any other data we read from the socket: this is going to be HTTP/2 data
    # that must be passed to the H2Connection.
    return rest


def main():
    """
    The client upgrade flow.
    """
    # Step 1: Establish the TCP connecton.
    connection = establish_tcp_connection()

    # Step 2: Create H2 Connection object, put it in upgrade mode, and get the
    # value of the HTTP2-Settings header we want to use.
    h2_connection = h2.connection.H2Connection()
    settings_header_value = h2_connection.initiate_upgrade_connection()

    # Step 3: Send the initial HTTP/1.1 request with the upgrade fields.
    send_initial_request(connection, settings_header_value)

    # Step 4: Read the HTTP/1.1 response, look for 101 response.
    extra_data = get_upgrade_response(connection)

    # Step 5: Immediately send the pending HTTP/2 data.
    connection.sendall(h2_connection.data_to_send())

    # Step 6: Feed the body data to the connection.
    events = connection.receive_data(extra_data)

    # Now you can enter your main loop, beginning by processing the first set
    # of events above. These events may include ResponseReceived, which will
    # contain the response to the request we made in Step 3.
    main_loop(events)
