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
# PC Flutter port — notes
Status: **partial.** This folder contains the engine port, the Python golden
tests and the golden generator. It does not yet contain the UI, the Generate
workbench, or the Windows runner. Nothing here has been compiled or run yet.
## What is in this folder
- `lib/engine/py_compat.dart`: Python `str.strip()` and `str.splitlines()` semantics.
- `lib/engine/vless_parser.dart`: port of `parser.py` (parse, split_candidates,
parse_first, build_config_link, Kotlin `toIntOrNull`, Java-style URL decoding,
Uri.encode-style encoding).
- `lib/engine/vless_scanner.dart`: port of `scanner.py` (VLESS header, HTTP payload,
masked WS frame, WS upgrade, the probe, the 3-probe confirmation, and the
12-worker subnet scan). Constants are copied from `scanner.py`:
timeout 2400 ms, mask `12 34 56 78`, target `www.google.com:80`, 8192-byte reply cap,
1024-byte reads, 4096-byte header cap, 254 hosts, 12 workers, 60 ms between probes.
- `test/golden/*.json`: expected outputs produced by the Python engine.
- `test/parser_golden_test.dart`, `test/binary_golden_test.dart`,
`test/split_helpers_test.dart`: Dart tests against the golden files.
- `tool/gen_golden.py`: regenerates the golden files from `parser.py` and `scanner.py`.
## Not in this folder yet (next steps)
1. UI in Flutter widgets (1:1 with `ui.html`), main screen and workbench, footer.
2. `lib/main.dart` and the `windows/` runner. Generate them with
`flutter create --platforms=windows --project-name railway_clean_ip_scanner .`
inside this folder, then add `flutter: sdk: flutter` and `flutter_test` to
pubspec.yaml. Copy `assets/icon.ico` into `windows/runner/resources/`.
3. Probe test against a local TLS/WebSocket mock (the Python suite has
`tests/mock_server.py`). Not covered by the golden tests.
4. Port of `test_workbench.py` and `test_web.py` behaviour. Those are server and
UI tests and only apply once the UI exists.
## Not yet verified (must be checked on the first compile)
- No Dart SDK was available when this was written. Every file is unverified until
`dart analyze` / `flutter test` passes on Windows CI.
- `SecurityContext(withTrustedRoots: false)` and `setAlpnProtocols(list, false)`:
ALPN is set on a context passed to `SecureSocket.secure`.
- `SecureSocket.secure(socket, host:, context:, onBadCertificate:)` with an
`.timeout(...)` wrapper.
- `Socket.connect(ip, port, timeout:)`.
- RegExp `\p{Nd}` with `unicode: true` (used for Python `isdecimal()`).
- `Future.wait` over a list of workers, and `StreamIterator` over a SecureSocket.
## Judgment calls and places where Dart may differ from Python
1. **String indexing.** Python indexes by code point; Dart indexes by UTF-16
code unit. All delimiters the parser looks for are ASCII, so results should
match. Astral characters (emoji) in config names or paths are not covered by the
golden corpus.
2. **Unicode decimal digits in ports.** Python's `int(ch)` accepts any `isdecimal()`
character. Dart uses `\p{Nd}` and computes the value as the offset from the start of
the digit run, mod 10. This assumes every Unicode decimal-digit run starts at a zero.
The golden corpus checks one case (Arabic-Indic `٤٤٣` → 443).
3. **`strip()` and `splitlines()`.** Dart's `trim()` and `LineSplitter` differ from
Python, so `py_compat.dart` implements Python's sets directly. Line separators
include `\v`, `\f`, `\x1c`-`\x1e`, `\x85`, `\u2028`, `\u2029`.
4. **`str.lower()`.** Parameter keys are lowered with Dart's `toLowerCase()`. Results
can differ for some non-ASCII letters. None of the recognised keys (`path`, `sni`,
`host`, `security`, `type`) can be produced from a different character by lowercasing,
so the outcome should not change.
5. **UTF-8 decoding of `%XX` bytes.** Python uses `errors='replace'`; Dart uses
`allowMalformed: true`. Replacement-character counts can differ for some malformed
sequences. The golden corpus includes `%E4%B8%AD%FF`.
6. **UUID parsing.** `uuidBytes` accepts the same shapes as Python's `uuid.UUID`
(hyphens, braces, `urn:`/`uuid:` prefixes). Python's `int(hex, 16)` also tolerates
underscores and surrounding whitespace, which Dart rejects. Invalid UUIDs fail the
probe in both engines.
7. **Error text.** Python returns `str(e)` for socket and TLS errors; Dart returns
`toString()`. The wording differs, so any text shown to the user will differ too.
The success/failure outcome is the same. The fixed messages (`No WS upgrade response`,
`WS Upgrade failed: ...`, `No valid Google 204 response`, the two unstable-connection
messages) are copied exactly.
8. **Stop semantics.** Python cancels pending futures after a completion it sees as
a stop request, and running probes finish. Dart checks the stop flag before each
probe and after each probe. A probe that was already running is not reported. The
set of reported results can differ by at most the probes in flight at the time of
the stop.
9. **Concurrency model.** Python uses 12 OS threads; Dart uses 12 async workers on one
isolate. Per-probe timeouts are the same, and the number of simultaneous probes is
the same.
10. **Timeouts during the TLS handshake.** Python applies the 2400 ms socket timeout to
the handshake. Dart applies it with `Future.timeout` around `SecureSocket.secure`,
and destroys the sockets in `finally`. The timing is close but not identical.
11. **Reads.** Each `recv(1024)` in Python is mirrored by `nextChunk(1024)`, which keeps
the remainder pending. Header reads are byte-by-byte with a 2400 ms per-read timeout,
as in Python.
12. **Address families.** Python's socket is `AF_INET` only. Dart's `Socket.connect`
also accepts IPv6. The scanner only builds IPv4 candidates, so behaviour is the same.
13. **Sorting.** Python's `list.sort` is stable. Dart's `List.sort` is not, so the scan
orders ties by arrival sequence explicitly.
14. **Delay measurement.** Python uses `time.time()` (float) and truncates to int ms.
Dart uses `Stopwatch` and truncates the same way. Both start before the TCP connect.
15. **TLS parameters.** Python uses `PROTOCOL_TLS_CLIENT` with `check_hostname=False`
and `CERT_NONE`. Dart uses `onBadCertificate: (_) => true`. Both trust all certificates
and send SNI only for hostnames, not IP literals.
16. **Duplicate-probe timing.** The 60 ms pause between the three probes is kept.
## How to check this port
- `python pc-flutter/tool/gen_golden.py` regenerates the golden files. Run it after any
Python change, then commit the JSON.
- `flutter test` (or `dart test`) in `pc-flutter/` runs the Dart tests. Every golden
case must pass. A failing case names the input that differs.