.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / lsp / lsprpc / lsprpc.go
1 // Copyright 2020 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 lsprpc implements a jsonrpc2.StreamServer that may be used to
6 // serve the LSP on a jsonrpc2 channel.
7 package lsprpc
8
9 import (
10         "context"
11         "encoding/json"
12         "fmt"
13         "log"
14         "net"
15         "os"
16         "strconv"
17         "sync/atomic"
18         "time"
19
20         "golang.org/x/tools/internal/event"
21         "golang.org/x/tools/internal/gocommand"
22         "golang.org/x/tools/internal/jsonrpc2"
23         "golang.org/x/tools/internal/lsp"
24         "golang.org/x/tools/internal/lsp/cache"
25         "golang.org/x/tools/internal/lsp/debug"
26         "golang.org/x/tools/internal/lsp/debug/tag"
27         "golang.org/x/tools/internal/lsp/protocol"
28         errors "golang.org/x/xerrors"
29 )
30
31 // AutoNetwork is the pseudo network type used to signal that gopls should use
32 // automatic discovery to resolve a remote address.
33 const AutoNetwork = "auto"
34
35 // Unique identifiers for client/server.
36 var serverIndex int64
37
38 // The StreamServer type is a jsonrpc2.StreamServer that handles incoming
39 // streams as a new LSP session, using a shared cache.
40 type StreamServer struct {
41         cache *cache.Cache
42         // daemon controls whether or not to log new connections.
43         daemon bool
44
45         // serverForTest may be set to a test fake for testing.
46         serverForTest protocol.Server
47 }
48
49 // NewStreamServer creates a StreamServer using the shared cache. If
50 // withTelemetry is true, each session is instrumented with telemetry that
51 // records RPC statistics.
52 func NewStreamServer(cache *cache.Cache, daemon bool) *StreamServer {
53         return &StreamServer{cache: cache, daemon: daemon}
54 }
55
56 // ServeStream implements the jsonrpc2.StreamServer interface, by handling
57 // incoming streams using a new lsp server.
58 func (s *StreamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error {
59         client := protocol.ClientDispatcher(conn)
60         session := s.cache.NewSession(ctx)
61         server := s.serverForTest
62         if server == nil {
63                 server = lsp.NewServer(session, client)
64                 debug.GetInstance(ctx).AddService(server, session)
65         }
66         // Clients may or may not send a shutdown message. Make sure the server is
67         // shut down.
68         // TODO(rFindley): this shutdown should perhaps be on a disconnected context.
69         defer func() {
70                 if err := server.Shutdown(ctx); err != nil {
71                         event.Error(ctx, "error shutting down", err)
72                 }
73         }()
74         executable, err := os.Executable()
75         if err != nil {
76                 log.Printf("error getting gopls path: %v", err)
77                 executable = ""
78         }
79         ctx = protocol.WithClient(ctx, client)
80         conn.Go(ctx,
81                 protocol.Handlers(
82                         handshaker(session, executable, s.daemon,
83                                 protocol.ServerHandler(server,
84                                         jsonrpc2.MethodNotFound))))
85         if s.daemon {
86                 log.Printf("Session %s: connected", session.ID())
87                 defer log.Printf("Session %s: exited", session.ID())
88         }
89         <-conn.Done()
90         return conn.Err()
91 }
92
93 // A Forwarder is a jsonrpc2.StreamServer that handles an LSP stream by
94 // forwarding it to a remote. This is used when the gopls process started by
95 // the editor is in the `-remote` mode, which means it finds and connects to a
96 // separate gopls daemon. In these cases, we still want the forwarder gopls to
97 // be instrumented with telemetry, and want to be able to in some cases hijack
98 // the jsonrpc2 connection with the daemon.
99 type Forwarder struct {
100         network, addr string
101
102         // goplsPath is the path to the current executing gopls binary.
103         goplsPath string
104
105         // configuration for the auto-started gopls remote.
106         remoteConfig remoteConfig
107 }
108
109 type remoteConfig struct {
110         debug         string
111         listenTimeout time.Duration
112         logfile       string
113 }
114
115 // A RemoteOption configures the behavior of the auto-started remote.
116 type RemoteOption interface {
117         set(*remoteConfig)
118 }
119
120 // RemoteDebugAddress configures the address used by the auto-started Gopls daemon
121 // for serving debug information.
122 type RemoteDebugAddress string
123
124 func (d RemoteDebugAddress) set(cfg *remoteConfig) {
125         cfg.debug = string(d)
126 }
127
128 // RemoteListenTimeout configures the amount of time the auto-started gopls
129 // daemon will wait with no client connections before shutting down.
130 type RemoteListenTimeout time.Duration
131
132 func (d RemoteListenTimeout) set(cfg *remoteConfig) {
133         cfg.listenTimeout = time.Duration(d)
134 }
135
136 // RemoteLogfile configures the logfile location for the auto-started gopls
137 // daemon.
138 type RemoteLogfile string
139
140 func (l RemoteLogfile) set(cfg *remoteConfig) {
141         cfg.logfile = string(l)
142 }
143
144 func defaultRemoteConfig() remoteConfig {
145         return remoteConfig{
146                 listenTimeout: 1 * time.Minute,
147         }
148 }
149
150 // NewForwarder creates a new Forwarder, ready to forward connections to the
151 // remote server specified by network and addr.
152 func NewForwarder(network, addr string, opts ...RemoteOption) *Forwarder {
153         gp, err := os.Executable()
154         if err != nil {
155                 log.Printf("error getting gopls path for forwarder: %v", err)
156                 gp = ""
157         }
158
159         rcfg := defaultRemoteConfig()
160         for _, opt := range opts {
161                 opt.set(&rcfg)
162         }
163
164         fwd := &Forwarder{
165                 network:      network,
166                 addr:         addr,
167                 goplsPath:    gp,
168                 remoteConfig: rcfg,
169         }
170         return fwd
171 }
172
173 // QueryServerState queries the server state of the current server.
174 func QueryServerState(ctx context.Context, network, address string) (*ServerState, error) {
175         if network == AutoNetwork {
176                 gp, err := os.Executable()
177                 if err != nil {
178                         return nil, errors.Errorf("getting gopls path: %w", err)
179                 }
180                 network, address = autoNetworkAddress(gp, address)
181         }
182         netConn, err := net.DialTimeout(network, address, 5*time.Second)
183         if err != nil {
184                 return nil, errors.Errorf("dialing remote: %w", err)
185         }
186         serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn))
187         serverConn.Go(ctx, jsonrpc2.MethodNotFound)
188         var state ServerState
189         if err := protocol.Call(ctx, serverConn, sessionsMethod, nil, &state); err != nil {
190                 return nil, errors.Errorf("querying server state: %w", err)
191         }
192         return &state, nil
193 }
194
195 // ServeStream dials the forwarder remote and binds the remote to serve the LSP
196 // on the incoming stream.
197 func (f *Forwarder) ServeStream(ctx context.Context, clientConn jsonrpc2.Conn) error {
198         client := protocol.ClientDispatcher(clientConn)
199
200         netConn, err := f.connectToRemote(ctx)
201         if err != nil {
202                 return errors.Errorf("forwarder: connecting to remote: %w", err)
203         }
204         serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn))
205         server := protocol.ServerDispatcher(serverConn)
206
207         // Forward between connections.
208         serverConn.Go(ctx,
209                 protocol.Handlers(
210                         protocol.ClientHandler(client,
211                                 jsonrpc2.MethodNotFound)))
212         // Don't run the clientConn yet, so that we can complete the handshake before
213         // processing any client messages.
214
215         // Do a handshake with the server instance to exchange debug information.
216         index := atomic.AddInt64(&serverIndex, 1)
217         serverID := strconv.FormatInt(index, 10)
218         var (
219                 hreq = handshakeRequest{
220                         ServerID:  serverID,
221                         GoplsPath: f.goplsPath,
222                 }
223                 hresp handshakeResponse
224         )
225         if di := debug.GetInstance(ctx); di != nil {
226                 hreq.Logfile = di.Logfile
227                 hreq.DebugAddr = di.ListenedDebugAddress
228         }
229         if err := protocol.Call(ctx, serverConn, handshakeMethod, hreq, &hresp); err != nil {
230                 // TODO(rfindley): at some point in the future we should return an error
231                 // here.  Handshakes have become functional in nature.
232                 event.Error(ctx, "forwarder: gopls handshake failed", err)
233         }
234         if hresp.GoplsPath != f.goplsPath {
235                 event.Error(ctx, "", fmt.Errorf("forwarder: gopls path mismatch: forwarder is %q, remote is %q", f.goplsPath, hresp.GoplsPath))
236         }
237         event.Log(ctx, "New server",
238                 tag.NewServer.Of(serverID),
239                 tag.Logfile.Of(hresp.Logfile),
240                 tag.DebugAddress.Of(hresp.DebugAddr),
241                 tag.GoplsPath.Of(hresp.GoplsPath),
242                 tag.ClientID.Of(hresp.SessionID),
243         )
244         clientConn.Go(ctx,
245                 protocol.Handlers(
246                         forwarderHandler(
247                                 protocol.ServerHandler(server,
248                                         jsonrpc2.MethodNotFound))))
249
250         select {
251         case <-serverConn.Done():
252                 clientConn.Close()
253         case <-clientConn.Done():
254                 serverConn.Close()
255         }
256
257         err = nil
258         if serverConn.Err() != nil {
259                 err = errors.Errorf("remote disconnected: %v", err)
260         } else if clientConn.Err() != nil {
261                 err = errors.Errorf("client disconnected: %v", err)
262         }
263         event.Log(ctx, fmt.Sprintf("forwarder: exited with error: %v", err))
264         return err
265 }
266
267 func (f *Forwarder) connectToRemote(ctx context.Context) (net.Conn, error) {
268         return connectToRemote(ctx, f.network, f.addr, f.goplsPath, f.remoteConfig)
269 }
270
271 func ConnectToRemote(ctx context.Context, network, addr string, opts ...RemoteOption) (net.Conn, error) {
272         rcfg := defaultRemoteConfig()
273         for _, opt := range opts {
274                 opt.set(&rcfg)
275         }
276         // This is not strictly necessary, as it won't be used if not connecting to
277         // the 'auto' remote.
278         goplsPath, err := os.Executable()
279         if err != nil {
280                 return nil, fmt.Errorf("unable to resolve gopls path: %v", err)
281         }
282         return connectToRemote(ctx, network, addr, goplsPath, rcfg)
283 }
284
285 func connectToRemote(ctx context.Context, inNetwork, inAddr, goplsPath string, rcfg remoteConfig) (net.Conn, error) {
286         var (
287                 netConn          net.Conn
288                 err              error
289                 network, address = inNetwork, inAddr
290         )
291         if inNetwork == AutoNetwork {
292                 // f.network is overloaded to support a concept of 'automatic' addresses,
293                 // which signals that the gopls remote address should be automatically
294                 // derived.
295                 // So we need to resolve a real network and address here.
296                 network, address = autoNetworkAddress(goplsPath, inAddr)
297         }
298         // Attempt to verify that we own the remote. This is imperfect, but if we can
299         // determine that the remote is owned by a different user, we should fail.
300         ok, err := verifyRemoteOwnership(network, address)
301         if err != nil {
302                 // If the ownership check itself failed, we fail open but log an error to
303                 // the user.
304                 event.Error(ctx, "unable to check daemon socket owner, failing open", err)
305         } else if !ok {
306                 // We successfully checked that the socket is not owned by us, we fail
307                 // closed.
308                 return nil, fmt.Errorf("socket %q is owned by a different user", address)
309         }
310         const dialTimeout = 1 * time.Second
311         // Try dialing our remote once, in case it is already running.
312         netConn, err = net.DialTimeout(network, address, dialTimeout)
313         if err == nil {
314                 return netConn, nil
315         }
316         // If our remote is on the 'auto' network, start it if it doesn't exist.
317         if inNetwork == AutoNetwork {
318                 if goplsPath == "" {
319                         return nil, fmt.Errorf("cannot auto-start remote: gopls path is unknown")
320                 }
321                 if network == "unix" {
322                         // Sometimes the socketfile isn't properly cleaned up when gopls shuts
323                         // down. Since we have already tried and failed to dial this address, it
324                         // should *usually* be safe to remove the socket before binding to the
325                         // address.
326                         // TODO(rfindley): there is probably a race here if multiple gopls
327                         // instances are simultaneously starting up.
328                         if _, err := os.Stat(address); err == nil {
329                                 if err := os.Remove(address); err != nil {
330                                         return nil, errors.Errorf("removing remote socket file: %w", err)
331                                 }
332                         }
333                 }
334                 args := []string{"serve",
335                         "-listen", fmt.Sprintf(`%s;%s`, network, address),
336                         "-listen.timeout", rcfg.listenTimeout.String(),
337                 }
338                 if rcfg.logfile != "" {
339                         args = append(args, "-logfile", rcfg.logfile)
340                 }
341                 if rcfg.debug != "" {
342                         args = append(args, "-debug", rcfg.debug)
343                 }
344                 if err := startRemote(goplsPath, args...); err != nil {
345                         return nil, errors.Errorf("startRemote(%q, %v): %w", goplsPath, args, err)
346                 }
347         }
348
349         const retries = 5
350         // It can take some time for the newly started server to bind to our address,
351         // so we retry for a bit.
352         for retry := 0; retry < retries; retry++ {
353                 startDial := time.Now()
354                 netConn, err = net.DialTimeout(network, address, dialTimeout)
355                 if err == nil {
356                         return netConn, nil
357                 }
358                 event.Log(ctx, fmt.Sprintf("failed attempt #%d to connect to remote: %v\n", retry+2, err))
359                 // In case our failure was a fast-failure, ensure we wait at least
360                 // f.dialTimeout before trying again.
361                 if retry != retries-1 {
362                         time.Sleep(dialTimeout - time.Since(startDial))
363                 }
364         }
365         return nil, errors.Errorf("dialing remote: %w", err)
366 }
367
368 // forwarderHandler intercepts 'exit' messages to prevent the shared gopls
369 // instance from exiting. In the future it may also intercept 'shutdown' to
370 // provide more graceful shutdown of the client connection.
371 func forwarderHandler(handler jsonrpc2.Handler) jsonrpc2.Handler {
372         return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
373                 // The gopls workspace environment defaults to the process environment in
374                 // which gopls daemon was started. To avoid discrepancies in Go environment
375                 // between the editor and daemon, inject any unset variables in `go env`
376                 // into the options sent by initialize.
377                 //
378                 // See also golang.org/issue/37830.
379                 if r.Method() == "initialize" {
380                         if newr, err := addGoEnvToInitializeRequest(ctx, r); err == nil {
381                                 r = newr
382                         } else {
383                                 log.Printf("unable to add local env to initialize request: %v", err)
384                         }
385                 }
386                 return handler(ctx, reply, r)
387         }
388 }
389
390 // addGoEnvToInitializeRequest builds a new initialize request in which we set
391 // any environment variables output by `go env` and not already present in the
392 // request.
393 //
394 // It returns an error if r is not an initialize requst, or is otherwise
395 // malformed.
396 func addGoEnvToInitializeRequest(ctx context.Context, r jsonrpc2.Request) (jsonrpc2.Request, error) {
397         var params protocol.ParamInitialize
398         if err := json.Unmarshal(r.Params(), &params); err != nil {
399                 return nil, err
400         }
401         var opts map[string]interface{}
402         switch v := params.InitializationOptions.(type) {
403         case nil:
404                 opts = make(map[string]interface{})
405         case map[string]interface{}:
406                 opts = v
407         default:
408                 return nil, fmt.Errorf("unexpected type for InitializationOptions: %T", v)
409         }
410         envOpt, ok := opts["env"]
411         if !ok {
412                 envOpt = make(map[string]interface{})
413         }
414         env, ok := envOpt.(map[string]interface{})
415         if !ok {
416                 return nil, fmt.Errorf(`env option is %T, expected a map`, envOpt)
417         }
418         goenv, err := getGoEnv(ctx, env)
419         if err != nil {
420                 return nil, err
421         }
422         for govar, value := range goenv {
423                 env[govar] = value
424         }
425         opts["env"] = env
426         params.InitializationOptions = opts
427         call, ok := r.(*jsonrpc2.Call)
428         if !ok {
429                 return nil, fmt.Errorf("%T is not a *jsonrpc2.Call", r)
430         }
431         return jsonrpc2.NewCall(call.ID(), "initialize", params)
432 }
433
434 func getGoEnv(ctx context.Context, env map[string]interface{}) (map[string]string, error) {
435         var runEnv []string
436         for k, v := range env {
437                 runEnv = append(runEnv, fmt.Sprintf("%s=%s", k, v))
438         }
439         runner := gocommand.Runner{}
440         output, err := runner.Run(ctx, gocommand.Invocation{
441                 Verb: "env",
442                 Args: []string{"-json"},
443                 Env:  runEnv,
444         })
445         if err != nil {
446                 return nil, err
447         }
448         envmap := make(map[string]string)
449         if err := json.Unmarshal(output.Bytes(), &envmap); err != nil {
450                 return nil, err
451         }
452         return envmap, nil
453 }
454
455 // A handshakeRequest identifies a client to the LSP server.
456 type handshakeRequest struct {
457         // ServerID is the ID of the server on the client. This should usually be 0.
458         ServerID string `json:"serverID"`
459         // Logfile is the location of the clients log file.
460         Logfile string `json:"logfile"`
461         // DebugAddr is the client debug address.
462         DebugAddr string `json:"debugAddr"`
463         // GoplsPath is the path to the Gopls binary running the current client
464         // process.
465         GoplsPath string `json:"goplsPath"`
466 }
467
468 // A handshakeResponse is returned by the LSP server to tell the LSP client
469 // information about its session.
470 type handshakeResponse struct {
471         // SessionID is the server session associated with the client.
472         SessionID string `json:"sessionID"`
473         // Logfile is the location of the server logs.
474         Logfile string `json:"logfile"`
475         // DebugAddr is the server debug address.
476         DebugAddr string `json:"debugAddr"`
477         // GoplsPath is the path to the Gopls binary running the current server
478         // process.
479         GoplsPath string `json:"goplsPath"`
480 }
481
482 // ClientSession identifies a current client LSP session on the server. Note
483 // that it looks similar to handshakeResposne, but in fact 'Logfile' and
484 // 'DebugAddr' now refer to the client.
485 type ClientSession struct {
486         SessionID string `json:"sessionID"`
487         Logfile   string `json:"logfile"`
488         DebugAddr string `json:"debugAddr"`
489 }
490
491 // ServerState holds information about the gopls daemon process, including its
492 // debug information and debug information of all of its current connected
493 // clients.
494 type ServerState struct {
495         Logfile         string          `json:"logfile"`
496         DebugAddr       string          `json:"debugAddr"`
497         GoplsPath       string          `json:"goplsPath"`
498         CurrentClientID string          `json:"currentClientID"`
499         Clients         []ClientSession `json:"clients"`
500 }
501
502 const (
503         handshakeMethod = "gopls/handshake"
504         sessionsMethod  = "gopls/sessions"
505 )
506
507 func handshaker(session *cache.Session, goplsPath string, logHandshakes bool, handler jsonrpc2.Handler) jsonrpc2.Handler {
508         return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error {
509                 switch r.Method() {
510                 case handshakeMethod:
511                         // We log.Printf in this handler, rather than event.Log when we want logs
512                         // to go to the daemon log rather than being reflected back to the
513                         // client.
514                         var req handshakeRequest
515                         if err := json.Unmarshal(r.Params(), &req); err != nil {
516                                 if logHandshakes {
517                                         log.Printf("Error processing handshake for session %s: %v", session.ID(), err)
518                                 }
519                                 sendError(ctx, reply, err)
520                                 return nil
521                         }
522                         if logHandshakes {
523                                 log.Printf("Session %s: got handshake. Logfile: %q, Debug addr: %q", session.ID(), req.Logfile, req.DebugAddr)
524                         }
525                         event.Log(ctx, "Handshake session update",
526                                 cache.KeyUpdateSession.Of(session),
527                                 tag.DebugAddress.Of(req.DebugAddr),
528                                 tag.Logfile.Of(req.Logfile),
529                                 tag.ServerID.Of(req.ServerID),
530                                 tag.GoplsPath.Of(req.GoplsPath),
531                         )
532                         resp := handshakeResponse{
533                                 SessionID: session.ID(),
534                                 GoplsPath: goplsPath,
535                         }
536                         if di := debug.GetInstance(ctx); di != nil {
537                                 resp.Logfile = di.Logfile
538                                 resp.DebugAddr = di.ListenedDebugAddress
539                         }
540
541                         return reply(ctx, resp, nil)
542                 case sessionsMethod:
543                         resp := ServerState{
544                                 GoplsPath:       goplsPath,
545                                 CurrentClientID: session.ID(),
546                         }
547                         if di := debug.GetInstance(ctx); di != nil {
548                                 resp.Logfile = di.Logfile
549                                 resp.DebugAddr = di.ListenedDebugAddress
550                                 for _, c := range di.State.Clients() {
551                                         resp.Clients = append(resp.Clients, ClientSession{
552                                                 SessionID: c.Session.ID(),
553                                                 Logfile:   c.Logfile,
554                                                 DebugAddr: c.DebugAddress,
555                                         })
556                                 }
557                         }
558                         return reply(ctx, resp, nil)
559                 }
560                 return handler(ctx, reply, r)
561         }
562 }
563
564 func sendError(ctx context.Context, reply jsonrpc2.Replier, err error) {
565         err = errors.Errorf("%v: %w", err, jsonrpc2.ErrParse)
566         if err := reply(ctx, nil, err); err != nil {
567                 event.Error(ctx, "", err)
568         }
569 }