Skip to content

Commit 4e39a4e

Browse files
committed
1 parent 4e6468e commit 4e39a4e

26 files changed

+274
-295
lines changed

conn.go

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright 2020 The Go Language Server Authors.
21
// SPDX-License-Identifier: BSD-3-Clause
2+
// SPDX-FileCopyrightText: Copyright 2021 The Go Language Server Authors
33

44
package jsonrpc2
55

@@ -8,10 +8,13 @@ import (
88
"fmt"
99
"sync"
1010
"sync/atomic"
11+
12+
"go.lsp.dev/pkg/event"
13+
"go.lsp.dev/pkg/event/label"
14+
"go.lsp.dev/pkg/event/tag"
1115
)
1216

1317
// Conn is the common interface to jsonrpc clients and servers.
14-
//
1518
// Conn is bidirectional; it does not have a designated server or client end.
1619
// It manages the jsonrpc2 protocol, connecting responses back to their calls.
1720
type Conn interface {
@@ -52,7 +55,7 @@ type Conn interface {
5255
}
5356

5457
type conn struct {
55-
seq int64 // access atomic
58+
seq int64 // access atomically
5659
writeMu sync.Mutex // protects writes to the stream
5760
stream Stream
5861
pendingMu sync.Mutex // protects the pending map
@@ -69,7 +72,6 @@ func NewConn(s Stream) Conn {
6972
pending: make(map[ID]chan *Response),
7073
done: make(chan struct{}),
7174
}
72-
7375
return conn
7476
}
7577

@@ -79,41 +81,51 @@ func (c *conn) Notify(ctx context.Context, method string, params interface{}) (e
7981
if err != nil {
8082
return fmt.Errorf("marshaling notify parameters: %w", err)
8183
}
82-
83-
_, err = c.write(ctx, notify)
84+
ctx, done := event.Start(ctx, method,
85+
tag.Method.Of(method),
86+
tag.RPCDirection.Of(tag.Outbound),
87+
)
88+
defer func() {
89+
recordStatus(ctx, err)
90+
done()
91+
}()
92+
93+
event.Metric(ctx, tag.Started.Of(1))
94+
n, err := c.write(ctx, notify)
95+
event.Metric(ctx, tag.SentBytes.Of(n))
8496
return err
8597
}
8698

87-
func (c *conn) replier(req Message) Replier {
88-
reply := func(ctx context.Context, result interface{}, err error) error {
89-
call, ok := req.(*Request)
99+
func (c *conn) replier(req Message, spanDone func()) Replier {
100+
return func(ctx context.Context, result interface{}, err error) error {
101+
defer func() {
102+
recordStatus(ctx, err)
103+
spanDone()
104+
}()
105+
call, ok := req.(*Call)
90106
if !ok {
91107
// request was a notify, no need to respond
92108
return nil
93109
}
94-
95110
response, err := NewResponse(call.id, result, err)
96111
if err != nil {
97112
return err
98113
}
99-
100-
_, err = c.write(ctx, response)
114+
n, err := c.write(ctx, response)
115+
event.Metric(ctx, tag.SentBytes.Of(n))
101116
if err != nil {
102117
// TODO(iancottrell): if a stream write fails, we really need to shut down
103118
// the whole stream
104119
return err
105120
}
106121
return nil
107122
}
108-
109-
return reply
110123
}
111124

112-
func (c *conn) write(ctx context.Context, msg Message) (n int64, err error) {
125+
func (c *conn) write(ctx context.Context, msg Message) (int64, error) {
113126
c.writeMu.Lock()
114-
n, err = c.stream.Write(ctx, msg)
115-
c.writeMu.Unlock()
116-
return
127+
defer c.writeMu.Unlock()
128+
return c.stream.Write(ctx, msg)
117129
}
118130

119131
// Go implemens Conn.
@@ -123,24 +135,34 @@ func (c *conn) Go(ctx context.Context, handler Handler) {
123135

124136
func (c *conn) run(ctx context.Context, handler Handler) {
125137
defer close(c.done)
126-
127138
for {
128139
// get the next message
129-
msg, _, err := c.stream.Read(ctx)
140+
msg, n, err := c.stream.Read(ctx)
130141
if err != nil {
131142
// The stream failed, we cannot continue.
132143
c.fail(err)
133144
return
134145
}
135-
136146
switch msg := msg.(type) {
137-
case Requester:
138-
if err := handler(ctx, c.replier(msg), msg); err != nil {
147+
case Request:
148+
labels := []label.Label{
149+
tag.Method.Of(msg.Method()),
150+
tag.RPCDirection.Of(tag.Inbound),
151+
{}, // reserved for ID if present
152+
}
153+
if call, ok := msg.(*Call); ok {
154+
labels[len(labels)-1] = tag.RPCID.Of(fmt.Sprintf("%q", call.ID()))
155+
} else {
156+
labels = labels[:len(labels)-1]
157+
}
158+
reqCtx, spanDone := event.Start(ctx, msg.Method(), labels...)
159+
event.Metric(reqCtx,
160+
tag.Started.Of(1),
161+
tag.ReceivedBytes.Of(n))
162+
if err := handler(reqCtx, c.replier(msg, spanDone), msg); err != nil {
139163
// delivery failed, not much we can do
140-
c.fail(err)
141-
return
164+
event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
142165
}
143-
144166
case *Response:
145167
// If method is not set, this should be a response, in which case we must
146168
// have an id to send the response back to the caller.
@@ -177,3 +199,11 @@ func (c *conn) fail(err error) {
177199
c.err.Store(err)
178200
c.stream.Close()
179201
}
202+
203+
func recordStatus(ctx context.Context, err error) {
204+
if err != nil {
205+
event.Label(ctx, tag.StatusCode.Of("ERROR"))
206+
} else {
207+
event.Label(ctx, tag.StatusCode.Of("OK"))
208+
}
209+
}

conn_gojay.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (ID, error) {
1919
// generate a new request identifier
2020
id := ID{number: atomic.AddInt64(&c.seq, 1)}
21-
call, err := NewRequest(id, method, params)
21+
call, err := NewCall(id, method, params)
2222
if err != nil {
2323
return id, fmt.Errorf("marshaling call parameters: %v", err)
2424
}

conn_json.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright 2020 The Go Language Server Authors.
21
// SPDX-License-Identifier: BSD-3-Clause
2+
// SPDX-FileCopyrightText: Copyright 2019 The Go Language Server Authors
33

44
// +build !gojay
55

@@ -12,17 +12,31 @@ import (
1212
"sync/atomic"
1313

1414
json "github.com/goccy/go-json"
15+
16+
"go.lsp.dev/pkg/event"
17+
"go.lsp.dev/pkg/event/tag"
1518
)
1619

1720
// Call implemens Conn.
18-
func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (ID, error) {
21+
func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (_ ID, err error) {
1922
// generate a new request identifier
2023
id := ID{number: atomic.AddInt64(&c.seq, 1)}
21-
call, err := NewRequest(id, method, params)
24+
call, err := NewCall(id, method, params)
2225
if err != nil {
2326
return id, fmt.Errorf("marshaling call parameters: %w", err)
2427
}
2528

29+
ctx, done := event.Start(ctx, method,
30+
tag.Method.Of(method),
31+
tag.RPCDirection.Of(tag.Outbound),
32+
tag.RPCID.Of(fmt.Sprintf("%q", id)),
33+
)
34+
defer func() {
35+
recordStatus(ctx, err)
36+
done()
37+
}()
38+
event.Metric(ctx, tag.Started.Of(1))
39+
2640
// We have to add ourselves to the pending map before we send, otherwise we
2741
// are racing the response. Also add a buffer to rchan, so that if we get a
2842
// wire response between the time this call is cancelled and id is deleted
@@ -40,7 +54,8 @@ func (c *conn) Call(ctx context.Context, method string, params, result interface
4054
}()
4155

4256
// now we are ready to send
43-
_, err = c.write(ctx, call)
57+
n, err := c.write(ctx, call)
58+
event.Metric(ctx, tag.SentBytes.Of(n))
4459
if err != nil {
4560
// sending failed, we will never get a response, so don't leave it pending
4661
return id, err
@@ -50,7 +65,7 @@ func (c *conn) Call(ctx context.Context, method string, params, result interface
5065
select {
5166
case response := <-rchan:
5267
switch {
53-
case response.err != nil:
68+
case response.err != nil: // is it an error response?
5469
return id, response.err
5570

5671
case result == nil || len(response.result) == 0:

errors.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright 2019 The Go Language Server Authors.
21
// SPDX-License-Identifier: BSD-3-Clause
2+
// SPDX-FileCopyrightText: Copyright 2019 The Go Language Server Authors
33

44
package jsonrpc2
55

@@ -29,7 +29,6 @@ func (e *Error) Error() string {
2929
if e == nil {
3030
return ""
3131
}
32-
3332
return e.Message
3433
}
3534

@@ -48,12 +47,10 @@ func NewError(c Code, message string) *Error {
4847

4948
// Errorf builds a Error struct for the suppied code, format and args.
5049
func Errorf(c Code, format string, args ...interface{}) *Error {
51-
e := &Error{
50+
return &Error{
5251
Code: c,
5352
Message: fmt.Sprintf(format, args...),
5453
}
55-
56-
return e
5754
}
5855

5956
// constErr represents a error constant.

fake/fake.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func NewTCPServer(ctx context.Context, server jsonrpc2.StreamServer, framer json
7373
panic(fmt.Sprintf("servertest: failed to listen: %v", err))
7474
}
7575
if framer == nil {
76-
framer = jsonrpc2.NewHeaderStream
76+
framer = jsonrpc2.NewStream
7777
}
7878

7979
go jsonrpc2.Serve(ctx, ln, server, 0)

fake/fake_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type msg struct {
1616
Msg string
1717
}
1818

19-
func fakeHandler(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Requester) error {
19+
func fakeHandler(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
2020
return reply(ctx, &msg{"pong"}, nil)
2121
}
2222

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ require (
66
github.com/francoispqt/gojay v1.2.13
77
github.com/goccy/go-json v0.3.3
88
github.com/google/go-cmp v0.5.4
9+
go.lsp.dev/pkg v0.0.0-20210124221122-cff50a23c8bc
910
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
101101
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
102102
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
103103
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
104+
go.lsp.dev/pkg v0.0.0-20210124221122-cff50a23c8bc h1:Kzr4G5KcZrGNOU9yDm0AGeOqkzaLP9KfQVRGKHtyBog=
105+
go.lsp.dev/pkg v0.0.0-20210124221122-cff50a23c8bc/go.mod h1:gtSHRuYfbCT0qnbLnovpie/WEmqyJ7T4n6VXiFMBtcw=
104106
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
105107
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
106108
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=

handler.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
// Copyright 2020 The Go Language Server Authors.
21
// SPDX-License-Identifier: BSD-3-Clause
2+
// SPDX-FileCopyrightText: Copyright 2019 The Go Language Server Authors
33

44
package jsonrpc2
55

66
import (
77
"context"
88
"fmt"
99
"sync"
10+
11+
"go.lsp.dev/pkg/event"
1012
)
1113

1214
// Handler is invoked to handle incoming requests.
1315
//
1416
// The Replier sends a reply to the request and must be called exactly once.
15-
type Handler func(ctx context.Context, reply Replier, req Requester) error
17+
type Handler func(ctx context.Context, reply Replier, req Request) error
1618

1719
// Replier is passed to handlers to allow them to reply to the request.
1820
//
@@ -23,14 +25,14 @@ type Replier func(ctx context.Context, result interface{}, err error) error
2325
// standard method not found response.
2426
//
2527
// This should normally be the final handler in a chain.
26-
func MethodNotFoundHandler(ctx context.Context, reply Replier, req Requester) error {
28+
func MethodNotFoundHandler(ctx context.Context, reply Replier, req Request) error {
2729
return reply(ctx, nil, fmt.Errorf("%w: %q", ErrMethodNotFound, req.Method()))
2830
}
2931

3032
// ReplyHandler creates a Handler that panics if the wrapped handler does
3133
// not call Reply for every request that it is passed.
3234
func ReplyHandler(handler Handler) Handler {
33-
return func(ctx context.Context, reply Replier, req Requester) error {
35+
return func(ctx context.Context, reply Replier, req Request) error {
3436
called := false
3537
err := handler(ctx, func(ctx context.Context, result interface{}, err error) error {
3638
if called {
@@ -51,8 +53,9 @@ func ReplyHandler(handler Handler) Handler {
5153
func CancelHandler(handler Handler) (h Handler, cancel func(id ID)) {
5254
var mu sync.Mutex
5355
handling := make(map[ID]context.CancelFunc)
54-
h = func(ctx context.Context, reply Replier, req Requester) error {
55-
if call, ok := req.(*Request); ok {
56+
57+
h = func(ctx context.Context, reply Replier, req Request) error {
58+
if call, ok := req.(*Call); ok {
5659
cancelCtx, cancel := context.WithCancel(ctx)
5760
ctx = cancelCtx
5861
mu.Lock()
@@ -66,8 +69,10 @@ func CancelHandler(handler Handler) (h Handler, cancel func(id ID)) {
6669
return innerReply(ctx, result, err)
6770
}
6871
}
72+
6973
return handler(ctx, reply, req)
7074
}
75+
7176
return h, func(id ID) {
7277
mu.Lock()
7378
cancel, found := handling[id]
@@ -89,7 +94,8 @@ func CancelHandler(handler Handler) (h Handler, cancel func(id ID)) {
8994
func AsyncHandler(handler Handler) Handler {
9095
nextRequest := make(chan struct{})
9196
close(nextRequest)
92-
return func(ctx context.Context, reply Replier, req Requester) error {
97+
98+
return func(ctx context.Context, reply Replier, req Request) error {
9399
waitForPrevious := nextRequest
94100
nextRequest = make(chan struct{})
95101
unlockNext := nextRequest
@@ -98,9 +104,13 @@ func AsyncHandler(handler Handler) Handler {
98104
close(unlockNext)
99105
return innerReply(ctx, result, err)
100106
}
107+
_, queueDone := event.Start(ctx, "queued")
101108
go func() {
102109
<-waitForPrevious
103-
handler(ctx, reply, req)
110+
queueDone()
111+
if err := handler(ctx, reply, req); err != nil {
112+
event.Error(ctx, "jsonrpc2 async message delivery failed", err)
113+
}
104114
}()
105115
return nil
106116
}

jsonrpc2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2019 The Go Language Server Authors.
21
// SPDX-License-Identifier: BSD-3-Clause
2+
// SPDX-FileCopyrightText: Copyright 2019 The Go Language Server Authors
33

44
package jsonrpc2

0 commit comments

Comments
 (0)