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