summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2026-04-30 14:50:53 -0300
committerEuAndreh <eu@euandre.org>2026-04-30 14:50:53 -0300
commit543a43386b472049367ee81857209c13bc394e98 (patch)
tree9db2ee5ac825f9b715900dc09168d21f606b42c3 /tests
parentsrc/wscat.go: Make Start a duplex multi-message relay (diff)
downloadwscat-543a43386b472049367ee81857209c13bc394e98.tar.gz
wscat-543a43386b472049367ee81857209c13bc394e98.tar.xz
Forward PROXY v2 AUTHORITY end-to-end
wscat now sits transparently in the untls → wscat → papod path: on Accept, it parses any PROXY v2 header from upstream and stashes the AUTHORITY TLV on the wrapped conn; on the downstream dial to papod, it re-emits a PROXY v2 header with the same authority before forwarding any WebSocket payload bytes. A 5-second read deadline caps the parse window (so a slowloris peer can't pin the accept goroutine), and Accept loops past per-connection wrap errors — without that, an "nc -z" liveness probe (open + close, zero bytes) bubbles up as EOF to http.Server.Serve and panics the whole process. When no PROXY header is present (e.g. the integration test stack where binder speaks raw bytes) wscat skips the re-emit and lets papod fall back to PAPOD_NETWORK_NAME. Tests cover header build/parse roundtrip, the no-signature-pass- through case, and header byte layout.
Diffstat (limited to 'tests')
-rw-r--r--tests/wscat.go63
1 files changed, 63 insertions, 0 deletions
diff --git a/tests/wscat.go b/tests/wscat.go
index 25fce9e..f21c4b0 100644
--- a/tests/wscat.go
+++ b/tests/wscat.go
@@ -1733,6 +1733,66 @@ func TestParseExtensions(t *testing.T) {
}
}
+func TestProxyV2Roundtrip(t *testing.T) {
+ hdr := proxyV2Header("papo.example.com")
+ if len(hdr) != 35 {
+ t.Fatalf("header length = %d, want 35", len(hdr))
+ }
+ tail := []byte("GET / HTTP/1.1\r\nHost: x\r\n\r\n")
+ stream := append(hdr, tail...)
+
+ br := bufio.NewReader(bytes.NewReader(stream))
+ authority, err := parseProxyV2Authority(br)
+ if err != nil {
+ t.Fatalf("parse error: %v", err)
+ }
+ if authority != "papo.example.com" {
+ t.Errorf("authority = %q, want papo.example.com", authority)
+ }
+
+ rest, err := io.ReadAll(br)
+ if err != nil {
+ t.Fatalf("ReadAll: %v", err)
+ }
+ if !bytes.Equal(rest, tail) {
+ t.Errorf("rest = %q, want %q", rest, tail)
+ }
+}
+
+func TestProxyV2NoSignatureLeavesStream(t *testing.T) {
+ tail := []byte("GET / HTTP/1.1\r\n")
+ br := bufio.NewReader(bytes.NewReader(tail))
+ authority, err := parseProxyV2Authority(br)
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ if authority != "" {
+ t.Errorf("authority = %q, want empty", authority)
+ }
+ rest, _ := io.ReadAll(br)
+ if !bytes.Equal(rest, tail) {
+ t.Errorf("non-PROXY bytes consumed: rest = %q", rest)
+ }
+}
+
+func TestProxyV2HeaderShape(t *testing.T) {
+ hdr := proxyV2Header("x")
+ if hdr[12] != 0x21 {
+ t.Errorf("ver_cmd = %#x, want 0x21", hdr[12])
+ }
+ if hdr[13] != 0x00 {
+ t.Errorf("fam = %#x, want 0x00 (AF_UNSPEC)", hdr[13])
+ }
+ // AUTHORITY TLV at offset 16: type=0x02, length=0x0001, value="x"
+ if hdr[16] != _pp2TypeAuthority {
+ t.Errorf("tlv type = %#x, want %#x",
+ hdr[16], _pp2TypeAuthority)
+ }
+ if hdr[19] != 'x' {
+ t.Errorf("tlv value byte = %#x", hdr[19])
+ }
+}
+
func test_parseArgs() {
given := parseArgs([]string { "x", "y", "z" })
expected := _CLIArgs {
@@ -1788,6 +1848,9 @@ func MainTest() {
{ "TestTokenListContainsValue", TestTokenListContainsValue },
{ "TestIsValidChallengeKey", TestIsValidChallengeKey },
{ "TestParseExtensions", TestParseExtensions },
+ { "TestProxyV2Roundtrip", TestProxyV2Roundtrip },
+ { "TestProxyV2NoSignatureLeavesStream", TestProxyV2NoSignatureLeavesStream },
+ { "TestProxyV2HeaderShape", TestProxyV2HeaderShape },
}
benchmarks := []testing.InternalBenchmark {