mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-08-27 12:30:13 +03:00
178 lines
5.8 KiB
Python
178 lines
5.8 KiB
Python
import asyncio
|
|
import contextlib
|
|
|
|
import pytest
|
|
import telnetlib3
|
|
|
|
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
|
|
|
|
|
|
class DummyUpstreamWriter:
|
|
def __init__(self):
|
|
self.buffer = bytearray()
|
|
self.write_event = asyncio.Event()
|
|
|
|
def write(self, data):
|
|
self.buffer.extend(data)
|
|
self.write_event.set()
|
|
|
|
async def drain(self):
|
|
await asyncio.sleep(0)
|
|
|
|
|
|
def _get_listen_port(server):
|
|
return server.sockets[0].getsockname()[1]
|
|
|
|
|
|
async def _wait_for_connection_count(server, expected_count, timeout=2):
|
|
deadline = asyncio.get_running_loop().time() + timeout
|
|
last_count = 0
|
|
while asyncio.get_running_loop().time() < deadline:
|
|
async with server._connections_lock:
|
|
last_count = len(server._connections)
|
|
if last_count == expected_count:
|
|
return
|
|
await asyncio.sleep(0.05)
|
|
assert last_count == expected_count
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telnet_server_forwards_client_input_to_upstream_writer():
|
|
upstream_reader = asyncio.StreamReader()
|
|
upstream_writer = DummyUpstreamWriter()
|
|
server = AsyncioTelnetServer(reader=upstream_reader, writer=upstream_writer, binary=True, echo=True)
|
|
listener = await server.start("127.0.0.1", 0)
|
|
port = _get_listen_port(listener)
|
|
|
|
client_writer = None
|
|
try:
|
|
_, client_writer = await telnetlib3.open_connection("127.0.0.1", port, encoding=False)
|
|
await _wait_for_connection_count(server, 1)
|
|
client_writer.write(b"ping\r\n")
|
|
await client_writer.drain()
|
|
|
|
await asyncio.wait_for(upstream_writer.write_event.wait(), timeout=2)
|
|
assert b"ping\r\n" in bytes(upstream_writer.buffer)
|
|
finally:
|
|
if client_writer is not None:
|
|
client_writer.close()
|
|
listener.close()
|
|
await listener.wait_closed()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telnet_server_broadcasts_upstream_output_to_all_clients():
|
|
upstream_reader = asyncio.StreamReader()
|
|
upstream_writer = DummyUpstreamWriter()
|
|
server = AsyncioTelnetServer(reader=upstream_reader, writer=upstream_writer, binary=True, echo=True)
|
|
listener = await server.start("127.0.0.1", 0)
|
|
port = _get_listen_port(listener)
|
|
|
|
client1_reader = client1_writer = None
|
|
client2_reader = client2_writer = None
|
|
|
|
try:
|
|
client1_reader, client1_writer = await telnetlib3.open_connection("127.0.0.1", port, encoding=False)
|
|
client2_reader, client2_writer = await telnetlib3.open_connection("127.0.0.1", port, encoding=False)
|
|
await _wait_for_connection_count(server, 2)
|
|
|
|
upstream_reader.feed_data(b"hello")
|
|
|
|
data1 = await asyncio.wait_for(client1_reader.read(5), timeout=2)
|
|
data2 = await asyncio.wait_for(client2_reader.read(5), timeout=2)
|
|
|
|
assert data1 == b"hello"
|
|
assert data2 == b"hello"
|
|
finally:
|
|
if client1_writer is not None:
|
|
client1_writer.close()
|
|
if client2_writer is not None:
|
|
client2_writer.close()
|
|
listener.close()
|
|
await listener.wait_closed()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telnet_server_naws_callback_uses_negotiated_dimensions():
|
|
callback_event = asyncio.Event()
|
|
callback_result = {}
|
|
|
|
async def window_size_changed(columns, rows):
|
|
callback_result["columns"] = columns
|
|
callback_result["rows"] = rows
|
|
callback_event.set()
|
|
|
|
server = AsyncioTelnetServer(
|
|
binary=True,
|
|
echo=True,
|
|
naws=True,
|
|
window_size_changed_callback=window_size_changed,
|
|
)
|
|
listener = await server.start("127.0.0.1", 0)
|
|
port = _get_listen_port(listener)
|
|
|
|
client_writer = None
|
|
try:
|
|
_, client_writer = await telnetlib3.open_connection(
|
|
"127.0.0.1", port, encoding=False, cols=132, rows=44
|
|
)
|
|
|
|
await asyncio.wait_for(callback_event.wait(), timeout=2)
|
|
assert callback_result == {"columns": 132, "rows": 44}
|
|
finally:
|
|
if client_writer is not None:
|
|
client_writer.close()
|
|
listener.close()
|
|
await listener.wait_closed()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telnet_server_broadcast_survives_client_disconnect():
|
|
upstream_reader = asyncio.StreamReader()
|
|
upstream_writer = DummyUpstreamWriter()
|
|
server = AsyncioTelnetServer(reader=upstream_reader, writer=upstream_writer, binary=True, echo=True)
|
|
listener = await server.start("127.0.0.1", 0)
|
|
port = _get_listen_port(listener)
|
|
|
|
client1_writer = None
|
|
client2_reader = client2_writer = None
|
|
|
|
try:
|
|
_, client1_writer = await telnetlib3.open_connection("127.0.0.1", port, encoding=False)
|
|
client2_reader, client2_writer = await telnetlib3.open_connection("127.0.0.1", port, encoding=False)
|
|
await _wait_for_connection_count(server, 2)
|
|
|
|
client1_writer.close()
|
|
await _wait_for_connection_count(server, 1)
|
|
|
|
upstream_reader.feed_data(b"ok")
|
|
data2 = await asyncio.wait_for(client2_reader.read(2), timeout=2)
|
|
|
|
assert data2 == b"ok"
|
|
async with server._connections_lock:
|
|
assert len(server._connections) <= 1
|
|
finally:
|
|
if client1_writer is not None:
|
|
with contextlib.suppress(Exception):
|
|
client1_writer.close()
|
|
if client2_writer is not None:
|
|
with contextlib.suppress(Exception):
|
|
client2_writer.close()
|
|
listener.close()
|
|
await listener.wait_closed()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telnet_server_listener_close_cleans_internal_tasks():
|
|
upstream_reader = asyncio.StreamReader()
|
|
upstream_writer = DummyUpstreamWriter()
|
|
server = AsyncioTelnetServer(reader=upstream_reader, writer=upstream_writer, binary=True, echo=True)
|
|
listener = await server.start("127.0.0.1", 0)
|
|
|
|
assert server._broadcast_task is not None
|
|
|
|
listener.close()
|
|
await listener.wait_closed()
|
|
|
|
assert server._broadcast_task is None
|