golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/jsonrpc2_v2/conn.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package jsonrpc2 6 7 import ( 8 "context" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "io" 13 "sync" 14 "sync/atomic" 15 "time" 16 17 "golang.org/x/tools/internal/event" 18 "golang.org/x/tools/internal/event/keys" 19 "golang.org/x/tools/internal/event/label" 20 "golang.org/x/tools/internal/jsonrpc2" 21 ) 22 23 // Binder builds a connection configuration. 24 // This may be used in servers to generate a new configuration per connection. 25 // ConnectionOptions itself implements Binder returning itself unmodified, to 26 // allow for the simple cases where no per connection information is needed. 27 type Binder interface { 28 // Bind returns the ConnectionOptions to use when establishing the passed-in 29 // Connection. 30 // 31 // The connection is not ready to use when Bind is called, 32 // but Bind may close it without reading or writing to it. 33 Bind(context.Context, *Connection) ConnectionOptions 34 } 35 36 // A BinderFunc implements the Binder interface for a standalone Bind function. 37 type BinderFunc func(context.Context, *Connection) ConnectionOptions 38 39 func (f BinderFunc) Bind(ctx context.Context, c *Connection) ConnectionOptions { 40 return f(ctx, c) 41 } 42 43 var _ Binder = BinderFunc(nil) 44 45 // ConnectionOptions holds the options for new connections. 46 type ConnectionOptions struct { 47 // Framer allows control over the message framing and encoding. 48 // If nil, HeaderFramer will be used. 49 Framer Framer 50 // Preempter allows registration of a pre-queue message handler. 51 // If nil, no messages will be preempted. 52 Preempter Preempter 53 // Handler is used as the queued message handler for inbound messages. 54 // If nil, all responses will be ErrNotHandled. 55 Handler Handler 56 // OnInternalError, if non-nil, is called with any internal errors that occur 57 // while serving the connection, such as protocol errors or invariant 58 // violations. (If nil, internal errors result in panics.) 59 OnInternalError func(error) 60 } 61 62 // Connection manages the jsonrpc2 protocol, connecting responses back to their 63 // calls. 64 // Connection is bidirectional; it does not have a designated server or client 65 // end. 66 type Connection struct { 67 seq int64 // must only be accessed using atomic operations 68 69 stateMu sync.Mutex 70 state inFlightState // accessed only in updateInFlight 71 done chan struct{} // closed (under stateMu) when state.closed is true and all goroutines have completed 72 73 writer chan Writer // 1-buffered; stores the writer when not in use 74 75 handler Handler 76 77 onInternalError func(error) 78 onDone func() 79 } 80 81 // inFlightState records the state of the incoming and outgoing calls on a 82 // Connection. 83 type inFlightState struct { 84 connClosing bool // true when the Connection's Close method has been called 85 reading bool // true while the readIncoming goroutine is running 86 readErr error // non-nil when the readIncoming goroutine exits (typically io.EOF) 87 writeErr error // non-nil if a call to the Writer has failed with a non-canceled Context 88 89 // closer shuts down and cleans up the Reader and Writer state, ideally 90 // interrupting any Read or Write call that is currently blocked. It is closed 91 // when the state is idle and one of: connClosing is true, readErr is non-nil, 92 // or writeErr is non-nil. 93 // 94 // After the closer has been invoked, the closer field is set to nil 95 // and the closeErr field is simultaneously set to its result. 96 closer io.Closer 97 closeErr error // error returned from closer.Close 98 99 outgoingCalls map[ID]*AsyncCall // calls only 100 outgoingNotifications int // # of notifications awaiting "write" 101 102 // incoming stores the total number of incoming calls and notifications 103 // that have not yet written or processed a result. 104 incoming int 105 106 incomingByID map[ID]*incomingRequest // calls only 107 108 // handlerQueue stores the backlog of calls and notifications that were not 109 // already handled by a preempter. 110 // The queue does not include the request currently being handled (if any). 111 handlerQueue []*incomingRequest 112 handlerRunning bool 113 } 114 115 // updateInFlight locks the state of the connection's in-flight requests, allows 116 // f to mutate that state, and closes the connection if it is idle and either 117 // is closing or has a read or write error. 118 func (c *Connection) updateInFlight(f func(*inFlightState)) { 119 c.stateMu.Lock() 120 defer c.stateMu.Unlock() 121 122 s := &c.state 123 124 f(s) 125 126 select { 127 case <-c.done: 128 // The connection was already completely done at the start of this call to 129 // updateInFlight, so it must remain so. (The call to f should have noticed 130 // that and avoided making any updates that would cause the state to be 131 // non-idle.) 132 if !s.idle() { 133 panic("jsonrpc2_v2: updateInFlight transitioned to non-idle when already done") 134 } 135 return 136 default: 137 } 138 139 if s.idle() && s.shuttingDown(ErrUnknown) != nil { 140 if s.closer != nil { 141 s.closeErr = s.closer.Close() 142 s.closer = nil // prevent duplicate Close calls 143 } 144 if s.reading { 145 // The readIncoming goroutine is still running. Our call to Close should 146 // cause it to exit soon, at which point it will make another call to 147 // updateInFlight, set s.reading to false, and mark the Connection done. 148 } else { 149 // The readIncoming goroutine has exited, or never started to begin with. 150 // Since everything else is idle, we're completely done. 151 if c.onDone != nil { 152 c.onDone() 153 } 154 close(c.done) 155 } 156 } 157 } 158 159 // idle reports whether the connection is in a state with no pending calls or 160 // notifications. 161 // 162 // If idle returns true, the readIncoming goroutine may still be running, 163 // but no other goroutines are doing work on behalf of the connection. 164 func (s *inFlightState) idle() bool { 165 return len(s.outgoingCalls) == 0 && s.outgoingNotifications == 0 && s.incoming == 0 && !s.handlerRunning 166 } 167 168 // shuttingDown reports whether the connection is in a state that should 169 // disallow new (incoming and outgoing) calls. It returns either nil or 170 // an error that is or wraps the provided errClosing. 171 func (s *inFlightState) shuttingDown(errClosing error) error { 172 if s.connClosing { 173 // If Close has been called explicitly, it doesn't matter what state the 174 // Reader and Writer are in: we shouldn't be starting new work because the 175 // caller told us not to start new work. 176 return errClosing 177 } 178 if s.readErr != nil { 179 // If the read side of the connection is broken, we cannot read new call 180 // requests, and cannot read responses to our outgoing calls. 181 return fmt.Errorf("%w: %v", errClosing, s.readErr) 182 } 183 if s.writeErr != nil { 184 // If the write side of the connection is broken, we cannot write responses 185 // for incoming calls, and cannot write requests for outgoing calls. 186 return fmt.Errorf("%w: %v", errClosing, s.writeErr) 187 } 188 return nil 189 } 190 191 // incomingRequest is used to track an incoming request as it is being handled 192 type incomingRequest struct { 193 *Request // the request being processed 194 ctx context.Context 195 cancel context.CancelFunc 196 endSpan func() // called (and set to nil) when the response is sent 197 } 198 199 // Bind returns the options unmodified. 200 func (o ConnectionOptions) Bind(context.Context, *Connection) ConnectionOptions { 201 return o 202 } 203 204 // newConnection creates a new connection and runs it. 205 // 206 // This is used by the Dial and Serve functions to build the actual connection. 207 // 208 // The connection is closed automatically (and its resources cleaned up) when 209 // the last request has completed after the underlying ReadWriteCloser breaks, 210 // but it may be stopped earlier by calling Close (for a clean shutdown). 211 func newConnection(bindCtx context.Context, rwc io.ReadWriteCloser, binder Binder, onDone func()) *Connection { 212 // TODO: Should we create a new event span here? 213 // This will propagate cancellation from ctx; should it? 214 ctx := notDone{bindCtx} 215 216 c := &Connection{ 217 state: inFlightState{closer: rwc}, 218 done: make(chan struct{}), 219 writer: make(chan Writer, 1), 220 onDone: onDone, 221 } 222 // It's tempting to set a finalizer on c to verify that the state has gone 223 // idle when the connection becomes unreachable. Unfortunately, the Binder 224 // interface makes that unsafe: it allows the Handler to close over the 225 // Connection, which could create a reference cycle that would cause the 226 // Connection to become uncollectable. 227 228 options := binder.Bind(bindCtx, c) 229 framer := options.Framer 230 if framer == nil { 231 framer = HeaderFramer() 232 } 233 c.handler = options.Handler 234 if c.handler == nil { 235 c.handler = defaultHandler{} 236 } 237 c.onInternalError = options.OnInternalError 238 239 c.writer <- framer.Writer(rwc) 240 reader := framer.Reader(rwc) 241 242 c.updateInFlight(func(s *inFlightState) { 243 select { 244 case <-c.done: 245 // Bind already closed the connection; don't start a goroutine to read it. 246 return 247 default: 248 } 249 250 // The goroutine started here will continue until the underlying stream is closed. 251 // 252 // (If the Binder closed the Connection already, this should error out and 253 // return almost immediately.) 254 s.reading = true 255 go c.readIncoming(ctx, reader, options.Preempter) 256 }) 257 return c 258 } 259 260 // Notify invokes the target method but does not wait for a response. 261 // The params will be marshaled to JSON before sending over the wire, and will 262 // be handed to the method invoked. 263 func (c *Connection) Notify(ctx context.Context, method string, params interface{}) (err error) { 264 ctx, done := event.Start(ctx, method, 265 jsonrpc2.Method.Of(method), 266 jsonrpc2.RPCDirection.Of(jsonrpc2.Outbound), 267 ) 268 attempted := false 269 270 defer func() { 271 labelStatus(ctx, err) 272 done() 273 if attempted { 274 c.updateInFlight(func(s *inFlightState) { 275 s.outgoingNotifications-- 276 }) 277 } 278 }() 279 280 c.updateInFlight(func(s *inFlightState) { 281 // If the connection is shutting down, allow outgoing notifications only if 282 // there is at least one call still in flight. The number of calls in flight 283 // cannot increase once shutdown begins, and allowing outgoing notifications 284 // may permit notifications that will cancel in-flight calls. 285 if len(s.outgoingCalls) == 0 && len(s.incomingByID) == 0 { 286 err = s.shuttingDown(ErrClientClosing) 287 if err != nil { 288 return 289 } 290 } 291 s.outgoingNotifications++ 292 attempted = true 293 }) 294 if err != nil { 295 return err 296 } 297 298 notify, err := NewNotification(method, params) 299 if err != nil { 300 return fmt.Errorf("marshaling notify parameters: %v", err) 301 } 302 303 event.Metric(ctx, jsonrpc2.Started.Of(1)) 304 return c.write(ctx, notify) 305 } 306 307 // Call invokes the target method and returns an object that can be used to await the response. 308 // The params will be marshaled to JSON before sending over the wire, and will 309 // be handed to the method invoked. 310 // You do not have to wait for the response, it can just be ignored if not needed. 311 // If sending the call failed, the response will be ready and have the error in it. 312 func (c *Connection) Call(ctx context.Context, method string, params interface{}) *AsyncCall { 313 // Generate a new request identifier. 314 id := Int64ID(atomic.AddInt64(&c.seq, 1)) 315 ctx, endSpan := event.Start(ctx, method, 316 jsonrpc2.Method.Of(method), 317 jsonrpc2.RPCDirection.Of(jsonrpc2.Outbound), 318 jsonrpc2.RPCID.Of(fmt.Sprintf("%q", id)), 319 ) 320 321 ac := &AsyncCall{ 322 id: id, 323 ready: make(chan struct{}), 324 ctx: ctx, 325 endSpan: endSpan, 326 } 327 // When this method returns, either ac is retired, or the request has been 328 // written successfully and the call is awaiting a response (to be provided by 329 // the readIncoming goroutine). 330 331 call, err := NewCall(ac.id, method, params) 332 if err != nil { 333 ac.retire(&Response{ID: id, Error: fmt.Errorf("marshaling call parameters: %w", err)}) 334 return ac 335 } 336 337 c.updateInFlight(func(s *inFlightState) { 338 err = s.shuttingDown(ErrClientClosing) 339 if err != nil { 340 return 341 } 342 if s.outgoingCalls == nil { 343 s.outgoingCalls = make(map[ID]*AsyncCall) 344 } 345 s.outgoingCalls[ac.id] = ac 346 }) 347 if err != nil { 348 ac.retire(&Response{ID: id, Error: err}) 349 return ac 350 } 351 352 event.Metric(ctx, jsonrpc2.Started.Of(1)) 353 if err := c.write(ctx, call); err != nil { 354 // Sending failed. We will never get a response, so deliver a fake one if it 355 // wasn't already retired by the connection breaking. 356 c.updateInFlight(func(s *inFlightState) { 357 if s.outgoingCalls[ac.id] == ac { 358 delete(s.outgoingCalls, ac.id) 359 ac.retire(&Response{ID: id, Error: err}) 360 } else { 361 // ac was already retired by the readIncoming goroutine: 362 // perhaps our write raced with the Read side of the connection breaking. 363 } 364 }) 365 } 366 return ac 367 } 368 369 type AsyncCall struct { 370 id ID 371 ready chan struct{} // closed after response has been set and span has been ended 372 response *Response 373 ctx context.Context // for event logging only 374 endSpan func() // close the tracing span when all processing for the message is complete 375 } 376 377 // ID used for this call. 378 // This can be used to cancel the call if needed. 379 func (ac *AsyncCall) ID() ID { return ac.id } 380 381 // IsReady can be used to check if the result is already prepared. 382 // This is guaranteed to return true on a result for which Await has already 383 // returned, or a call that failed to send in the first place. 384 func (ac *AsyncCall) IsReady() bool { 385 select { 386 case <-ac.ready: 387 return true 388 default: 389 return false 390 } 391 } 392 393 // retire processes the response to the call. 394 func (ac *AsyncCall) retire(response *Response) { 395 select { 396 case <-ac.ready: 397 panic(fmt.Sprintf("jsonrpc2: retire called twice for ID %v", ac.id)) 398 default: 399 } 400 401 ac.response = response 402 labelStatus(ac.ctx, response.Error) 403 ac.endSpan() 404 // Allow the trace context, which may retain a lot of reachable values, 405 // to be garbage-collected. 406 ac.ctx, ac.endSpan = nil, nil 407 408 close(ac.ready) 409 } 410 411 // Await waits for (and decodes) the results of a Call. 412 // The response will be unmarshaled from JSON into the result. 413 func (ac *AsyncCall) Await(ctx context.Context, result interface{}) error { 414 select { 415 case <-ctx.Done(): 416 return ctx.Err() 417 case <-ac.ready: 418 } 419 if ac.response.Error != nil { 420 return ac.response.Error 421 } 422 if result == nil { 423 return nil 424 } 425 return json.Unmarshal(ac.response.Result, result) 426 } 427 428 // Respond delivers a response to an incoming Call. 429 // 430 // Respond must be called exactly once for any message for which a handler 431 // returns ErrAsyncResponse. It must not be called for any other message. 432 func (c *Connection) Respond(id ID, result interface{}, err error) error { 433 var req *incomingRequest 434 c.updateInFlight(func(s *inFlightState) { 435 req = s.incomingByID[id] 436 }) 437 if req == nil { 438 return c.internalErrorf("Request not found for ID %v", id) 439 } 440 441 if err == ErrAsyncResponse { 442 // Respond is supposed to supply the asynchronous response, so it would be 443 // confusing to call Respond with an error that promises to call Respond 444 // again. 445 err = c.internalErrorf("Respond called with ErrAsyncResponse for %q", req.Method) 446 } 447 return c.processResult("Respond", req, result, err) 448 } 449 450 // Cancel cancels the Context passed to the Handle call for the inbound message 451 // with the given ID. 452 // 453 // Cancel will not complain if the ID is not a currently active message, and it 454 // will not cause any messages that have not arrived yet with that ID to be 455 // cancelled. 456 func (c *Connection) Cancel(id ID) { 457 var req *incomingRequest 458 c.updateInFlight(func(s *inFlightState) { 459 req = s.incomingByID[id] 460 }) 461 if req != nil { 462 req.cancel() 463 } 464 } 465 466 // Wait blocks until the connection is fully closed, but does not close it. 467 func (c *Connection) Wait() error { 468 var err error 469 <-c.done 470 c.updateInFlight(func(s *inFlightState) { 471 err = s.closeErr 472 }) 473 return err 474 } 475 476 // Close stops accepting new requests, waits for in-flight requests and enqueued 477 // Handle calls to complete, and then closes the underlying stream. 478 // 479 // After the start of a Close, notification requests (that lack IDs and do not 480 // receive responses) will continue to be passed to the Preempter, but calls 481 // with IDs will receive immediate responses with ErrServerClosing, and no new 482 // requests (not even notifications!) will be enqueued to the Handler. 483 func (c *Connection) Close() error { 484 // Stop handling new requests, and interrupt the reader (by closing the 485 // connection) as soon as the active requests finish. 486 c.updateInFlight(func(s *inFlightState) { s.connClosing = true }) 487 488 return c.Wait() 489 } 490 491 // readIncoming collects inbound messages from the reader and delivers them, either responding 492 // to outgoing calls or feeding requests to the queue. 493 func (c *Connection) readIncoming(ctx context.Context, reader Reader, preempter Preempter) { 494 var err error 495 for { 496 var ( 497 msg Message 498 n int64 499 ) 500 msg, n, err = reader.Read(ctx) 501 if err != nil { 502 break 503 } 504 505 switch msg := msg.(type) { 506 case *Request: 507 c.acceptRequest(ctx, msg, n, preempter) 508 509 case *Response: 510 c.updateInFlight(func(s *inFlightState) { 511 if ac, ok := s.outgoingCalls[msg.ID]; ok { 512 delete(s.outgoingCalls, msg.ID) 513 ac.retire(msg) 514 } else { 515 // TODO: How should we report unexpected responses? 516 } 517 }) 518 519 default: 520 c.internalErrorf("Read returned an unexpected message of type %T", msg) 521 } 522 } 523 524 c.updateInFlight(func(s *inFlightState) { 525 s.reading = false 526 s.readErr = err 527 528 // Retire any outgoing requests that were still in flight: with the Reader no 529 // longer being processed, they necessarily cannot receive a response. 530 for id, ac := range s.outgoingCalls { 531 ac.retire(&Response{ID: id, Error: err}) 532 } 533 s.outgoingCalls = nil 534 }) 535 } 536 537 // acceptRequest either handles msg synchronously or enqueues it to be handled 538 // asynchronously. 539 func (c *Connection) acceptRequest(ctx context.Context, msg *Request, msgBytes int64, preempter Preempter) { 540 // Add a span to the context for this request. 541 labels := append(make([]label.Label, 0, 3), // Make space for the ID if present. 542 jsonrpc2.Method.Of(msg.Method), 543 jsonrpc2.RPCDirection.Of(jsonrpc2.Inbound), 544 ) 545 if msg.IsCall() { 546 labels = append(labels, jsonrpc2.RPCID.Of(fmt.Sprintf("%q", msg.ID))) 547 } 548 ctx, endSpan := event.Start(ctx, msg.Method, labels...) 549 event.Metric(ctx, 550 jsonrpc2.Started.Of(1), 551 jsonrpc2.ReceivedBytes.Of(msgBytes)) 552 553 // In theory notifications cannot be cancelled, but we build them a cancel 554 // context anyway. 555 ctx, cancel := context.WithCancel(ctx) 556 req := &incomingRequest{ 557 Request: msg, 558 ctx: ctx, 559 cancel: cancel, 560 endSpan: endSpan, 561 } 562 563 // If the request is a call, add it to the incoming map so it can be 564 // cancelled (or responded) by ID. 565 var err error 566 c.updateInFlight(func(s *inFlightState) { 567 s.incoming++ 568 569 if req.IsCall() { 570 if s.incomingByID[req.ID] != nil { 571 err = fmt.Errorf("%w: request ID %v already in use", ErrInvalidRequest, req.ID) 572 req.ID = ID{} // Don't misattribute this error to the existing request. 573 return 574 } 575 576 if s.incomingByID == nil { 577 s.incomingByID = make(map[ID]*incomingRequest) 578 } 579 s.incomingByID[req.ID] = req 580 581 // When shutting down, reject all new Call requests, even if they could 582 // theoretically be handled by the preempter. The preempter could return 583 // ErrAsyncResponse, which would increase the amount of work in flight 584 // when we're trying to ensure that it strictly decreases. 585 err = s.shuttingDown(ErrServerClosing) 586 } 587 }) 588 if err != nil { 589 c.processResult("acceptRequest", req, nil, err) 590 return 591 } 592 593 if preempter != nil { 594 result, err := preempter.Preempt(req.ctx, req.Request) 595 596 if req.IsCall() && errors.Is(err, ErrAsyncResponse) { 597 // This request will remain in flight until Respond is called for it. 598 return 599 } 600 601 if !errors.Is(err, ErrNotHandled) { 602 c.processResult("Preempt", req, result, err) 603 return 604 } 605 } 606 607 c.updateInFlight(func(s *inFlightState) { 608 // If the connection is shutting down, don't enqueue anything to the 609 // handler — not even notifications. That ensures that if the handler 610 // continues to make progress, it will eventually become idle and 611 // close the connection. 612 err = s.shuttingDown(ErrServerClosing) 613 if err != nil { 614 return 615 } 616 617 // We enqueue requests that have not been preempted to an unbounded slice. 618 // Unfortunately, we cannot in general limit the size of the handler 619 // queue: we have to read every response that comes in on the wire 620 // (because it may be responding to a request issued by, say, an 621 // asynchronous handler), and in order to get to that response we have 622 // to read all of the requests that came in ahead of it. 623 s.handlerQueue = append(s.handlerQueue, req) 624 if !s.handlerRunning { 625 // We start the handleAsync goroutine when it has work to do, and let it 626 // exit when the queue empties. 627 // 628 // Otherwise, in order to synchronize the handler we would need some other 629 // goroutine (probably readIncoming?) to explicitly wait for handleAsync 630 // to finish, and that would complicate error reporting: either the error 631 // report from the goroutine would be blocked on the handler emptying its 632 // queue (which was tried, and introduced a deadlock detected by 633 // TestCloseCallRace), or the error would need to be reported separately 634 // from synchronizing completion. Allowing the handler goroutine to exit 635 // when idle seems simpler than trying to implement either of those 636 // alternatives correctly. 637 s.handlerRunning = true 638 go c.handleAsync() 639 } 640 }) 641 if err != nil { 642 c.processResult("acceptRequest", req, nil, err) 643 } 644 } 645 646 // handleAsync invokes the handler on the requests in the handler queue 647 // sequentially until the queue is empty. 648 func (c *Connection) handleAsync() { 649 for { 650 var req *incomingRequest 651 c.updateInFlight(func(s *inFlightState) { 652 if len(s.handlerQueue) > 0 { 653 req, s.handlerQueue = s.handlerQueue[0], s.handlerQueue[1:] 654 } else { 655 s.handlerRunning = false 656 } 657 }) 658 if req == nil { 659 return 660 } 661 662 // Only deliver to the Handler if not already canceled. 663 if err := req.ctx.Err(); err != nil { 664 c.updateInFlight(func(s *inFlightState) { 665 if s.writeErr != nil { 666 // Assume that req.ctx was canceled due to s.writeErr. 667 // TODO(#51365): use a Context API to plumb this through req.ctx. 668 err = fmt.Errorf("%w: %v", ErrServerClosing, s.writeErr) 669 } 670 }) 671 c.processResult("handleAsync", req, nil, err) 672 continue 673 } 674 675 result, err := c.handler.Handle(req.ctx, req.Request) 676 c.processResult(c.handler, req, result, err) 677 } 678 } 679 680 // processResult processes the result of a request and, if appropriate, sends a response. 681 func (c *Connection) processResult(from interface{}, req *incomingRequest, result interface{}, err error) error { 682 switch err { 683 case ErrAsyncResponse: 684 if !req.IsCall() { 685 return c.internalErrorf("%#v returned ErrAsyncResponse for a %q Request without an ID", from, req.Method) 686 } 687 return nil // This request is still in flight, so don't record the result yet. 688 case ErrNotHandled, ErrMethodNotFound: 689 // Add detail describing the unhandled method. 690 err = fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method) 691 } 692 693 if req.endSpan == nil { 694 return c.internalErrorf("%#v produced a duplicate %q Response", from, req.Method) 695 } 696 697 if result != nil && err != nil { 698 c.internalErrorf("%#v returned a non-nil result with a non-nil error for %s:\n%v\n%#v", from, req.Method, err, result) 699 result = nil // Discard the spurious result and respond with err. 700 } 701 702 if req.IsCall() { 703 if result == nil && err == nil { 704 err = c.internalErrorf("%#v returned a nil result and nil error for a %q Request that requires a Response", from, req.Method) 705 } 706 707 response, respErr := NewResponse(req.ID, result, err) 708 709 // The caller could theoretically reuse the request's ID as soon as we've 710 // sent the response, so ensure that it is removed from the incoming map 711 // before sending. 712 c.updateInFlight(func(s *inFlightState) { 713 delete(s.incomingByID, req.ID) 714 }) 715 if respErr == nil { 716 writeErr := c.write(notDone{req.ctx}, response) 717 if err == nil { 718 err = writeErr 719 } 720 } else { 721 err = c.internalErrorf("%#v returned a malformed result for %q: %w", from, req.Method, respErr) 722 } 723 } else { // req is a notification 724 if result != nil { 725 err = c.internalErrorf("%#v returned a non-nil result for a %q Request without an ID", from, req.Method) 726 } else if err != nil { 727 err = fmt.Errorf("%w: %q notification failed: %v", ErrInternal, req.Method, err) 728 } 729 if err != nil { 730 // TODO: can/should we do anything with this error beyond writing it to the event log? 731 // (Is this the right label to attach to the log?) 732 event.Label(req.ctx, keys.Err.Of(err)) 733 } 734 } 735 736 labelStatus(req.ctx, err) 737 738 // Cancel the request and finalize the event span to free any associated resources. 739 req.cancel() 740 req.endSpan() 741 req.endSpan = nil 742 c.updateInFlight(func(s *inFlightState) { 743 if s.incoming == 0 { 744 panic("jsonrpc2_v2: processResult called when incoming count is already zero") 745 } 746 s.incoming-- 747 }) 748 return nil 749 } 750 751 // write is used by all things that write outgoing messages, including replies. 752 // it makes sure that writes are atomic 753 func (c *Connection) write(ctx context.Context, msg Message) error { 754 writer := <-c.writer 755 defer func() { c.writer <- writer }() 756 n, err := writer.Write(ctx, msg) 757 event.Metric(ctx, jsonrpc2.SentBytes.Of(n)) 758 759 if err != nil && ctx.Err() == nil { 760 // The call to Write failed, and since ctx.Err() is nil we can't attribute 761 // the failure (even indirectly) to Context cancellation. The writer appears 762 // to be broken, and future writes are likely to also fail. 763 // 764 // If the read side of the connection is also broken, we might not even be 765 // able to receive cancellation notifications. Since we can't reliably write 766 // the results of incoming calls and can't receive explicit cancellations, 767 // cancel the calls now. 768 c.updateInFlight(func(s *inFlightState) { 769 if s.writeErr == nil { 770 s.writeErr = err 771 for _, r := range s.incomingByID { 772 r.cancel() 773 } 774 } 775 }) 776 } 777 778 return err 779 } 780 781 // internalErrorf reports an internal error. By default it panics, but if 782 // c.onInternalError is non-nil it instead calls that and returns an error 783 // wrapping ErrInternal. 784 func (c *Connection) internalErrorf(format string, args ...interface{}) error { 785 err := fmt.Errorf(format, args...) 786 if c.onInternalError == nil { 787 panic("jsonrpc2: " + err.Error()) 788 } 789 c.onInternalError(err) 790 791 return fmt.Errorf("%w: %v", ErrInternal, err) 792 } 793 794 // labelStatus labels the status of the event in ctx based on whether err is nil. 795 func labelStatus(ctx context.Context, err error) { 796 if err == nil { 797 event.Label(ctx, jsonrpc2.StatusCode.Of("OK")) 798 } else { 799 event.Label(ctx, jsonrpc2.StatusCode.Of("ERROR")) 800 } 801 } 802 803 // notDone is a context.Context wrapper that returns a nil Done channel. 804 type notDone struct{ ctx context.Context } 805 806 func (ic notDone) Value(key interface{}) interface{} { 807 return ic.ctx.Value(key) 808 } 809 810 func (notDone) Done() <-chan struct{} { return nil } 811 func (notDone) Err() error { return nil } 812 func (notDone) Deadline() (time.Time, bool) { return time.Time{}, false }