Skip to content

Commit e66f981

Browse files
fix(fake): prevent panic in StreamCopy when client disconnects
- Add recover() in goroutine to handle panics when ResponseWriter becomes invalid after client disconnect - Check context before starting new goroutines - Add type assertion with fallback for writers without ReaderFrom Assisted-By: cagent
1 parent 7a96bbb commit e66f981

File tree

1 file changed

+18
-1
lines changed

1 file changed

+18
-1
lines changed

pkg/fake/proxy.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,16 +395,33 @@ type streamReadResult struct {
395395
// It properly handles context cancellation during blocking reads.
396396
func StreamCopy(c echo.Context, resp *http.Response) error {
397397
ctx := c.Request().Context()
398-
writer := c.Response().Writer.(io.ReaderFrom)
398+
writer, ok := c.Response().Writer.(io.ReaderFrom)
399+
if !ok {
400+
// Fallback to io.Copy if writer doesn't implement ReaderFrom
401+
_, err := io.Copy(c.Response().Writer, resp.Body)
402+
return err
403+
}
399404

400405
// Use a channel to receive read results from a goroutine.
401406
// This allows us to properly select on context cancellation
402407
// even when the read is blocking.
403408
resultCh := make(chan streamReadResult, 1)
404409

405410
for {
411+
// Check context before starting new goroutine
412+
if ctx.Err() != nil {
413+
return nil
414+
}
415+
406416
// Start a goroutine to perform the blocking read
407417
go func() {
418+
defer func() {
419+
// Recover from panic if writer becomes invalid
420+
if r := recover(); r != nil {
421+
slog.Warn("StreamCopy recovered from panic", "panic", r)
422+
resultCh <- streamReadResult{n: 0, err: io.EOF}
423+
}
424+
}()
408425
n, err := writer.ReadFrom(io.LimitReader(resp.Body, 256))
409426
resultCh <- streamReadResult{n: n, err: err}
410427
}()

0 commit comments

Comments
 (0)