mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-22 06:06:59 +00:00
fix: v0.2 review sweep
Controller-ruled small fixes accumulated from T3/T4 reviews: 1. routes_media.go: multipart Content-Type check is now an exact mediaType != "multipart/form-data" comparison (HasPrefix admitted "multipart/form-datax"); a malformed-tail multipart body (fails to parse at all) now reports "malformed multipart body", distinct from "model is required" (a body that parses fine but carries no model part) and "duplicate model field". 2. provider_openai.go: audioTranscription's doc comment no longer claims the body is always "replayed unchanged"/the boundary always "verbatim" — it now reflects the rewrite path routes_media.go already takes for an aliased model id. 3. rewriteMultipartModel: buf.Grow(len(body)+512) up front, avoiding repeated reallocate-and-copy growth while rebuilding an aliased multipart body. 4. limits.go currentUsage: guards len(vals) == len(keys) before indexing storeGetMulti's result, so a non-conforming counterStore's short slice reports storeDown instead of panicking. 5. admin.go dashboard JS: the 5s poll no longer wipes an existing auth-error message when no key is stored (only a successful load or a new submission clears it now); clearStoredKey on a 401/403 now only fires when the failing request's own key still matches what's currently stored, guarding against a late response for a key the user has since replaced. 6. Spec §4's duplicated /admin/api/overview bullet and the nosniff/no-store sentence's placement were already fixed upstream (docs/superpowers/specs/2026-08-20-llmgateway-v02-design.md, commit c02010d) — verified identical, nothing further to commit here. 7. Added TestAdmin_JSONSecurityHeaders (nosniff/no-store/CSP on both admin JSON APIs) and TestHandleAudioTranscriptions_MalformedTail_400 (covers fix 1's new malformed-body branch). go test ./... -count=1 -race, gofmt, vet, make yaegi-check, golangci-lint (incl. gosec) all pass.
This commit is contained in:
@@ -552,6 +552,54 @@ func TestHandleAudioTranscriptions_ContentTypeNotFormData_400(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleAudioTranscriptions_MalformedTail_400 proves a syntactically
|
||||
// valid Content-Type and boundary, but a body whose closing boundary
|
||||
// marker is truncated, is reported as "malformed multipart body" — a
|
||||
// review-sweep fix (2026-08-20) distinct from "model is required": before
|
||||
// it, any error extractMultipartModel returned that was not
|
||||
// errDuplicateModelField collapsed into the same misleading
|
||||
// "model is required" 400, even though the body never got far enough to
|
||||
// evaluate whether a "model" part was present at all.
|
||||
func TestHandleAudioTranscriptions_MalformedTail_400(t *testing.T) {
|
||||
gw := newMediaTestGateway(t, newMediaTestConfig("http://127.0.0.1:1", "whisper-test"))
|
||||
|
||||
var buf bytes.Buffer
|
||||
mw := multipart.NewWriter(&buf)
|
||||
fw, err := mw.CreateFormField("model")
|
||||
if err != nil {
|
||||
t.Fatalf("CreateFormField(model): %v", err)
|
||||
}
|
||||
if _, err := fw.Write([]byte("whisper-test")); err != nil {
|
||||
t.Fatalf("write model field: %v", err)
|
||||
}
|
||||
if err := mw.Close(); err != nil {
|
||||
t.Fatalf("close multipart writer: %v", err)
|
||||
}
|
||||
contentType := mw.FormDataContentType()
|
||||
|
||||
// Cut the last 20 bytes off — well within the closing "--boundary--"
|
||||
// marker for Go's own (60-character) generated boundary — so reading
|
||||
// the "model" part's content runs past the truncated tail without ever
|
||||
// finding a delimiter, instead of cleanly reaching io.EOF.
|
||||
truncated := buf.Bytes()[:buf.Len()-20]
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, audioTranscriptionsPath, bytes.NewReader(truncated))
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
req.Header.Set("Authorization", "Bearer sk-alice")
|
||||
rec := httptest.NewRecorder()
|
||||
gw.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("status = %d, want 400, body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
if !strings.Contains(rec.Body.String(), "malformed multipart body") {
|
||||
t.Errorf("body = %q, want it to mention %q", rec.Body.String(), "malformed multipart body")
|
||||
}
|
||||
if strings.Contains(rec.Body.String(), "model is required") {
|
||||
t.Errorf("body = %q, must not report the misleading %q for a body that never parsed far enough to know", rec.Body.String(), "model is required")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleAudioTranscriptions_DuplicateModelField_400 proves a
|
||||
// multipart body carrying two "model" parts is rejected outright (400)
|
||||
// rather than silently resolving to whichever one extractMultipartModel
|
||||
|
||||
Reference in New Issue
Block a user