Skip to content

Commit 12d7d4a

Browse files
feat: support socks proxy
1 parent 62196fc commit 12d7d4a

File tree

5 files changed

+40
-7
lines changed

5 files changed

+40
-7
lines changed

examples/example.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import asyncio
22

3+
from aiosocks import Socks5Addr
4+
35
from tcp_modbus_aio.client import TCPModbusClient
46
from tcp_modbus_aio.exceptions import (
57
ModbusCommunicationFailureError,
@@ -13,7 +15,11 @@
1315

1416
async def example() -> None:
1517

16-
async with TCPModbusClient("192.168.250.207", enforce_pingable=False) as conn:
18+
async with TCPModbusClient(
19+
"192.168.250.207",
20+
enforce_pingable=False,
21+
socks_proxy_addr=Socks5Addr("localhost", 1080),
22+
) as conn:
1723
for _ in range(1000):
1824
for digital_in_coil in DIGITAL_IN_COILS:
1925
example_message = ReadCoils()

poetry.lock

+11-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ umodbus = "^1.0.4"
1212
cachetools = "^5.3.3"
1313
types-cachetools = "^5.3.0.7"
1414
typing-extensions = "^4.11.0"
15+
aiosocks = "^0.2.6"
1516

1617
[tool.poetry.dev-dependencies]
1718
flake8 = "^6.0.0"

setup.cfg

+3
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ ignore_missing_imports = True
2222

2323
[mypy-umodbus.*]
2424
ignore_missing_imports = True
25+
26+
[mypy-aiosocks.*]
27+
ignore_missing_imports = True

tcp_modbus_aio/client.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from dataclasses import dataclass
99
from typing import TYPE_CHECKING, Any, ClassVar
1010

11+
import aiosocks
1112
from cachetools import TTLCache
1213

1314
from tcp_modbus_aio.exceptions import (
@@ -50,7 +51,6 @@ class CoilWatchStatus:
5051
MBAP_HEADER_STRUCT_FORMAT = ">HHHB"
5152

5253

53-
@dataclass
5454
class TCPModbusClient:
5555
KEEPALIVE_AFTER_IDLE_SEC: ClassVar = 10
5656
KEEPALIVE_INTERVAL_SEC: ClassVar = 10
@@ -68,12 +68,16 @@ def __init__(
6868
logger: logging.Logger | None = None,
6969
enforce_pingable: bool = True,
7070
ping_timeout: float = 0.5,
71+
socks_proxy_addr: aiosocks.SocksAddr | None = None,
72+
socks_proxy_auth: aiosocks.Socks4Auth | aiosocks.Socks5Auth | None = None,
7173
) -> None:
7274
self.host = host
7375
self.port = port
7476
self.slave_id = slave_id
7577
self.logger = logger
7678
self.ping_timeout = ping_timeout
79+
self.socks_proxy_addr = socks_proxy_addr
80+
self.socks_proxy_auth = socks_proxy_auth
7781

7882
# If True, will throw an exception if attempting to send a request and the device is not pingable
7983
self.enforce_pingable = enforce_pingable
@@ -160,9 +164,19 @@ async def _get_tcp_connection(
160164
)
161165

162166
try:
163-
reader, writer = await asyncio.wait_for(
164-
asyncio.open_connection(host=self.host, port=self.port), timeout
165-
)
167+
if self.socks_proxy_addr is None:
168+
open_connection_coroutine = asyncio.open_connection(
169+
host=self.host, port=self.port
170+
)
171+
else:
172+
open_connection_coroutine = aiosocks.open_connection(
173+
proxy=self.socks_proxy_addr,
174+
proxy_auth=self.socks_proxy_auth,
175+
dst=(self.host, self.port),
176+
remote_resolve=True,
177+
)
178+
179+
reader, writer = await asyncio.wait_for(open_connection_coroutine, timeout)
166180
except asyncio.TimeoutError:
167181
msg = (
168182
f"Timed out connecting to TCP modbus device at {self.host}:{self.port}"

0 commit comments

Comments
 (0)