Skip to content

Commit 54723e3

Browse files
committed
PEP 748: shutdown(show: 0|1|2) instead of close(force: bool)
1 parent 506c877 commit 54723e3

1 file changed

Lines changed: 59 additions & 10 deletions

File tree

peps/pep-0748.rst

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ need to implement the following:
422422

423423
* ``recv`` and ``send``
424424
* ``accept`` (server-side)
425-
* ``close``
425+
* ``shutdown`` and ``close``
426426
* ``getsockname``
427427
* ``getpeername``
428428

@@ -464,15 +464,64 @@ The following code describes these functions in more detail:
464464
...
465465
466466
@abstractmethod
467-
def close(self, force: bool = False) -> None:
468-
"""Shuts down the connection and mark the socket closed.
469-
If force is True, this method should send the close_notify alert and shut down
470-
the socket without waiting for the other side.
471-
If force is False, this method should send the close_notify alert and raise
472-
the WantReadError exception until a corresponding close_notify alert has been
473-
received from the other side.
474-
In either case, this method should return WantWriteError if sending the
475-
close_notify alert currently fails."""
467+
def shutdown(self, how: Literal[0, 1, 2]) -> None:
468+
"""
469+
Shutdown TLS and the underlying socket.
470+
471+
Proper TLS applications ought to signal their peers when they're
472+
done sending data by sending a closing alert.
473+
474+
* ``socket.SHUT_WR`` (``1``) sends the closing alert to the peer and
475+
then prevents sending any further message. Proper TLS application
476+
**MUST** call this method with this parameter to gracefully close
477+
the TLS connection. *Safe* in TLS 1.3. Actually acts like
478+
``SHUT_RDWD`` in TLS 1.2 and is as *unsafe* as ``SHUT_RD``.
479+
480+
* ``socket.SHUT_RD`` (``0``) simulates receiving the closing alert
481+
from the peer and then ignores all further received messages.
482+
*Unsafe* (risk of data loss) unless the connection is otherwise
483+
known to be over thanks to the application-layer protocol (e.g.
484+
HTTP Content-Length).
485+
486+
* ``socket.SHUT_RDWR`` (``2``) does both ``SHUT_RD`` and ``SHUT_WR``.
487+
As *unsafe* as ``SHUT_RD``.
488+
489+
In TLS 1.2, the closing alert is synchronous, receiving it triggers
490+
an immediate closing alert response, and both connections are
491+
immediately shut. It means that ``SHUT_WR`` acts like ``SHUT_RDWR``
492+
and is unsafe unless all the data have been received.
493+
494+
In TLS 1.3, the closing alert is asynchronous, sending the closing
495+
alert only closes the sender's sending-end and receiver's
496+
receiving-end. The peer can keep on sending data until he
497+
independently decides to close its own sending-end of the
498+
connection. There is not risk of data truncation with ``SHUT_WR``.
499+
500+
.. danger::
501+
502+
Both ``socket.SHUT_RD`` (``0``) and ``socket.SHUT_RDWR`` (``2``)
503+
pose a risk of data loss, only use them when facing a bad actor
504+
or when the connection is otherwise known to be over.
505+
506+
In TLS 1.2 the same risk applies also to ``socket.SHUT_WR`` (``1``).
507+
"""
508+
...
509+
510+
@abstractmethod
511+
def close(self) -> None:
512+
"""
513+
Close the underlying socket, but only when it is safe to do so.
514+
515+
When the sending-end of the connection is still open, it sends a
516+
closing alert before closing the socket, raising ``WantWriteError``
517+
when it fails.
518+
519+
When the receiving-end of the connection is still open, it always
520+
raises ``WantReadError`` as there's a risk of data loss / truncation
521+
attack. The user must first either: (safe) ``recv`` until the peer
522+
closes its sending-end of the connection, or (unsafe) take the risk
523+
and unilateraly ``shutdown`` the reading-end of the connection.
524+
"""
476525
...
477526
478527
@abstractmethod

0 commit comments

Comments
 (0)