texas2step 2.2.2__tar.gz → 2.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {texas2step-2.2.2 → texas2step-2.2.3}/PKG-INFO +1 -1
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/access.md +6 -2
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/configuration.md +3 -2
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/roadmap.md +5 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/__init__.py +1 -1
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/api/access.py +4 -1
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/api/measure.py +2 -1
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/api/results.py +1 -1
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/cli.py +12 -1
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/config.py +19 -1
- {texas2step-2.2.2 → texas2step-2.2.3}/.gitignore +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/LICENSE.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/README.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/nginx/texas2step-http.conf +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/nginx/texas2step-log.conf +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/nginx/texas2step-speedtest.conf +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/nginx/texas2step.conf +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/texas2step-update.service +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/texas2step-update.timer +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/texas2step.default +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/deploy/texas2step.service +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/api.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/development.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/experience.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/images/hero-dark.jpg +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/images/results-dark.jpg +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/images/results-light.jpg +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/images/upload-light.jpg +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/install.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/methodology.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/security.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/docs/traffic.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/legacy/README.md +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/pyproject.toml +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/__main__.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/access.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/api/__init__.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/api/meta.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/api/wire.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/app.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/cluster.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/config_edit.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/config_merge.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/config_template.toml +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/engine.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/iface.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/lockouts.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/logs.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/page.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/payload.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/quality.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/rtc.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/app.css +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/app.js +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/swagger/LICENSE +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/swagger/NOTICE +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/swagger/swagger-ui-bundle.js +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/swagger/swagger-ui-bundle.js.LICENSE.txt +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/swagger/swagger-ui.css +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/favicon.svg +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/index.html +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/stats.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/store.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/traffic.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/update.py +0 -0
- {texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/words.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: texas2step
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Summary: Web-centric throughput, latency, jitter and packet-loss measurement between endpoints.
|
|
5
5
|
Project-URL: Homepage, https://github.com/robert-patrick-texas/texas2step
|
|
6
6
|
Project-URL: Documentation, https://robert-patrick-texas.github.io/texas2step/
|
|
@@ -49,10 +49,14 @@ networks, as addresses or CIDR blocks. Include the corporate addresses the desk'
|
|
|
49
49
|
browsers reach the servers from.
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
sudo texas2step config set access_code=daily internal_networks=10.0.0.0/8,198.51.100.0/24 --write
|
|
52
|
+
sudo texas2step config set access_code=daily internal_networks=10.0.0.0/8,2001:db8::/32,198.51.100.0/24 --write
|
|
53
53
|
sudo systemctl restart texas2step
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
On the command line, separate the networks with commas. In `config.toml` they are a list
|
|
57
|
+
of quoted strings: `internal_networks = ["10.0.0.0/8", "2001:db8::/32", "198.51.100.0/24"]`.
|
|
58
|
+
One string of networks separated by spaces is refused.
|
|
59
|
+
|
|
56
60
|
**3. Check** that the servers agree. On each:
|
|
57
61
|
|
|
58
62
|
```bash
|
|
@@ -87,7 +91,7 @@ To turn codes off again: `sudo texas2step config set access_code=off --write`, t
|
|
|
87
91
|
| `access_pass_hours` | `12` | How long an entered code lets a browser keep testing |
|
|
88
92
|
| `access_key_file` | `/etc/texas2step/access.key` | The key |
|
|
89
93
|
| `access_phrases_file` | `/etc/texas2step/access-phrases.toml` | [Standing phrases](#standing-phrases), if the file exists |
|
|
90
|
-
| `internal_networks` | `[]` | Your own networks: no code, and the desk's tools |
|
|
94
|
+
| `internal_networks` | `[]` | Your own networks: no code, and the desk's tools. A list of IPv4 and IPv6 addresses and networks in CIDR form, mixed freely: `["192.168.0.0/16", "2001:db8::/32", "203.0.113.7"]`. An address alone is a single host. Bits past the prefix are ignored, so `10.2.1.30/24` means all of `10.2.1.0/24`. Give the addresses as the server sees them: staff who come in through your firewall's NAT arrive from its public address. |
|
|
91
95
|
|
|
92
96
|
**Built-in words.** A code is two words and the digits: `maple.river.42`. The words come
|
|
93
97
|
from the EFF's short word list of easily spelled words (credited in the package's
|
|
@@ -7,8 +7,9 @@ Settings come from, in increasing priority:
|
|
|
7
7
|
3. environment variables `TEXAS2STEP_<SETTING>` (upper case).
|
|
8
8
|
|
|
9
9
|
Lists in environment variables are comma separated. Booleans accept `1/0`,
|
|
10
|
-
`true/false`, `yes/no` and `on/off`.
|
|
11
|
-
|
|
10
|
+
`true/false`, `yes/no` and `on/off`. Spaces around any value, or around each item of a
|
|
11
|
+
list, are ignored, wherever the setting comes from. Unknown keys in the TOML file are an
|
|
12
|
+
error, so typos don't fail silently. The package installs a fully commented file listing every
|
|
12
13
|
setting and its default as `/etc/texas2step/config.toml` (source:
|
|
13
14
|
[`src/texas2step/config_template.toml`](../src/texas2step/config_template.toml)); restart
|
|
14
15
|
the service after editing it. After an upgrade, `texas2step config merge` brings your
|
|
@@ -116,6 +116,11 @@ links and footer credit. Three places still say texas2step:
|
|
|
116
116
|
|
|
117
117
|
Newest first. Each release's notes have the details and its upgrade steps.
|
|
118
118
|
|
|
119
|
+
### [2.2.3](https://github.com/robert-patrick-texas/texas2step/releases/tag/v2.2.3) (2026-10-24)
|
|
120
|
+
|
|
121
|
+
- [x] Spaces around any setting or typed value are ignored; a stray one in
|
|
122
|
+
`internal_networks` no longer breaks every request.
|
|
123
|
+
|
|
119
124
|
### [2.2.2](https://github.com/robert-patrick-texas/texas2step/releases/tag/v2.2.2) (2026-10-24)
|
|
120
125
|
|
|
121
126
|
- [x] UDP works in Firefox: Monitor, Diag and the Test page's packet loss no longer fall
|
|
@@ -9,7 +9,7 @@ from datetime import UTC, datetime
|
|
|
9
9
|
from typing import Any, Literal
|
|
10
10
|
|
|
11
11
|
from fastapi import APIRouter, HTTPException, Query, Request, Response
|
|
12
|
-
from pydantic import BaseModel, Field, ValidationError
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
|
13
13
|
|
|
14
14
|
from .. import cluster
|
|
15
15
|
from ..access import Codes, is_internal
|
|
@@ -24,10 +24,12 @@ PASS_COOKIE = "texas2step_pass" # the pass again, for loading this server's own
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class CodeIn(BaseModel):
|
|
27
|
+
model_config = ConfigDict(str_strip_whitespace=True) # as typed or pasted
|
|
27
28
|
code: str = Field(max_length=200, description="The code, with any case and separators")
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class BlockIn(BaseModel):
|
|
32
|
+
model_config = ConfigDict(str_strip_whitespace=True)
|
|
31
33
|
network: str = Field(
|
|
32
34
|
max_length=64,
|
|
33
35
|
description="An address or network outside internal_networks, e.g. 203.0.113.0/24",
|
|
@@ -261,6 +263,7 @@ async def unblock(
|
|
|
261
263
|
),
|
|
262
264
|
) -> dict[str, Any]:
|
|
263
265
|
ip = _desk_client(request)
|
|
266
|
+
network = network.strip()
|
|
264
267
|
try:
|
|
265
268
|
target = None if network == "all" else str(parse_network(network))
|
|
266
269
|
except ValueError as exc:
|
|
@@ -15,7 +15,7 @@ from typing import Any
|
|
|
15
15
|
|
|
16
16
|
import httpx
|
|
17
17
|
from fastapi import APIRouter, HTTPException, Request
|
|
18
|
-
from pydantic import BaseModel, Field
|
|
18
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
19
19
|
|
|
20
20
|
from .. import engine
|
|
21
21
|
from ..store import TRANSFERS
|
|
@@ -27,6 +27,7 @@ _slots = asyncio.Semaphore(2)
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class Target(BaseModel):
|
|
30
|
+
model_config = ConfigDict(str_strip_whitespace=True)
|
|
30
31
|
target: str = Field(description="Base URL of a texas2step server, e.g. https://peer.example")
|
|
31
32
|
|
|
32
33
|
|
|
@@ -50,7 +50,7 @@ async def recent(
|
|
|
50
50
|
|
|
51
51
|
@router.get("/{rid}", summary="A saved result, in full")
|
|
52
52
|
async def one(rid: str, request: Request) -> dict[str, Any]:
|
|
53
|
-
result = await run_in_threadpool(_store(request).get, rid)
|
|
53
|
+
result = await run_in_threadpool(_store(request).get, rid.strip())
|
|
54
54
|
if result is None:
|
|
55
55
|
raise HTTPException(404, "no such result")
|
|
56
56
|
return result
|
|
@@ -1361,6 +1361,17 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1361
1361
|
return parser
|
|
1362
1362
|
|
|
1363
1363
|
|
|
1364
|
+
def trimmed(args: argparse.Namespace) -> argparse.Namespace:
|
|
1365
|
+
"""The arguments without spaces around their text: pasted values often bring them, with
|
|
1366
|
+
an address, a URL or a network."""
|
|
1367
|
+
for name, value in vars(args).items():
|
|
1368
|
+
if isinstance(value, str):
|
|
1369
|
+
setattr(args, name, value.strip())
|
|
1370
|
+
elif isinstance(value, list):
|
|
1371
|
+
setattr(args, name, [v.strip() if isinstance(v, str) else v for v in value])
|
|
1372
|
+
return args
|
|
1373
|
+
|
|
1374
|
+
|
|
1364
1375
|
def main(argv: list[str] | None = None) -> None:
|
|
1365
|
-
args = build_parser().parse_args(argv)
|
|
1376
|
+
args = trimmed(build_parser().parse_args(argv))
|
|
1366
1377
|
args.func(args)
|
|
@@ -190,6 +190,11 @@ class Settings:
|
|
|
190
190
|
brand_credit: str = "link"
|
|
191
191
|
|
|
192
192
|
def __post_init__(self) -> None:
|
|
193
|
+
# However a setting was given (file, environment, `config set`), spaces around its
|
|
194
|
+
# text are no part of it: a stray space inside quotes would otherwise pass here and
|
|
195
|
+
# fail later, as in an address network that every request is checked against.
|
|
196
|
+
for f in dataclasses.fields(self):
|
|
197
|
+
setattr(self, f.name, _trimmed(getattr(self, f.name)))
|
|
193
198
|
ports = (self.rtc_port_min, self.rtc_port_max)
|
|
194
199
|
if ports != (0, 0) and not 1 <= ports[0] <= ports[1] <= 65535:
|
|
195
200
|
raise ValueError(
|
|
@@ -317,6 +322,19 @@ class Settings:
|
|
|
317
322
|
)
|
|
318
323
|
|
|
319
324
|
|
|
325
|
+
def _trimmed(value: Any) -> Any:
|
|
326
|
+
"""A setting with the whitespace around each piece of its text taken off: in lists, and
|
|
327
|
+
in the fields of [[locations]] and links too."""
|
|
328
|
+
if isinstance(value, str):
|
|
329
|
+
return value.strip()
|
|
330
|
+
if isinstance(value, list):
|
|
331
|
+
return [_trimmed(v) for v in value]
|
|
332
|
+
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
333
|
+
fields = {f.name: _trimmed(getattr(value, f.name)) for f in dataclasses.fields(value)}
|
|
334
|
+
return dataclasses.replace(value, **fields)
|
|
335
|
+
return value
|
|
336
|
+
|
|
337
|
+
|
|
320
338
|
def _coerce(raw: Any, hint: Any, name: str) -> Any:
|
|
321
339
|
origin = typing.get_origin(hint)
|
|
322
340
|
if origin in (typing.Union, types.UnionType):
|
|
@@ -336,7 +354,7 @@ def _coerce(raw: Any, hint: Any, name: str) -> Any:
|
|
|
336
354
|
if hint is float:
|
|
337
355
|
return float(raw)
|
|
338
356
|
if hint is str:
|
|
339
|
-
return str(raw)
|
|
357
|
+
return str(raw).strip()
|
|
340
358
|
if origin is list:
|
|
341
359
|
(item,) = typing.get_args(hint)
|
|
342
360
|
if isinstance(raw, str):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{texas2step-2.2.2 → texas2step-2.2.3}/src/texas2step/static/assets/swagger/swagger-ui-bundle.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|