fix(nano): cap upstream response headers at 16 KiB
The hasJoined and name-lookup clients limited the body to 64 KiB but left headers at the transport default of 1 MiB. A configured root could answer with a megabyte of headers and stall the body, holding a few MiB of heap per in-flight login for the full five seconds; enough parallel logins take down the host, and every source's logins with it. Both clients now share a transport with MaxResponseHeaderBytes set to 16 KiB. Real roots come nowhere near it: Mojang's sessionserver sends 338 bytes of headers, LittleSkin 752, api.mojang.com 327. A source over the cap fails the request and the resolver moves on to the next one. The new subtest puts a source with 64 KiB of headers and a valid profile ahead of an honest one and expects the honest player. Without the cap the padded source wins.
This commit is contained in:
2 files changed
+36
-2
No files matched your search
@@ -247,6 +247,29 @@ func TestHasJoined(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// A root whose headers blow past the cap is dropped like any failed source, even when the
|
||||
// body behind them is a well-formed profile.
|
||||
t.Run("oversized response headers skip the source", func(t *testing.T) {
|
||||
stubMojangNames(t)
|
||||
bloated := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-Padding", strings.Repeat("a", 64<<10))
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": notchMojangID, "name": "Steve0"})
|
||||
}))
|
||||
t.Cleanup(bloated.Close)
|
||||
honest := fakeYgg(t, "0123456789abcdef0123456789abcdef", "Steve0")
|
||||
api := newTestAPI(newFakeRepo(), newFakeCluster())
|
||||
api.AuthSources = []AuthSource{
|
||||
{Tag: "evil", Prefix: "EV", URL: bloated.URL},
|
||||
{Tag: "littleskin", Prefix: "LS", URL: honest.URL},
|
||||
}
|
||||
|
||||
w := getHasJoined(api.InternalHandler(), "Steve0", "abc")
|
||||
want := undashed(uuid.NewMD5(felisAuthNS, []byte("littleskin:0123456789abcdef0123456789abcdef")))
|
||||
if w.Code != http.StatusOK || profileOf(t, w).ID != want {
|
||||
t.Fatalf("code = %d body = %q, want the next source's player", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
// The reused bar gate: a barred CANONICAL UUID is rejected at the resolver, so a
|
||||
// reclaimed squatter stays out even on a consumer with no limbo plugin. Keyed on the
|
||||
// dashed canonical (post-rewrite), the same form Repo.ReclaimUsername stores.
|
||||
|
||||
Reference in new issue
Block a user