1 | """ |
---|
2 | Define a protocol for listening on a transport such that Tahoe-LAFS can |
---|
3 | communicate over it, manage configuration for it in its configuration file, |
---|
4 | detect when it is possible to use it, etc. |
---|
5 | """ |
---|
6 | |
---|
7 | from __future__ import annotations |
---|
8 | |
---|
9 | from typing import Any, Protocol, Sequence, Mapping, Optional, Union, Awaitable |
---|
10 | from typing_extensions import Literal |
---|
11 | |
---|
12 | from attrs import frozen |
---|
13 | from twisted.python.usage import Options |
---|
14 | |
---|
15 | from .interfaces import IAddressFamily |
---|
16 | from .util.iputil import allocate_tcp_port |
---|
17 | from .node import _Config |
---|
18 | |
---|
19 | @frozen |
---|
20 | class ListenerConfig: |
---|
21 | """ |
---|
22 | :ivar tub_ports: Entries to merge into ``[node]tub.port``. |
---|
23 | |
---|
24 | :ivar tub_locations: Entries to merge into ``[node]tub.location``. |
---|
25 | |
---|
26 | :ivar node_config: Entries to add into the overall Tahoe-LAFS |
---|
27 | configuration beneath a section named after this listener. |
---|
28 | """ |
---|
29 | tub_ports: Sequence[str] |
---|
30 | tub_locations: Sequence[str] |
---|
31 | node_config: Mapping[str, Sequence[tuple[str, str]]] |
---|
32 | |
---|
33 | class Listener(Protocol): |
---|
34 | """ |
---|
35 | An object which can listen on a transport and allow Tahoe-LAFS |
---|
36 | communication to happen over it. |
---|
37 | """ |
---|
38 | def is_available(self) -> bool: |
---|
39 | """ |
---|
40 | Can this type of listener actually be used in this runtime |
---|
41 | environment? |
---|
42 | """ |
---|
43 | |
---|
44 | def can_hide_ip(self) -> bool: |
---|
45 | """ |
---|
46 | Can the transport supported by this type of listener conceal the |
---|
47 | node's public internet address from peers? |
---|
48 | """ |
---|
49 | |
---|
50 | async def create_config(self, reactor: Any, cli_config: Options) -> Optional[ListenerConfig]: |
---|
51 | """ |
---|
52 | Set up an instance of this listener according to the given |
---|
53 | configuration parameters. |
---|
54 | |
---|
55 | This may also allocate ephemeral resources if necessary. |
---|
56 | |
---|
57 | :return: The created configuration which can be merged into the |
---|
58 | overall *tahoe.cfg* configuration file. |
---|
59 | """ |
---|
60 | |
---|
61 | def create(self, reactor: Any, config: _Config) -> IAddressFamily: |
---|
62 | """ |
---|
63 | Instantiate this listener according to the given |
---|
64 | previously-generated configuration. |
---|
65 | |
---|
66 | :return: A handle on the listener which can be used to integrate it |
---|
67 | into the Tahoe-LAFS node. |
---|
68 | """ |
---|
69 | |
---|
70 | class TCPProvider: |
---|
71 | """ |
---|
72 | Support plain TCP connections. |
---|
73 | """ |
---|
74 | def is_available(self) -> Literal[True]: |
---|
75 | return True |
---|
76 | |
---|
77 | def can_hide_ip(self) -> Literal[False]: |
---|
78 | return False |
---|
79 | |
---|
80 | async def create_config(self, reactor: Any, cli_config: Options) -> ListenerConfig: |
---|
81 | tub_ports = [] |
---|
82 | tub_locations = [] |
---|
83 | if cli_config["port"]: # --port/--location are a pair |
---|
84 | tub_ports.append(cli_config["port"]) |
---|
85 | tub_locations.append(cli_config["location"]) |
---|
86 | else: |
---|
87 | assert "hostname" in cli_config |
---|
88 | hostname = cli_config["hostname"] |
---|
89 | new_port = allocate_tcp_port() |
---|
90 | tub_ports.append(f"tcp:{new_port}") |
---|
91 | tub_locations.append(f"tcp:{hostname}:{new_port}") |
---|
92 | |
---|
93 | return ListenerConfig(tub_ports, tub_locations, {}) |
---|
94 | |
---|
95 | def create(self, reactor: Any, config: _Config) -> IAddressFamily: |
---|
96 | raise NotImplementedError() |
---|
97 | |
---|
98 | |
---|
99 | @frozen |
---|
100 | class StaticProvider: |
---|
101 | """ |
---|
102 | A provider that uses all pre-computed values. |
---|
103 | """ |
---|
104 | _available: bool |
---|
105 | _hide_ip: bool |
---|
106 | _config: Union[Awaitable[Optional[ListenerConfig]], Optional[ListenerConfig]] |
---|
107 | _address: IAddressFamily |
---|
108 | |
---|
109 | def is_available(self) -> bool: |
---|
110 | return self._available |
---|
111 | |
---|
112 | def can_hide_ip(self) -> bool: |
---|
113 | return self._hide_ip |
---|
114 | |
---|
115 | async def create_config(self, reactor: Any, cli_config: Options) -> Optional[ListenerConfig]: |
---|
116 | if self._config is None or isinstance(self._config, ListenerConfig): |
---|
117 | return self._config |
---|
118 | return await self._config |
---|
119 | |
---|
120 | def create(self, reactor: Any, config: _Config) -> IAddressFamily: |
---|
121 | return self._address |
---|