Skip to content

Commit b1b0e09

Browse files
committed
conn: use goccy/go-json
1 parent 0a57493 commit b1b0e09

File tree

1 file changed

+56
-33
lines changed

1 file changed

+56
-33
lines changed

conn.go

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

44
package jsonrpc2
55

66
import (
7-
"bytes"
87
"context"
9-
"encoding/json"
108
"fmt"
119
"sync"
1210
"sync/atomic"
1311

12+
json "github.com/goccy/go-json"
13+
1414
"go.lsp.dev/pkg/event"
1515
"go.lsp.dev/pkg/event/label"
1616
"go.lsp.dev/pkg/event/tag"
1717
)
1818

1919
// Conn is the common interface to jsonrpc clients and servers.
20+
//
2021
// Conn is bidirectional; it does not have a designated server or client end.
2122
// It manages the jsonrpc2 protocol, connecting responses back to their calls.
2223
type Conn interface {
2324
// Call invokes the target method and waits for a response.
25+
//
2426
// The params will be marshaled to JSON before sending over the wire, and will
2527
// be handed to the method invoked.
28+
//
2629
// The response will be unmarshaled from JSON into the result.
30+
//
2731
// The id returned will be unique from this connection, and can be used for
2832
// logging or tracking.
2933
Call(ctx context.Context, method string, params, result interface{}) (ID, error)
3034

3135
// Notify invokes the target method but does not wait for a response.
36+
//
3237
// The params will be marshaled to JSON before sending over the wire, and will
3338
// be handed to the method invoked.
3439
Notify(ctx context.Context, method string, params interface{}) error
3540

3641
// Go starts a goroutine to handle the connection.
37-
// It must be called exactly once for each Conn.
38-
// It returns immediately.
39-
// You must block on Done() to wait for the connection to shut down.
42+
//
43+
// It must be called exactly once for each Conn. It returns immediately.
44+
// Must block on Done() to wait for the connection to shut down.
45+
//
4046
// This is a temporary measure, this should be started automatically in the
4147
// future.
4248
Go(ctx context.Context, handler Handler)
4349

4450
// Close closes the connection and it's underlying stream.
51+
//
4552
// It does not wait for the close to complete, use the Done() channel for
4653
// that.
4754
Close() error
@@ -52,19 +59,20 @@ type Conn interface {
5259
Done() <-chan struct{}
5360

5461
// Err returns an error if there was one from within the processing goroutine.
62+
//
5563
// If err returns non nil, the connection will be already closed or closing.
5664
Err() error
5765
}
5866

5967
type conn struct {
60-
seq int64 // access atomically
61-
writeMu sync.Mutex // protects writes to the stream
62-
stream Stream
63-
pendingMu sync.Mutex // protects the pending map
64-
pending map[ID]chan *Response
65-
66-
done chan struct{}
67-
err atomic.Value
68+
seq int64 // access atomically
69+
writeMu sync.Mutex // protects writes to the stream
70+
stream Stream // supplied stream
71+
pendingMu sync.Mutex // protects the pending map
72+
pending map[ID]chan *Response // holds the pending response channel with the ID as the key.
73+
74+
done chan struct{} // closed when done
75+
err atomic.Value // holds run error
6876
}
6977

7078
// NewConn creates a new connection object around the supplied stream.
@@ -78,9 +86,9 @@ func NewConn(s Stream) Conn {
7886
}
7987

8088
// Call implements Conn.
81-
func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (_ ID, err error) {
89+
func (c *conn) Call(ctx context.Context, method string, params, result interface{}) (id ID, err error) {
8290
// generate a new request identifier
83-
id := ID{number: atomic.AddInt64(&c.seq, 1)}
91+
id = NewNumberID(atomic.AddInt64(&c.seq, 1))
8492
call, err := NewCall(id, method, params)
8593
if err != nil {
8694
return id, fmt.Errorf("marshaling call parameters: %w", err)
@@ -123,22 +131,22 @@ func (c *conn) Call(ctx context.Context, method string, params, result interface
123131

124132
// now wait for the response
125133
select {
126-
case response := <-rchan:
127-
switch {
128-
case response.err != nil: // is it an error response?
129-
return id, response.err
134+
case resp := <-rchan:
135+
// is it an error response?
136+
if resp.err != nil {
137+
return id, resp.err
138+
}
130139

131-
case result == nil || len(response.result) == 0:
140+
if result == nil || len(resp.result) == 0 {
132141
return id, nil
142+
}
133143

134-
default:
135-
dec := json.NewDecoder(bytes.NewReader(response.result))
136-
if err := dec.Decode(result); err != nil {
137-
return id, fmt.Errorf("unmarshaling result: %w", err)
138-
}
139-
return id, nil
144+
if err := json.Unmarshal(resp.result, result); err != nil {
145+
return id, fmt.Errorf("unmarshaling result: %w", err)
140146
}
141147

148+
return id, nil
149+
142150
case <-ctx.Done():
143151
return id, ctx.Err()
144152
}
@@ -150,6 +158,7 @@ func (c *conn) Notify(ctx context.Context, method string, params interface{}) (e
150158
if err != nil {
151159
return fmt.Errorf("marshaling notify parameters: %w", err)
152160
}
161+
153162
ctx, done := event.Start(ctx, method,
154163
tag.Method.Of(method),
155164
tag.RPCDirection.Of(tag.Outbound),
@@ -162,6 +171,7 @@ func (c *conn) Notify(ctx context.Context, method string, params interface{}) (e
162171
event.Metric(ctx, tag.Started.Of(1))
163172
n, err := c.write(ctx, notify)
164173
event.Metric(ctx, tag.SentBytes.Of(n))
174+
165175
return err
166176
}
167177

@@ -176,25 +186,31 @@ func (c *conn) replier(req Message, spanDone func()) Replier {
176186
// request was a notify, no need to respond
177187
return nil
178188
}
189+
179190
response, err := NewResponse(call.id, result, err)
180191
if err != nil {
181192
return err
182193
}
194+
183195
n, err := c.write(ctx, response)
184196
event.Metric(ctx, tag.SentBytes.Of(n))
185197
if err != nil {
186-
// TODO(iancottrell): if a stream write fails, we really need to shut down
187-
// the whole stream
198+
// TODO(iancottrell): if a stream write fails, we really need to shut down the whole stream
188199
return err
189200
}
190201
return nil
191202
}
192203
}
193204

194-
func (c *conn) write(ctx context.Context, msg Message) (int64, error) {
205+
func (c *conn) write(ctx context.Context, msg Message) (n int64, err error) {
195206
c.writeMu.Lock()
196-
defer c.writeMu.Unlock()
197-
return c.stream.Write(ctx, msg)
207+
n, err = c.stream.Write(ctx, msg)
208+
c.writeMu.Unlock()
209+
if err != nil {
210+
return 0, fmt.Errorf("write to stream: %w", err)
211+
}
212+
213+
return n, nil
198214
}
199215

200216
// Go implements Conn.
@@ -204,6 +220,7 @@ func (c *conn) Go(ctx context.Context, handler Handler) {
204220

205221
func (c *conn) run(ctx context.Context, handler Handler) {
206222
defer close(c.done)
223+
207224
for {
208225
// get the next message
209226
msg, n, err := c.stream.Read(ctx)
@@ -212,6 +229,7 @@ func (c *conn) run(ctx context.Context, handler Handler) {
212229
c.fail(err)
213230
return
214231
}
232+
215233
switch msg := msg.(type) {
216234
case Request:
217235
labels := []label.Label{
@@ -224,14 +242,18 @@ func (c *conn) run(ctx context.Context, handler Handler) {
224242
} else {
225243
labels = labels[:len(labels)-1]
226244
}
245+
227246
reqCtx, spanDone := event.Start(ctx, msg.Method(), labels...)
228247
event.Metric(reqCtx,
229248
tag.Started.Of(1),
230-
tag.ReceivedBytes.Of(n))
249+
tag.ReceivedBytes.Of(n),
250+
)
251+
231252
if err := handler(reqCtx, c.replier(msg, spanDone), msg); err != nil {
232253
// delivery failed, not much we can do
233254
event.Error(reqCtx, "jsonrpc2 message delivery failed", err)
234255
}
256+
235257
case *Response:
236258
// If method is not set, this should be a response, in which case we must
237259
// have an id to send the response back to the caller.
@@ -269,6 +291,7 @@ func (c *conn) fail(err error) {
269291
c.stream.Close()
270292
}
271293

294+
// recordStatus records the status code based on the error.
272295
func recordStatus(ctx context.Context, err error) {
273296
if err != nil {
274297
event.Label(ctx, tag.StatusCode.Of("ERROR"))

0 commit comments

Comments
 (0)