Skip to content

Commit 50ae968

Browse files
committed
jsonrpc2: add initial implements Conn
1 parent 4794c60 commit 50ae968

File tree

1 file changed

+93
-54
lines changed

1 file changed

+93
-54
lines changed

jsonrpc2.go

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,75 +5,114 @@
55
package jsonrpc2
66

77
import (
8-
"encoding/json"
8+
"context"
9+
"fmt"
10+
"sync"
911
)
1012

11-
// ID is a Request identifier.
12-
// Only one of either the Name or Number members will be set, using the
13-
// number form if the Name is the empty string.
14-
type ID struct {
15-
Name string
16-
Number int64
17-
}
18-
19-
// Message is a general message as defined by JSON-RPC. The language server protocol always uses "2.0" as the jsonrpc version.
20-
type Message struct {
21-
JSONRPC string `json:"jsonrpc"`
22-
}
13+
type Interface interface {
14+
Call(ctx context.Context, method string, params, result interface{}) error
2315

24-
// Request is a request message to describe a request between the client and the server.
25-
// Every processed request must send a response back to the sender of the request.
26-
type Request struct {
27-
Message
16+
Reply(ctx context.Context, req *Request, result interface{}, err error) error
2817

29-
// The request id.
30-
ID *ID `json:"id"`
18+
Notify(ctx context.Context, method string, params interface{}) error
3119

32-
// The method to be invoked.
33-
Method string `json:"method"`
20+
Cancel(id ID)
3421

35-
// The method's params.
36-
Params *json.RawMessage `json:"params,omitempty"`
22+
Wait(ctx context.Context) error
3723
}
3824

39-
// Response is a response ressage sent as a result of a request.
40-
// If a request doesn't provide a result value the receiver of a request still needs to return a response message to
41-
// conform to the JSON RPC specification.
42-
// The result property of the ResponseMessage should be set to null in this case to signal a successful request.
43-
type Response struct {
44-
Message
45-
46-
// The error object in case a request fails.
47-
Error *ResponseError `json:"error,omitempty"`
48-
49-
// The request id.
50-
ID *ID `json:"id"`
25+
// Conn is a JSON RPC 2 client server connection.
26+
// Conn is bidirectional; it does not have a designated server or client end.
27+
type Conn struct {
28+
handle Handler
29+
cancel Canceler
30+
stream Stream
31+
done chan struct{}
32+
err error
33+
seq int64 // must only be accessed using atomic operations
34+
pendingMu sync.Mutex // protects the pending map
35+
pending map[ID]chan *Response
36+
}
5137

52-
// The result of a request. This member is REQUIRED on success.
53-
// This member MUST NOT exist if there was an error invoking the method.
54-
Result *json.RawMessage `json:"result,omitempty"`
38+
// Handler is an option you can pass to NewConn to handle incoming requests.
39+
// If the request returns false from IsNotify then the Handler must eventually
40+
// call Reply on the Conn with the supplied request.
41+
// Handlers are called synchronously, they should pass the work off to a go
42+
// routine if they are going to take a long time.
43+
type Handler func(context.Context, *Conn, *Request)
44+
45+
// Canceler is an option you can pass to NewConn which is invoked for
46+
// cancelled outgoing requests.
47+
// The request will have the ID filled in, which can be used to propagate the
48+
// cancel to the other process if needed.
49+
// It is okay to use the connection to send notifications, but the context will
50+
// be in the cancelled state, so you must do it with the background context
51+
// instead.
52+
type Canceler func(context.Context, *Conn, *Request)
53+
54+
// NewErrorf builds a Error struct for the suppied message and code.
55+
// If args is not empty, message and args will be passed to Sprintf.
56+
func NewErrorf(code ErrorCode, format string, args ...interface{}) *Error {
57+
return &Error{
58+
Code: code,
59+
Message: fmt.Sprintf(format, args...),
60+
}
5561
}
5662

57-
// ResponseError ...
58-
type ResponseError struct {
63+
// NewConn creates a new connection object that reads and writes messages from
64+
// the supplied stream and dispatches incoming messages to the supplied handler.
65+
func NewConn(ctx context.Context, s Stream, options ...interface{}) *Conn {
66+
conn := &Conn{
67+
stream: s,
68+
done: make(chan struct{}),
69+
pending: make(map[ID]chan *Response),
70+
}
71+
for _, opt := range options {
72+
switch opt := opt.(type) {
73+
case Handler:
74+
if conn.handle != nil {
75+
panic("Duplicate Handler function in options list")
76+
}
77+
conn.handle = opt
78+
case Canceler:
79+
if conn.cancel != nil {
80+
panic("Duplicate Canceler function in options list")
81+
}
82+
conn.cancel = opt
83+
default:
84+
panic(fmt.Errorf("Unknown option type %T in options list", opt))
85+
}
86+
}
87+
if conn.handle == nil {
88+
// the default handler reports a method error
89+
conn.handle = func(ctx context.Context, c *Conn, r *Request) {
90+
if r.IsNotify() {
91+
c.Reply(ctx, r, nil, NewErrorf(MethodNotFound, "method %q not found", r.Method))
92+
}
93+
}
94+
}
95+
if conn.cancel == nil {
96+
// the default canceller does nothing
97+
conn.cancel = func(context.Context, *Conn, *Request) {}
98+
}
99+
go func() {
100+
conn.err = conn.run(ctx)
101+
close(conn.done)
102+
}()
103+
return conn
104+
}
59105

60-
// Code a number indicating the error type that occurred.
61-
Code ErrorCode `json:"code"`
106+
func (c *Conn) run(ctx context.Context) error { return nil }
62107

63-
// Data a Primitive or Structured value that contains additional
64-
// information about the error. Can be omitted.
65-
Data *json.RawMessage `json:"data"`
108+
func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) error { return nil }
66109

67-
// Message a string providing a short description of the error.
68-
Message string `json:"message"`
110+
func (c *Conn) Reply(ctx context.Context, req *Request, result interface{}, err error) error {
111+
return nil
69112
}
70113

71-
type NotificationMessage struct {
72-
Message
114+
func (c *Conn) Notify(ctx context.Context, method string, params interface{}) error { return nil }
73115

74-
// Method is the method to be invoked.
75-
Method string `json:"method"`
116+
func (c *Conn) Cancel(id ID) {}
76117

77-
// Params is the notification's params.
78-
Params interface{} `json:"params,omitempty"`
79-
}
118+
func (c *Conn) Wait(ctx context.Context) error { return nil }

0 commit comments

Comments
 (0)