Skip to content

Commit

Permalink
Rename Connection to Protocol.
Browse files Browse the repository at this point in the history
This makes the following naming possible:

* Connection = TCP/TLS connection + pointer to protocol; concerned with
  opening the network connection, moving bytes, and closing it.
* Protocol = Sans-I/O protocol; concerned with parsing and serializing
  messages and with the state of the connection.

Previously, these names were reversed in the sockets & threads branch.

The previous choice was influenced by the legacy implementation using
"protocol" to describe the two layers together, itself influenced by
asyncio using the same word.
  • Loading branch information
aaugustin committed Nov 27, 2022
1 parent 39c53fb commit f5ea94a
Show file tree
Hide file tree
Showing 21 changed files with 2,722 additions and 2,613 deletions.
130 changes: 66 additions & 64 deletions docs/howto/sansio.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,81 +33,83 @@ If you're building a client, parse the URI you'd like to connect to::
Open a TCP connection to ``(wsuri.host, wsuri.port)`` and perform a TLS
handshake if ``wsuri.secure`` is :obj:`True`.

Initialize a :class:`~client.ClientConnection`::
Initialize a :class:`~client.ClientProtocol`::

from websockets.client import ClientConnection
from websockets.client import ClientProtocol

connection = ClientConnection(wsuri)
protocol = ClientProtocol(wsuri)

Create a WebSocket handshake request
with :meth:`~client.ClientConnection.connect` and send it
with :meth:`~client.ClientConnection.send_request`::
with :meth:`~client.ClientProtocol.connect` and send it
with :meth:`~client.ClientProtocol.send_request`::

request = connection.connect()
connection.send_request(request)
request = protocol.connect()
protocol.send_request(request)

Then, call :meth:`~connection.Connection.data_to_send` and send its output to
Then, call :meth:`~protocol.Protocol.data_to_send` and send its output to
the network, as described in `Send data`_ below.

The first event returned by :meth:`~connection.Connection.events_received` is
the WebSocket handshake response.
Once you receive enough data, as explained in `Receive data`_ below, the first
event returned by :meth:`~protocol.Protocol.events_received` is the WebSocket
handshake response.

When the handshake fails, the reason is available in
:attr:`~client.ClientConnection.handshake_exc`::
:attr:`~client.ClientProtocol.handshake_exc`::

if connection.handshake_exc is not None:
raise connection.handshake_exc
if protocol.handshake_exc is not None:
raise protocol.handshake_exc

Else, the WebSocket connection is open.

A WebSocket client API usually performs the handshake then returns a wrapper
around the network connection and the :class:`~client.ClientConnection`.
around the network socket and the :class:`~client.ClientProtocol`.

Server-side
...........

If you're building a server, accept network connections from clients and
perform a TLS handshake if desired.

For each connection, initialize a :class:`~server.ServerConnection`::
For each connection, initialize a :class:`~server.ServerProtocol`::

from websockets.server import ServerConnection
from websockets.server import ServerProtocol

connection = ServerConnection()
protocol = ServerProtocol()

The first event returned by :meth:`~connection.Connection.events_received` is
the WebSocket handshake request.
Once you receive enough data, as explained in `Receive data`_ below, the first
event returned by :meth:`~protocol.Protocol.events_received` is the WebSocket
handshake request.

Create a WebSocket handshake response
with :meth:`~server.ServerConnection.accept` and send it
with :meth:`~server.ServerConnection.send_response`::
with :meth:`~server.ServerProtocol.accept` and send it
with :meth:`~server.ServerProtocol.send_response`::

response = connection.accept(request)
connection.send_response(response)
response = protocol.accept(request)
protocol.send_response(response)

Alternatively, you may reject the WebSocket handshake and return an HTTP
response with :meth:`~server.ServerConnection.reject`::
response with :meth:`~server.ServerProtocol.reject`::

response = connection.reject(status, explanation)
connection.send_response(response)
response = protocol.reject(status, explanation)
protocol.send_response(response)

Then, call :meth:`~connection.Connection.data_to_send` and send its output to
Then, call :meth:`~protocol.Protocol.data_to_send` and send its output to
the network, as described in `Send data`_ below.

Even when you call :meth:`~server.ServerConnection.accept`, the WebSocket
Even when you call :meth:`~server.ServerProtocol.accept`, the WebSocket
handshake may fail if the request is incorrect or unsupported.

When the handshake fails, the reason is available in
:attr:`~server.ServerConnection.handshake_exc`::
:attr:`~server.ServerProtocol.handshake_exc`::

if connection.handshake_exc is not None:
raise connection.handshake_exc
if protocol.handshake_exc is not None:
raise protocol.handshake_exc

Else, the WebSocket connection is open.

A WebSocket server API usually builds a wrapper around the network connection
and the :class:`~server.ServerConnection`. Then it invokes a connection
handler that accepts the wrapper in argument.
A WebSocket server API usually builds a wrapper around the network socket and
the :class:`~server.ServerProtocol`. Then it invokes a connection handler that
accepts the wrapper in argument.

It may also provide a way to close all connections and to shut down the server
gracefully.
Expand All @@ -122,11 +124,11 @@ Go through the five steps below until you reach the end of the data stream.
Receive data
............

When receiving data from the network, feed it to the connection's
:meth:`~connection.Connection.receive_data` method.
When receiving data from the network, feed it to the protocol's
:meth:`~protocol.Protocol.receive_data` method.

When reaching the end of the data stream, call the connection's
:meth:`~connection.Connection.receive_eof` method.
When reaching the end of the data stream, call the protocol's
:meth:`~protocol.Protocol.receive_eof` method.

For example, if ``sock`` is a :obj:`~socket.socket`::

Expand All @@ -135,21 +137,21 @@ For example, if ``sock`` is a :obj:`~socket.socket`::
except OSError: # socket closed
data = b""
if data:
connection.receive_data(data)
protocol.receive_data(data)
else:
connection.receive_eof()
protocol.receive_eof()

These methods aren't expected to raise exceptions — unless you call them again
after calling :meth:`~connection.Connection.receive_eof`, which is an error.
after calling :meth:`~protocol.Protocol.receive_eof`, which is an error.
(If you get an exception, please file a bug!)

Send data
.........

Then, call :meth:`~connection.Connection.data_to_send` and send its output to
Then, call :meth:`~protocol.Protocol.data_to_send` and send its output to
the network::

for data in connection.data_to_send():
for data in protocol.data_to_send():
if data:
sock.sendall(data)
else:
Expand All @@ -170,7 +172,7 @@ server starts the four-way TCP closing handshake. If the network fails at the
wrong point, you can end up waiting until the TCP timeout, which is very long.

To prevent dangling TCP connections when you expect the end of the data stream
but you never reach it, call :meth:`~connection.Connection.close_expected`
but you never reach it, call :meth:`~protocol.Protocol.close_expected`
and, if it returns :obj:`True`, schedule closing the TCP connection after a
short timeout::

Expand All @@ -185,11 +187,11 @@ data stream, possibly with an exception.
Close TCP connection
....................

If you called :meth:`~connection.Connection.receive_eof`, close the TCP
If you called :meth:`~protocol.Protocol.receive_eof`, close the TCP
connection now. This is a clean closure because the receive buffer is empty.

After :meth:`~connection.Connection.receive_eof` signals the end of the read
stream, :meth:`~connection.Connection.data_to_send` always signals the end of
After :meth:`~protocol.Protocol.receive_eof` signals the end of the read
stream, :meth:`~protocol.Protocol.data_to_send` always signals the end of
the write stream, unless it already ended. So, at this point, the TCP
connection is already half-closed. The only reason for closing it now is to
release resources related to the socket.
Expand All @@ -199,8 +201,8 @@ Now you can exit the loop relaying data from the network to the application.
Receive events
..............

Finally, call :meth:`~connection.Connection.events_received` to obtain events
parsed from the data provided to :meth:`~connection.Connection.receive_data`::
Finally, call :meth:`~protocol.Protocol.events_received` to obtain events
parsed from the data provided to :meth:`~protocol.Protocol.receive_data`::

events = connection.events_received()

Expand All @@ -224,31 +226,31 @@ The connection object provides one method for each type of WebSocket frame.

For sending a data frame:

* :meth:`~connection.Connection.send_continuation`
* :meth:`~connection.Connection.send_text`
* :meth:`~connection.Connection.send_binary`
* :meth:`~protocol.Protocol.send_continuation`
* :meth:`~protocol.Protocol.send_text`
* :meth:`~protocol.Protocol.send_binary`

These methods raise :exc:`~exceptions.ProtocolError` if you don't set
the :attr:`FIN <websockets.frames.Frame.fin>` bit correctly in fragmented
messages.

For sending a control frame:

* :meth:`~connection.Connection.send_close`
* :meth:`~connection.Connection.send_ping`
* :meth:`~connection.Connection.send_pong`
* :meth:`~protocol.Protocol.send_close`
* :meth:`~protocol.Protocol.send_ping`
* :meth:`~protocol.Protocol.send_pong`

:meth:`~connection.Connection.send_close` initiates the closing handshake.
:meth:`~protocol.Protocol.send_close` initiates the closing handshake.
See `Closing a connection`_ below for details.

If you encounter an unrecoverable error and you must fail the WebSocket
connection, call :meth:`~connection.Connection.fail`.
connection, call :meth:`~protocol.Protocol.fail`.

After any of the above, call :meth:`~connection.Connection.data_to_send` and
After any of the above, call :meth:`~protocol.Protocol.data_to_send` and
send its output to the network, as shown in `Send data`_ above.

If you called :meth:`~connection.Connection.send_close`
or :meth:`~connection.Connection.fail`, you expect the end of the data
If you called :meth:`~protocol.Protocol.send_close`
or :meth:`~protocol.Protocol.fail`, you expect the end of the data
stream. You should follow the process described in `Close TCP connection`_
above in order to prevent dangling TCP connections.

Expand All @@ -272,10 +274,10 @@ When a client wants to close the TCP connection:
Applying the rules described earlier in this document gives the intended
result. As a reminder, the rules are:

* When :meth:`~connection.Connection.data_to_send` returns the empty
* When :meth:`~protocol.Protocol.data_to_send` returns the empty
bytestring, close the write side of the TCP connection.
* When you reach the end of the read stream, close the TCP connection.
* When :meth:`~connection.Connection.close_expected` returns :obj:`True`, if
* When :meth:`~protocol.Protocol.close_expected` returns :obj:`True`, if
you don't reach the end of the read stream quickly, close the TCP connection.

Fragmentation
Expand Down Expand Up @@ -306,14 +308,14 @@ should happen automatically in a cooperative multitasking environment.

However, you still have to make sure you don't break this property by
accident. For example, serialize writes to the network
when :meth:`~connection.Connection.data_to_send` returns multiple values to
when :meth:`~protocol.Protocol.data_to_send` returns multiple values to
prevent concurrent writes from interleaving incorrectly.

Avoid buffers
.............

The Sans-I/O layer doesn't do any buffering. It makes events available in
:meth:`~connection.Connection.events_received` as soon as they're received.
:meth:`~protocol.Protocol.events_received` as soon as they're received.

You should make incoming messages available to the application immediately and
stop further processing until the application fetches them. This will usually
Expand Down
17 changes: 14 additions & 3 deletions docs/project/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ They may change at any time.
Backwards-incompatible changes
..............................

.. admonition:: The Sans-I/O implementation was moved.
:class: caution

Aliases provide compatibility for all previously public APIs according to
the `backwards-compatibility policy`_

* The ``connection`` module was renamed to ``protocol``.

* The ``connection.Connection``, ``server.ServerConnection``, and
``client.ClientConnection`` classes were renamed to ``protocol.Protocol``,
``server.ServerProtocol``, and ``client.ClientProtocol``.

.. admonition:: Closing a connection without an empty close frame is OK.
:class: note

Expand All @@ -43,7 +55,6 @@ Backwards-incompatible changes
As a consequence, calling ``WebSocket.close()`` without arguments in a
browser isn't reported as an error anymore.


New features
............

Expand Down Expand Up @@ -86,8 +97,8 @@ Backwards-incompatible changes
.. admonition:: The ``exception`` attribute of :class:`~http11.Request` and :class:`~http11.Response` is deprecated.
:class: note

Use the ``handshake_exc`` attribute of :class:`~server.ServerConnection` and
:class:`~client.ClientConnection` instead.
Use the ``handshake_exc`` attribute of :class:`~server.ServerProtocol` and
:class:`~client.ClientProtocol` instead.

See :doc:`../howto/sansio` for details.

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Using a connection
Sans-I/O
--------

.. autoclass:: ClientConnection(wsuri, origin=None, extensions=None, subprotocols=None, state=State.CONNECTING, max_size=2 ** 20, logger=None)
.. autoclass:: ClientProtocol(wsuri, origin=None, extensions=None, subprotocols=None, state=State.CONNECTING, max_size=2 ** 20, logger=None)

.. automethod:: receive_data

Expand Down
4 changes: 2 additions & 2 deletions docs/reference/common.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ asyncio
Sans-I/O
--------

.. automodule:: websockets.connection
.. automodule:: websockets.protocol

.. autoclass:: Connection(side, state=State.OPEN, max_size=2 ** 20, logger=None)
.. autoclass:: Protocol(side, state=State.OPEN, max_size=2 ** 20, logger=None)

.. automethod:: receive_data

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ websockets supports HTTP Basic Authentication according to
Sans-I/O
--------

.. autoclass:: ServerConnection(origins=None, extensions=None, subprotocols=None, state=State.CONNECTING, max_size=2 ** 20, logger=None)
.. autoclass:: ServerProtocol(origins=None, extensions=None, subprotocols=None, state=State.CONNECTING, max_size=2 ** 20, logger=None)

.. automethod:: receive_data

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Types

.. autodata:: ExtensionParameter

.. autodata:: websockets.connection.Event
.. autodata:: websockets.protocol.Event

.. autodata:: websockets.datastructures.HeadersLike

Expand Down
8 changes: 4 additions & 4 deletions src/websockets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"basic_auth_protocol_factory",
"BasicAuthWebSocketServerProtocol",
"broadcast",
"ClientConnection",
"ClientProtocol",
"connect",
"ConnectionClosed",
"ConnectionClosedError",
Expand Down Expand Up @@ -40,7 +40,7 @@
"RedirectHandshake",
"SecurityError",
"serve",
"ServerConnection",
"ServerProtocol",
"Subprotocol",
"unix_connect",
"unix_serve",
Expand All @@ -60,7 +60,7 @@
"basic_auth_protocol_factory": ".legacy.auth",
"BasicAuthWebSocketServerProtocol": ".legacy.auth",
"broadcast": ".legacy.protocol",
"ClientConnection": ".client",
"ClientProtocol": ".client",
"connect": ".legacy.client",
"unix_connect": ".legacy.client",
"WebSocketClientProtocol": ".legacy.client",
Expand Down Expand Up @@ -93,7 +93,7 @@
"WebSocketProtocolError": ".exceptions",
"protocol": ".legacy",
"WebSocketCommonProtocol": ".legacy.protocol",
"ServerConnection": ".server",
"ServerProtocol": ".server",
"serve": ".legacy.server",
"unix_serve": ".legacy.server",
"WebSocketServerProtocol": ".legacy.server",
Expand Down

0 comments on commit f5ea94a

Please sign in to comment.