tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

basic-usage.rst (29141B)


      1 Getting Started: Writing Your Own HTTP/2 Server
      2 ===============================================
      3 
      4 This document explains how to get started writing fully-fledged HTTP/2
      5 implementations using h2 as the underlying protocol stack. It covers the
      6 basic concepts you need to understand, and talks you through writing a very
      7 simple HTTP/2 server.
      8 
      9 This document assumes you're moderately familiar with writing Python, and have
     10 *some* understanding of how computer networks work. If you don't, you'll find
     11 it a lot easier if you get some understanding of those concepts first and then
     12 return to this documentation.
     13 
     14 
     15 .. _h2-connection-basic:
     16 
     17 Connections
     18 -----------
     19 
     20 h2's core object is the
     21 :class:`H2Connection <h2.connection.H2Connection>` object. This object is an
     22 abstract representation of the state of a single HTTP/2 connection, and holds
     23 all the important protocol state. When using h2, this object will be the
     24 first thing you create and the object that does most of the heavy lifting.
     25 
     26 The interface to this object is relatively simple. For sending data, you
     27 call the object with methods indicating what actions you want to perform: for
     28 example, you may want to send headers (you'd use the
     29 :meth:`send_headers <h2.connection.H2Connection.send_headers>` method), or
     30 send data (you'd use the
     31 :meth:`send_data <h2.connection.H2Connection.send_data>` method). After you've
     32 decided what actions you want to perform, you get some bytes out of the object
     33 that represent the HTTP/2-encoded representation of your actions, and send them
     34 out over the network however you see fit.
     35 
     36 When you receive data from the network, you pass that data in to the
     37 ``H2Connection`` object, which returns a list of *events*.
     38 These events, covered in more detail later in :ref:`h2-events-basic`, define
     39 the set of actions the remote peer has performed on the connection, as
     40 represented by the HTTP/2-encoded data you just passed to the object.
     41 
     42 Thus, you end up with a simple loop (which you may recognise as a more-specific
     43 form of an `event loop`_):
     44 
     45    1. First, you perform some actions.
     46    2. You send the data created by performing those actions to the network.
     47    3. You read data from the network.
     48    4. You decode those into events.
     49    5. The events cause you to trigger some actions: go back to step 1.
     50 
     51 Of course, HTTP/2 is more complex than that, but in the very simplest case you
     52 can write a fairly effective HTTP/2 tool using just that kind of loop. Later in
     53 this document, we'll do just that.
     54 
     55 Some important subtleties of ``H2Connection`` objects are covered in
     56 :doc:`advanced-usage`: see :ref:`h2-connection-advanced` for more information.
     57 However, one subtlety should be covered, and that is this: h2's
     58 ``H2Connection`` object doesn't do I/O. Let's talk briefly about why.
     59 
     60 I/O
     61 ~~~
     62 
     63 Any useful HTTP/2 tool eventually needs to do I/O. This is because it's not
     64 very useful to be able to speak to other computers using a protocol like HTTP/2
     65 unless you actually *speak* to them sometimes.
     66 
     67 However, doing I/O is not a trivial thing: there are lots of different ways to
     68 do it, and once you choose a way to do it your code usually won't work well
     69 with the approaches you *didn't* choose.
     70 
     71 While there are lots of different ways to do I/O, when it comes down to it
     72 all HTTP/2 implementations transform bytes received into events, and events
     73 into bytes to send. So there's no reason to have lots of different versions of
     74 this core protocol code: one for Twisted, one for gevent, one for threading,
     75 and one for synchronous code.
     76 
     77 This is why we said at the top that h2 is a *HTTP/2 Protocol Stack*, not
     78 a *fully-fledged implementation*. h2 knows how to transform bytes into
     79 events and back, but that's it. The I/O and smarts might be different, but
     80 the core HTTP/2 logic is the same: that's what h2 provides.
     81 
     82 Not doing I/O makes h2 general, and also relatively simple. It has an
     83 easy-to-understand performance envelope, it's easy to test (and as a result
     84 easy to get correct behaviour out of), and it behaves in a reproducible way.
     85 These are all great traits to have in a library that is doing something quite
     86 complex.
     87 
     88 This document will talk you through how to build a relatively simple HTTP/2
     89 implementation using h2, to give you an understanding of where it fits in
     90 your software.
     91 
     92 
     93 .. _h2-events-basic:
     94 
     95 Events
     96 ------
     97 
     98 When writing a HTTP/2 implementation it's important to know what the remote
     99 peer is doing: if you didn't care, writing networked programs would be a lot
    100 easier!
    101 
    102 h2 encodes the actions of the remote peer in the form of *events*. When
    103 you receive data from the remote peer and pass it into your ``H2Connection``
    104 object (see :ref:`h2-connection-basic`), the ``H2Connection`` returns a list
    105 of objects, each one representing a single event that has occurred. Each
    106 event refers to a single action the remote peer has taken.
    107 
    108 Some events are fairly high-level, referring to things that are more general
    109 than HTTP/2: for example, the
    110 :class:`RequestReceived <h2.events.RequestReceived>` event is a general HTTP
    111 concept, not just a HTTP/2 one. Other events are extremely HTTP/2-specific:
    112 for example, :class:`PushedStreamReceived <h2.events.PushedStreamReceived>`
    113 refers to Server Push, a very HTTP/2-specific concept.
    114 
    115 The reason these events exist is that h2 is intended to be very general.
    116 This means that, in many cases, h2 does not know exactly what to do in
    117 response to an event. Your code will need to handle these events, and make
    118 decisions about what to do. That's the major role of any HTTP/2 implementation
    119 built on top of h2.
    120 
    121 A full list of events is available in :ref:`h2-events-api`. For the purposes
    122 of this example, we will handle only a small set of events.
    123 
    124 
    125 Writing Your Server
    126 -------------------
    127 
    128 Armed with the knowledge you just obtained, we're going to write a very simple
    129 HTTP/2 web server. The goal of this server is to write a server that can handle
    130 a HTTP GET, and that returns the headers sent by the client, encoded in JSON.
    131 Basically, something a lot like `httpbin.org/get`_. Nothing fancy, but this is
    132 a good way to get a handle on how you should interact with h2.
    133 
    134 For the sake of simplicity, we're going to write this using the Python standard
    135 library, in Python 3. In reality, you'll probably want to use an asynchronous
    136 framework of some kind: see the `examples directory`_ in the repository for
    137 some examples of how you'd do that.
    138 
    139 Before we start, create a new file called ``h2server.py``: we'll use that as
    140 our workspace. Additionally, you should install h2: follow the
    141 instructions in :doc:`installation`.
    142 
    143 Step 1: Sockets
    144 ~~~~~~~~~~~~~~~
    145 
    146 To begin with, we need to make sure we can listen for incoming data and send it
    147 back. To do that, we need to use the `standard library's socket module`_. For
    148 now we're going to skip doing TLS: if you want to reach your server from your
    149 web browser, though, you'll need to add TLS and some other function. Consider
    150 looking at our examples in our `examples directory`_ instead.
    151 
    152 Let's begin. First, open up ``h2server.py``. We need to import the socket
    153 module and start listening for connections.
    154 
    155 This is not a socket tutorial, so we're not going to dive too deeply into how
    156 this works. If you want more detail about sockets, there are lots of good
    157 tutorials on the web that you should investigate.
    158 
    159 When you want to listen for incoming connections, the you need to *bind* an
    160 address first. So let's do that. Try setting up your file to look like this:
    161 
    162 .. code-block:: python
    163 
    164    import socket
    165 
    166    sock = socket.socket()
    167    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    168    sock.bind(('0.0.0.0', 8080))
    169    sock.listen(5)
    170 
    171    while True:
    172        print(sock.accept())
    173 
    174 In a shell window, execute this program (``python h2server.py``). Then, open
    175 another shell and run ``curl http://localhost:8080/``. In the first shell, you
    176 should see something like this:
    177 
    178 .. code-block:: console
    179 
    180    $ python h2server.py
    181    (<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 58800)>, ('127.0.0.1', 58800))
    182 
    183 Run that ``curl`` command a few more times. You should see a few more similar
    184 lines appear. Note that the ``curl`` command itself will exit with an error.
    185 That's fine: it happens because we didn't send any data.
    186 
    187 Now go ahead and stop the server running by hitting Ctrl+C in the first shell.
    188 You should see a ``KeyboardInterrupt`` error take the process down.
    189 
    190 What's the program above doing? Well, first it creates a
    191 :func:`socket <python:socket.socket>` object. This socket is then *bound* to
    192 a specific address: ``('0.0.0.0', 8080)``. This is a special address: it means
    193 that this socket should be listening for any traffic to TCP port 8080. Don't
    194 worry about the call to ``setsockopt``: it just makes sure you can run this
    195 program repeatedly.
    196 
    197 We then loop forever calling the :meth:`accept <python:socket.socket.accept>`
    198 method on the socket. The accept method blocks until someone attempts to
    199 connect to our TCP port: when they do, it returns a tuple: the first element is
    200 a new socket object, the second element is a tuple of the address the new
    201 connection is from. You can see this in the output from our ``h2server.py``
    202 script.
    203 
    204 At this point, we have a script that can accept inbound connections. This is a
    205 good start! Let's start getting HTTP/2 involved.
    206 
    207 
    208 Step 2: Add a H2Connection
    209 ~~~~~~~~~~~~~~~~~~~~~~~~~~
    210 
    211 Now that we can listen for socket information, we want to prepare our HTTP/2
    212 connection object and start handing it data. For now, let's just see what
    213 happens as we feed it data.
    214 
    215 To make HTTP/2 connections, we need a tool that knows how to speak HTTP/2.
    216 Most versions of curl in the wild don't, so let's install a Python tool. In
    217 your Python environment, run ``pip install hyper``. This will install a Python
    218 command-line HTTP/2 tool called ``hyper``. To confirm that it works, try
    219 running this command and verifying that the output looks similar to the one
    220 shown below:
    221 
    222 .. code-block:: console
    223 
    224    $ hyper GET https://nghttp2.org/httpbin/get
    225    {'args': {},
    226     'headers': {'Host': 'nghttp2.org'},
    227     'origin': '10.0.0.2',
    228     'url': 'https://nghttp2.org/httpbin/get'}
    229 
    230 Assuming it works, you're now ready to start sending HTTP/2 data.
    231 
    232 Back in our ``h2server.py`` script, we're going to want to start handling data.
    233 Let's add a function that takes a socket returned from ``accept``, and reads
    234 data from it. Let's call that function ``handle``. That function should create
    235 a :class:`H2Connection <h2.connection.H2Connection>` object and then loop on
    236 the socket, reading data and passing it to the connection.
    237 
    238 To read data from a socket we need to call ``recv``. The ``recv`` function
    239 takes a number as its argument, which is the *maximum* amount of data to be
    240 returned from a single call (note that ``recv`` will return as soon as any data
    241 is available, even if that amount is vastly less than the number you passed to
    242 it). For the purposes of writing this kind of software the specific value is
    243 not enormously useful, but should not be overly large. For that reason, when
    244 you're unsure, a number like 4096 or 65535 is a good bet. We'll use 65535 for
    245 this example.
    246 
    247 The function should look something like this:
    248 
    249 .. code-block:: python
    250 
    251    import h2.connection
    252    import h2.config
    253 
    254    def handle(sock):
    255        config = h2.config.H2Configuration(client_side=False)
    256        conn = h2.connection.H2Connection(config=config)
    257 
    258        while True:
    259            data = sock.recv(65535)
    260            print(conn.receive_data(data))
    261 
    262 Let's update our main loop so that it passes data on to our new data handling
    263 function. Your ``h2server.py`` should end up looking a like this:
    264 
    265 .. code-block:: python
    266 
    267    import socket
    268 
    269    import h2.connection
    270    import h2.config
    271 
    272    def handle(sock):
    273        config = h2.config.H2Configuration(client_side=False)
    274        conn = h2.connection.H2Connection(config=config)
    275 
    276        while True:
    277            data = sock.recv(65535)
    278            if not data:
    279                break
    280 
    281            print(conn.receive_data(data))
    282 
    283 
    284    sock = socket.socket()
    285    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    286    sock.bind(('0.0.0.0', 8080))
    287    sock.listen(5)
    288 
    289    while True:
    290        handle(sock.accept()[0])
    291 
    292 Running that in one shell, in your other shell you can run
    293 ``hyper --h2 GET http://localhost:8080/``. That shell should hang, and you
    294 should then see the following output from your ``h2server.py`` shell:
    295 
    296 .. code-block:: console
    297 
    298    $ python h2server.py
    299    [<h2.events.RemoteSettingsChanged object at 0x10c4ee390>]
    300 
    301 You'll then need to kill ``hyper`` and ``h2server.py`` with Ctrl+C. Feel free
    302 to do this a few times, to see how things behave.
    303 
    304 So, what did we see here? When the connection was opened, we used the
    305 :meth:`recv <python:socket.socket.recv>` method to read some data from the
    306 socket, in a loop. We then passed that data to the connection object, which
    307 returned us a single event object:
    308 :class:`RemoteSettingsChanged <h2.events.RemoteSettingsChanged>`.
    309 
    310 But what we didn't see was anything else. So it seems like all ``hyper`` did
    311 was change its settings, but nothing else. If you look at the other ``hyper``
    312 window, you'll notice that it hangs for a while and then eventually fails with
    313 a socket timeout. It was waiting for something: what?
    314 
    315 Well, it turns out that at the start of a connection, both sides need to send
    316 a bit of data, called "the HTTP/2 preamble". We don't need to get into too much
    317 detail here, but basically both sides need to send a single block of HTTP/2
    318 data that tells the other side what their settings are. ``hyper`` did that,
    319 but we didn't.
    320 
    321 Let's do that next.
    322 
    323 
    324 Step 3: Sending the Preamble
    325 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    326 
    327 h2 makes doing connection setup really easy. All you need to do is call
    328 the
    329 :meth:`initiate_connection <h2.connection.H2Connection.initiate_connection>`
    330 method, and then send the corresponding data. Let's update our ``handle``
    331 function to do just that:
    332 
    333 .. code-block:: python
    334 
    335    def handle(sock):
    336        config = h2.config.H2Configuration(client_side=False)
    337        conn = h2.connection.H2Connection(config=config)
    338        conn.initiate_connection()
    339        sock.sendall(conn.data_to_send())
    340 
    341        while True:
    342            data = sock.recv(65535)
    343            print(conn.receive_data(data))
    344 
    345 
    346 The big change here is the call to ``initiate_connection``, but there's another
    347 new method in there:
    348 :meth:`data_to_send <h2.connection.H2Connection.data_to_send>`.
    349 
    350 When you make function calls on your ``H2Connection`` object, these will often
    351 want to cause HTTP/2 data to be written out to the network. But h2
    352 doesn't do any I/O, so it can't do that itself. Instead, it writes it to an
    353 internal buffer. You can retrieve data from this buffer using the
    354 ``data_to_send`` method. There are some subtleties about that method, but we
    355 don't need to worry about them right now: all we need to do is make sure we're
    356 sending whatever data is outstanding.
    357 
    358 Your ``h2server.py`` script should now look like this:
    359 
    360 .. code-block:: python
    361 
    362    import socket
    363 
    364    import h2.connection
    365    import h2.config
    366 
    367    def handle(sock):
    368        config = h2.config.H2Configuration(client_side=False)
    369        conn = h2.connection.H2Connection(config=config)
    370        conn.initiate_connection()
    371        sock.sendall(conn.data_to_send())
    372 
    373        while True:
    374            data = sock.recv(65535)
    375            if not data:
    376                break
    377 
    378            print(conn.receive_data(data))
    379 
    380 
    381    sock = socket.socket()
    382    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    383    sock.bind(('0.0.0.0', 8080))
    384    sock.listen(5)
    385 
    386    while True:
    387        handle(sock.accept()[0])
    388 
    389 
    390 With this change made, rerun your ``h2server.py`` script and hit it with the
    391 same ``hyper`` command: ``hyper --h2 GET http://localhost:8080/``. The
    392 ``hyper`` command still hangs, but this time we get a bit more output from our
    393 ``h2server.py`` script:
    394 
    395 .. code-block:: console
    396 
    397    $ python h2server.py
    398    [<h2.events.RemoteSettingsChanged object at 0x10292d390>]
    399    [<h2.events.SettingsAcknowledged object at 0x102b3a160>]
    400    [<h2.events.RequestReceived object at 0x102b3a3c8>, <h2.events.StreamEnded object at 0x102b3a400>]
    401 
    402 So, what's happening?
    403 
    404 The first thing to note is that we're going around our loop more than once now.
    405 First, we receive some data that triggers a
    406 :class:`RemoteSettingsChanged <h2.events.RemoteSettingsChanged>` event.
    407 Then, we get some more data that triggers a
    408 :class:`SettingsAcknowledged <h2.events.SettingsAcknowledged>` event.
    409 Finally, even more data that triggers *two* events:
    410 :class:`RequestReceived <h2.events.RequestReceived>` and
    411 :class:`StreamEnded <h2.events.StreamEnded>`.
    412 
    413 So, what's happening is that ``hyper`` is telling us about its settings,
    414 acknowledging ours, and then sending us a request. Then it ends a *stream*,
    415 which is a HTTP/2 communications channel that holds a request and response
    416 pair.
    417 
    418 A stream isn't done until it's either *reset* or both sides *close* it:
    419 in this sense it's bi-directional. So what the ``StreamEnded`` event tells us
    420 is that ``hyper`` is closing its half of the stream: it won't send us any more
    421 data on that stream. That means the request is done.
    422 
    423 So why is ``hyper`` hanging? Well, we haven't sent a response yet: let's do
    424 that.
    425 
    426 
    427 Step 4: Handling Events
    428 ~~~~~~~~~~~~~~~~~~~~~~~
    429 
    430 What we want to do is send a response when we receive a request. Happily, we
    431 get an event when we receive a request, so we can use that to be our signal.
    432 
    433 Let's define a new function that sends a response. For now, this response can
    434 just be a little bit of data that prints "it works!".
    435 
    436 The function should take the ``H2Connection`` object, and the event that
    437 signaled the request. Let's define it.
    438 
    439 .. code-block:: python
    440 
    441    def send_response(conn, event):
    442        stream_id = event.stream_id
    443        conn.send_headers(
    444            stream_id=stream_id,
    445            headers=[
    446                (':status', '200'),
    447                ('server', 'basic-h2-server/1.0')
    448            ],
    449        )
    450        conn.send_data(
    451            stream_id=stream_id,
    452            data=b'it works!',
    453            end_stream=True
    454        )
    455 
    456 So while this is only a short function, there's quite a lot going on here we
    457 need to unpack. Firstly, what's a stream ID? Earlier we discussed streams
    458 briefly, to say that they're a bi-directional communications channel that holds
    459 a request and response pair. Part of what makes HTTP/2 great is that there can
    460 be lots of streams going on at once, sending and receiving different requests
    461 and responses. To identify each stream, we use a *stream ID*. These are unique
    462 across the lifetime of a connection, and they go in ascending order.
    463 
    464 Most ``H2Connection`` functions take a stream ID: they require you to actively
    465 tell the connection which one to use. In this case, as a simple server, we will
    466 never need to choose a stream ID ourselves: the client will always choose one
    467 for us. That means we'll always be able to get the one we need off the events
    468 that fire.
    469 
    470 Next, we send some *headers*. In HTTP/2, a response is made up of some set of
    471 headers, and optionally some data. The headers have to come first: if you're a
    472 client then you'll be sending *request* headers, but in our case these headers
    473 are our *response* headers.
    474 
    475 Mostly these aren't very exciting, but you'll notice once special header in
    476 there: ``:status``. This is a HTTP/2-specific header, and it's used to hold the
    477 HTTP status code that used to go at the top of a HTTP response. Here, we're
    478 saying the response is ``200 OK``, which is successful.
    479 
    480 To send headers in h2, you use the
    481 :meth:`send_headers <h2.connection.H2Connection.send_headers>` function.
    482 
    483 Next, we want to send the body data. To do that, we use the
    484 :meth:`send_data <h2.connection.H2Connection.send_data>` function. This also
    485 takes a stream ID. Note that the data is binary: h2 does not work with
    486 unicode strings, so you *must* pass bytestrings to the ``H2Connection``. The
    487 one exception is headers: h2 will automatically encode those into UTF-8.
    488 
    489 The last thing to note is that on our call to ``send_data``, we set
    490 ``end_stream`` to ``True``. This tells h2 (and the remote peer) that
    491 we're done with sending data: the response is over. Because we know that
    492 ``hyper`` will have ended its side of the stream, when we end ours the stream
    493 will be totally done with.
    494 
    495 We're nearly ready to go with this: we just need to plumb this function in.
    496 Let's amend our ``handle`` function again:
    497 
    498 .. code-block:: python
    499 
    500    import h2.events
    501    import h2.config
    502 
    503    def handle(sock):
    504        config = h2.config.H2Configuration(client_side=False)
    505        conn = h2.connection.H2Connection(config=config)
    506        conn.initiate_connection()
    507        sock.sendall(conn.data_to_send())
    508 
    509        while True:
    510            data = sock.recv(65535)
    511            if not data:
    512                break
    513 
    514            events = conn.receive_data(data)
    515            for event in events:
    516                if isinstance(event, h2.events.RequestReceived):
    517                    send_response(conn, event)
    518 
    519            data_to_send = conn.data_to_send()
    520            if data_to_send:
    521                sock.sendall(data_to_send)
    522 
    523 The changes here are all at the end. Now, when we receive some events, we
    524 look through them for the ``RequestReceived`` event. If we find it, we make
    525 sure we send a response.
    526 
    527 Then, at the bottom of the loop we check whether we have any data to send, and
    528 if we do, we send it. Then, we repeat again.
    529 
    530 With these changes, your ``h2server.py`` file should look like this:
    531 
    532 .. code-block:: python
    533 
    534    import socket
    535 
    536    import h2.connection
    537    import h2.events
    538    import h2.config
    539 
    540    def send_response(conn, event):
    541        stream_id = event.stream_id
    542        conn.send_headers(
    543            stream_id=stream_id,
    544            headers=[
    545                (':status', '200'),
    546                ('server', 'basic-h2-server/1.0')
    547            ],
    548        )
    549        conn.send_data(
    550            stream_id=stream_id,
    551            data=b'it works!',
    552            end_stream=True
    553        )
    554 
    555    def handle(sock):
    556        config = h2.config.H2Configuration(client_side=False)
    557        conn = h2.connection.H2Connection(config=config)
    558        conn.initiate_connection()
    559        sock.sendall(conn.data_to_send())
    560 
    561        while True:
    562            data = sock.recv(65535)
    563            if not data:
    564                break
    565 
    566            events = conn.receive_data(data)
    567            for event in events:
    568                if isinstance(event, h2.events.RequestReceived):
    569                    send_response(conn, event)
    570 
    571            data_to_send = conn.data_to_send()
    572            if data_to_send:
    573                sock.sendall(data_to_send)
    574 
    575 
    576    sock = socket.socket()
    577    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    578    sock.bind(('0.0.0.0', 8080))
    579    sock.listen(5)
    580 
    581    while True:
    582        handle(sock.accept()[0])
    583 
    584 Alright. Let's run this, and then run our ``hyper`` command again.
    585 
    586 This time, nothing is printed from our server, and the ``hyper`` side prints
    587 ``it works!``. Success! Try running it a few more times, and we can see that
    588 not only does it work the first time, it works the other times too!
    589 
    590 We can speak HTTP/2! Let's add the final step: returning the JSON-encoded
    591 request headers.
    592 
    593 Step 5: Returning Headers
    594 ~~~~~~~~~~~~~~~~~~~~~~~~~
    595 
    596 If we want to return the request headers in JSON, the first thing we have to do
    597 is find them. Handily, if you check the documentation for
    598 :class:`RequestReceived <h2.events.RequestReceived>` you'll find that this
    599 event carries, in addition to the stream ID, the request headers.
    600 
    601 This means we can make a really simple change to our ``send_response``
    602 function to take those headers and encode them as a JSON object. Let's do that:
    603 
    604 .. code-block:: python
    605 
    606    import json
    607 
    608    def send_response(conn, event):
    609        stream_id = event.stream_id
    610        response_data = json.dumps(dict(event.headers)).encode('utf-8')
    611 
    612        conn.send_headers(
    613            stream_id=stream_id,
    614            headers=[
    615                (':status', '200'),
    616                ('server', 'basic-h2-server/1.0'),
    617                ('content-length', str(len(response_data))),
    618                ('content-type', 'application/json'),
    619            ],
    620        )
    621        conn.send_data(
    622            stream_id=stream_id,
    623            data=response_data,
    624            end_stream=True
    625        )
    626 
    627 This is a really simple change, but it's all we need to do: a few extra headers
    628 and the JSON dump, but that's it.
    629 
    630 Section 6: Bringing It All Together
    631 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    632 
    633 This should be all we need!
    634 
    635 Let's take all the work we just did and throw that into our ``h2server.py``
    636 file, which should now look like this:
    637 
    638 .. code-block:: python
    639 
    640    import json
    641    import socket
    642 
    643    import h2.connection
    644    import h2.events
    645    import h2.config
    646 
    647    def send_response(conn, event):
    648        stream_id = event.stream_id
    649        response_data = json.dumps(dict(event.headers)).encode('utf-8')
    650 
    651        conn.send_headers(
    652            stream_id=stream_id,
    653            headers=[
    654                (':status', '200'),
    655                ('server', 'basic-h2-server/1.0'),
    656                ('content-length', str(len(response_data))),
    657                ('content-type', 'application/json'),
    658            ],
    659        )
    660        conn.send_data(
    661            stream_id=stream_id,
    662            data=response_data,
    663            end_stream=True
    664        )
    665 
    666    def handle(sock):
    667        config = h2.config.H2Configuration(client_side=False)
    668        conn = h2.connection.H2Connection(config=config)
    669        conn.initiate_connection()
    670        sock.sendall(conn.data_to_send())
    671 
    672        while True:
    673            data = sock.recv(65535)
    674            if not data:
    675                break
    676 
    677            events = conn.receive_data(data)
    678            for event in events:
    679                if isinstance(event, h2.events.RequestReceived):
    680                    send_response(conn, event)
    681 
    682            data_to_send = conn.data_to_send()
    683            if data_to_send:
    684                sock.sendall(data_to_send)
    685 
    686 
    687    sock = socket.socket()
    688    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    689    sock.bind(('0.0.0.0', 8080))
    690    sock.listen(5)
    691 
    692    while True:
    693        handle(sock.accept()[0])
    694 
    695 Now, execute ``h2server.py`` and then point ``hyper`` at it again. You should
    696 see something like the following output from ``hyper``:
    697 
    698 .. code-block:: console
    699 
    700    $ hyper --h2 GET http://localhost:8080/
    701    {":scheme": "http", ":authority": "localhost", ":method": "GET", ":path": "/"}
    702 
    703 Here you can see the HTTP/2 request 'special headers' that ``hyper`` sends.
    704 These are similar to the ``:status`` header we have to send on our response:
    705 they encode important parts of the HTTP request in a clearly-defined way. If
    706 you were writing a client stack using h2, you'd need to make sure you
    707 were sending those headers.
    708 
    709 Congratulations!
    710 ~~~~~~~~~~~~~~~~
    711 
    712 Congratulations! You've written your first HTTP/2 server! If you want to extend
    713 it, there are a few directions you could investigate:
    714 
    715 - We didn't handle a few events that we saw were being raised: you could add
    716  some methods to handle those appropriately.
    717 - Right now our server is single threaded, so it can only handle one client at
    718  a time. Consider rewriting this server to use threads, or writing this
    719  server again using your favourite asynchronous programming framework.
    720 
    721  If you plan to use threads, you should know that a ``H2Connection`` object is
    722  deliberately not thread-safe. As a possible design pattern, consider creating
    723  threads and passing the sockets returned by ``accept`` to those threads, and
    724  then letting those threads create their own ``H2Connection`` objects.
    725 - Take a look at some of our long-form code examples in :doc:`examples`.
    726 - Alternatively, try playing around with our examples in our repository's
    727  `examples directory`_. These examples are a bit more fully-featured, and can
    728  be reached from your web browser. Try adjusting what they do, or adding new
    729  features to them!
    730 - You may want to make this server reachable from your web browser. To do that,
    731  you'll need to add proper TLS support to your server. This can be tricky, and
    732  in many cases requires `PyOpenSSL`_ in addition to the other libraries you
    733  have installed. Check the `Eventlet example`_ to see what PyOpenSSL code is
    734  required to TLS-ify your server.
    735 
    736 
    737 
    738 .. _event loop: https://en.wikipedia.org/wiki/Event_loop
    739 .. _httpbin.org/get: https://httpbin.org/get
    740 .. _examples directory: https://github.com/python-hyper/h2/tree/master/examples
    741 .. _standard library's socket module: https://docs.python.org/3/library/socket.html
    742 .. _Application Layer Protocol Negotiation: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
    743 .. _get your certificate here: https://raw.githubusercontent.com/python-hyper/h2/master/examples/twisted/server.crt
    744 .. _get your private key here: https://raw.githubusercontent.com/python-hyper/h2/master/examples/twisted/server.key
    745 .. _PyOpenSSL: http://pyopenssl.readthedocs.org/
    746 .. _Eventlet example: https://github.com/python-hyper/h2/blob/master/examples/eventlet/eventlet-server.py