Skip to content

Commit 74a7ac2

Browse files
committed
Add topic guide on routing.
1 parent 89b037f commit 74a7ac2

File tree

5 files changed

+96
-27
lines changed

5 files changed

+96
-27
lines changed

docs/deploy/index.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ architecture will almost certainly look like the following diagram:
1414
The basic unit for scaling a websockets server is "one server process". Each
1515
blue box in the diagram represents one server process.
1616

17-
There's more variation in routing. While the routing layer is shown as one big
18-
box, it is likely to involve several subsystems.
17+
There's more variation in routing connections to processes. While the routing
18+
layer is shown as one big box, it is likely to involve several subsystems.
1919

2020
As a consequence, when you design a deployment, you must answer two questions:
2121

@@ -153,8 +153,8 @@ If you override the default signal handler for SIGINT, which raises
153153
:exc:`KeyboardInterrupt`, be aware that you won't be able to interrupt a
154154
program with Ctrl-C anymore when it's stuck in a loop.
155155

156-
Routing connections
157-
-------------------
156+
Routing connections to processes
157+
--------------------------------
158158

159159
What does routing involve?
160160
..........................

docs/faq/server.rst

+2-20
Original file line numberDiff line numberDiff line change
@@ -208,26 +208,8 @@ How do I access the request path?
208208

209209
It is available in the :attr:`~ServerConnection.request` object.
210210

211-
You may route a connection to different handlers depending on the request path::
212-
213-
async def handler(websocket):
214-
if websocket.request.path == "/blue":
215-
await blue_handler(websocket)
216-
elif websocket.request.path == "/green":
217-
await green_handler(websocket)
218-
else:
219-
# No handler for this path; close the connection.
220-
return
221-
222-
For more complex routing, you may use :func:`~websockets.asyncio.router.route`.
223-
224-
You may also route the connection based on the first message received from the
225-
client, as shown in the :doc:`tutorial <../intro/tutorial2>`. When you want to
226-
authenticate the connection before routing it, this is usually more convenient.
227-
228-
Generally speaking, there is far less emphasis on the request path in WebSocket
229-
servers than in HTTP servers. When a WebSocket server provides a single endpoint,
230-
it may ignore the request path entirely.
211+
Refer to the :doc:`routing guide <../topics/routing>` for details on how to
212+
route connections to different handlers depending on the request path.
231213

232214
How do I access HTTP headers?
233215
-----------------------------

docs/project/changelog.rst

+5-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ Backwards-incompatible changes
4343
SOCKS proxies require installing the third-party library `python-socks`_.
4444

4545
If you want to disable the proxy, add ``proxy=None`` when calling
46-
:func:`~asyncio.client.connect`. See :doc:`../topics/proxies` for details.
46+
:func:`~asyncio.client.connect`.
47+
48+
See :doc:`proxies <../topics/proxies>` for details.
4749

4850
.. _python-socks: https://github.com/romis2012/python-socks
4951

@@ -60,7 +62,8 @@ New features
6062
............
6163

6264
* Added :func:`~asyncio.router.route` and :func:`~asyncio.router.unix_route` to
63-
dispatch connections to different handlers depending on the URL.
65+
dispatch connections to handlers based on the request path. Read more about
66+
routing in :doc:`routing <../topics/routing>`.
6467

6568
Improvements
6669
............

docs/topics/index.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ Get a deeper understanding of how websockets is built and why.
66
.. toctree::
77
:titlesonly:
88

9-
logging
109
authentication
1110
broadcast
1211
compression
1312
keepalive
13+
logging
1414
memory
1515
security
1616
performance
1717
proxies
18+
routing

docs/topics/routing.rst

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
Routing
2+
=======
3+
4+
.. currentmodule:: websockets
5+
6+
Many WebSocket servers provide just one endpoint. That's why
7+
:func:`~asyncio.server.serve` accepts a single connection handler as its first
8+
argument.
9+
10+
This may come as a surprise to you if you're used to HTTP servers. In a standard
11+
HTTP application, each request gets dispatched to a handler based on the request
12+
path. Clients know which path to use for which operation.
13+
14+
In a WebSocket application, clients open a persistent connection then they send
15+
all messages over that unique connection. When different messages correspond to
16+
different operations, they must be dispatched based on the message content.
17+
18+
Simple routing
19+
--------------
20+
21+
If you need different handlers for different clients or different use cases, you
22+
may route each connection to the right handler based on the request path.
23+
24+
Since WebSocket servers typically provide fewer routes than HTTP servers, you
25+
can keep it simple::
26+
27+
async def handler(websocket):
28+
match websocket.request.path:
29+
case "/blue":
30+
await blue_handler(websocket)
31+
case "/green":
32+
await green_handler(websocket)
33+
case _:
34+
# No handler for this path. Close the connection.
35+
return
36+
37+
You may also route connections based on the first message received from the
38+
client, as demonstrated in the :doc:`tutorial <../intro/tutorial2>`::
39+
40+
import json
41+
42+
async def handler(websocket):
43+
message = await websocket.recv()
44+
settings = json.loads(message)
45+
match settings["color"]:
46+
case "blue":
47+
await blue_handler(websocket)
48+
case "green":
49+
await green_handler(websocket)
50+
case _:
51+
# No handler for this message. Close the connection.
52+
return
53+
54+
When you need to authenticate the connection before routing it, this pattern is
55+
more convenient.
56+
57+
Complex routing
58+
---------------
59+
60+
If you have outgrow these simple patterns, websockets provides full-fledged
61+
routing based on the request path with :func:`~asyncio.router.route`.
62+
63+
This feature builds upon Flask_'s router. To use it, you must install the
64+
third-party library `werkzeug`_::
65+
66+
$ pip install werkzeug
67+
68+
.. _Flask: https://flask.palletsprojects.com/
69+
.. _werkzeug: https://werkzeug.palletsprojects.com/
70+
71+
:func:`~asyncio.router.route` expects a :class:`werkzeug.routing.Map` as its
72+
first argument to declare which URL patterns map to which handlers. Review the
73+
documentation of :mod:`werkzeug.routing` to learn about its functionality.
74+
75+
To give you a sense of what's possible, here's the URL map of the example in
76+
`example/routing.py`_:
77+
78+
.. _example/routing.py: https://github.com/python-websockets/websockets/blob/main/example/routing.py
79+
80+
.. literalinclude:: ../../example/routing.py
81+
:language: python
82+
:start-at: url_map = Map(
83+
:end-at: await server.serve_forever()

0 commit comments

Comments
 (0)