github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/lsprpc/middleware.go (about) 1 // Copyright 2021 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 6 7 import ( 8 "context" 9 "encoding/json" 10 "sync" 11 12 "github.com/powerman/golang-tools/internal/event" 13 jsonrpc2_v2 "github.com/powerman/golang-tools/internal/jsonrpc2_v2" 14 "golang.org/x/xerrors" 15 ) 16 17 // Metadata holds arbitrary data transferred between jsonrpc2 peers. 18 type Metadata map[string]interface{} 19 20 // PeerInfo holds information about a peering between jsonrpc2 servers. 21 type PeerInfo struct { 22 // RemoteID is the identity of the current server on its peer. 23 RemoteID int64 24 25 // LocalID is the identity of the peer on the server. 26 LocalID int64 27 28 // IsClient reports whether the peer is a client. If false, the peer is a 29 // server. 30 IsClient bool 31 32 // Metadata holds arbitrary information provided by the peer. 33 Metadata Metadata 34 } 35 36 // Handshaker handles both server and client handshaking over jsonrpc2. To 37 // instrument server-side handshaking, use Handshaker.Middleware. To instrument 38 // client-side handshaking, call Handshaker.ClientHandshake for any new 39 // client-side connections. 40 type Handshaker struct { 41 // Metadata will be shared with peers via handshaking. 42 Metadata Metadata 43 44 mu sync.Mutex 45 prevID int64 46 peers map[int64]PeerInfo 47 } 48 49 // Peers returns the peer info this handshaker knows about by way of either the 50 // server-side handshake middleware, or client-side handshakes. 51 func (h *Handshaker) Peers() []PeerInfo { 52 h.mu.Lock() 53 defer h.mu.Unlock() 54 55 var c []PeerInfo 56 for _, v := range h.peers { 57 c = append(c, v) 58 } 59 return c 60 } 61 62 // Middleware is a jsonrpc2 middleware function to augment connection binding 63 // to handle the handshake method, and record disconnections. 64 func (h *Handshaker) Middleware(inner jsonrpc2_v2.Binder) jsonrpc2_v2.Binder { 65 return BinderFunc(func(ctx context.Context, conn *jsonrpc2_v2.Connection) (jsonrpc2_v2.ConnectionOptions, error) { 66 opts, err := inner.Bind(ctx, conn) 67 if err != nil { 68 return opts, err 69 } 70 71 localID := h.nextID() 72 info := &PeerInfo{ 73 RemoteID: localID, 74 Metadata: h.Metadata, 75 } 76 77 // Wrap the delegated handler to accept the handshake. 78 delegate := opts.Handler 79 opts.Handler = jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { 80 if req.Method == handshakeMethod { 81 var peerInfo PeerInfo 82 if err := json.Unmarshal(req.Params, &peerInfo); err != nil { 83 return nil, xerrors.Errorf("%w: unmarshaling client info: %v", jsonrpc2_v2.ErrInvalidParams, err) 84 } 85 peerInfo.LocalID = localID 86 peerInfo.IsClient = true 87 h.recordPeer(peerInfo) 88 return info, nil 89 } 90 return delegate.Handle(ctx, req) 91 }) 92 93 // Record the dropped client. 94 go h.cleanupAtDisconnect(conn, localID) 95 96 return opts, nil 97 }) 98 } 99 100 // ClientHandshake performs a client-side handshake with the server at the 101 // other end of conn, recording the server's peer info and watching for conn's 102 // disconnection. 103 func (h *Handshaker) ClientHandshake(ctx context.Context, conn *jsonrpc2_v2.Connection) { 104 localID := h.nextID() 105 info := &PeerInfo{ 106 RemoteID: localID, 107 Metadata: h.Metadata, 108 } 109 110 call := conn.Call(ctx, handshakeMethod, info) 111 var serverInfo PeerInfo 112 if err := call.Await(ctx, &serverInfo); err != nil { 113 event.Error(ctx, "performing handshake", err) 114 return 115 } 116 serverInfo.LocalID = localID 117 h.recordPeer(serverInfo) 118 119 go h.cleanupAtDisconnect(conn, localID) 120 } 121 122 func (h *Handshaker) nextID() int64 { 123 h.mu.Lock() 124 defer h.mu.Unlock() 125 126 h.prevID++ 127 return h.prevID 128 } 129 130 func (h *Handshaker) cleanupAtDisconnect(conn *jsonrpc2_v2.Connection, peerID int64) { 131 conn.Wait() 132 133 h.mu.Lock() 134 defer h.mu.Unlock() 135 delete(h.peers, peerID) 136 } 137 138 func (h *Handshaker) recordPeer(info PeerInfo) { 139 h.mu.Lock() 140 defer h.mu.Unlock() 141 if h.peers == nil { 142 h.peers = make(map[int64]PeerInfo) 143 } 144 h.peers[info.LocalID] = info 145 }