Create a context for writing to the connection from user code. On entry, :meth:`send_context` acquires the connection lock and checks that the connection is open; on exit, it writes outgoing data to the socket and releases the connection lock:: with sel
(
self,
*,
expected_state: State = OPEN, # CONNECTING during the opening handshake
)
| 912 | |
| 913 | @contextlib.contextmanager |
| 914 | def send_context( |
| 915 | self, |
| 916 | *, |
| 917 | expected_state: State = OPEN, # CONNECTING during the opening handshake |
| 918 | ) -> Iterator[None]: |
| 919 | """ |
| 920 | Create a context for writing to the connection from user code. |
| 921 | |
| 922 | On entry, :meth:`send_context` acquires the connection lock and checks |
| 923 | that the connection is open; on exit, it writes outgoing data to the |
| 924 | socket and releases the connection lock:: |
| 925 | |
| 926 | with self.send_context(): |
| 927 | self.protocol.send_text(message.encode()) |
| 928 | |
| 929 | When the connection isn't open on entry, when the connection is expected |
| 930 | to close on exit, or when an unexpected error happens, terminating the |
| 931 | connection, :meth:`send_context` waits until the connection is closed |
| 932 | then raises :exc:`~websockets.exceptions.ConnectionClosed`. |
| 933 | |
| 934 | """ |
| 935 | # Should we wait until the connection is closed? |
| 936 | wait_for_close = False |
| 937 | # Should we close the socket and raise ConnectionClosed? |
| 938 | raise_close_exc = False |
| 939 | # What exception should we chain ConnectionClosed to? |
| 940 | original_exc: BaseException | None = None |
| 941 | |
| 942 | # Acquire the protocol lock. |
| 943 | with self.protocol_mutex: |
| 944 | if self.protocol.state is expected_state: |
| 945 | # Let the caller interact with the protocol. |
| 946 | try: |
| 947 | yield |
| 948 | except (ProtocolError, ConcurrencyError): |
| 949 | # The protocol state wasn't changed. Exit immediately. |
| 950 | raise |
| 951 | except Exception as exc: |
| 952 | self.logger.error("unexpected internal error", exc_info=True) |
| 953 | # This branch should never run. It's a safety net in case of |
| 954 | # bugs. Since we don't know what happened, we will close the |
| 955 | # connection and raise the exception to the caller. |
| 956 | wait_for_close = False |
| 957 | raise_close_exc = True |
| 958 | original_exc = exc |
| 959 | else: |
| 960 | # Check if the connection is expected to close soon. |
| 961 | if self.protocol.close_expected(): |
| 962 | wait_for_close = True |
| 963 | # Set the close deadline based on the close timeout. |
| 964 | # Since we tested earlier that protocol.state is OPEN |
| 965 | # (or CONNECTING) and we didn't release protocol_mutex, |
| 966 | # self.close_deadline is still None. |
| 967 | assert self.close_deadline is None |
| 968 | self.close_deadline = Deadline(self.close_timeout) |
| 969 | # Write outgoing data to the socket. |
| 970 | try: |
| 971 | self.send_data() |
no test coverage detected