Skip to content

Commit 2730c5b

Browse files
authored
Merge pull request #1283 from bmwant/bmwant/update-docs-1
Update docs
2 parents 0dd0bf2 + 9ca45d7 commit 2730c5b

File tree

6 files changed

+93
-84
lines changed

6 files changed

+93
-84
lines changed

docs/source/asyncio-example.rst

+9
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,13 @@ This example demonstrates some basic asyncio techniques.
1414
:encoding: utf-8
1515

1616

17+
You can use ``cert.crt`` and ``cert.key`` files provided within the repository
18+
or generate your own certificates using `OpenSSL`_:
19+
20+
.. code-block:: console
21+
22+
$ openssl req -x509 -newkey rsa:2048 -keyout cert.key -out cert.crt -days 365 -nodes
23+
24+
1725
.. _asyncio: https://docs.python.org/3/library/asyncio.html
26+
.. _OpenSSL: https://openssl-library.org/source/index.html

docs/source/basic-usage.rst

+33-26
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ This is not a socket tutorial, so we're not going to dive too deeply into how
156156
this works. If you want more detail about sockets, there are lots of good
157157
tutorials on the web that you should investigate.
158158

159-
When you want to listen for incoming connections, the you need to *bind* an
159+
When you want to listen for incoming connections, you need to *bind* an
160160
address first. So let's do that. Try setting up your file to look like this:
161161

162162
.. code-block:: python
@@ -213,20 +213,23 @@ connection object and start handing it data. For now, let's just see what
213213
happens as we feed it data.
214214

215215
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:
216+
You can simply use `curl`_ or any other client with HTTP/2 support like
217+
`httpx`_. To confirm that it works, try running this command and verifying that
218+
the output looks similar to the one shown below:
221219

222220
.. code-block:: console
223221
224-
$ hyper GET https://nghttp2.org/httpbin/get
222+
$ curl --http2 https://nghttp2.org/httpbin/get
225223
{'args': {},
226224
'headers': {'Host': 'nghttp2.org'},
227225
'origin': '10.0.0.2',
228226
'url': 'https://nghttp2.org/httpbin/get'}
229227
228+
229+
To use it with our server though, you will need to invoke it with a different
230+
``--http2-prior-knowledge`` flag as we are going to serve over the insecure
231+
connection.
232+
230233
Assuming it works, you're now ready to start sending HTTP/2 data.
231234

232235
Back in our ``h2server.py`` script, we're going to want to start handling data.
@@ -290,15 +293,16 @@ function. Your ``h2server.py`` should end up looking a like this:
290293
handle(sock.accept()[0])
291294
292295
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:
296+
``curl -v --http2-prior-knowledge http://localhost:8080/`` command.
297+
That shell should hang, and you should then see the following output from your
298+
``h2server.py`` shell:
295299

296300
.. code-block:: console
297301
298302
$ python h2server.py
299303
[<h2.events.RemoteSettingsChanged object at 0x10c4ee390>]
300304
301-
You'll then need to kill ``hyper`` and ``h2server.py`` with Ctrl+C. Feel free
305+
You'll then need to kill ``curl`` and ``h2server.py`` with Ctrl+C. Feel free
302306
to do this a few times, to see how things behave.
303307

304308
So, what did we see here? When the connection was opened, we used the
@@ -307,15 +311,15 @@ socket, in a loop. We then passed that data to the connection object, which
307311
returned us a single event object:
308312
:class:`RemoteSettingsChanged <h2.events.RemoteSettingsChanged>`.
309313

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``
314+
But what we didn't see was anything else. So it seems like all ``curl`` did
315+
was change its settings, but nothing else. If you look at the other ``curl``
312316
window, you'll notice that it hangs for a while and then eventually fails with
313317
a socket timeout. It was waiting for something: what?
314318

315319
Well, it turns out that at the start of a connection, both sides need to send
316320
a bit of data, called "the HTTP/2 preamble". We don't need to get into too much
317321
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,
322+
data that tells the other side what their settings are. ``curl`` did that,
319323
but we didn't.
320324

321325
Let's do that next.
@@ -388,9 +392,10 @@ Your ``h2server.py`` script should now look like this:
388392
389393
390394
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:
395+
same ``curl`` command:
396+
``curl -v --http2-prior-knowledge http://localhost:8080/``.
397+
The ``curl`` command still hangs, but this time we get a bit more output from
398+
our ``h2server.py`` script:
394399

395400
.. code-block:: console
396401
@@ -410,17 +415,17 @@ Finally, even more data that triggers *two* events:
410415
:class:`RequestReceived <h2.events.RequestReceived>` and
411416
:class:`StreamEnded <h2.events.StreamEnded>`.
412417

413-
So, what's happening is that ``hyper`` is telling us about its settings,
418+
So, what's happening is that ``curl`` is telling us about its settings,
414419
acknowledging ours, and then sending us a request. Then it ends a *stream*,
415420
which is a HTTP/2 communications channel that holds a request and response
416421
pair.
417422

418423
A stream isn't done until it's either *reset* or both sides *close* it:
419424
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
425+
is that ``curl`` is closing its half of the stream: it won't send us any more
421426
data on that stream. That means the request is done.
422427

423-
So why is ``hyper`` hanging? Well, we haven't sent a response yet: let's do
428+
So why is ``curl`` hanging? Well, we haven't sent a response yet: let's do
424429
that.
425430

426431

@@ -489,7 +494,7 @@ one exception is headers: h2 will automatically encode those into UTF-8.
489494
The last thing to note is that on our call to ``send_data``, we set
490495
``end_stream`` to ``True``. This tells h2 (and the remote peer) that
491496
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
497+
``curl`` will have ended its side of the stream, when we end ours the stream
493498
will be totally done with.
494499

495500
We're nearly ready to go with this: we just need to plumb this function in.
@@ -581,9 +586,9 @@ With these changes, your ``h2server.py`` file should look like this:
581586
while True:
582587
handle(sock.accept()[0])
583588
584-
Alright. Let's run this, and then run our ``hyper`` command again.
589+
Alright. Let's run this, and then run our ``curl`` command again.
585590

586-
This time, nothing is printed from our server, and the ``hyper`` side prints
591+
This time, nothing is printed from our server, and the ``curl`` side prints
587592
``it works!``. Success! Try running it a few more times, and we can see that
588593
not only does it work the first time, it works the other times too!
589594

@@ -692,15 +697,15 @@ file, which should now look like this:
692697
while True:
693698
handle(sock.accept()[0])
694699
695-
Now, execute ``h2server.py`` and then point ``hyper`` at it again. You should
696-
see something like the following output from ``hyper``:
700+
Now, execute ``h2server.py`` and then point ``curl`` at it again. You should
701+
see something like the following output from ``curl``:
697702

698703
.. code-block:: console
699704
700-
$ hyper --h2 GET http://localhost:8080/
705+
$ curl -v --http2-prior-knowledge http://localhost:8080/
701706
{":scheme": "http", ":authority": "localhost", ":method": "GET", ":path": "/"}
702707
703-
Here you can see the HTTP/2 request 'special headers' that ``hyper`` sends.
708+
Here you can see the HTTP/2 request 'special headers' that ``curl`` sends.
704709
These are similar to the ``:status`` header we have to send on our response:
705710
they encode important parts of the HTTP request in a clearly-defined way. If
706711
you were writing a client stack using h2, you'd need to make sure you
@@ -744,3 +749,5 @@ it, there are a few directions you could investigate:
744749
.. _get your private key here: https://raw.githubusercontent.com/python-hyper/h2/master/examples/twisted/server.key
745750
.. _PyOpenSSL: http://pyopenssl.readthedocs.org/
746751
.. _Eventlet example: https://github.com/python-hyper/h2/blob/master/examples/eventlet/eventlet-server.py
752+
.. _curl: https://curl.se/docs/http2.html
753+
.. _httpx: https://www.python-httpx.org/

docs/source/wsgi-example.rst

+8
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,12 @@ The main advantages of this example are:
2020
:encoding: utf-8
2121

2222

23+
You can use ``cert.crt`` and ``cert.key`` files provided within the repository
24+
or generate your own certificates using `OpenSSL`_:
25+
26+
.. code-block:: console
27+
28+
$ openssl req -x509 -newkey rsa:2048 -keyout cert.key -out cert.crt -days 365 -nodes
29+
2330
.. _asyncio: https://docs.python.org/3/library/asyncio.html
31+
.. _OpenSSL: https://openssl-library.org/source/index.html

examples/asyncio/asyncio-server.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ def window_updated(self, stream_id, delta):
208208
loop.run_forever()
209209
except KeyboardInterrupt:
210210
pass
211-
212-
# Close the server
213-
server.close()
214-
loop.run_until_complete(server.wait_closed())
215-
loop.close()
211+
finally:
212+
# Close the server
213+
server.close()
214+
loop.run_until_complete(server.wait_closed())
215+
loop.close()

examples/asyncio/wsgi-server.py

+37-52
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,9 @@ def window_opened(self, event):
193193
for data in self._flow_controlled_data.values():
194194
self._stream_data.put_nowait(data)
195195

196-
self._flow_controlled_data = {}
196+
self._flow_controlled_data.clear()
197197

198-
@asyncio.coroutine
199-
def sending_loop(self):
198+
async def sending_loop(self):
200199
"""
201200
A call that loops forever, attempting to send data. This sending loop
202201
contains most of the flow-control smarts of this class: it pulls data
@@ -216,7 +215,7 @@ def sending_loop(self):
216215
This coroutine explicitly *does not end*.
217216
"""
218217
while True:
219-
stream_id, data, event = yield from self._stream_data.get()
218+
stream_id, data, event = await self._stream_data.get()
220219

221220
# If this stream got reset, just drop the data on the floor. Note
222221
# that we need to reset the event here to make sure that
@@ -327,7 +326,7 @@ def reset_stream(self, event):
327326
data.
328327
"""
329328
if event.stream_id in self._flow_controlled_data:
330-
del self._flow_controlled_data
329+
del self._flow_controlled_data[event.stream_id]
331330

332331
self._reset_streams.add(event.stream_id)
333332
self.end_stream(event)
@@ -534,23 +533,9 @@ def readline(self, hint=None):
534533
def readlines(self, hint=None):
535534
"""
536535
Called by the WSGI application to read several lines of data.
537-
538-
This method is really pretty stupid. It rigorously observes the
539-
``hint`` parameter, and quite happily returns the input split into
540-
lines.
541536
"""
542-
# This method is *crazy inefficient*, but it's also a pretty stupid
543-
# method to call.
544537
data = self.read(hint)
545-
lines = data.split(b'\n')
546-
547-
# Split removes the newline character, but we want it, so put it back.
548-
lines = [line + b'\n' for line in lines]
549-
550-
# Except if the last character was a newline character we now have an
551-
# extra line that is just a newline: pull that out.
552-
if lines[-1] == b'\n':
553-
lines = lines[:-1]
538+
lines = data.splitlines(keepends=True)
554539
return lines
555540

556541
def start_response(self, status, response_headers, exc_info=None):
@@ -688,41 +673,41 @@ def _build_environ_dict(headers, stream):
688673
version you'd want to fix it.
689674
"""
690675
header_dict = dict(headers)
691-
path = header_dict.pop(u':path')
676+
path = header_dict.pop(':path')
692677
try:
693-
path, query = path.split(u'?', 1)
678+
path, query = path.split('?', 1)
694679
except ValueError:
695-
query = u""
696-
server_name = header_dict.pop(u':authority')
680+
query = ""
681+
server_name = header_dict.pop(':authority')
697682
try:
698-
server_name, port = server_name.split(u':', 1)
699-
except ValueError as e:
683+
server_name, port = server_name.split(':', 1)
684+
except ValueError:
700685
port = "8443"
701686

702687
environ = {
703-
u'REQUEST_METHOD': header_dict.pop(u':method'),
704-
u'SCRIPT_NAME': u'',
705-
u'PATH_INFO': path,
706-
u'QUERY_STRING': query,
707-
u'SERVER_NAME': server_name,
708-
u'SERVER_PORT': port,
709-
u'SERVER_PROTOCOL': u'HTTP/2',
710-
u'HTTPS': u"on",
711-
u'SSL_PROTOCOL': u'TLSv1.2',
712-
u'wsgi.version': (1, 0),
713-
u'wsgi.url_scheme': header_dict.pop(u':scheme'),
714-
u'wsgi.input': stream,
715-
u'wsgi.errors': sys.stderr,
716-
u'wsgi.multithread': True,
717-
u'wsgi.multiprocess': False,
718-
u'wsgi.run_once': False,
688+
'REQUEST_METHOD': header_dict.pop(':method'),
689+
'SCRIPT_NAME': '',
690+
'PATH_INFO': path,
691+
'QUERY_STRING': query,
692+
'SERVER_NAME': server_name,
693+
'SERVER_PORT': port,
694+
'SERVER_PROTOCOL': 'HTTP/2',
695+
'HTTPS': "on",
696+
'SSL_PROTOCOL': 'TLSv1.2',
697+
'wsgi.version': (1, 0),
698+
'wsgi.url_scheme': header_dict.pop(':scheme'),
699+
'wsgi.input': stream,
700+
'wsgi.errors': sys.stderr,
701+
'wsgi.multithread': True,
702+
'wsgi.multiprocess': False,
703+
'wsgi.run_once': False,
719704
}
720-
if u'content-type' in header_dict:
721-
environ[u'CONTENT_TYPE'] = header_dict[u'content-type']
722-
if u'content-length' in header_dict:
723-
environ[u'CONTENT_LENGTH'] = header_dict[u'content-length']
705+
if 'content-type' in header_dict:
706+
environ['CONTENT_TYPE'] = header_dict.pop('content-type')
707+
if 'content-length' in header_dict:
708+
environ['CONTENT_LENGTH'] = header_dict.pop('content-length')
724709
for name, value in header_dict.items():
725-
environ[u'HTTP_' + name.upper()] = value
710+
environ['HTTP_' + name.upper()] = value
726711
return environ
727712

728713

@@ -753,8 +738,8 @@ def _build_environ_dict(headers, stream):
753738
loop.run_forever()
754739
except KeyboardInterrupt:
755740
pass
756-
757-
# Close the server
758-
server.close()
759-
loop.run_until_complete(server.wait_closed())
760-
loop.close()
741+
finally:
742+
# Close the server
743+
server.close()
744+
loop.run_until_complete(server.wait_closed())
745+
loop.close()

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ commands = flake8 src/ test/
3333

3434
[testenv:docs]
3535
deps =
36-
sphinx>=4.0.2,<5
36+
sphinx>=5.0.2,<6
3737
allowlist_externals = make
3838
changedir = {toxinidir}/docs
3939
commands =

0 commit comments

Comments
 (0)