mirror of
https://github.com/cfal/shoes.git
synced 2026-11-06 16:14:11 +00:00
XHTTP required-header support still carried leftovers from its earlier connection-closing design. This removes `close_on_http1_error` entirely (configs that set it now fail to load), so header and path mismatches return the same plain 404 on every HTTP version, and makes every XHTTP error response a bare status with an empty body. `matching_headers` values can now be lists so secrets can rotate without downtime, and client `headers` now reject names that differ only in case and values with leading or trailing whitespace, neither of which could ever match. Redundant H2/H3 reset handling is removed while the ordering and H3 upload-reset handling that prevent session closures and held request slots are kept, documented and covered by a regression test; new interop tests cover Xray and Mihomo clients sending required headers, and the README explains that Xray clients using `extra` must put `headers` inside it.
170 lines
9.8 KiB
Markdown
170 lines
9.8 KiB
Markdown
# XHTTP Interoperability Tests
|
|
|
|
The `xhttp_interop` integration target runs real Shoes processes against Shoes,
|
|
Xray-core, and Mihomo Alpha. CI builds and caches these peers from pinned source:
|
|
|
|
- Xray-core: `1b1647cd9ddf0abc34b981d4611fc03bcac1ca74` (26.10.21).
|
|
- Mihomo Alpha: `94b13aed49b8dc41973e4bb3f721c5fa2c5c5c1f`.
|
|
|
|
Use Go 1.27.3, `GOMAXPROCS=1`, and `go build -p 1`. Build Xray's `./main` package.
|
|
Build Mihomo's root package with `CGO_ENABLED=0` and
|
|
`-ldflags '-X github.com/metacubex/mihomo/constant.Version=alpha-94b13aed'`.
|
|
The full peer setup is in `.github/workflows/test.yml`; the stable Mihomo peer
|
|
used by `optional_peer` is deliberately unchanged.
|
|
|
|
```sh
|
|
export SHOES_TEST_XRAY_BIN=/absolute/path/to/xray
|
|
export SHOES_TEST_MIHOMO_BIN=/absolute/path/to/mihomo
|
|
CARGO_BUILD_JOBS=1 cargo test --locked --features integration-tests \
|
|
--test xhttp_interop -- --test-threads=1
|
|
```
|
|
|
|
Missing or incorrectly versioned peers fail the tests rather than skipping them.
|
|
Use the `shoes_to_shoes`, `shoes_backpressure`, or `wire::` test filters to run
|
|
local-only coverage without external peers.
|
|
|
|
## Coverage
|
|
|
|
- VLESS in both external-peer directions: cleartext H1/H2C, TLS H1/H2/H3,
|
|
and REALITY/H2 where supported; all three explicit modes plus representative
|
|
`auto` cases. Xray also covers Trojan. Shoes covers default HTTP selection.
|
|
- Fixed 32 KiB packet limits and offset/session-dependent binary payloads force
|
|
fragmentation. Concurrent sessions, sequential reuse, and two UDP destinations
|
|
check integrity and routing, not just successful handshakes. Dedicated UDP
|
|
cases use 1 KiB POST limits and concurrent associations so 4 KiB datagrams cross
|
|
POST boundaries without exceeding the external peers' UDP buffer limits.
|
|
- H2/H3 packet-up transfers pause consumers with 16 MiB uploads and 64 MiB downloads,
|
|
for at least twice the measured unpaused transfer duration (minimum one second),
|
|
then resume and verify exact bytes and continued session progress. Shoes uses
|
|
256 KiB per-session and 1 MiB global byte budgets, with the default 30-packet
|
|
reorder window and unlimited per-session requests. Test-owned sending and
|
|
receiving sockets use 64 KiB buffers
|
|
to keep kernel autotuning from absorbing the entire paused transfer.
|
|
Downloads exceed the combined peer transport windows and intermediate TCP buffers;
|
|
a 16 MiB download can fit in those buffers without backpressuring the source.
|
|
Cancellation and connection churn use the same processes.
|
|
- Separate Xray cases use the default unlimited aggregate and per-session byte
|
|
and request budgets with 32 KiB POSTs and 30 ms pacing. H2 and H3 recover after
|
|
a ten-second destination pause while download heartbeats continue on that same
|
|
session. An additional H2 variant explicitly caps live packet requests at 128
|
|
to exercise refusal and Xray transport replay without closing the logical
|
|
session. The per-connection request limit remains 250 in these cases.
|
|
A separate H2 case uses a two-second packet inactivity
|
|
timeout to check permanent-stall containment and subsequent session availability.
|
|
- Mihomo server recovery is also checked with the default Shoes client byte limits.
|
|
The client sequence window prevents ACKs for later packets from advancing past
|
|
a stalled earlier packet and overflowing the peer's reorder queue.
|
|
H1's single-POST client path undergoes the same stall/resume checks against both peers.
|
|
- Raw H1 requests exercise reordering, matching/conflicting duplicates, exclusive
|
|
download ownership, wrong host/path, missing padding, oversized packets,
|
|
reorder-window rejection, and recovery after rejected requests.
|
|
- A single POST trickles at about 5 KiB/s for four seconds with a two-second
|
|
inactivity timeout. Stalled bodies must time out and release admission.
|
|
- Wrong UUIDs must fail without dialing the destination; matching controls pass.
|
|
Xray and Mihomo client `headers` satisfy Shoes `matching_headers` in all three modes
|
|
on H2 and in H1/H3 packet-up; missing or wrong values fail the same way.
|
|
Existing TLS/QUIC allowlist and REALITY ML-DSA verification cases remain enabled.
|
|
- SOCKS -> XHTTP and QUIC XHTTP -> VLESS chains carry TCP/UDP and fail closed when
|
|
a hop disappears.
|
|
|
|
## Boundaries
|
|
|
|
Mihomo has no XHTTP H3 listener or Trojan/XHTTP adapter. Its client supports H3;
|
|
its REALITY fixture permits classical X25519 as well as hybrid key exchange.
|
|
Mihomo uses a simple path because its normalization treats query syntax as path
|
|
text. Shoes/Xray retain the escaped-path/query interoperability fixture.
|
|
The XHTTP fixture disables Mihomo's loopback detector only in its peer subprocess.
|
|
The detector compares inbound source IP/port against outbound local IP/port without
|
|
considering the destination; independent connections in this all-loopback topology
|
|
can share that pair and trigger a false routing-loop rejection during churn.
|
|
Other Mihomo fixtures retain their existing environment and loop detection.
|
|
|
|
Xray client H2C is not included. Packet-up does not signal upload FIN. A completely
|
|
stalled session can retain shared-carrier flow-control credit, so the pressure
|
|
tests require recovery after consumption resumes, not independent progress while
|
|
the shared window is exhausted. Attached out-of-window POSTs wait for their
|
|
predecessors subject to packet inactivity. Normal pressure fixtures leave aggregate
|
|
and per-session request caps unlimited; client pipeline depth, transport flow
|
|
control and the 250-request per-connection limit still constrain concurrency.
|
|
The explicit 128-request Xray H2 case covers per-session refusal and recovery.
|
|
Deterministic GOAWAY, credit starvation, timeout races, and memory-release ordering
|
|
remain covered by the XHTTP library tests.
|
|
|
|
## Throughput And Stall Qualification
|
|
|
|
The ignored `packet_up_throughput_and_stall_qualification` test compares
|
|
Xray-to-Xray, Xray-to-Shoes and Shoes-to-Xray using 16 MiB in each direction per
|
|
session. A bounded TCP relay delays each byte batch by half the requested RTT
|
|
without serializing a whole RTT per batch. It models latency, not loss or congestion.
|
|
Linux `/proc` sampling records aggregate peer RSS; debug resource logs record
|
|
application byte and request-budget peaks. Neither measurement includes relay RSS
|
|
in the peer figure or equates application byte accounting with total RSS.
|
|
|
|
```sh
|
|
XHTTP_BENCH_RTT_MS=0,50,150,300 \
|
|
XHTTP_BENCH_PAUSE_SECS=0,5,10,30,60 \
|
|
XHTTP_BENCH_POST_BYTES=32768,1000000 \
|
|
XHTTP_BENCH_REQUEST_CAP=128,1024 \
|
|
XHTTP_BENCH_SESSIONS=1,4 \
|
|
CARGO_BUILD_JOBS=1 cargo test --locked --features integration-tests \
|
|
--test xhttp_interop packet_up_throughput_and_stall_qualification \
|
|
-- --ignored --nocapture --test-threads=1
|
|
```
|
|
|
|
Each variable accepts a comma-separated list. Defaults run RTT 0/150 ms, no stall,
|
|
32 KiB/1 MB packets, cap 128 and one session. `XHTTP_QUALIFICATION` JSON records
|
|
integrity success, elapsed time, aggregate duplex throughput and sampled peak RSS.
|
|
The server-request-cap override applies only to the Shoes server, not Xray.
|
|
Set `XHTTP_BENCH_H2_WINDOW_BYTES=262144` to compare the previous fixed H2 receive
|
|
windows with the new defaults without changing the binary.
|
|
`XHTTP_BENCH_DIRECTIONS` selects comma-separated `xray-xray`, `xray-shoes`, or
|
|
`shoes-xray` cases. Set `SHOES_TEST_SHOES_BIN` to a release binary for throughput
|
|
comparisons; debug builds are useful for correctness, not production rate claims.
|
|
This extended sweep is opt-in; normal CI runs the focused recovery/containment cases.
|
|
|
|
### Local Stall Qualification
|
|
|
|
On 2026-10-16, the then-pinned Xray 26.10.10 (`701af607`) client completed all six
|
|
16 MiB duplex cases against Shoes with 32 KiB POSTs, 30 ms pacing, no added RTT,
|
|
and destination pauses of 10/30/60 seconds. These correctness runs used a debug
|
|
Shoes binary; their rates are not production throughput measurements.
|
|
|
|
| Server request cap | Total duration, 10/30/60 s pause | Peak service handlers | Peak application bytes | Combined peer RSS range |
|
|
| --- | --- | --- | --- | --- |
|
|
| 128 | 16.2 / 47.8 / 83.1 s | 133 | 1.03 MiB | 89.4-103.8 MiB |
|
|
| 1024 | 16.3 / 31.0 / 61.1 s | 397 | 1.03 MiB | 93.4-117.0 MiB |
|
|
|
|
Service handlers include the GET and briefly rejected requests, not just admitted
|
|
packet requests. RSS includes both Shoes and Xray and excludes the test harness.
|
|
The then-default 128 cap survived every tested pause, but Xray's retry backoff delayed
|
|
completion after longer pauses. A larger cap reduced that delay while retaining
|
|
more requests. These samples qualify this workload, not arbitrary stalls, packet
|
|
sizes, transport-credit contention, or overload. These measurements predate the
|
|
unlimited admission defaults.
|
|
|
|
A release-build run with four concurrent sessions, 150 ms added RTT, 32 KiB POSTs
|
|
and a ten-second destination pause also passed all three directions. Total times
|
|
were 19.2 seconds for Xray-to-Xray, 19.2 seconds for Xray-to-Shoes and 25.0 seconds
|
|
for Shoes-to-Xray. Each session resumed with exact upload and download bytes.
|
|
|
|
### Local Throughput Qualification
|
|
|
|
Using the then-pinned Xray 26.10.10 (`701af607`), the release Shoes binary passed
|
|
all 24 combinations of 0/50/150/300 ms added RTT, 32 KiB/1,000,000-byte POST limits,
|
|
and the three peer directions. All used 30 ms
|
|
pacing and exact 16 MiB transfers each way; Shoes used its default client pipeline
|
|
depth of 30. Representative aggregate duplex rates for 1,000,000-byte POSTs:
|
|
|
|
| Direction | 150 ms RTT | 300 ms RTT |
|
|
| --- | --- | --- |
|
|
| Xray to Xray | 8.22 MiB/s | 4.84 MiB/s |
|
|
| Xray to Shoes | 14.31 MiB/s | 10.14 MiB/s |
|
|
| Shoes to Xray | 7.62 MiB/s | 4.62 MiB/s |
|
|
|
|
With 32 KiB POSTs, pacing dominated: all directions were within about 3% of the
|
|
reference across these RTTs. At 150 ms RTT, overriding only the Shoes H2 windows
|
|
to the former 256 KiB values reduced the 1,000,000-byte cases to 2.31 MiB/s inbound
|
|
and 2.28 MiB/s outbound. This supports larger bounded windows rather than an
|
|
unbounded request pipeline. These are single local samples, including session
|
|
setup and harness overhead, not link-capacity guarantees or statistical benchmarks.
|