Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions internal/jsonrpc2/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func (s *inFlightState) shuttingDown(errClosing error) error {
type incomingRequest struct {
*Request // the request being processed
ctx context.Context
cancel context.CancelFunc
cancel context.CancelCauseFunc
}

// Reader abstracts the transport mechanics from the JSON RPC protocol.
Expand Down Expand Up @@ -458,7 +458,7 @@ func (c *Connection) Cancel(id ID) {
req = s.incomingByID[id]
})
if req != nil {
req.cancel()
req.cancel(nil)
}
}

Expand Down Expand Up @@ -552,9 +552,11 @@ func (c *Connection) readIncoming(ctx context.Context, reader Reader, preempter
// Cancel any incoming requests still in flight: with the reader gone we
// cannot receive cancellation notifications, and likely cannot write a
// response either, so parked handlers have nothing useful left to do.
// Mirrors the equivalent cleanup on write failure.
// The read error (typically io.EOF from the peer disconnecting) is the
// cause, rather than a directional ErrServerClosing/ErrClientClosing
// sentinel: at this layer the connection has no designated end.
for _, r := range s.incomingByID {
r.cancel()
r.cancel(err)
}
Comment thread
aha-jansen marked this conversation as resolved.
})
}
Expand All @@ -564,7 +566,7 @@ func (c *Connection) readIncoming(ctx context.Context, reader Reader, preempter
func (c *Connection) acceptRequest(ctx context.Context, msg *Request, preempter Preempter) {
// In theory notifications cannot be cancelled, but we build them a cancel
// context anyway.
reqCtx, cancel := context.WithCancel(ctx)
reqCtx, cancel := context.WithCancelCause(ctx)
req := &incomingRequest{
Request: msg,
ctx: reqCtx,
Expand Down Expand Up @@ -665,15 +667,14 @@ func (c *Connection) handleAsync() {
return
}

// Only deliver to the Handler if not already canceled.
// Only deliver to the Handler if not already canceled. If the request
// was canceled with a cause (e.g. a write failure cancels every
// in-flight request), report that cause rather than the bare
// context.Canceled.
if err := req.ctx.Err(); err != nil {
c.updateInFlight(func(s *inFlightState) {
if s.writeErr != nil {
// Assume that req.ctx was canceled due to s.writeErr.
// TODO(#51365): use a Context API to plumb this through req.ctx.
Comment thread
guglielmo-san marked this conversation as resolved.
err = fmt.Errorf("%w: %v", ErrServerClosing, s.writeErr)
}
})
if cause := context.Cause(req.ctx); cause != nil {
err = cause
}
c.processResult("handleAsync", req, nil, err)
continue
}
Expand Down Expand Up @@ -734,7 +735,7 @@ func (c *Connection) processResult(from any, req *incomingRequest, result any, e
}

// Cancel the request to free any associated resources.
req.cancel()
req.cancel(nil)
c.updateInFlight(func(s *inFlightState) {
if s.incoming == 0 {
panic("jsonrpc2: processResult called when incoming count is already zero")
Expand Down Expand Up @@ -777,7 +778,7 @@ func (c *Connection) write(ctx context.Context, msg Message) error {
if s.writeErr == nil {
s.writeErr = err
for _, r := range s.incomingByID {
r.cancel()
r.cancel(fmt.Errorf("%w: %v", ErrServerClosing, err))
Comment thread
guglielmo-san marked this conversation as resolved.
}
}
})
Expand Down