Skip to content

Update docs #1283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/source/asyncio-example.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ This example demonstrates some basic asyncio techniques.
:encoding: utf-8


You can use ``cert.crt`` and ``cert.key`` files provided within the repository
or generate your own certificates using `OpenSSL`_:

.. code-block:: console

$ openssl req -x509 -newkey rsa:2048 -keyout cert.key -out cert.crt -days 365 -nodes


.. _asyncio: https://docs.python.org/3/library/asyncio.html
.. _OpenSSL: https://openssl-library.org/source/index.html
28 changes: 21 additions & 7 deletions docs/source/basic-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ This is not a socket tutorial, so we're not going to dive too deeply into how
this works. If you want more detail about sockets, there are lots of good
tutorials on the web that you should investigate.

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

.. code-block:: python
Expand Down Expand Up @@ -213,11 +213,12 @@ connection object and start handing it data. For now, let's just see what
happens as we feed it data.

To make HTTP/2 connections, we need a tool that knows how to speak HTTP/2.
Most versions of curl in the wild don't, so let's install a Python tool. In
your Python environment, run ``pip install hyper``. This will install a Python
command-line HTTP/2 tool called ``hyper``. To confirm that it works, try
running this command and verifying that the output looks similar to the one
shown below:
You can simply use `curl`_ or install a Python tool used throughout this
tutorial. In your Python environment, run ``pip install hyper`` (Make sure to
use ``Python < 3.10`` or install it in separate environment). This will
install a Python command-line HTTP/2 tool called ``hyper``. To confirm that
it works, try running this command and verifying that the output looks similar
to the one shown below:

.. code-block:: console

Expand All @@ -227,6 +228,16 @@ shown below:
'origin': '10.0.0.2',
'url': 'https://nghttp2.org/httpbin/get'}

Equivalent code with curl would look like this:

.. code-block:: console

$ curl --http2 https://nghttp2.org/httpbin/get

To use it with our server though, you will need to invoke it with a different
Copy link
Member

@Kriechi Kriechi Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To use it with our server though, you will need to invoke it with a different
To use it with a ``python-hyper/h2``-based server, you can invoke it with a different

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this context I meant to use it with the server from this example, not any arbitrary hyper/h2-based server (as generally you will serve it with TLS), so you should or you have to is probably better.

``--http2-prior-knowledge`` flag as we are going to serve over the insecure
connection.

Assuming it works, you're now ready to start sending HTTP/2 data.

Back in our ``h2server.py`` script, we're going to want to start handling data.
Expand Down Expand Up @@ -290,7 +301,9 @@ function. Your ``h2server.py`` should end up looking a like this:
handle(sock.accept()[0])

Running that in one shell, in your other shell you can run
``hyper --h2 GET http://localhost:8080/``. That shell should hang, and you
``hyper --h2 GET http://localhost:8080/``. For the ``curl`` use
``curl -v --http2-prior-knowledge http://localhost:8080/`` command.
That shell should hang, and you
should then see the following output from your ``h2server.py`` shell:

.. code-block:: console
Expand Down Expand Up @@ -744,3 +757,4 @@ it, there are a few directions you could investigate:
.. _get your private key here: https://raw.githubusercontent.com/python-hyper/h2/master/examples/twisted/server.key
.. _PyOpenSSL: http://pyopenssl.readthedocs.org/
.. _Eventlet example: https://github.com/python-hyper/h2/blob/master/examples/eventlet/eventlet-server.py
.. _curl: https://curl.se/docs/http2.html
8 changes: 8 additions & 0 deletions docs/source/wsgi-example.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,12 @@ The main advantages of this example are:
:encoding: utf-8


You can use ``cert.crt`` and ``cert.key`` files provided within the repository
or generate your own certificates using `OpenSSL`_:

.. code-block:: console

$ openssl req -x509 -newkey rsa:2048 -keyout cert.key -out cert.crt -days 365 -nodes

.. _asyncio: https://docs.python.org/3/library/asyncio.html
.. _OpenSSL: https://openssl-library.org/source/index.html
10 changes: 5 additions & 5 deletions examples/asyncio/asyncio-server.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ def window_updated(self, stream_id, delta):
loop.run_forever()
except KeyboardInterrupt:
pass

# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
finally:
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
89 changes: 37 additions & 52 deletions examples/asyncio/wsgi-server.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,9 @@ def window_opened(self, event):
for data in self._flow_controlled_data.values():
self._stream_data.put_nowait(data)

self._flow_controlled_data = {}
self._flow_controlled_data.clear()

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

# If this stream got reset, just drop the data on the floor. Note
# that we need to reset the event here to make sure that
Expand Down Expand Up @@ -327,7 +326,7 @@ def reset_stream(self, event):
data.
"""
if event.stream_id in self._flow_controlled_data:
del self._flow_controlled_data
del self._flow_controlled_data[event.stream_id]

self._reset_streams.add(event.stream_id)
self.end_stream(event)
Expand Down Expand Up @@ -534,23 +533,9 @@ def readline(self, hint=None):
def readlines(self, hint=None):
"""
Called by the WSGI application to read several lines of data.

This method is really pretty stupid. It rigorously observes the
``hint`` parameter, and quite happily returns the input split into
lines.
"""
# This method is *crazy inefficient*, but it's also a pretty stupid
# method to call.
data = self.read(hint)
lines = data.split(b'\n')

# Split removes the newline character, but we want it, so put it back.
lines = [line + b'\n' for line in lines]

# Except if the last character was a newline character we now have an
# extra line that is just a newline: pull that out.
if lines[-1] == b'\n':
lines = lines[:-1]
lines = data.splitlines(keepends=True)
return lines

def start_response(self, status, response_headers, exc_info=None):
Expand Down Expand Up @@ -688,41 +673,41 @@ def _build_environ_dict(headers, stream):
version you'd want to fix it.
"""
header_dict = dict(headers)
path = header_dict.pop(u':path')
path = header_dict.pop(':path')
try:
path, query = path.split(u'?', 1)
path, query = path.split('?', 1)
except ValueError:
query = u""
server_name = header_dict.pop(u':authority')
query = ""
server_name = header_dict.pop(':authority')
try:
server_name, port = server_name.split(u':', 1)
except ValueError as e:
server_name, port = server_name.split(':', 1)
except ValueError:
port = "8443"

environ = {
u'REQUEST_METHOD': header_dict.pop(u':method'),
u'SCRIPT_NAME': u'',
u'PATH_INFO': path,
u'QUERY_STRING': query,
u'SERVER_NAME': server_name,
u'SERVER_PORT': port,
u'SERVER_PROTOCOL': u'HTTP/2',
u'HTTPS': u"on",
u'SSL_PROTOCOL': u'TLSv1.2',
u'wsgi.version': (1, 0),
u'wsgi.url_scheme': header_dict.pop(u':scheme'),
u'wsgi.input': stream,
u'wsgi.errors': sys.stderr,
u'wsgi.multithread': True,
u'wsgi.multiprocess': False,
u'wsgi.run_once': False,
'REQUEST_METHOD': header_dict.pop(':method'),
'SCRIPT_NAME': '',
'PATH_INFO': path,
'QUERY_STRING': query,
'SERVER_NAME': server_name,
'SERVER_PORT': port,
'SERVER_PROTOCOL': 'HTTP/2',
'HTTPS': "on",
'SSL_PROTOCOL': 'TLSv1.2',
'wsgi.version': (1, 0),
'wsgi.url_scheme': header_dict.pop(':scheme'),
'wsgi.input': stream,
'wsgi.errors': sys.stderr,
'wsgi.multithread': True,
'wsgi.multiprocess': False,
'wsgi.run_once': False,
}
if u'content-type' in header_dict:
environ[u'CONTENT_TYPE'] = header_dict[u'content-type']
if u'content-length' in header_dict:
environ[u'CONTENT_LENGTH'] = header_dict[u'content-length']
if 'content-type' in header_dict:
environ['CONTENT_TYPE'] = header_dict.pop('content-type')
if 'content-length' in header_dict:
environ['CONTENT_LENGTH'] = header_dict.pop('content-length')
for name, value in header_dict.items():
environ[u'HTTP_' + name.upper()] = value
environ['HTTP_' + name.upper()] = value
return environ


Expand Down Expand Up @@ -753,8 +738,8 @@ def _build_environ_dict(headers, stream):
loop.run_forever()
except KeyboardInterrupt:
pass

# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
finally:
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()