github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/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  	"fmt"
    11  	"io"
    12  	"sync/atomic"
    13  
    14  	"github.com/jhump/golang-x-tools/internal/event"
    15  	"github.com/jhump/golang-x-tools/internal/event/label"
    16  	"github.com/jhump/golang-x-tools/internal/lsp/debug/tag"
    17  	errors "golang.org/x/xerrors"
    18  )
    19  
    20  // Binder builds a connection configuration.
    21  // This may be used in servers to generate a new configuration per connection.
    22  // ConnectionOptions itself implements Binder returning itself unmodified, to
    23  // allow for the simple cases where no per connection information is needed.
    24  type Binder interface {
    25  	// Bind is invoked when creating a new connection.
    26  	// The connection is not ready to use when Bind is called.
    27  	Bind(context.Context, *Connection) (ConnectionOptions, error)
    28  }
    29  
    30  // ConnectionOptions holds the options for new connections.
    31  type ConnectionOptions struct {
    32  	// Framer allows control over the message framing and encoding.
    33  	// If nil, HeaderFramer will be used.
    34  	Framer Framer
    35  	// Preempter allows registration of a pre-queue message handler.
    36  	// If nil, no messages will be preempted.
    37  	Preempter Preempter
    38  	// Handler is used as the queued message handler for inbound messages.
    39  	// If nil, all responses will be ErrNotHandled.
    40  	Handler Handler
    41  }
    42  
    43  // Connection manages the jsonrpc2 protocol, connecting responses back to their
    44  // calls.
    45  // Connection is bidirectional; it does not have a designated server or client
    46  // end.
    47  type Connection struct {
    48  	seq         int64 // must only be accessed using atomic operations
    49  	closer      io.Closer
    50  	writerBox   chan Writer
    51  	outgoingBox chan map[ID]chan<- *Response
    52  	incomingBox chan map[ID]*incoming
    53  	async       *async
    54  }
    55  
    56  type AsyncCall struct {
    57  	id        ID
    58  	response  chan *Response // the channel a response will be delivered on
    59  	resultBox chan asyncResult
    60  	endSpan   func() // close the tracing span when all processing for the message is complete
    61  }
    62  
    63  type asyncResult struct {
    64  	result []byte
    65  	err    error
    66  }
    67  
    68  // incoming is used to track an incoming request as it is being handled
    69  type incoming struct {
    70  	request   *Request        // the request being processed
    71  	baseCtx   context.Context // a base context for the message processing
    72  	done      func()          // a function called when all processing for the message is complete
    73  	handleCtx context.Context // the context for handling the message, child of baseCtx
    74  	cancel    func()          // a function that cancels the handling context
    75  }
    76  
    77  // Bind returns the options unmodified.
    78  func (o ConnectionOptions) Bind(context.Context, *Connection) (ConnectionOptions, error) {
    79  	return o, nil
    80  }
    81  
    82  // newConnection creates a new connection and runs it.
    83  // This is used by the Dial and Serve functions to build the actual connection.
    84  func newConnection(ctx context.Context, rwc io.ReadWriteCloser, binder Binder) (*Connection, error) {
    85  	c := &Connection{
    86  		closer:      rwc,
    87  		writerBox:   make(chan Writer, 1),
    88  		outgoingBox: make(chan map[ID]chan<- *Response, 1),
    89  		incomingBox: make(chan map[ID]*incoming, 1),
    90  		async:       newAsync(),
    91  	}
    92  
    93  	options, err := binder.Bind(ctx, c)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	if options.Framer == nil {
    98  		options.Framer = HeaderFramer()
    99  	}
   100  	if options.Preempter == nil {
   101  		options.Preempter = defaultHandler{}
   102  	}
   103  	if options.Handler == nil {
   104  		options.Handler = defaultHandler{}
   105  	}
   106  	c.outgoingBox <- make(map[ID]chan<- *Response)
   107  	c.incomingBox <- make(map[ID]*incoming)
   108  	// the goroutines started here will continue until the underlying stream is closed
   109  	reader := options.Framer.Reader(rwc)
   110  	readToQueue := make(chan *incoming)
   111  	queueToDeliver := make(chan *incoming)
   112  	go c.readIncoming(ctx, reader, readToQueue)
   113  	go c.manageQueue(ctx, options.Preempter, readToQueue, queueToDeliver)
   114  	go c.deliverMessages(ctx, options.Handler, queueToDeliver)
   115  
   116  	// releaseing the writer must be the last thing we do in case any requests
   117  	// are blocked waiting for the connection to be ready
   118  	c.writerBox <- options.Framer.Writer(rwc)
   119  	return c, nil
   120  }
   121  
   122  // Notify invokes the target method but does not wait for a response.
   123  // The params will be marshaled to JSON before sending over the wire, and will
   124  // be handed to the method invoked.
   125  func (c *Connection) Notify(ctx context.Context, method string, params interface{}) error {
   126  	notify, err := NewNotification(method, params)
   127  	if err != nil {
   128  		return errors.Errorf("marshaling notify parameters: %v", err)
   129  	}
   130  	ctx, done := event.Start(ctx, method,
   131  		tag.Method.Of(method),
   132  		tag.RPCDirection.Of(tag.Outbound),
   133  	)
   134  	event.Metric(ctx, tag.Started.Of(1))
   135  	err = c.write(ctx, notify)
   136  	switch {
   137  	case err != nil:
   138  		event.Label(ctx, tag.StatusCode.Of("ERROR"))
   139  	default:
   140  		event.Label(ctx, tag.StatusCode.Of("OK"))
   141  	}
   142  	done()
   143  	return err
   144  }
   145  
   146  // Call invokes the target method and returns an object that can be used to await the response.
   147  // The params will be marshaled to JSON before sending over the wire, and will
   148  // be handed to the method invoked.
   149  // You do not have to wait for the response, it can just be ignored if not needed.
   150  // If sending the call failed, the response will be ready and have the error in it.
   151  func (c *Connection) Call(ctx context.Context, method string, params interface{}) *AsyncCall {
   152  	result := &AsyncCall{
   153  		id:        Int64ID(atomic.AddInt64(&c.seq, 1)),
   154  		resultBox: make(chan asyncResult, 1),
   155  	}
   156  	// generate a new request identifier
   157  	call, err := NewCall(result.id, method, params)
   158  	if err != nil {
   159  		//set the result to failed
   160  		result.resultBox <- asyncResult{err: errors.Errorf("marshaling call parameters: %w", err)}
   161  		return result
   162  	}
   163  	ctx, endSpan := event.Start(ctx, method,
   164  		tag.Method.Of(method),
   165  		tag.RPCDirection.Of(tag.Outbound),
   166  		tag.RPCID.Of(fmt.Sprintf("%q", result.id)),
   167  	)
   168  	result.endSpan = endSpan
   169  	event.Metric(ctx, tag.Started.Of(1))
   170  	// We have to add ourselves to the pending map before we send, otherwise we
   171  	// are racing the response.
   172  	// rchan is buffered in case the response arrives without a listener.
   173  	result.response = make(chan *Response, 1)
   174  	pending := <-c.outgoingBox
   175  	pending[result.id] = result.response
   176  	c.outgoingBox <- pending
   177  	// now we are ready to send
   178  	if err := c.write(ctx, call); err != nil {
   179  		// sending failed, we will never get a response, so deliver a fake one
   180  		r, _ := NewResponse(result.id, nil, err)
   181  		c.incomingResponse(r)
   182  	}
   183  	return result
   184  }
   185  
   186  // ID used for this call.
   187  // This can be used to cancel the call if needed.
   188  func (a *AsyncCall) ID() ID { return a.id }
   189  
   190  // IsReady can be used to check if the result is already prepared.
   191  // This is guaranteed to return true on a result for which Await has already
   192  // returned, or a call that failed to send in the first place.
   193  func (a *AsyncCall) IsReady() bool {
   194  	select {
   195  	case r := <-a.resultBox:
   196  		a.resultBox <- r
   197  		return true
   198  	default:
   199  		return false
   200  	}
   201  }
   202  
   203  // Await the results of a Call.
   204  // The response will be unmarshaled from JSON into the result.
   205  func (a *AsyncCall) Await(ctx context.Context, result interface{}) error {
   206  	defer a.endSpan()
   207  	var r asyncResult
   208  	select {
   209  	case response := <-a.response:
   210  		// response just arrived, prepare the result
   211  		switch {
   212  		case response.Error != nil:
   213  			r.err = response.Error
   214  			event.Label(ctx, tag.StatusCode.Of("ERROR"))
   215  		default:
   216  			r.result = response.Result
   217  			event.Label(ctx, tag.StatusCode.Of("OK"))
   218  		}
   219  	case r = <-a.resultBox:
   220  		// result already available
   221  	case <-ctx.Done():
   222  		event.Label(ctx, tag.StatusCode.Of("CANCELLED"))
   223  		return ctx.Err()
   224  	}
   225  	// refill the box for the next caller
   226  	a.resultBox <- r
   227  	// and unpack the result
   228  	if r.err != nil {
   229  		return r.err
   230  	}
   231  	if result == nil || len(r.result) == 0 {
   232  		return nil
   233  	}
   234  	return json.Unmarshal(r.result, result)
   235  }
   236  
   237  // Respond deliverers a response to an incoming Call.
   238  // It is an error to not call this exactly once for any message for which a
   239  // handler has previously returned ErrAsyncResponse. It is also an error to
   240  // call this for any other message.
   241  func (c *Connection) Respond(id ID, result interface{}, rerr error) error {
   242  	pending := <-c.incomingBox
   243  	defer func() { c.incomingBox <- pending }()
   244  	entry, found := pending[id]
   245  	if !found {
   246  		return nil
   247  	}
   248  	delete(pending, id)
   249  	return c.respond(entry, result, rerr)
   250  }
   251  
   252  // Cancel is used to cancel an inbound message by ID, it does not cancel
   253  // outgoing messages.
   254  // This is only used inside a message handler that is layering a
   255  // cancellation protocol on top of JSON RPC 2.
   256  // It will not complain if the ID is not a currently active message, and it will
   257  // not cause any messages that have not arrived yet with that ID to be
   258  // cancelled.
   259  func (c *Connection) Cancel(id ID) {
   260  	pending := <-c.incomingBox
   261  	defer func() { c.incomingBox <- pending }()
   262  	if entry, found := pending[id]; found && entry.cancel != nil {
   263  		entry.cancel()
   264  		entry.cancel = nil
   265  	}
   266  }
   267  
   268  // Wait blocks until the connection is fully closed, but does not close it.
   269  func (c *Connection) Wait() error {
   270  	return c.async.wait()
   271  }
   272  
   273  // Close can be used to close the underlying stream, and then wait for the connection to
   274  // fully shut down.
   275  // This does not cancel in flight requests, but waits for them to gracefully complete.
   276  func (c *Connection) Close() error {
   277  	// close the underlying stream
   278  	if err := c.closer.Close(); err != nil && !isClosingError(err) {
   279  		return err
   280  	}
   281  	// and then wait for it to cause the connection to close
   282  	if err := c.Wait(); err != nil && !isClosingError(err) {
   283  		return err
   284  	}
   285  	return nil
   286  }
   287  
   288  // readIncoming collects inbound messages from the reader and delivers them, either responding
   289  // to outgoing calls or feeding requests to the queue.
   290  func (c *Connection) readIncoming(ctx context.Context, reader Reader, toQueue chan<- *incoming) {
   291  	defer close(toQueue)
   292  	for {
   293  		// get the next message
   294  		// no lock is needed, this is the only reader
   295  		msg, n, err := reader.Read(ctx)
   296  		if err != nil {
   297  			// The stream failed, we cannot continue
   298  			c.async.setError(err)
   299  			return
   300  		}
   301  		switch msg := msg.(type) {
   302  		case *Request:
   303  			entry := &incoming{
   304  				request: msg,
   305  			}
   306  			// add a span to the context for this request
   307  			labels := append(make([]label.Label, 0, 3), // make space for the id if present
   308  				tag.Method.Of(msg.Method),
   309  				tag.RPCDirection.Of(tag.Inbound),
   310  			)
   311  			if msg.IsCall() {
   312  				labels = append(labels, tag.RPCID.Of(fmt.Sprintf("%q", msg.ID)))
   313  			}
   314  			entry.baseCtx, entry.done = event.Start(ctx, msg.Method, labels...)
   315  			event.Metric(entry.baseCtx,
   316  				tag.Started.Of(1),
   317  				tag.ReceivedBytes.Of(n))
   318  			// in theory notifications cannot be cancelled, but we build them a cancel context anyway
   319  			entry.handleCtx, entry.cancel = context.WithCancel(entry.baseCtx)
   320  			// if the request is a call, add it to the incoming map so it can be
   321  			// cancelled by id
   322  			if msg.IsCall() {
   323  				pending := <-c.incomingBox
   324  				c.incomingBox <- pending
   325  				pending[msg.ID] = entry
   326  			}
   327  			// send the message to the incoming queue
   328  			toQueue <- entry
   329  		case *Response:
   330  			// If method is not set, this should be a response, in which case we must
   331  			// have an id to send the response back to the caller.
   332  			c.incomingResponse(msg)
   333  		}
   334  	}
   335  }
   336  
   337  func (c *Connection) incomingResponse(msg *Response) {
   338  	pending := <-c.outgoingBox
   339  	response, ok := pending[msg.ID]
   340  	if ok {
   341  		delete(pending, msg.ID)
   342  	}
   343  	c.outgoingBox <- pending
   344  	if response != nil {
   345  		response <- msg
   346  	}
   347  }
   348  
   349  // manageQueue reads incoming requests, attempts to proccess them with the preempter, or queue them
   350  // up for normal handling.
   351  func (c *Connection) manageQueue(ctx context.Context, preempter Preempter, fromRead <-chan *incoming, toDeliver chan<- *incoming) {
   352  	defer close(toDeliver)
   353  	q := []*incoming{}
   354  	ok := true
   355  	for {
   356  		var nextReq *incoming
   357  		if len(q) == 0 {
   358  			// no messages in the queue
   359  			// if we were closing, then we are done
   360  			if !ok {
   361  				return
   362  			}
   363  			// not closing, but nothing in the queue, so just block waiting for a read
   364  			nextReq, ok = <-fromRead
   365  		} else {
   366  			// we have a non empty queue, so pick whichever of reading or delivering
   367  			// that we can make progress on
   368  			select {
   369  			case nextReq, ok = <-fromRead:
   370  			case toDeliver <- q[0]:
   371  				//TODO: this causes a lot of shuffling, should we use a growing ring buffer? compaction?
   372  				q = q[1:]
   373  			}
   374  		}
   375  		if nextReq != nil {
   376  			// TODO: should we allow to limit the queue size?
   377  			var result interface{}
   378  			rerr := nextReq.handleCtx.Err()
   379  			if rerr == nil {
   380  				// only preempt if not already cancelled
   381  				result, rerr = preempter.Preempt(nextReq.handleCtx, nextReq.request)
   382  			}
   383  			switch {
   384  			case rerr == ErrNotHandled:
   385  				// message not handled, add it to the queue for the main handler
   386  				q = append(q, nextReq)
   387  			case rerr == ErrAsyncResponse:
   388  				// message handled but the response will come later
   389  			default:
   390  				// anything else means the message is fully handled
   391  				c.reply(nextReq, result, rerr)
   392  			}
   393  		}
   394  	}
   395  }
   396  
   397  func (c *Connection) deliverMessages(ctx context.Context, handler Handler, fromQueue <-chan *incoming) {
   398  	defer c.async.done()
   399  	for entry := range fromQueue {
   400  		// cancel any messages in the queue that we have a pending cancel for
   401  		var result interface{}
   402  		rerr := entry.handleCtx.Err()
   403  		if rerr == nil {
   404  			// only deliver if not already cancelled
   405  			result, rerr = handler.Handle(entry.handleCtx, entry.request)
   406  		}
   407  		switch {
   408  		case rerr == ErrNotHandled:
   409  			// message not handled, report it back to the caller as an error
   410  			c.reply(entry, nil, errors.Errorf("%w: %q", ErrMethodNotFound, entry.request.Method))
   411  		case rerr == ErrAsyncResponse:
   412  			// message handled but the response will come later
   413  		default:
   414  			c.reply(entry, result, rerr)
   415  		}
   416  	}
   417  }
   418  
   419  // reply is used to reply to an incoming request that has just been handled
   420  func (c *Connection) reply(entry *incoming, result interface{}, rerr error) {
   421  	if entry.request.IsCall() {
   422  		// we have a call finishing, remove it from the incoming map
   423  		pending := <-c.incomingBox
   424  		defer func() { c.incomingBox <- pending }()
   425  		delete(pending, entry.request.ID)
   426  	}
   427  	if err := c.respond(entry, result, rerr); err != nil {
   428  		// no way to propagate this error
   429  		//TODO: should we do more than just log it?
   430  		event.Error(entry.baseCtx, "jsonrpc2 message delivery failed", err)
   431  	}
   432  }
   433  
   434  // respond sends a response.
   435  // This is the code shared between reply and SendResponse.
   436  func (c *Connection) respond(entry *incoming, result interface{}, rerr error) error {
   437  	var err error
   438  	if entry.request.IsCall() {
   439  		// send the response
   440  		if result == nil && rerr == nil {
   441  			// call with no response, send an error anyway
   442  			rerr = errors.Errorf("%w: %q produced no response", ErrInternal, entry.request.Method)
   443  		}
   444  		var response *Response
   445  		response, err = NewResponse(entry.request.ID, result, rerr)
   446  		if err == nil {
   447  			// we write the response with the base context, in case the message was cancelled
   448  			err = c.write(entry.baseCtx, response)
   449  		}
   450  	} else {
   451  		switch {
   452  		case rerr != nil:
   453  			// notification failed
   454  			err = errors.Errorf("%w: %q notification failed: %v", ErrInternal, entry.request.Method, rerr)
   455  			rerr = nil
   456  		case result != nil:
   457  			//notification produced a response, which is an error
   458  			err = errors.Errorf("%w: %q produced unwanted response", ErrInternal, entry.request.Method)
   459  		default:
   460  			// normal notification finish
   461  		}
   462  	}
   463  	switch {
   464  	case rerr != nil || err != nil:
   465  		event.Label(entry.baseCtx, tag.StatusCode.Of("ERROR"))
   466  	default:
   467  		event.Label(entry.baseCtx, tag.StatusCode.Of("OK"))
   468  	}
   469  	// and just to be clean, invoke and clear the cancel if needed
   470  	if entry.cancel != nil {
   471  		entry.cancel()
   472  		entry.cancel = nil
   473  	}
   474  	// mark the entire request processing as done
   475  	entry.done()
   476  	return err
   477  }
   478  
   479  // write is used by all things that write outgoing messages, including replies.
   480  // it makes sure that writes are atomic
   481  func (c *Connection) write(ctx context.Context, msg Message) error {
   482  	writer := <-c.writerBox
   483  	defer func() { c.writerBox <- writer }()
   484  	n, err := writer.Write(ctx, msg)
   485  	event.Metric(ctx, tag.SentBytes.Of(n))
   486  	return err
   487  }