golang.org/x/tools/gopls@v0.15.3/internal/protocol/protocol.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 protocol 6 7 import ( 8 "bytes" 9 "context" 10 "encoding/json" 11 "fmt" 12 "io" 13 14 "golang.org/x/tools/gopls/internal/telemetry" 15 "golang.org/x/tools/gopls/internal/util/bug" 16 "golang.org/x/tools/internal/event" 17 "golang.org/x/tools/internal/jsonrpc2" 18 jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" 19 "golang.org/x/tools/internal/xcontext" 20 ) 21 22 var ( 23 // RequestCancelledError should be used when a request is cancelled early. 24 RequestCancelledError = jsonrpc2.NewError(-32800, "JSON RPC cancelled") 25 RequestCancelledErrorV2 = jsonrpc2_v2.NewError(-32800, "JSON RPC cancelled") 26 ) 27 28 type ClientCloser interface { 29 Client 30 io.Closer 31 } 32 33 type connSender interface { 34 io.Closer 35 36 Notify(ctx context.Context, method string, params interface{}) error 37 Call(ctx context.Context, method string, params, result interface{}) error 38 } 39 40 type clientDispatcher struct { 41 sender connSender 42 } 43 44 func (c *clientDispatcher) Close() error { 45 return c.sender.Close() 46 } 47 48 // ClientDispatcher returns a Client that dispatches LSP requests across the 49 // given jsonrpc2 connection. 50 func ClientDispatcher(conn jsonrpc2.Conn) ClientCloser { 51 return &clientDispatcher{sender: clientConn{conn}} 52 } 53 54 type clientConn struct { 55 conn jsonrpc2.Conn 56 } 57 58 func (c clientConn) Close() error { 59 return c.conn.Close() 60 } 61 62 func (c clientConn) Notify(ctx context.Context, method string, params interface{}) error { 63 return c.conn.Notify(ctx, method, params) 64 } 65 66 func (c clientConn) Call(ctx context.Context, method string, params interface{}, result interface{}) error { 67 id, err := c.conn.Call(ctx, method, params, result) 68 if ctx.Err() != nil { 69 cancelCall(ctx, c, id) 70 } 71 return err 72 } 73 74 func ClientDispatcherV2(conn *jsonrpc2_v2.Connection) ClientCloser { 75 return &clientDispatcher{clientConnV2{conn}} 76 } 77 78 type clientConnV2 struct { 79 conn *jsonrpc2_v2.Connection 80 } 81 82 func (c clientConnV2) Close() error { 83 return c.conn.Close() 84 } 85 86 func (c clientConnV2) Notify(ctx context.Context, method string, params interface{}) error { 87 return c.conn.Notify(ctx, method, params) 88 } 89 90 func (c clientConnV2) Call(ctx context.Context, method string, params interface{}, result interface{}) error { 91 call := c.conn.Call(ctx, method, params) 92 err := call.Await(ctx, result) 93 if ctx.Err() != nil { 94 detached := xcontext.Detach(ctx) 95 c.conn.Notify(detached, "$/cancelRequest", &CancelParams{ID: call.ID().Raw()}) 96 } 97 return err 98 } 99 100 // ServerDispatcher returns a Server that dispatches LSP requests across the 101 // given jsonrpc2 connection. 102 func ServerDispatcher(conn jsonrpc2.Conn) Server { 103 return &serverDispatcher{sender: clientConn{conn}} 104 } 105 106 func ServerDispatcherV2(conn *jsonrpc2_v2.Connection) Server { 107 return &serverDispatcher{sender: clientConnV2{conn}} 108 } 109 110 type serverDispatcher struct { 111 sender connSender 112 } 113 114 func ClientHandler(client Client, handler jsonrpc2.Handler) jsonrpc2.Handler { 115 return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { 116 if ctx.Err() != nil { 117 ctx := xcontext.Detach(ctx) 118 return reply(ctx, nil, RequestCancelledError) 119 } 120 handled, err := clientDispatch(ctx, client, reply, req) 121 if handled || err != nil { 122 return err 123 } 124 return handler(ctx, reply, req) 125 } 126 } 127 128 func ClientHandlerV2(client Client) jsonrpc2_v2.Handler { 129 return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { 130 if ctx.Err() != nil { 131 return nil, RequestCancelledErrorV2 132 } 133 req1 := req2to1(req) 134 var ( 135 result interface{} 136 resErr error 137 ) 138 replier := func(_ context.Context, res interface{}, err error) error { 139 if err != nil { 140 resErr = err 141 return nil 142 } 143 result = res 144 return nil 145 } 146 _, err := clientDispatch(ctx, client, replier, req1) 147 if err != nil { 148 return nil, err 149 } 150 return result, resErr 151 }) 152 } 153 154 func ServerHandler(server Server, handler jsonrpc2.Handler) jsonrpc2.Handler { 155 return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { 156 if ctx.Err() != nil { 157 ctx := xcontext.Detach(ctx) 158 return reply(ctx, nil, RequestCancelledError) 159 } 160 handled, err := serverDispatch(ctx, server, reply, req) 161 if handled || err != nil { 162 return err 163 } 164 return handler(ctx, reply, req) 165 } 166 } 167 168 func ServerHandlerV2(server Server) jsonrpc2_v2.Handler { 169 return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { 170 if ctx.Err() != nil { 171 return nil, RequestCancelledErrorV2 172 } 173 req1 := req2to1(req) 174 var ( 175 result interface{} 176 resErr error 177 ) 178 replier := func(_ context.Context, res interface{}, err error) error { 179 if err != nil { 180 resErr = err 181 return nil 182 } 183 result = res 184 return nil 185 } 186 _, err := serverDispatch(ctx, server, replier, req1) 187 if err != nil { 188 return nil, err 189 } 190 return result, resErr 191 }) 192 } 193 194 func req2to1(req2 *jsonrpc2_v2.Request) jsonrpc2.Request { 195 if req2.ID.IsValid() { 196 raw := req2.ID.Raw() 197 var idv1 jsonrpc2.ID 198 switch v := raw.(type) { 199 case int64: 200 idv1 = jsonrpc2.NewIntID(v) 201 case string: 202 idv1 = jsonrpc2.NewStringID(v) 203 default: 204 panic(fmt.Sprintf("unsupported ID type %T", raw)) 205 } 206 req1, err := jsonrpc2.NewCall(idv1, req2.Method, req2.Params) 207 if err != nil { 208 panic(err) 209 } 210 return req1 211 } 212 req1, err := jsonrpc2.NewNotification(req2.Method, req2.Params) 213 if err != nil { 214 panic(err) 215 } 216 return req1 217 } 218 219 func Handlers(handler jsonrpc2.Handler) jsonrpc2.Handler { 220 return CancelHandler( 221 jsonrpc2.AsyncHandler( 222 jsonrpc2.MustReplyHandler(handler))) 223 } 224 225 func CancelHandler(handler jsonrpc2.Handler) jsonrpc2.Handler { 226 handler, canceller := jsonrpc2.CancelHandler(handler) 227 return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { 228 if req.Method() != "$/cancelRequest" { 229 // TODO(iancottrell): See if we can generate a reply for the request to be cancelled 230 // at the point of cancellation rather than waiting for gopls to naturally reply. 231 // To do that, we need to keep track of whether a reply has been sent already and 232 // be careful about racing between the two paths. 233 // TODO(iancottrell): Add a test that watches the stream and verifies the response 234 // for the cancelled request flows. 235 replyWithDetachedContext := func(ctx context.Context, resp interface{}, err error) error { 236 // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest 237 if ctx.Err() != nil && err == nil { 238 err = RequestCancelledError 239 } 240 ctx = xcontext.Detach(ctx) 241 return reply(ctx, resp, err) 242 } 243 return handler(ctx, replyWithDetachedContext, req) 244 } 245 var params CancelParams 246 if err := UnmarshalJSON(req.Params(), ¶ms); err != nil { 247 return sendParseError(ctx, reply, err) 248 } 249 if n, ok := params.ID.(float64); ok { 250 canceller(jsonrpc2.NewIntID(int64(n))) 251 } else if s, ok := params.ID.(string); ok { 252 canceller(jsonrpc2.NewStringID(s)) 253 } else { 254 return sendParseError(ctx, reply, fmt.Errorf("request ID %v malformed", params.ID)) 255 } 256 return reply(ctx, nil, nil) 257 } 258 } 259 260 func Call(ctx context.Context, conn jsonrpc2.Conn, method string, params interface{}, result interface{}) error { 261 id, err := conn.Call(ctx, method, params, result) 262 if ctx.Err() != nil { 263 cancelCall(ctx, clientConn{conn}, id) 264 } 265 return err 266 } 267 268 func cancelCall(ctx context.Context, sender connSender, id jsonrpc2.ID) { 269 ctx = xcontext.Detach(ctx) 270 ctx, done := event.Start(ctx, "protocol.canceller") 271 defer done() 272 // Note that only *jsonrpc2.ID implements json.Marshaler. 273 sender.Notify(ctx, "$/cancelRequest", &CancelParams{ID: &id}) 274 } 275 276 // UnmarshalJSON unmarshals msg into the variable pointed to by 277 // params. In JSONRPC, optional messages may be 278 // "null", in which case it is a no-op. 279 func UnmarshalJSON(msg json.RawMessage, v any) error { 280 if len(msg) == 0 || bytes.Equal(msg, []byte("null")) { 281 return nil 282 } 283 return json.Unmarshal(msg, v) 284 } 285 286 func sendParseError(ctx context.Context, reply jsonrpc2.Replier, err error) error { 287 return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err)) 288 } 289 290 // NonNilSlice returns x, or an empty slice if x was nil. 291 // 292 // (Many slice fields of protocol structs must be non-nil 293 // to avoid being encoded as JSON "null".) 294 func NonNilSlice[T comparable](x []T) []T { 295 if x == nil { 296 return []T{} 297 } 298 return x 299 } 300 301 func recoverHandlerPanic(method string) { 302 // Report panics in the handler goroutine, 303 // unless we have enabled the monitor, 304 // which reports all crashes. 305 if !telemetry.CrashMonitorSupported() { 306 defer func() { 307 if x := recover(); x != nil { 308 bug.Reportf("panic in %s request", method) 309 panic(x) 310 } 311 }() 312 } 313 }