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 }