check / check (push) Failing after 2s
Every response carries X-Request-Id, the upstream fetch sends it, and the "upstream fetched", "image converted" and "image served" lines log it as request_id. pixa's own RequestID middleware keeps a request's own ID only when it is at most 64 letters, digits, '-', '_' or '.', and otherwise makes a random one with crypto/rand, so nothing a client chooses freely and nothing about the host reaches upstream. /v1/e/ now sets ETag, answers a matching If-None-Match with 304 and is routed for HEAD, through notModified, which both image handlers call. No Vary is added: go-chi/cors already sends Vary: Origin. Model: opus-5-5
119 lines
3.2 KiB
Go
119 lines
3.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
|
|
"sneak.berlin/go/pixa/internal/config"
|
|
)
|
|
|
|
// sendRequestID sends a request through the RequestID middleware, carrying
|
|
// incoming as its X-Request-Id unless that is empty. It returns the ID the
|
|
// next handler found in the request context, which the upstream fetch sends
|
|
// and the log lines carry, and the X-Request-Id of the response.
|
|
func sendRequestID(t *testing.T, incoming string) (string, string) {
|
|
t.Helper()
|
|
|
|
mw := &Middleware{log: slog.Default(), config: &config.Config{}}
|
|
|
|
var inContext string
|
|
|
|
handler := mw.RequestID()(http.HandlerFunc(
|
|
func(_ http.ResponseWriter, r *http.Request) {
|
|
inContext = middleware.GetReqID(r.Context())
|
|
}))
|
|
|
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
|
if incoming != "" {
|
|
req.Header.Set("X-Request-Id", incoming)
|
|
}
|
|
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
|
|
return inContext, rec.Header().Get("X-Request-Id")
|
|
}
|
|
|
|
// TestRequestIDKeepsShortPlainID verifies that a request's own X-Request-Id of
|
|
// at most 64 letters, digits, '-', '_' or '.' is kept as its ID.
|
|
func TestRequestIDKeepsShortPlainID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, incoming := range []string{
|
|
"client-request-id",
|
|
"A1_b2.c3-d4",
|
|
strings.Repeat("a", 64),
|
|
} {
|
|
inContext, inResponse := sendRequestID(t, incoming)
|
|
|
|
if inContext != incoming || inResponse != incoming {
|
|
t.Errorf("incoming %q: context has %q, response %q, want both %q",
|
|
incoming, inContext, inResponse, incoming)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestRequestIDReplacesLongOrUnusualID verifies that a request's own
|
|
// X-Request-Id that is over 64 characters or holds anything but letters,
|
|
// digits, '-', '_' or '.' is neither sent back nor sent upstream: the request
|
|
// gets a fresh ID instead.
|
|
func TestRequestIDReplacesLongOrUnusualID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, incoming := range []string{
|
|
strings.Repeat("a", 65),
|
|
strings.Repeat("a", 9000),
|
|
"has space",
|
|
"a/b",
|
|
"a,b",
|
|
"<script>",
|
|
"ünicode",
|
|
} {
|
|
inContext, inResponse := sendRequestID(t, incoming)
|
|
t.Logf("incoming %.20q: made up %q", incoming, inResponse)
|
|
|
|
if inResponse == "" || inResponse == incoming {
|
|
t.Errorf("incoming %.20q: response has %q, want a fresh ID",
|
|
incoming, inResponse)
|
|
}
|
|
|
|
if inContext != inResponse {
|
|
t.Errorf("incoming %.20q: context has %q, want the response's %q",
|
|
incoming, inContext, inResponse)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestRequestIDMadeUpTellsNothing verifies that the ID made up for a request
|
|
// that sent none differs for every request and does not hold the host name.
|
|
func TestRequestIDMadeUpTellsNothing(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
t.Fatalf("os.Hostname() error = %v", err)
|
|
}
|
|
|
|
firstInContext, first := sendRequestID(t, "")
|
|
_, second := sendRequestID(t, "")
|
|
t.Logf("host %q, made up %q and %q", hostname, first, second)
|
|
|
|
if first == "" || first == second {
|
|
t.Errorf("made up %q and %q, want two different IDs", first, second)
|
|
}
|
|
|
|
if firstInContext != first {
|
|
t.Errorf("context has %q, want the response's %q", firstInContext, first)
|
|
}
|
|
|
|
if strings.Contains(first, hostname) {
|
|
t.Errorf("made-up ID %q holds the host name %q", first, hostname)
|
|
}
|
|
}
|