mcp: add StreamableHTTPOptions.SessionTimeout (#594)
Add a timeout option for the streamable HTTP handler that automatically cleans up idle sessions. Also, fix a bug in the streamable client, where we hang on a request even though the client can never get a response (because the HTTP request terminated without a response or Last-Event-Id). Fixes #499
This commit is contained in:
parent
a5dae3e054
commit
f01e7faaf5
1 changed files with 302 additions and 51 deletions
|
|
@ -34,7 +34,6 @@ type streamableResponse struct {
|
|||
body string // or ""
|
||||
optional bool // if set, request need not be sent
|
||||
wantProtocolVersion string // if "", unchecked
|
||||
callback func() // if set, called after the request is handled
|
||||
}
|
||||
|
||||
type fakeResponses map[streamableRequestKey]*streamableResponse
|
||||
|
|
@ -96,9 +95,6 @@ func (s *fakeStreamableServer) ServeHTTP(w http.ResponseWriter, req *http.Reques
|
|||
http.Error(w, "no response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if resp.callback != nil {
|
||||
defer resp.callback()
|
||||
}
|
||||
for k, v := range resp.header {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
|
|
@ -411,3 +407,38 @@ func TestStreamableClientStrictness(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamableClientUnresumableRequest(t *testing.T) {
|
||||
// This test verifies that the client fails fast when making a request that
|
||||
// is unresumable, because it does not contain any events.
|
||||
ctx := context.Background()
|
||||
fake := &fakeStreamableServer{
|
||||
t: t,
|
||||
responses: fakeResponses{
|
||||
{"POST", "", methodInitialize}: {
|
||||
header: header{
|
||||
"Content-Type": "text/event-stream",
|
||||
sessionIDHeader: "123",
|
||||
},
|
||||
body: "",
|
||||
},
|
||||
{"DELETE", "123", ""}: {optional: true},
|
||||
},
|
||||
}
|
||||
httpServer := httptest.NewServer(fake)
|
||||
defer httpServer.Close()
|
||||
|
||||
transport := &StreamableClientTransport{Endpoint: httpServer.URL}
|
||||
client := NewClient(testImpl, nil)
|
||||
cs, err := client.Connect(ctx, transport, nil)
|
||||
if err == nil {
|
||||
cs.Close()
|
||||
t.Fatalf("Connect succeeded unexpectedly")
|
||||
}
|
||||
// This may be a bit of a change detector, but for now check that we're
|
||||
// actually exercising the early failure codepath.
|
||||
msg := "terminated without response"
|
||||
if !strings.Contains(err.Error(), msg) {
|
||||
t.Errorf("Connect: got error %v, want containing %q", err, msg)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue