Skip to content

Commit

Permalink
Clean up quick start guides.
Browse files Browse the repository at this point in the history
* Move most examples back to the getting started section.
* Add a dedicated howto on encryption.

And some drive-by fixes.

Refs #1209.
  • Loading branch information
aaugustin committed Feb 15, 2025
1 parent 82dfd83 commit 61a6a7a
Show file tree
Hide file tree
Showing 28 changed files with 258 additions and 243 deletions.
1 change: 0 additions & 1 deletion docs/deploy/koyeb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ or press Ctrl-D to terminate the connection:
< Hello!
Connection closed: 1000 (OK).
You can also confirm that your application shuts down gracefully.

Connect an interactive client again:
Expand Down
65 changes: 65 additions & 0 deletions docs/howto/encryption.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
Encrypt connections
====================

.. currentmodule:: websockets

You should always secure WebSocket connections with TLS_ (Transport Layer
Security).

.. admonition:: TLS vs. SSL
:class: tip

TLS is sometimes referred to as SSL (Secure Sockets Layer). SSL was an
earlier encryption protocol; the name stuck.

The ``wss`` protocol is to ``ws`` what ``https`` is to ``http``.

Secure WebSocket connections require certificates just like HTTPS.

.. _TLS: https://developer.mozilla.org/en-US/docs/Web/Security/Transport_Layer_Security

.. admonition:: Configure the TLS context securely
:class: attention

The examples below demonstrate the ``ssl`` argument with a TLS certificate
shared between the client and the server. This is a simplistic setup.

Please review the advice and security considerations in the documentation of
the :mod:`ssl` module to configure the TLS context appropriately.

Servers
-------

In a typical :doc:`deployment <../deploy/index>`, the server is behind a reverse
proxy that terminates TLS. The client connects to the reverse proxy with TLS and
the reverse proxy connects to the server without TLS.

In that case, you don't need to configure TLS in websockets.

If needed in your setup, you can terminate TLS in the server.

In the example below, :func:`~asyncio.server.serve` is configured to receive
secure connections. Before running this server, download
:download:`localhost.pem <../../example/tls/localhost.pem>` and save it in the
same directory as ``server.py``.

.. literalinclude:: ../../example/tls/server.py
:caption: server.py

Receive both plain and TLS connections on the same port isn't supported.

Clients
-------

:func:`~asyncio.client.connect` enables TLS automatically when connecting to a
``wss://...`` URI.

This works out of the box when the TLS certificate of the server is valid,
meaning it's signed by a certificate authority that your Python installation
trusts.

In the example above, since the server uses a self-signed certificate, the
client needs to be configured to trust the certificate. Here's how to do so.

.. literalinclude:: ../../example/tls/client.py
:caption: client.py
12 changes: 8 additions & 4 deletions docs/howto/index.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
How-to guides
=============

In a hurry? Check out these examples.
Set up your development environment comfortably.

.. toctree::

quickstart
debugging
autoreload
debugging

Configure websockets securely in production.

.. toctree::

encryption

If you're stuck, perhaps you'll find the answer here.

Expand All @@ -25,7 +30,6 @@ Upgrading from the legacy :mod:`asyncio` implementation to the new one?
Read this.

.. toctree::
:maxdepth: 2

upgrade

Expand Down
170 changes: 0 additions & 170 deletions docs/howto/quickstart.rst

This file was deleted.

112 changes: 112 additions & 0 deletions docs/intro/examples.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
Quick examples
==============

.. currentmodule:: websockets

Start a server
--------------

This WebSocket server receives a name from the client, sends a greeting, and
closes the connection.

.. literalinclude:: ../../example/quick/server.py
:caption: server.py
:language: python

:func:`~asyncio.server.serve` executes the connection handler coroutine
``hello()`` once for each WebSocket connection. It closes the WebSocket
connection when the handler returns.

Connect a client
----------------

This WebSocket client sends a name to the server, receives a greeting, and
closes the connection.

.. literalinclude:: ../../example/quick/client.py
:caption: client.py
:language: python

Using :func:`~sync.client.connect` as a context manager ensures that the
WebSocket connection is closed.

Connect a browser
-----------------

The WebSocket protocol was invented for the web — as the name says!

Here's how to connect a browser to a WebSocket server.

Run this script in a console:

.. literalinclude:: ../../example/quick/show_time.py
:caption: show_time.py
:language: python

Save this file as ``show_time.html``:

.. literalinclude:: ../../example/quick/show_time.html
:caption: show_time.html
:language: html

Save this file as ``show_time.js``:

.. literalinclude:: ../../example/quick/show_time.js
:caption: show_time.js
:language: js

Then, open ``show_time.html`` in several browsers or tabs. Clocks tick
irregularly.

Broadcast messages
------------------

Let's send the same timestamps to everyone instead of generating independent
sequences for each connection.

Stop the previous script if it's still running and run this script in a console:

.. literalinclude:: ../../example/quick/sync_time.py
:caption: sync_time.py
:language: python

Refresh ``show_time.html`` in all browsers or tabs. Clocks tick in sync.

Manage application state
------------------------

A WebSocket server can receive events from clients, process them to update the
application state, and broadcast the updated state to all connected clients.

Here's an example where any client can increment or decrement a counter. The
concurrency model of :mod:`asyncio` guarantees that updates are serialized.

This example keep tracks of connected users explicitly in ``USERS`` instead of
relying on :attr:`server.connections <asyncio.server.Server.connections>`. The
result is the same.

Run this script in a console:

.. literalinclude:: ../../example/quick/counter.py
:caption: counter.py
:language: python

Save this file as ``counter.html``:

.. literalinclude:: ../../example/quick/counter.html
:caption: counter.html
:language: html

Save this file as ``counter.css``:

.. literalinclude:: ../../example/quick/counter.css
:caption: counter.css
:language: css

Save this file as ``counter.js``:

.. literalinclude:: ../../example/quick/counter.js
:caption: counter.js
:language: js

Then open ``counter.html`` file in several browsers and play with [+] and [-].
Loading

0 comments on commit 61a6a7a

Please sign in to comment.