mirror of
https://github.com/esphome/esphome.git
synced 2026-10-13 01:32:49 +00:00
[socket] Share the server side as TcpListener (#20037)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
J. Nick Koston
parent
cbe89c940d
commit
b6c79fdc9a
@@ -7,5 +7,8 @@ uart_tcp:
|
||||
uart_id: uart_bus
|
||||
role: server
|
||||
port: 502
|
||||
allowed_ips:
|
||||
- 192.168.1.10
|
||||
- 192.168.1.0/24
|
||||
connected:
|
||||
name: UART TCP Connected
|
||||
|
||||
@@ -18,5 +18,13 @@ uart_tcp:
|
||||
uart_id: uart_bus
|
||||
role: server
|
||||
port: 18126
|
||||
allowed_ips:
|
||||
- 127.0.0.1
|
||||
connected:
|
||||
name: Bridge Connected
|
||||
- id: denied_bridge
|
||||
uart_id: uart_bus
|
||||
role: server
|
||||
port: 18127
|
||||
allowed_ips:
|
||||
- 192.0.2.1
|
||||
|
||||
@@ -25,6 +25,7 @@ async def test_uart_tcp_bridge(
|
||||
unused_tcp_port_factory,
|
||||
) -> None:
|
||||
server_port = unused_tcp_port_factory()
|
||||
denied_port = unused_tcp_port_factory()
|
||||
controller_fd, device_fd = os.openpty()
|
||||
os.set_blocking(controller_fd, False)
|
||||
# uart's validate_port wants a two segment device path; Linux ptys live at
|
||||
@@ -32,6 +33,7 @@ async def test_uart_tcp_bridge(
|
||||
pty_link = f"/tmp/uart-tcp-pty-{os.getpid()}"
|
||||
pathlib.Path(pty_link).symlink_to(os.ttyname(device_fd))
|
||||
yaml_config = yaml_config.replace("port: 18126", f"port: {server_port}")
|
||||
yaml_config = yaml_config.replace("port: 18127", f"port: {denied_port}")
|
||||
yaml_config = yaml_config.replace("PTY_PATH", pty_link)
|
||||
|
||||
lines = LineWaiter()
|
||||
@@ -103,6 +105,14 @@ async def test_uart_tcp_bridge(
|
||||
await writer.drain()
|
||||
assert await read_uart(5) == b"down2"
|
||||
writer.close()
|
||||
|
||||
# A peer outside the allow list is rejected and closed.
|
||||
denied_reader, denied_writer = await asyncio.open_connection(
|
||||
"127.0.0.1", denied_port
|
||||
)
|
||||
await lines.wait_for("Rejected 127.0.0.1")
|
||||
assert await asyncio.wait_for(denied_reader.read(8), 10) == b""
|
||||
denied_writer.close()
|
||||
finally:
|
||||
loop.remove_reader(controller_fd)
|
||||
os.close(controller_fd)
|
||||
|
||||
@@ -18,9 +18,13 @@ def test_network_order_swaps_to_sockaddr_value() -> None:
|
||||
|
||||
def test_add_ipv4_allow_emits_nothing_for_an_empty_list() -> None:
|
||||
setter = MagicMock()
|
||||
with patch.object(socket.cg, "add") as add:
|
||||
with (
|
||||
patch.object(socket.cg, "add") as add,
|
||||
patch.object(socket.cg, "add_define") as add_define,
|
||||
):
|
||||
socket.add_ipv4_allow(setter, [], "bridge")
|
||||
add.assert_not_called()
|
||||
add_define.assert_not_called()
|
||||
setter.assert_not_called()
|
||||
|
||||
|
||||
@@ -29,6 +33,7 @@ def test_add_ipv4_allow_wires_the_setter_with_cleared_host_bits() -> None:
|
||||
networks = [IPv4Network("192.168.175.33/24", strict=False)]
|
||||
with (
|
||||
patch.object(socket.cg, "add") as add,
|
||||
patch.object(socket.cg, "add_define") as add_define,
|
||||
patch.object(socket.cg, "progmem_array") as array,
|
||||
):
|
||||
socket.add_ipv4_allow(setter, networks, "bridge")
|
||||
@@ -37,6 +42,7 @@ def test_add_ipv4_allow_wires_the_setter_with_cleared_host_bits() -> None:
|
||||
assert str(socket._network_order(IPv4Address("255.255.255.0"))) in rendered
|
||||
setter.assert_called_once_with(array.return_value, 1)
|
||||
add.assert_called_once()
|
||||
add_define.assert_called_once_with("USE_SOCKET_IPV4_ALLOW")
|
||||
|
||||
|
||||
def test_schema_caps_the_list_length() -> None:
|
||||
|
||||
@@ -13,6 +13,7 @@ def test_helper_files_filtered_until_required() -> None:
|
||||
filtered = socket.FILTER_SOURCE_FILES()
|
||||
assert "ipv4_resolve.cpp" in filtered
|
||||
assert "tcp_client_link.cpp" in filtered
|
||||
assert "tcp_listener.cpp" in filtered
|
||||
|
||||
mock_core.defines = {Define("USE_SOCKET_IPV4_RESOLVE")}
|
||||
filtered = socket.FILTER_SOURCE_FILES()
|
||||
@@ -22,10 +23,12 @@ def test_helper_files_filtered_until_required() -> None:
|
||||
mock_core.defines = {
|
||||
Define("USE_SOCKET_IPV4_RESOLVE"),
|
||||
Define("USE_SOCKET_TCP_CLIENT_LINK"),
|
||||
Define("USE_SOCKET_TCP_LISTENER"),
|
||||
}
|
||||
filtered = socket.FILTER_SOURCE_FILES()
|
||||
assert "ipv4_resolve.cpp" not in filtered
|
||||
assert "tcp_client_link.cpp" not in filtered
|
||||
assert "tcp_listener.cpp" not in filtered
|
||||
|
||||
|
||||
def test_require_tcp_client_link_pulls_in_the_resolver() -> None:
|
||||
@@ -36,3 +39,13 @@ def test_require_tcp_client_link_pulls_in_the_resolver() -> None:
|
||||
"USE_SOCKET_IPV4_RESOLVE",
|
||||
"USE_SOCKET_TCP_CLIENT_LINK",
|
||||
}
|
||||
|
||||
|
||||
def test_require_tcp_listener_pulls_in_the_link() -> None:
|
||||
with patch.object(socket.cg, "add_define") as add_define:
|
||||
socket.require_tcp_listener()
|
||||
assert {call.args[0] for call in add_define.call_args_list} == {
|
||||
"USE_SOCKET_IPV4_RESOLVE",
|
||||
"USE_SOCKET_TCP_CLIENT_LINK",
|
||||
"USE_SOCKET_TCP_LISTENER",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user