Skip to content

Commit

Permalink
Await in websockets (#2475)
Browse files Browse the repository at this point in the history
* Work on

* Fix tests

* Update docs
asvetlov authored Nov 7, 2017

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 132495a commit c29b630
Showing 14 changed files with 163 additions and 144 deletions.
2 changes: 2 additions & 0 deletions CHANGES/2475.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
`send_str()`, `send_bytes()`, `send_json()`, `ping()` and `pong()` are
genuine async functions now.
24 changes: 12 additions & 12 deletions aiohttp/client_ws.py
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ def _reset_heartbeat(self):

def _send_heartbeat(self):
if self._heartbeat is not None and not self._closed:
self.ping()
self._writer.ping()

if self._pong_response_cb is not None:
self._pong_response_cb.cancel()
@@ -106,25 +106,25 @@ def get_extra_info(self, name, default=None):
def exception(self):
return self._exception

def ping(self, message='b'):
self._writer.ping(message)
async def ping(self, message='b'):
await self._writer.ping(message)

def pong(self, message='b'):
self._writer.pong(message)
async def pong(self, message='b'):
await self._writer.pong(message)

def send_str(self, data):
async def send_str(self, data):
if not isinstance(data, str):
raise TypeError('data argument must be str (%r)' % type(data))
return self._writer.send(data, binary=False)
await self._writer.send(data, binary=False)

def send_bytes(self, data):
async def send_bytes(self, data):
if not isinstance(data, (bytes, bytearray, memoryview)):
raise TypeError('data argument must be byte-ish (%r)' %
type(data))
return self._writer.send(data, binary=True)
await self._writer.send(data, binary=True)

def send_json(self, data, *, dumps=json.dumps):
return self.send_str(dumps(data))
async def send_json(self, data, *, dumps=json.dumps):
await self.send_str(dumps(data))

async def close(self, *, code=1000, message=b''):
# we need to break `receive()` cycle first,
@@ -223,7 +223,7 @@ async def receive(self, timeout=None):
elif msg.type == WSMsgType.CLOSING:
self._closing = True
elif msg.type == WSMsgType.PING and self._autoping:
self.pong(msg.data)
await self.pong(msg.data)
continue
elif msg.type == WSMsgType.PONG and self._autoping:
continue
15 changes: 6 additions & 9 deletions aiohttp/http_writer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Http related parsers and protocol."""

import asyncio
import collections
import socket
import zlib
@@ -112,17 +111,16 @@ def set_tcp_cork(self, value):
except OSError:
pass

@asyncio.coroutine
def drain(self):
async def drain(self):
"""Flush the write buffer.
The intended use is to write
w.write(data)
yield from w.drain()
await w.drain()
"""
if self._protocol.transport is not None:
yield from self._protocol._drain_helper()
await self._protocol._drain_helper()


class PayloadWriter(AbstractPayloadWriter):
@@ -281,17 +279,16 @@ async def write_eof(self, chunk=b''):
self._transport = None
self._stream.release()

@asyncio.coroutine
def drain(self, last=False):
async def drain(self, last=False):
if self._transport is not None:
if self._buffer:
self._transport.write(b''.join(self._buffer))
if not last:
self._buffer.clear()
yield from self._stream.drain()
await self._stream.drain()
else:
# wait for transport
if self._drain_waiter is None:
self._drain_waiter = self.loop.create_future()

yield from self._drain_waiter
await self._drain_waiter
4 changes: 2 additions & 2 deletions aiohttp/payload_streamer.py
Original file line number Diff line number Diff line change
@@ -3,11 +3,11 @@
As a simple case, you can upload data from file::
@aiohttp.streamer
def file_sender(writer, file_name=None):
async def file_sender(writer, file_name=None):
with open(file_name, 'rb') as f:
chunk = f.read(2**16)
while chunk:
yield from writer.write(chunk)
await writer.write(chunk)
chunk = f.read(2**16)
26 changes: 13 additions & 13 deletions aiohttp/web_ws.py
Original file line number Diff line number Diff line change
@@ -76,7 +76,7 @@ def _reset_heartbeat(self):

def _send_heartbeat(self):
if self._heartbeat is not None and not self._closed:
self.ping()
self._writer.ping()

if self._pong_response_cb is not None:
self._pong_response_cb.cancel()
@@ -166,34 +166,34 @@ def compress(self):
def exception(self):
return self._exception

def ping(self, message='b'):
async def ping(self, message='b'):
if self._writer is None:
raise RuntimeError('Call .prepare() first')
self._writer.ping(message)
await self._writer.ping(message)

def pong(self, message='b'):
async def pong(self, message='b'):
# unsolicited pong
if self._writer is None:
raise RuntimeError('Call .prepare() first')
self._writer.pong(message)
await self._writer.pong(message)

def send_str(self, data):
async def send_str(self, data):
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))
return self._writer.send(data, binary=False)
await self._writer.send(data, binary=False)

def send_bytes(self, data):
async def send_bytes(self, data):
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))
return self._writer.send(data, binary=True)
await self._writer.send(data, binary=True)

def send_json(self, data, *, dumps=json.dumps):
return self.send_str(dumps(data))
async def send_json(self, data, *, dumps=json.dumps):
await self.send_str(dumps(data))

async def write_eof(self):
if self._eof_sent:
@@ -303,7 +303,7 @@ async def receive(self, timeout=None):
elif msg.type == WSMsgType.CLOSING:
self._closing = True
elif msg.type == WSMsgType.PING and self._autoping:
self.pong(msg.data)
await self.pong(msg.data)
continue
elif msg.type == WSMsgType.PONG and self._autoping:
continue
@@ -330,7 +330,7 @@ async def receive_json(self, *, loads=json.loads, timeout=None):
data = await self.receive_str(timeout=timeout)
return loads(data)

def write(self, data):
async def write(self, data):
raise RuntimeError("Cannot call .write() for websocket")

def __aiter__(self):
30 changes: 29 additions & 1 deletion docs/client_reference.rst
Original file line number Diff line number Diff line change
@@ -1256,14 +1256,30 @@ manually.

Returns exception if any occurs or returns None.

.. method:: ping(message=b'')
.. comethod:: ping(message=b'')

Send :const:`~aiohttp.WSMsgType.PING` to peer.

:param message: optional payload of *ping* message,
:class:`str` (converted to *UTF-8* encoded bytes)
or :class:`bytes`.

.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: pong(message=b'')

Send :const:`~aiohttp.WSMsgType.PONG` to peer.

:param message: optional payload of *pong* message,
:class:`str` (converted to *UTF-8* encoded bytes)
or :class:`bytes`.

.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: send_str(data)

Send *data* to peer as :const:`~aiohttp.WSMsgType.TEXT` message.
@@ -1272,6 +1288,10 @@ manually.

:raise TypeError: if data is not :class:`str`

.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: send_bytes(data)

Send *data* to peer as :const:`~aiohttp.WSMsgType.BINARY` message.
@@ -1281,6 +1301,10 @@ manually.
:raise TypeError: if data is not :class:`bytes`,
:class:`bytearray` or :class:`memoryview`.

.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: send_json(data, *, dumps=json.dumps)

Send *data* to peer as JSON string.
@@ -1298,6 +1322,10 @@ manually.
:raise TypeError: if value returned by ``dumps(data)`` is not
:class:`str`

.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: close(*, code=1000, message=b'')

A :ref:`coroutine<coroutine>` that initiates closing handshake by sending
81 changes: 48 additions & 33 deletions docs/web_reference.rst
Original file line number Diff line number Diff line change
@@ -885,11 +885,10 @@ WebSocketResponse
communicate with websocket client by :meth:`send_str`,
:meth:`receive` and others.

.. versionadded:: 1.3.0

To enable back-pressure from slow websocket clients treat methods
`ping()`, `pong()`, `send_str()`, `send_bytes()`, `send_json()` as
coroutines. By default write buffer size is set to 64k.
:meth:`ping()`, :meth:`pong()`, :meth:`send_str()`,
:meth:`send_bytes()`, :meth:`send_json()` as coroutines. By
default write buffer size is set to 64k.

:param bool autoping: Automatically send
:const:`~aiohttp.WSMsgType.PONG` on
@@ -902,8 +901,6 @@ WebSocketResponse
requests, you need to do this explicitly
using :meth:`ping` method.

.. versionadded:: 1.3.0

:param float heartbeat: Send `ping` message every `heartbeat`
seconds and wait `pong` response, close
connection if `pong` response is not
@@ -916,16 +913,14 @@ WebSocketResponse
:param float compress: Enable per-message deflate extension support.
False for disabled, default value is True.

.. versionadded:: 0.19

The class supports ``async for`` statement for iterating over
incoming messages::
The class supports ``async for`` statement for iterating over
incoming messages::

ws = web.WebSocketResponse()
await ws.prepare(request)
ws = web.WebSocketResponse()
await ws.prepare(request)

async for msg in ws:
print(msg.data)
async for msg in ws:
print(msg.data)


.. coroutinemethod:: prepare(request)
@@ -938,8 +933,6 @@ WebSocketResponse

:raises HTTPException: if websocket handshake has failed.

.. versionadded:: 0.18

.. method:: can_prepare(request)

Performs checks for *request* data to figure out if websocket
@@ -985,7 +978,7 @@ WebSocketResponse

Returns last occurred exception or None.

.. method:: ping(message=b'')
.. comethod:: ping(message=b'')

Send :const:`~aiohttp.WSMsgType.PING` to peer.

@@ -995,7 +988,11 @@ WebSocketResponse

:raise RuntimeError: if connections is not started or closing.

.. method:: pong(message=b'')
.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: pong(message=b'')

Send *unsolicited* :const:`~aiohttp.WSMsgType.PONG` to peer.

@@ -1005,7 +1002,11 @@ WebSocketResponse

:raise RuntimeError: if connections is not started or closing.

.. coroutinemethod:: send_str(data)
.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: send_str(data)

Send *data* to peer as :const:`~aiohttp.WSMsgType.TEXT` message.

@@ -1015,7 +1016,11 @@ WebSocketResponse

:raise TypeError: if data is not :class:`str`

.. coroutinemethod:: send_bytes(data)
.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: send_bytes(data)

Send *data* to peer as :const:`~aiohttp.WSMsgType.BINARY` message.

@@ -1026,7 +1031,11 @@ WebSocketResponse
:raise TypeError: if data is not :class:`bytes`,
:class:`bytearray` or :class:`memoryview`.

.. coroutinemethod:: send_json(data, *, dumps=json.dumps)
.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: send_json(data, *, dumps=json.dumps)

Send *data* to peer as JSON string.

@@ -1042,7 +1051,11 @@ WebSocketResponse

:raise TypeError: if value returned by ``dumps`` param is not :class:`str`

.. coroutinemethod:: close(*, code=1000, message=b'')
.. versionchanged:: 3.0

The method is converted into :term:`coroutine`

.. comethod:: close(*, code=1000, message=b'')

A :ref:`coroutine<coroutine>` that initiates closing
handshake by sending :const:`~aiohttp.WSMsgType.CLOSE` message.
@@ -1057,7 +1070,7 @@ WebSocketResponse

:raise RuntimeError: if connection is not started

.. coroutinemethod:: receive(timeout=None)
.. comethod:: receive(timeout=None)

A :ref:`coroutine<coroutine>` that waits upcoming *data*
message from peer and returns it.
@@ -1075,13 +1088,14 @@ WebSocketResponse
Can only be called by the request handling task.

:param timeout: timeout for `receive` operation.
timeout value overrides response`s receive_timeout attribute.

timeout value overrides response`s receive_timeout attribute.

:return: :class:`~aiohttp.WSMessage`

:raise RuntimeError: if connection is not started

.. coroutinemethod:: receive_str(*, timeout=None)
.. comethod:: receive_str(*, timeout=None)

A :ref:`coroutine<coroutine>` that calls :meth:`receive` but
also asserts the message type is :const:`~aiohttp.WSMsgType.TEXT`.
@@ -1091,13 +1105,14 @@ WebSocketResponse
Can only be called by the request handling task.

:param timeout: timeout for `receive` operation.
timeout value overrides response`s receive_timeout attribute.

timeout value overrides response`s receive_timeout attribute.

:return str: peer's message content.

:raise TypeError: if message is :const:`~aiohttp.WSMsgType.BINARY`.

.. coroutinemethod:: receive_bytes(*, timeout=None)
.. comethod:: receive_bytes(*, timeout=None)

A :ref:`coroutine<coroutine>` that calls :meth:`receive` but
also asserts the message type is
@@ -1108,13 +1123,14 @@ WebSocketResponse
Can only be called by the request handling task.

:param timeout: timeout for `receive` operation.
timeout value overrides response`s receive_timeout attribute.

timeout value overrides response`s receive_timeout attribute.

:return bytes: peer's message content.

:raise TypeError: if message is :const:`~aiohttp.WSMsgType.TEXT`.

.. coroutinemethod:: receive_json(*, loads=json.loads, timeout=None)
.. comethod:: receive_json(*, loads=json.loads, timeout=None)

A :ref:`coroutine<coroutine>` that calls :meth:`receive_str` and loads the
JSON string to a Python dict.
@@ -1128,16 +1144,15 @@ WebSocketResponse
with parsed JSON (:func:`json.loads` by
default).

:param timeout: timeout for `receive` operation.
timeout value overrides response`s receive_timeout attribute.
:param timeout: timeout for `receive` operation.

timeout value overrides response`s receive_timeout attribute.

:return dict: loaded JSON content

:raise TypeError: if message is :const:`~aiohttp.WSMsgType.BINARY`.
:raise ValueError: if message is not valid JSON.

.. versionadded:: 0.22


.. seealso:: :ref:`WebSockets handling<aiohttp-web-websockets>`

5 changes: 3 additions & 2 deletions tests/test_client_request.py
Original file line number Diff line number Diff line change
@@ -59,15 +59,16 @@ def conn(stream):


@pytest.fixture
def stream(buf, transport):
def stream(buf, transport, loop):
stream = mock.Mock()
stream.transport = transport

def acquire(writer):
writer.set_transport(transport)

stream.acquire.side_effect = acquire
stream.drain.return_value = ()
stream.drain.return_value = loop.create_future()
stream.drain.return_value.set_result(None)
return stream


11 changes: 7 additions & 4 deletions tests/test_client_ws.py
Original file line number Diff line number Diff line change
@@ -353,7 +353,7 @@ async def test_send_data_after_close(ws_key, key_data, loop, mocker):
(resp.send_str, ('s',)),
(resp.send_bytes, (b'b',)),
(resp.send_json, ({},))):
meth(*args)
await meth(*args)
assert ws_logger.warning.called
ws_logger.warning.reset_mock()

@@ -377,9 +377,12 @@ async def test_send_data_type_errors(ws_key, key_data, loop):
resp = await aiohttp.ClientSession(loop=loop).ws_connect(
'http://test.org')

pytest.raises(TypeError, resp.send_str, b's')
pytest.raises(TypeError, resp.send_bytes, 'b')
pytest.raises(TypeError, resp.send_json, set())
with pytest.raises(TypeError):
await resp.send_str(b's')
with pytest.raises(TypeError):
await resp.send_bytes('b')
with pytest.raises(TypeError):
await resp.send_json(set())


async def test_reader_read_exception(ws_key, key_data, loop):
14 changes: 7 additions & 7 deletions tests/test_client_ws_functional.py
Original file line number Diff line number Diff line change
@@ -127,7 +127,7 @@ async def handler(request):
client = await test_client(app)
resp = await client.ws_connect('/')
payload = {'request': 'test'}
resp.send_json(payload)
await resp.send_json(payload)

data = await resp.receive_json()
assert data['response'] == payload['request']
@@ -143,7 +143,7 @@ async def handler(request):
await ws.prepare(request)

msg = await ws.receive_bytes()
ws.ping()
await ws.ping()
await ws.send_bytes(msg+b'/answer')
try:
await ws.close()
@@ -156,7 +156,7 @@ async def handler(request):
client = await test_client(app)
resp = await client.ws_connect('/')

resp.ping()
await resp.ping()
await resp.send_bytes(b'ask')

msg = await resp.receive()
@@ -179,7 +179,7 @@ async def handler(request):
await ws.prepare(request)

msg = await ws.receive_bytes()
ws.ping()
await ws.ping()
await ws.send_bytes(msg+b'/answer')
try:
await ws.close()
@@ -192,15 +192,15 @@ async def handler(request):
client = await test_client(app)
resp = await client.ws_connect('/', autoping=False)

resp.ping()
await resp.ping()
await resp.send_bytes(b'ask')

msg = await resp.receive()
assert msg.type == aiohttp.WSMsgType.PONG

msg = await resp.receive()
assert msg.type == aiohttp.WSMsgType.PING
resp.pong()
await resp.pong()

msg = await resp.receive()
assert msg.data == b'ask/answer'
@@ -770,7 +770,7 @@ async def handler(request):
async for msg in resp:
messages.append(msg)
if b'started' == msg.data:
resp.send_bytes(b'ask')
await resp.send_bytes(b'ask')
await resp.close()

assert 1 == len(messages)
5 changes: 3 additions & 2 deletions tests/test_http_writer.py
Original file line number Diff line number Diff line change
@@ -25,14 +25,15 @@ def write(chunk):


@pytest.fixture
def stream(transport):
def stream(transport, loop):
stream = mock.Mock(transport=transport)

def acquire(writer):
writer.set_transport(transport)

stream.acquire = acquire
stream.drain.return_value = ()
stream.drain.return_value = loop.create_future()
stream.drain.return_value.set_result(None)
return stream


4 changes: 2 additions & 2 deletions tests/test_test_utils.py
Original file line number Diff line number Diff line change
@@ -180,11 +180,11 @@ async def test_get_route():

async def test_client_websocket(loop, test_client):
resp = await test_client.ws_connect("/websocket")
resp.send_str("foo")
await resp.send_str("foo")
msg = await resp.receive()
assert msg.type == aiohttp.WSMsgType.TEXT
assert "foo" in msg.data
resp.send_str("close")
await resp.send_str("close")
msg = await resp.receive()
assert msg.type == aiohttp.WSMsgType.CLOSE

40 changes: 20 additions & 20 deletions tests/test_web_websocket.py
Original file line number Diff line number Diff line change
@@ -59,34 +59,34 @@ def maker(method, path, headers=None, protocols=False):
return maker


def test_nonstarted_ping():
async def test_nonstarted_ping():
ws = WebSocketResponse()
with pytest.raises(RuntimeError):
ws.ping()
await ws.ping()


def test_nonstarted_pong():
async def test_nonstarted_pong():
ws = WebSocketResponse()
with pytest.raises(RuntimeError):
ws.pong()
await ws.pong()


def test_nonstarted_send_str():
async def test_nonstarted_send_str():
ws = WebSocketResponse()
with pytest.raises(RuntimeError):
ws.send_str('string')
await ws.send_str('string')


def test_nonstarted_send_bytes():
async def test_nonstarted_send_bytes():
ws = WebSocketResponse()
with pytest.raises(RuntimeError):
ws.send_bytes(b'bytes')
await ws.send_bytes(b'bytes')


def test_nonstarted_send_json():
async def test_nonstarted_send_json():
ws = WebSocketResponse()
with pytest.raises(RuntimeError):
ws.send_json({'type': 'json'})
await ws.send_json({'type': 'json'})


async def test_nonstarted_close():
@@ -146,29 +146,29 @@ async def test_send_str_nonstring(make_request):
ws = WebSocketResponse()
await ws.prepare(req)
with pytest.raises(TypeError):
ws.send_str(b'bytes')
await ws.send_str(b'bytes')


async def test_send_bytes_nonbytes(make_request):
req = make_request('GET', '/')
ws = WebSocketResponse()
await ws.prepare(req)
with pytest.raises(TypeError):
ws.send_bytes('string')
await ws.send_bytes('string')


async def test_send_json_nonjson(make_request):
req = make_request('GET', '/')
ws = WebSocketResponse()
await ws.prepare(req)
with pytest.raises(TypeError):
ws.send_json(set())
await ws.send_json(set())


def test_write_non_prepared():
async def test_write_non_prepared():
ws = WebSocketResponse()
with pytest.raises(RuntimeError):
ws.write(b'data')
await ws.write(b'data')


def test_websocket_ready():
@@ -248,7 +248,7 @@ async def test_send_str_closed(make_request, mocker):
await ws.close()

mocker.spy(ws_logger, 'warning')
ws.send_str('string')
await ws.send_str('string')
assert ws_logger.warning.called


@@ -260,7 +260,7 @@ async def test_send_bytes_closed(make_request, mocker):
await ws.close()

mocker.spy(ws_logger, 'warning')
ws.send_bytes(b'bytes')
await ws.send_bytes(b'bytes')
assert ws_logger.warning.called


@@ -272,7 +272,7 @@ async def test_send_json_closed(make_request, mocker):
await ws.close()

mocker.spy(ws_logger, 'warning')
ws.send_json({'type': 'json'})
await ws.send_json({'type': 'json'})
assert ws_logger.warning.called


@@ -284,7 +284,7 @@ async def test_ping_closed(make_request, mocker):
await ws.close()

mocker.spy(ws_logger, 'warning')
ws.ping()
await ws.ping()
assert ws_logger.warning.called


@@ -296,7 +296,7 @@ async def test_pong_closed(make_request, mocker):
await ws.close()

mocker.spy(ws_logger, 'warning')
ws.pong()
await ws.pong()
assert ws_logger.warning.called


46 changes: 9 additions & 37 deletions tests/test_web_websocket_functional.py
Original file line number Diff line number Diff line change
@@ -115,34 +115,6 @@ async def handler(request):
assert data['test'] == expected_value


async def test_websocket_send_drain(loop, test_client):

async def handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)

ws._writer._limit = 1

data = await ws.receive_json()
drain = ws.send_json(data)
assert drain

await drain
await ws.close()
return ws

app = web.Application()
app.router.add_route('GET', '/', handler)
client = await test_client(app)

ws = await client.ws_connect('/')
expected_value = 'value'
await ws.send_json({'test': expected_value})

data = await ws.receive_json()
assert data['test'] == expected_value


async def test_websocket_receive_json(loop, test_client):

async def handler(request):
@@ -386,7 +358,7 @@ async def handler(request):
client = await test_client(app)

ws = await client.ws_connect('/', autoclose=False, autoping=False)
ws.ping()
await ws.ping()
await ws.send_str('ask')

msg = await ws.receive()
@@ -403,7 +375,7 @@ async def handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)

ws.ping('data')
await ws.ping('data')
await ws.receive()
closed.set_result(None)
return ws
@@ -417,7 +389,7 @@ async def handler(request):
msg = await ws.receive()
assert msg.type == WSMsgType.PING
assert msg.data == b'data'
ws.pong()
await ws.pong()
await ws.close()
await closed

@@ -440,11 +412,11 @@ async def handler(request):

ws = await client.ws_connect('/', autoping=False)

ws.ping('data')
await ws.ping('data')
msg = await ws.receive()
assert msg.type == WSMsgType.PONG
assert msg.data == b'data'
ws.pong()
await ws.pong()
await ws.close()


@@ -458,7 +430,7 @@ async def handler(request):

msg = await ws.receive()
assert msg.type == WSMsgType.PING
ws.pong('data')
await ws.pong('data')

msg = await ws.receive()
assert msg.type == WSMsgType.CLOSE
@@ -473,7 +445,7 @@ async def handler(request):

ws = await client.ws_connect('/', autoping=False)

ws.ping('data')
await ws.ping('data')
msg = await ws.receive()
assert msg.type == WSMsgType.PONG
assert msg.data == b'data'
@@ -613,7 +585,7 @@ async def handler(request):

await ws.send_str('text')
await ws.send_bytes(b'bytes')
ws.ping()
await ws.ping()

await ws.close()
await closed
@@ -743,7 +715,7 @@ async def handler(request):

items = ['q1', 'q2', 'q3']
for item in items:
resp.send_str(item)
await resp.send_str(item)
msg = await resp.receive()
assert msg.type == aiohttp.WSMsgType.TEXT
assert item + '/answer' == msg.data

0 comments on commit c29b630

Please sign in to comment.