-
-
Notifications
You must be signed in to change notification settings - Fork 23
/
jsonrpc.go
109 lines (91 loc) · 2.48 KB
/
jsonrpc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package jsonrpc
import (
"bytes"
"fmt"
"net/http"
"strings"
"github.com/goccy/go-json"
)
const (
// Version is JSON-RPC 2.0.
Version = "2.0"
batchRequestKey = '['
contentTypeKey = "Content-Type"
contentTypeValue = "application/json"
)
type (
// A Request represents a JSON-RPC request received by the server.
Request struct {
Version string `json:"jsonrpc"`
Method string `json:"method"`
Params *json.RawMessage `json:"params"`
ID *json.RawMessage `json:"id"`
}
// A Response represents a JSON-RPC response returned by the server.
Response struct {
Version string `json:"jsonrpc"`
Result any `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
ID *json.RawMessage `json:"id,omitempty"`
}
)
// ParseRequest parses a HTTP request to JSON-RPC request.
func ParseRequest(r *http.Request) ([]*Request, bool, *Error) {
var rerr *Error
if !strings.HasPrefix(r.Header.Get(contentTypeKey), contentTypeValue) {
return nil, false, ErrInvalidRequest()
}
buf := bytes.NewBuffer(make([]byte, 0, r.ContentLength))
if _, err := buf.ReadFrom(r.Body); err != nil {
return nil, false, ErrInvalidRequest()
}
defer func(r *http.Request) {
err := r.Body.Close()
if err != nil {
rerr = ErrInternal()
}
}(r)
if buf.Len() == 0 {
return nil, false, ErrInvalidRequest()
}
f, _, err := buf.ReadRune()
if err != nil {
return nil, false, ErrInvalidRequest()
}
if err := buf.UnreadRune(); err != nil {
return nil, false, ErrInvalidRequest()
}
var rs []*Request
if f != batchRequestKey {
var req *Request
if err := json.NewDecoder(buf).Decode(&req); err != nil {
return nil, false, ErrParse()
}
return append(rs, req), false, nil
}
if err := json.NewDecoder(buf).Decode(&rs); err != nil {
return nil, false, ErrParse()
}
return rs, true, rerr
}
// NewResponse generates a JSON-RPC response.
func NewResponse(r *Request) *Response {
return &Response{
Version: r.Version,
ID: r.ID,
}
}
// SendResponse writes JSON-RPC response.
func SendResponse(w http.ResponseWriter, resp []*Response, batch bool) error {
w.Header().Set(contentTypeKey, contentTypeValue)
if batch || len(resp) > 1 {
if err := json.NewEncoder(w).Encode(resp); err != nil {
return fmt.Errorf("jsonrpc: failed to encode: %w", err)
}
} else if len(resp) == 1 {
if err := json.NewEncoder(w).Encode(resp[0]); err != nil {
return fmt.Errorf("jsonrpc: failed to encode: %w", err)
}
}
return nil
}