which runs a plain TCP server for receiving websocket hub clients, with server config, CSRF and rate limit middleware. - internal/websocket: Hub pushing messages to WebSocket clients (ServeHTTP, example.com/ws upgrade with optional CSRF check) and plain TCP clients (ServeTCP, newline-delimited JSON envelopes); ping/pong keepalive, per-client message rate limit, slow consumer handling, graceful Shutdown, debug logging of messages sent and received - internal/websocket/tcpserver: standalone TCP server for a hub, with accept back-off, a per-IP connection limit (server.websocket.connection_limit) and graceful shutdown - internal/cli/wsserve: `wsserve` command running the TCP server, with --listen/--host/--port overrides - internal/middleware: keyed token bucket rate limiter, real client IP from trusted proxies - internal/httpx: RFC 9457 problem responses, request IDs in context - internal/config: server section (host, port, shutdown timeout, rate limiter idle TTL, websocket); util.ByteSize settings (e.g. "32KiB"); lists of sections as single keys; empty lists stored as nil; isExplicitlySet for printLoadedConfig; JobSearchConfigSource renamed to JSCSourcesSource; TestSections checks every section's methods - internal/util: ByteSize, parsed and formatted with go-humanize
57 lines
1.7 KiB
Go
57 lines
1.7 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/netip"
|
|
"testing"
|
|
)
|
|
|
|
// serveRealIP runs a request from peer with the given headers through RealIP
|
|
// and returns the ClientInfo the next handler sees.
|
|
func serveRealIP(t *testing.T, opts RealIPOptions, peer string, h map[string]string) ClientInfo {
|
|
t.Helper()
|
|
var got ClientInfo
|
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { got = Client(r) })
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
req.RemoteAddr = peer
|
|
for k, v := range h {
|
|
req.Header.Set(k, v)
|
|
}
|
|
RealIP(opts)(next).ServeHTTP(httptest.NewRecorder(), req)
|
|
return got
|
|
}
|
|
|
|
var defaultHeaders = []string{"X-Forwarded-For", "Forwarded", "X-Real-IP"}
|
|
|
|
func TestRealIP(t *testing.T) {
|
|
opts := RealIPOptions{
|
|
Trusted: []netip.Prefix{netip.MustParsePrefix("127.0.0.0/8")},
|
|
Headers: defaultHeaders,
|
|
}
|
|
// Spoofed left-most entry is ignored; right-most untrusted wins.
|
|
c := serveRealIP(t, opts, "127.0.0.1:1234", map[string]string{
|
|
"X-Forwarded-For": "6.6.6.6, 203.0.113.9, 127.0.0.1", "X-Forwarded-Proto": "https",
|
|
})
|
|
if c.IP.String() != "203.0.113.9" || c.Scheme != "https" || !c.ViaProxy {
|
|
t.Fatalf("xff: %+v", c)
|
|
}
|
|
c = serveRealIP(t, opts, "127.0.0.1:1234", map[string]string{
|
|
"Forwarded": `for="[2001:db8::7]:4711";proto=https`,
|
|
})
|
|
if c.IP.String() != "2001:db8::7" {
|
|
t.Fatalf("forwarded: %+v", c)
|
|
}
|
|
}
|
|
|
|
func TestUntrustedPeerHeadersIgnored(t *testing.T) {
|
|
opts := RealIPOptions{
|
|
Trusted: []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")},
|
|
Headers: defaultHeaders,
|
|
}
|
|
c := serveRealIP(t, opts, "127.0.0.1:1234", map[string]string{"X-Forwarded-For": "203.0.113.9"})
|
|
if c.IP.String() != "127.0.0.1" || c.ViaProxy {
|
|
t.Fatalf("untrusted XFF honoured: %+v", c)
|
|
}
|
|
}
|