turbo-editors/turbo-corepublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

jsonrpc.go · 146 lines · 5.0 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 20h ago1// Package jsonrpc is a JSON-RPC 2.0 connection over a framed byte stream.
2//
3// It holds everything the protocol says and nothing about any particular use
4// of it: requests and their answers, notifications, requests arriving the
5// other way, and the error object a failure travels in. How a message is
6// marked off from the next one is the caller's, through a Framer — the
7// Language Server Protocol puts an HTTP-style header in front of each message,
8// the Agent Client Protocol separates them with newlines, and everything above
9// that layer is the same.
10//
11// A connection is safe for concurrent use: several goroutines may call and
12// notify at once, and answers are matched back to their callers by request id.
13// The ids a peer chooses are its own and never collide with the ones sent from
14// here, because the two directions are tracked separately.
15//
16// conn := jsonrpc.NewConn(stream, framer, onNotify, onRequest)
17// go conn.Run()
18//
19// var result Position
20// err := conn.Call(ctx, "textDocument/definition", params, &result)
21package jsonrpc
22
23import (
24 "bufio"
25 "encoding/json"
26 "errors"
27 "fmt"
28 "io"
29)
30
31// Version is the only value the protocol's jsonrpc field ever takes.
32const Version = "2.0"
33
34// ErrClosed is returned once the connection has been closed or its peer has
35// gone away.
36var ErrClosed = errors.New("jsonrpc: connection closed")
37
38// Framer marks off one message from the next on a stream.
39//
40// The two halves are deliberately separate functions rather than a codec
41// object: framing is stateless, and a reader and a writer of the same
42// connection are used from different goroutines.
43//
44// type NewlineFramer struct{}
45//
46// func (NewlineFramer) WriteMessage(w io.Writer, body []byte) error {
47// _, err := fmt.Fprintf(w, "%s\n", body)
48// return err
49// }
50type Framer interface {
51 // ReadMessage reads one whole message and returns its body. It returns
52 // io.EOF when the stream ends cleanly between messages, which is how a
53 // peer shutting down is told apart from one that died mid-message.
54 ReadMessage(r *bufio.Reader) ([]byte, error)
55
56 // WriteMessage frames body and writes it out.
57 WriteMessage(w io.Writer, body []byte) error
58}
59
60// ResponseError is the error object a response may carry.
61//
62// It is an error, so a failure at the far end reads like any other one at the
63// call site, and a handler may return one to choose the code the peer sees.
64//
65// return nil, &jsonrpc.ResponseError{Code: jsonrpc.CodeInvalidParams, Message: "no uri"}
66type ResponseError struct {
67 Code int `json:"code"`
68 Message string `json:"message"`
69 Data json.RawMessage `json:"data,omitempty"`
70}
71
72// Error makes ResponseError an error.
73func (e *ResponseError) Error() string {
74 return fmt.Sprintf("jsonrpc: peer error %d: %s", e.Code, e.Message)
75}
76
77// The standard error codes, of which a client normally sends only the first.
78const (
79 CodeParseError = -32700
80 CodeInvalidRequest = -32600
81 CodeMethodNotFound = -32601
82 CodeInvalidParams = -32602
83 CodeInternalError = -32603
84)
85
86// message is any frame: a request, a notification or a response. Which one it
87// is follows from which fields are present.
88type message struct {
89 JSONRPC string `json:"jsonrpc"`
90 ID json.RawMessage `json:"id,omitempty"`
91 Method string `json:"method,omitempty"`
92 Params json.RawMessage `json:"params,omitempty"`
93 Result json.RawMessage `json:"result,omitempty"`
94 Error *ResponseError `json:"error,omitempty"`
95}
96
97// NotificationFunc handles a notification the peer sent. It is called from the
98// connection's read loop, so it must not block for long.
99type NotificationFunc func(method string, params json.RawMessage)
100
101// RequestFunc handles a request the peer sent.
102//
103// It is called from the connection's read loop and must not block: a handler
104// whose answer has to come from somewhere else — the user, another goroutine,
105// the next turn of an event loop — keeps the Request and calls Reply when it
106// has one. Answering twice is harmless; never answering leaves the peer
107// waiting until the connection ends.
108type RequestFunc func(req *Request)
109
110// asResponseError turns any error into one the protocol can carry.
111func asResponseError(err error) *ResponseError {
112 var responseErr *ResponseError
113 if errors.As(err, &responseErr) {
114 return responseErr
115 }
116 return &ResponseError{Code: CodeInternalError, Message: err.Error()}
117}
118
119// encodeParams encodes a parameter object, treating nil as absent.
120func encodeParams(params any) (json.RawMessage, error) {
121 if params == nil {
122 return nil, nil
123 }
124 encoded, err := json.Marshal(params)
125 if err != nil {
126 return nil, fmt.Errorf("jsonrpc: encoding the parameters: %w", err)
127 }
128 return encoded, nil
129}
130
131// decodeReply turns a response into an error or a decoded result.
132func decodeReply(reply *message, result any) error {
133 if reply == nil {
134 return ErrClosed
135 }
136 if reply.Error != nil {
137 return reply.Error
138 }
139 if result == nil || len(reply.Result) == 0 {
140 return nil
141 }
142 if err := json.Unmarshal(reply.Result, result); err != nil {
143 return fmt.Errorf("jsonrpc: unreadable result: %w", err)
144 }
145 return nil
146}