-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathasync_telnet_client.py
More file actions
126 lines (104 loc) · 3.64 KB
/
Copy pathasync_telnet_client.py
File metadata and controls
126 lines (104 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""
Async telnet client implementation using asyncio.
"""
import asyncio
from typing import Optional
class AsyncTelnetClient:
"""Asynchronous telnet client using asyncio."""
def __init__(self, host: str, port: int, timeout: float = 5.0):
"""
Initialize async telnet client.
Args:
host: Hostname or IP address
port: Port number
timeout: Connection timeout in seconds
"""
self.host = host
self.port = port
self.timeout = timeout
self.reader: Optional[asyncio.StreamReader] = None
self.writer: Optional[asyncio.StreamWriter] = None
self._lock = asyncio.Lock()
async def open(self) -> None:
"""Open connection to remote host."""
async with self._lock:
if self.writer:
await self.close()
self.reader, self.writer = await asyncio.wait_for(
asyncio.open_connection(self.host, self.port),
timeout=self.timeout
)
async def close(self) -> None:
"""Close the connection."""
async with self._lock:
if self.writer:
try:
self.writer.close()
await self.writer.wait_closed()
except Exception:
pass
finally:
self.writer = None
self.reader = None
async def write(self, data: bytes) -> None:
"""
Write data to the stream.
Args:
data: Bytes to send
Raises:
ConnectionError: If not connected or send fails
"""
async with self._lock:
if not self.writer:
raise ConnectionError("Not connected")
self.writer.write(data)
await self.writer.drain()
async def read_until(self, delimiter: bytes, timeout: Optional[float] = None) -> bytes:
"""
Read data until delimiter is found.
Args:
delimiter: Byte sequence to read until
timeout: Optional timeout override
Returns:
Bytes read including delimiter
Raises:
ConnectionError: If not connected
asyncio.TimeoutError: If timeout occurs
"""
async with self._lock:
if not self.reader:
raise ConnectionError("Not connected")
read_timeout = timeout if timeout is not None else self.timeout
try:
data = await asyncio.wait_for(
self.reader.readuntil(delimiter),
timeout=read_timeout
)
return data
except asyncio.LimitOverrunError:
# Delimiter not found within limit, read what we can
data = await asyncio.wait_for(
self.reader.read(8192),
timeout=read_timeout
)
return data
async def readline(self, timeout: Optional[float] = None) -> bytes:
"""
Read a single line.
Args:
timeout: Optional timeout override
Returns:
Bytes read including newline
Raises:
ConnectionError: If not connected
asyncio.TimeoutError: If timeout occurs
"""
async with self._lock:
if not self.reader:
raise ConnectionError("Not connected")
read_timeout = timeout if timeout is not None else self.timeout
data = await asyncio.wait_for(
self.reader.readline(),
timeout=read_timeout
)
return data