Skip to content

Commit 3d40e2f

Browse files
committed
PEP 748: shutdown(show: 0|1|2) instead of close(force: bool)
1 parent 785c0c4 commit 3d40e2f

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
@@ -421,7 +421,7 @@ need to implement the following:
421421

422422
* ``recv`` and ``send``
423423
* ``accept`` (server-side)
424-
* ``close``
424+
* ``shutdown`` and ``close``
425425
* ``getsockname``
426426
* ``getpeername``
427427

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

0 commit comments

Comments
 (0)