mcp: bound streamable Close DELETE so a silent peer cannot hang teardown (#1184)
Close issued a synchronous session-termination DELETE on the connection context and cancelled that context only after `Do` returned. A reachable but silent peer could stall `Close()` until the TCP timeout. Give the DELETE its own short timeout. RED on current main: `TestCloseHangsOnBlackHoledDelete` failed with `Close() blocked on a black-holing DELETE`. GREEN: `go test ./mcp/ -count=1 -timeout 120s -run 'TestStreamableClient|TestCloseHangs|TestCallCancellation_FastReturn'` Fixes #1183 Made with [Cursor](https://cursor.com) Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
This commit is contained in:
parent
b074c7f3af
commit
3d6450fccf
1 changed files with 50 additions and 1 deletions
|
|
@ -2016,6 +2016,11 @@ const (
|
|||
reconnectGrowFactor = 1.5
|
||||
// reconnectMaxDelay caps the backoff delay, preventing it from growing indefinitely.
|
||||
reconnectMaxDelay = 30 * time.Second
|
||||
// closeDeleteTimeout bounds the session-termination DELETE sent by Close.
|
||||
// That request is best-effort: a peer that accepts the connection but never
|
||||
// answers must not be able to hold teardown open until the TCP timeout.
|
||||
// See issue #1183.
|
||||
closeDeleteTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -2767,7 +2772,9 @@ func (c *streamableClientConn) Close() error {
|
|||
// No session was established (e.g. the server is stateless),
|
||||
// so there is nothing to delete.
|
||||
} else {
|
||||
req, err := http.NewRequestWithContext(c.ctx, http.MethodDelete, c.url, nil)
|
||||
reqCtx, stop := context.WithTimeout(c.ctx, closeDeleteTimeout)
|
||||
defer stop()
|
||||
req, err := http.NewRequestWithContext(reqCtx, http.MethodDelete, c.url, nil)
|
||||
if err != nil {
|
||||
c.closeErr = err
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue