This repository has been archived by the owner on Jul 8, 2021. It is now read-only.
forked from filecoin-project/go-jsonrpc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
123 lines (101 loc) · 2.42 KB
/
server.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package jsonrpc
import (
"context"
"encoding/json"
"io"
"net/http"
"reflect"
"strings"
"github.com/gorilla/websocket"
)
const (
rpcParseError = -32700
rpcMethodNotFound = -32601
rpcInvalidParams = -32602
)
// RPCServer provides a jsonrpc 2.0 http server handler
type RPCServer struct {
methods map[string]rpcHandler
paramDecoders map[reflect.Type]ParamDecoder
}
// NewServer creates new RPCServer instance
func NewServer(opts ...ServerOption) *RPCServer {
config := defaultServerConfig()
for _, o := range opts {
o(&config)
}
return &RPCServer{
methods: map[string]rpcHandler{},
paramDecoders: config.paramDecoders,
}
}
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func (s *RPCServer) handleWS(ctx context.Context, w http.ResponseWriter, r *http.Request) {
// TODO: allow setting
// (note that we still are mostly covered by jwt tokens)
w.Header().Set("Access-Control-Allow-Origin", "*")
if r.Header.Get("Sec-WebSocket-Protocol") != "" {
w.Header().Set("Sec-WebSocket-Protocol", r.Header.Get("Sec-WebSocket-Protocol"))
}
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Error(err)
w.WriteHeader(500)
return
}
(&wsConn{
conn: c,
handler: s,
exiting: make(chan struct{}),
}).handleWsConn(ctx)
if err := c.Close(); err != nil {
log.Error(err)
return
}
}
// TODO: return errors to clients per spec
func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
h := strings.ToLower(r.Header.Get("Connection"))
if strings.Contains(h, "upgrade") {
s.handleWS(ctx, w, r)
return
}
s.handleReader(ctx, r.Body, w, rpcError)
}
func rpcError(wf func(func(io.Writer)), req *request, code int, err error) {
log.Errorf("RPC Error: %s", err)
wf(func(w io.Writer) {
if hw, ok := w.(http.ResponseWriter); ok {
hw.WriteHeader(500)
}
log.Warnf("rpc error: %s", err)
if req.ID == nil { // notification
return
}
resp := response{
Jsonrpc: "2.0",
ID: *req.ID,
Error: &respError{
Code: code,
Message: err.Error(),
},
}
err = json.NewEncoder(w).Encode(resp)
if err != nil {
log.Warnf("failed to write rpc error: %s", err)
return
}
})
}
// Register registers new RPC handler
//
// Handler is any value with methods defined
func (s *RPCServer) Register(namespace string, handler interface{}) {
s.register(namespace, handler)
}
var _ error = &respError{}