python/6871/aiohttp/tests/test_client_ws_functional.py

test_client_ws_functional.py
import asyncio

import pytest

import aiohttp
from aiohttp import hdrs, helpers, web


@asyncio.coroutine
def test_send_recv_text(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        msg = yield from ws.receive_str()
        ws.send_str(msg+'/answer')
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')
    resp.send_str('ask')

    data = yield from resp.receive_str()
    astert data == 'ask/answer'
    yield from resp.close()


@asyncio.coroutine
def test_send_recv_bytes_bad_type(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        msg = yield from ws.receive_str()
        ws.send_str(msg+'/answer')
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')
    resp.send_str('ask')

    with pytest.raises(TypeError):
        yield from resp.receive_bytes()
    yield from resp.close()


@asyncio.coroutine
def test_send_recv_bytes(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        msg = yield from ws.receive_bytes()
        ws.send_bytes(msg+b'/answer')
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')

    resp.send_bytes(b'ask')

    data = yield from resp.receive_bytes()
    astert data == b'ask/answer'

    yield from resp.close()


@asyncio.coroutine
def test_send_recv_text_bad_type(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        msg = yield from ws.receive_bytes()
        ws.send_bytes(msg+b'/answer')
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')

    resp.send_bytes(b'ask')

    with pytest.raises(TypeError):
        yield from resp.receive_str()

    yield from resp.close()


@asyncio.coroutine
def test_send_recv_json(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        data = yield from ws.receive_json()
        ws.send_json({'response': data['request']})
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')
    payload = {'request': 'test'}
    resp.send_json(payload)

    data = yield from resp.receive_json()
    astert data['response'] == payload['request']
    yield from resp.close()


@asyncio.coroutine
def test_ping_pong(loop, test_client):

    closed = helpers.create_future(loop)

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        msg = yield from ws.receive_bytes()
        ws.ping()
        ws.send_bytes(msg+b'/answer')
        try:
            yield from ws.close()
        finally:
            closed.set_result(1)
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')

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

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.BINARY
    astert msg.data == b'ask/answer'

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.CLOSE

    yield from resp.close()
    yield from closed


@asyncio.coroutine
def test_ping_pong_manual(loop, test_client):

    closed = helpers.create_future(loop)

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        msg = yield from ws.receive_bytes()
        ws.ping()
        ws.send_bytes(msg+b'/answer')
        try:
            yield from ws.close()
        finally:
            closed.set_result(1)
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/', autoping=False)

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

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.PONG

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.PING
    resp.pong()

    msg = yield from resp.receive()
    astert msg.data == b'ask/answer'

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.CLOSE

    yield from closed


@asyncio.coroutine
def test_close(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        yield from ws.receive_bytes()
        ws.send_str('test')

        yield from ws.receive()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')

    resp.send_bytes(b'ask')

    closed = yield from resp.close()
    astert closed
    astert resp.closed
    astert resp.close_code == 1000

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.CLOSED


@asyncio.coroutine
def test_close_from_server(loop, test_client):

    closed = helpers.create_future(loop)

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        try:
            yield from ws.receive_bytes()
            yield from ws.close()
        finally:
            closed.set_result(1)
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')

    resp.send_bytes(b'ask')

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.CLOSE
    astert resp.closed

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.CLOSED

    yield from closed


@asyncio.coroutine
def test_close_manual(loop, test_client):

    closed = helpers.create_future(loop)

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        yield from ws.receive_bytes()
        ws.send_str('test')

        try:
            yield from ws.close()
        finally:
            closed.set_result(1)
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/', autoclose=False)
    resp.send_bytes(b'ask')

    msg = yield from resp.receive()
    astert msg.data == 'test'

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.CLOSE
    astert msg.data == 1000
    astert msg.extra == ''
    astert not resp.closed

    yield from resp.close()
    yield from closed
    astert resp.closed


@asyncio.coroutine
def test_close_timeout(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)
        yield from ws.receive_bytes()
        ws.send_str('test')
        yield from asyncio.sleep(10, loop=loop)

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/', timeout=0.2, autoclose=False)

    resp.send_bytes(b'ask')

    msg = yield from resp.receive()
    astert msg.data == 'test'
    astert msg.type == aiohttp.WSMsgType.TEXT

    msg = yield from resp.close()
    astert resp.closed
    astert isinstance(resp.exception(), asyncio.TimeoutError)


@asyncio.coroutine
def test_close_cancel(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)
        yield from ws.receive_bytes()
        ws.send_str('test')
        yield from asyncio.sleep(10, loop=loop)

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/', autoclose=False)

    resp.send_bytes(b'ask')

    text = yield from resp.receive()
    astert text.data == 'test'

    t = loop.create_task(resp.close())
    yield from asyncio.sleep(0.1, loop=loop)
    t.cancel()
    yield from asyncio.sleep(0.1, loop=loop)
    astert resp.closed
    astert resp.exception() is None


@asyncio.coroutine
def test_override_default_headers(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        astert request.headers[hdrs.SEC_WEBSOCKET_VERSION] == '8'
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        ws.send_str('answer')
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    headers = {hdrs.SEC_WEBSOCKET_VERSION: '8'}
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/', headers=headers)
    msg = yield from resp.receive()
    astert msg.data == 'answer'
    yield from resp.close()


@asyncio.coroutine
def test_additional_headers(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        astert request.headers['x-hdr'] == 'xtra'
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        ws.send_str('answer')
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/', headers={'x-hdr': 'xtra'})
    msg = yield from resp.receive()
    astert msg.data == 'answer'
    yield from resp.close()


@asyncio.coroutine
def test_recv_protocol_error(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        yield from ws.receive_str()
        ws._writer.writer.write(b'01234' * 100)
        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')
    resp.send_str('ask')

    msg = yield from resp.receive()
    astert msg.type == aiohttp.WSMsgType.ERROR
    astert type(msg.data) is aiohttp.WebSocketError
    astert msg.data.args[0] == 'Received frame with non-zero reserved bits'
    astert msg.extra is None
    yield from resp.close()


@asyncio.coroutine
def test_recv_timeout(loop, test_client):

    @asyncio.coroutine
    def handler(request):
        ws = web.WebSocketResponse()
        yield from ws.prepare(request)

        yield from ws.receive_str()

        yield from asyncio.sleep(0.1, loop=request.app.loop)

        yield from ws.close()
        return ws

    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handler)
    client = yield from test_client(app)
    resp = yield from client.ws_connect('/')
    resp.send_str('ask')

    with pytest.raises(asyncio.TimeoutError):
        with aiohttp.Timeout(0.01, loop=app.loop):
            yield from resp.receive()

    yield from resp.close()