Preserve authentication cancellation at the response limit
This commit is contained in:
@@ -3,6 +3,7 @@ package promptkit_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -13,6 +14,45 @@ import (
|
|||||||
"go.maximumdirect.net/promptkit"
|
"go.maximumdirect.net/promptkit"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type cancelingAuthenticationBody struct {
|
||||||
|
remaining int
|
||||||
|
cancel context.CancelFunc
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *cancelingAuthenticationBody) Read(p []byte) (int, error) {
|
||||||
|
n := min(len(p), b.remaining)
|
||||||
|
for i := range p[:n] {
|
||||||
|
p[i] = 'x'
|
||||||
|
}
|
||||||
|
b.remaining -= n
|
||||||
|
if b.remaining == 0 {
|
||||||
|
b.cancel()
|
||||||
|
return n, context.Canceled
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
func (b *cancelingAuthenticationBody) Close() error { b.closed = true; return nil }
|
||||||
|
|
||||||
|
func TestAuthenticationReadCancellationWinsOverOverflow(t *testing.T) {
|
||||||
|
for _, size := range []int{1024, (1 << 20) + 1} {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
body := &cancelingAuthenticationBody{remaining: size, cancel: cancel}
|
||||||
|
engine, err := promptkit.NewEngine(promptkit.Config{HTTPClient: &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{StatusCode: 200, Header: make(http.Header), Body: body}, nil
|
||||||
|
})}})
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
report, err := engine.SmokeTest(ctx, promptkit.SmokeTestRequest{Kind: promptkit.SmokeTestAuthentication, BackendID: "openrouter"})
|
||||||
|
cancel()
|
||||||
|
if !errors.Is(err, context.Canceled) || report == nil || report.Completed || report.Failure != promptkit.ExecutionFailureCanceled || report.Facts.CredentialsAccepted != nil || report.Facts.ResponseReceived == nil || !*report.Facts.ResponseReceived || !body.closed {
|
||||||
|
t.Fatalf("size %d: report=%+v error=%v closed=%v", size, report, err, body.closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBundledSmokeHTTPDefaults(t *testing.T) {
|
func TestBundledSmokeHTTPDefaults(t *testing.T) {
|
||||||
t.Setenv("OPENROUTER_API_KEY", "synthetic-key")
|
t.Setenv("OPENROUTER_API_KEY", "synthetic-key")
|
||||||
var urls []string
|
var urls []string
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ contracts.
|
|||||||
|
|
||||||
### 1. Preserve Caller Cancellation When Authentication Also Overflows [P2]
|
### 1. Preserve Caller Cancellation When Authentication Also Overflows [P2]
|
||||||
|
|
||||||
**Status:** Open. **Stages:** 7, 21–22.
|
**Status:** Complete. **Stages:** 7, 21–22.
|
||||||
|
|
||||||
|
Completion: interrupted body reads preserve caller cancellation before overflow
|
||||||
|
classification. Public below-limit and overflow-boundary regressions retain HTTP
|
||||||
|
facts, unknown acceptance, incomplete cancellation reports, and body closure.
|
||||||
|
|
||||||
**Locations:** [authentication adapter](../../internal/llm/openrouter_authentication.go),
|
**Locations:** [authentication adapter](../../internal/llm/openrouter_authentication.go),
|
||||||
`ProbeOpenRouterKey`, lines 61–67;
|
`ProbeOpenRouterKey`, lines 61–67;
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ func (c *OpenAICompatibleClient) ProbeOpenRouterKey(ctx context.Context, endpoin
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
contents, err := io.ReadAll(io.LimitReader(response.Body, maxAuthenticationResponseBytes+1))
|
contents, err := io.ReadAll(io.LimitReader(response.Body, maxAuthenticationResponseBytes+1))
|
||||||
|
if err != nil && ctx.Err() != nil {
|
||||||
|
return result, ctx.Err()
|
||||||
|
}
|
||||||
if len(contents) > maxAuthenticationResponseBytes {
|
if len(contents) > maxAuthenticationResponseBytes {
|
||||||
result.Overflow = true
|
result.Overflow = true
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|||||||
Reference in New Issue
Block a user