Files
VeillaxandClaude Sonnet 5.5 b53af0e196
CI / test (push) Successful in 2m32s
Add Spinner, a Minecraft hostname-routing reverse proxy
Routes Java Edition connections by the hostname in the handshake, with
wildcard domains, backend failover, PROXY protocol v2, cached server-list
status from health probes, hot-reloaded config, and an admin page and
metrics endpoint. Includes CI, tagged releases and a container image.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 19:40:40 -06:00

88 lines
2.2 KiB
Go

package protocol
import (
"bytes"
"net"
"testing"
"time"
)
func TestHandshakeRoundTrip(t *testing.T) {
in := BuildHandshake(767, "mc.example.com", 25565, 2)
h, raw, err := ReadHandshake(bytes.NewReader(in))
if err != nil {
t.Fatal(err)
}
want := Handshake{Protocol: 767, Host: "mc.example.com", Port: 25565, NextState: 2}
if h != want {
t.Fatalf("got %+v want %+v", h, want)
}
if !bytes.Equal(raw, in) {
t.Fatal("raw bytes not preserved")
}
}
func TestReadHandshakeRejects(t *testing.T) {
cases := map[string][]byte{
"legacy": {0xfe, 0x01, 0xfa},
"huge length": {0xff, 0xff, 0xff, 0xff, 0x07},
"zero length": {0x00},
"wrong id": Frame([]byte{0x05, 0x00}),
"short host": Frame([]byte{0x00, 0x01, 0x40, 'a'}),
"truncated": {0x10, 0x00},
}
for name, b := range cases {
if _, _, err := ReadHandshake(bytes.NewReader(b)); err == nil {
t.Errorf("%s: expected error", name)
}
}
if _, _, err := ReadHandshake(bytes.NewReader([]byte{0xfe, 0x01})); err != ErrLegacy {
t.Errorf("want ErrLegacy, got %v", err)
}
}
func TestVarInt(t *testing.T) {
if got := AppendVarInt(nil, 300); !bytes.Equal(got, []byte{0xac, 0x02}) {
t.Fatalf("got %x", got)
}
if got := AppendVarInt(nil, -1); len(got) != 5 {
t.Fatalf("negative should take 5 bytes, got %x", got)
}
}
func TestStatusExchange(t *testing.T) {
c, s := net.Pipe()
defer c.Close()
go func() {
defer s.Close()
ServeStatus(s, `{"ok":true}`)
}()
c.SetDeadline(time.Now().Add(2 * time.Second))
c.Write(Frame([]byte{0}))
id, payload, err := ReadPacket(c, maxStatus)
if err != nil || id != 0 || !bytes.Contains(payload, []byte(`{"ok":true}`)) {
t.Fatalf("id=%d payload=%q err=%v", id, payload, err)
}
c.Write(Frame(append([]byte{1}, 0, 0, 0, 0, 0, 0, 0, 42)))
id, payload, err = ReadPacket(c, 32)
if err != nil || id != 1 || payload[7] != 42 {
t.Fatalf("pong id=%d payload=%x err=%v", id, payload, err)
}
}
func TestQueryStatus(t *testing.T) {
c, s := net.Pipe()
defer c.Close()
go func() {
defer s.Close()
if _, _, err := ReadHandshake(s); err != nil {
return
}
ServeStatus(s, `{"players":{"online":3}}`)
}()
got, err := QueryStatus(c, "x", 25565, 2*time.Second)
if err != nil || got != `{"players":{"online":3}}` {
t.Fatalf("got %q err=%v", got, err)
}
}