diff --git a/aiohttp/web_ws.py b/aiohttp/web_ws.py index a0ea53df716..6430e34c9b1 100644 --- a/aiohttp/web_ws.py +++ b/aiohttp/web_ws.py @@ -36,9 +36,9 @@ def __bool__(self): class WebSocketResponse(StreamResponse): def __init__(self, *, - timeout=10.0, receive_timeout=None, - autoclose=True, autoping=True, heartbeat=None, - protocols=(), compress=True): + timeout=None, receive_timeout=None, send_timeout=None, + close_timeout=10.0, autoclose=True, autoping=True, + heartbeat=None, protocols=(), compress=True): super().__init__(status=101) self._protocols = protocols self._ws_protocol = None @@ -51,8 +51,9 @@ def __init__(self, *, self._loop = None self._waiting = None self._exception = None - self._timeout = timeout self._receive_timeout = receive_timeout + self._send_timeout = send_timeout + self._close_timeout = timeout or close_timeout self._autoclose = autoclose self._autoping = autoping self._heartbeat = heartbeat @@ -241,23 +242,27 @@ async def pong(self, message='b'): raise RuntimeError('Call .prepare() first') await self._writer.pong(message) - async def send_str(self, data): + async def send_str(self, data, *, timeout=None): if self._writer is None: raise RuntimeError('Call .prepare() first') if not isinstance(data, str): raise TypeError('data argument must be str (%r)' % type(data)) - await self._writer.send(data, binary=False) + with async_timeout.timeout(timeout or self._send_timeout, + loop=self._loop): + await self._writer.send(data, binary=False) - async def send_bytes(self, data): + async def send_bytes(self, data, *, timeout=None): if self._writer is None: raise RuntimeError('Call .prepare() first') if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError('data argument must be byte-ish (%r)' % type(data)) - await self._writer.send(data, binary=True) + with async_timeout.timeout(timeout or self._send_timeout, + loop=self._loop): + await self._writer.send(data, binary=True) - async def send_json(self, data, *, dumps=json.dumps): - await self.send_str(dumps(data)) + async def send_json(self, data, *, timeout=None, dumps=json.dumps): + await self.send_str(dumps(data), timeout=timeout) async def write_eof(self): if self._eof_sent: @@ -297,7 +302,8 @@ async def close(self, *, code=1000, message=b''): return True try: - with async_timeout.timeout(self._timeout, loop=self._loop): + with async_timeout.timeout(self._close_timeout, + loop=self._loop): msg = await self._reader.read() except asyncio.CancelledError: self._close_code = 1006 diff --git a/changes/2310.doc b/changes/2310.doc new file mode 100644 index 00000000000..19b5b92d49e --- /dev/null +++ b/changes/2310.doc @@ -0,0 +1 @@ +Document the send_timeout, close_timeout, and the timeout parameters for the send_x calls (#2310) diff --git a/changes/2310.feature b/changes/2310.feature new file mode 100644 index 00000000000..179d29c6e63 --- /dev/null +++ b/changes/2310.feature @@ -0,0 +1 @@ +Add send_timeout and close_timeout to WebSocketResponse along with a timeout parameter for the send_x calls (#2310) diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 8fea5d9ef12..f1018805e91 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -856,7 +856,8 @@ Response WebSocketResponse ^^^^^^^^^^^^^^^^^ -.. class:: WebSocketResponse(*, timeout=10.0, receive_timeout=None, \ +.. class:: WebSocketResponse(*, timeout=None, receive_timeout=None, \ + send_timeout=None, close_timeout=10.0, \ autoclose=True, autoping=True, heartbeat=None, \ protocols=(), compress=True) @@ -989,12 +990,16 @@ WebSocketResponse The method is converted into :term:`coroutine` - .. comethod:: send_str(data) + .. comethod:: send_str(data, *, timeout=None) Send *data* to peer as :const:`~aiohttp.WSMsgType.TEXT` message. :param str data: data to send. + :param timeout: timeout for `send` operation. + + timeout value overrides response`s send_timeout attribute. + :raise RuntimeError: if connection is not started or closing :raise TypeError: if data is not :class:`str` @@ -1003,12 +1008,16 @@ WebSocketResponse The method is converted into :term:`coroutine` - .. comethod:: send_bytes(data) + .. comethod:: send_bytes(data, *, timeout=None) Send *data* to peer as :const:`~aiohttp.WSMsgType.BINARY` message. :param data: data to send. + :param timeout: timeout for `send` operation. + + timeout value overrides response`s send_timeout attribute. + :raise RuntimeError: if connection is not started or closing :raise TypeError: if data is not :class:`bytes`, @@ -1018,12 +1027,16 @@ WebSocketResponse The method is converted into :term:`coroutine` - .. comethod:: send_json(data, *, dumps=json.dumps) + .. comethod:: send_json(data, *, timeout=None, dumps=json.dumps) Send *data* to peer as JSON string. :param data: data to send. + :param timeout: timeout for `send` operation. + + timeout value overrides response`s send_timeout attribute. + :param callable dumps: any :term:`callable` that accepts an object and returns a JSON string (:func:`json.dumps` by default).