golang.org/x/tools@v0.21.0/internal/jsonrpc2/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  	"sync"
    12  	"sync/atomic"
    13  
    14  	"golang.org/x/tools/internal/event"
    15  	"golang.org/x/tools/internal/event/label"
    16  )
    17  
    18  // Conn is the common interface to jsonrpc clients and servers.
    19  // Conn is bidirectional; it does not have a designated server or client end.
    20  // It manages the jsonrpc2 protocol, connecting responses back to their calls.
    21  type Conn interface {
    22  	// Call invokes the target method and waits for a response.
    23  	// The params will be marshaled to JSON before sending over the wire, and will
    24  	// be handed to the method invoked.
    25  	// The response will be unmarshaled from JSON into the result.
    26  	// The id returned will be unique from this connection, and can be used for
    27  	// logging or tracking.
    28  	Call(ctx context.Context, method string, params, result interface{}) (ID, error)
    29  
    30  	// Notify invokes the target method but does not wait for a response.
    31  	// The params will be marshaled to JSON before sending over the wire, and will
    32  	// be handed to the method invoked.
    33  	Notify(ctx context.Context, method string, params interface{}) error
    34  
    35  	// Go starts a goroutine to handle the connection.
    36  	// It must be called exactly once for each Conn.
    37  	// It returns immediately.
    38  	// You must block on Done() to wait for the connection to shut down.
    39  	// This is a temporary measure, this should be started automatically in the
    40  	// future.
    41  	Go(ctx context.Context, handler Handler)
    42  
    43  	// Close closes the connection and it's underlying stream.
    44  	// It does not wait for the close to complete, use the Done() channel for
    45  	// that.
    46  	Close() error
    47  
    48  	// Done returns a channel that will be closed when the processing goroutine
    49  	// has terminated, which will happen if Close() is called or an underlying
    50  	// stream is closed.
    51  	Done() <-chan struct{}
    52  
    53  	// Err returns an error if there was one from within the processing goroutine.
    54  	// If err returns non nil, the connection will be already closed or closing.
    55  	Err() error
    56  }
    57  
    58  type conn struct {
    59  	seq       int64      // must only be accessed using atomic operations
    60  	writeMu   sync.Mutex // protects writes to the stream
    61  	stream    Stream
    62  	pendingMu sync.Mutex // protects the pending map
    63  	pending   map[ID]chan *Response
    64  
    65  	done chan struct{}
    66  	err  atomic.Value
    67  }
    68  
    69  // NewConn creates a new connection object around the supplied stream.
    70  func NewConn(s Stream) Conn {
    71  	conn := &conn{
    72  		stream:  s,
    73  		pending: make(map[ID]chan *Response),
    74  		done:    make(chan struct{}),
    75  	}
    76  	return conn
    77  }
    78  
    79  func (c *conn) Notify(ctx context.Context, method string, params interface{}) (err error) {
    80  	notify, err := NewNotification(method, params)
    81  	if err != nil {
    82  		return fmt.Errorf("marshaling notify parameters: %v", err)
    83  	}
    84  	ctx, done := event.Start(ctx, method,
    85  		Method.Of(method),
    86  		RPCDirection.Of(Outbound),
    87  	)
    88  	defer func() {
    89  		recordStatus(ctx, err)
    90  		done()
    91  	}()
    92  
    93  	event.Metric(ctx, Started.Of(1))
    94  	n, err := c.write(ctx, notify)
    95  	event.Metric(ctx, SentBytes.Of(n))
    96  	return err
    97  }
    98  
    99  func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (_ ID, err error) {
   100  	// generate a new request identifier
   101  	id := ID{number: atomic.AddInt64(&c.seq, 1)}
   102  	call, err := NewCall(id, method, params)
   103  	if err != nil {
   104  		return id, fmt.Errorf("marshaling call parameters: %v", err)
   105  	}
   106  	ctx, done := event.Start(ctx, method,
   107  		Method.Of(method),
   108  		RPCDirection.Of(Outbound),
   109  		RPCID.Of(fmt.Sprintf("%q", id)),
   110  	)
   111  	defer func() {
   112  		recordStatus(ctx, err)
   113  		done()
   114  	}()
   115  	event.Metric(ctx, Started.Of(1))
   116  	// We have to add ourselves to the pending map before we send, otherwise we
   117  	// are racing the response. Also add a buffer to rchan, so that if we get a
   118  	// wire response between the time this call is cancelled and id is deleted
   119  	// from c.pending, the send to rchan will not block.
   120  	rchan := make(chan *Response, 1)
   121  	c.pendingMu.Lock()
   122  	c.pending[id] = rchan
   123  	c.pendingMu.Unlock()
   124  	defer func() {
   125  		c.pendingMu.Lock()
   126  		delete(c.pending, id)
   127  		c.pendingMu.Unlock()
   128  	}()
   129  	// now we are ready to send
   130  	n, err := c.write(ctx, call)
   131  	event.Metric(ctx, SentBytes.Of(n))
   132  	if err != nil {
   133  		// sending failed, we will never get a response, so don't leave it pending
   134  		return id, err
   135  	}
   136  	// now wait for the response
   137  	select {
   138  	case response := <-rchan:
   139  		// is it an error response?
   140  		if response.err != nil {
   141  			return id, response.err
   142  		}
   143  		if result == nil || len(response.result) == 0 {
   144  			return id, nil
   145  		}
   146  		if err := json.Unmarshal(response.result, result); err != nil {
   147  			return id, fmt.Errorf("unmarshaling result: %v", err)
   148  		}
   149  		return id, nil
   150  	case <-ctx.Done():
   151  		return id, ctx.Err()
   152  	}
   153  }
   154  
   155  func (c *conn) replier(req Request, spanDone func()) Replier {
   156  	return func(ctx context.Context, result interface{}, err error) error {
   157  		defer func() {
   158  			recordStatus(ctx, err)
   159  			spanDone()
   160  		}()
   161  		call, ok := req.(*Call)
   162  		if !ok {
   163  			// request was a notify, no need to respond
   164  			return nil
   165  		}
   166  		response, err := NewResponse(call.id, result, err)
   167  		if err != nil {
   168  			return err
   169  		}
   170  		n, err := c.write(ctx, response)
   171  		event.Metric(ctx, SentBytes.Of(n))
   172  		if err != nil {
   173  			// TODO(iancottrell): if a stream write fails, we really need to shut down
   174  			// the whole stream
   175  			return err
   176  		}
   177  		return nil
   178  	}
   179  }
   180  
   181  func (c *conn) write(ctx context.Context, msg Message) (int64, error) {
   182  	c.writeMu.Lock()
   183  	defer c.writeMu.Unlock()
   184  	return c.stream.Write(ctx, msg)
   185  }
   186  
   187  func (c *conn) Go(ctx context.Context, handler Handler) {
   188  	go c.run(ctx, handler)
   189  }
   190  
   191  func (c *conn) run(ctx context.Context, handler Handler) {
   192  	defer close(c.done)
   193  	for {
   194  		// get the next message
   195  		msg, n, err := c.stream.Read(ctx)
   196  		if err != nil {
   197  			// The stream failed, we cannot continue.
   198  			c.fail(err)
   199  			return
   200  		}
   201  		switch msg := msg.(type) {
   202  		case Request:
   203  			labels := []label.Label{
   204  				Method.Of(msg.Method()),
   205  				RPCDirection.Of(Inbound),
   206  				{}, // reserved for ID if present
   207  			}
   208  			if call, ok := msg.(*Call); ok {
   209  				labels[len(labels)-1] = RPCID.Of(fmt.Sprintf("%q", call.ID()))
   210  			} else {
   211  				labels = labels[:len(labels)-1]
   212  			}
   213  			reqCtx, spanDone := event.Start(ctx, msg.Method(), labels...)
   214  			event.Metric(reqCtx,
   215  				Started.Of(1),
   216  				ReceivedBytes.Of(n))
   217  			if err := handler(reqCtx, c.replier(msg, spanDone), msg); err != nil {
   218  				// delivery failed, not much we can do
   219  				event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
   220  			}
   221  		case *Response:
   222  			// If method is not set, this should be a response, in which case we must
   223  			// have an id to send the response back to the caller.
   224  			c.pendingMu.Lock()
   225  			rchan, ok := c.pending[msg.id]
   226  			c.pendingMu.Unlock()
   227  			if ok {
   228  				rchan <- msg
   229  			}
   230  		}
   231  	}
   232  }
   233  
   234  func (c *conn) Close() error {
   235  	return c.stream.Close()
   236  }
   237  
   238  func (c *conn) Done() <-chan struct{} {
   239  	return c.done
   240  }
   241  
   242  func (c *conn) Err() error {
   243  	if err := c.err.Load(); err != nil {
   244  		return err.(error)
   245  	}
   246  	return nil
   247  }
   248  
   249  // fail sets a failure condition on the stream and closes it.
   250  func (c *conn) fail(err error) {
   251  	c.err.Store(err)
   252  	c.stream.Close()
   253  }
   254  
   255  func recordStatus(ctx context.Context, err error) {
   256  	if err != nil {
   257  		event.Label(ctx, StatusCode.Of("ERROR"))
   258  	} else {
   259  		event.Label(ctx, StatusCode.Of("OK"))
   260  	}
   261  }