2019-07-12 17:12:38 +00:00
|
|
|
package jsonrpc
|
|
|
|
|
|
|
|
import (
|
2019-07-18 17:41:19 +00:00
|
|
|
"context"
|
2019-07-12 17:12:38 +00:00
|
|
|
"encoding/json"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
2019-07-24 17:09:00 +00:00
|
|
|
"strings"
|
2019-07-24 01:16:17 +00:00
|
|
|
|
|
|
|
"github.com/gorilla/websocket"
|
2019-07-12 17:12:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
rpcParseError = -32700
|
|
|
|
rpcMethodNotFound = -32601
|
|
|
|
rpcInvalidParams = -32602
|
|
|
|
)
|
|
|
|
|
|
|
|
// RPCServer provides a jsonrpc 2.0 http server handler
|
|
|
|
type RPCServer struct {
|
|
|
|
methods handlers
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewServer creates new RPCServer instance
|
|
|
|
func NewServer() *RPCServer {
|
|
|
|
return &RPCServer{
|
|
|
|
methods: map[string]rpcHandler{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-24 17:09:00 +00:00
|
|
|
var upgrader = websocket.Upgrader{
|
|
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
}
|
2019-07-12 17:12:38 +00:00
|
|
|
|
2019-07-18 17:41:19 +00:00
|
|
|
func (s *RPCServer) handleWS(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
2019-07-24 17:09:00 +00:00
|
|
|
// 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"))
|
|
|
|
}
|
|
|
|
|
2019-07-12 17:12:38 +00:00
|
|
|
c, err := upgrader.Upgrade(w, r, nil)
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
w.WriteHeader(500)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-07-23 01:20:48 +00:00
|
|
|
(&wsConn{
|
2019-07-23 01:45:10 +00:00
|
|
|
conn: c,
|
|
|
|
handler: s.methods,
|
2019-07-18 17:41:19 +00:00
|
|
|
}).handleWsConn(ctx)
|
2019-07-15 16:32:43 +00:00
|
|
|
|
|
|
|
if err := c.Close(); err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2019-07-12 17:12:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: return errors to clients per spec
|
|
|
|
func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
2019-07-18 17:41:19 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
|
2019-07-24 17:09:00 +00:00
|
|
|
if strings.Contains(r.Header.Get("Connection"), "Upgrade") {
|
2019-07-18 17:41:19 +00:00
|
|
|
s.handleWS(ctx, w, r)
|
2019-07-12 17:12:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-07-18 17:41:19 +00:00
|
|
|
s.methods.handleReader(ctx, r.Body, w, rpcError)
|
2019-07-12 17:12:38 +00:00
|
|
|
}
|
|
|
|
|
2019-07-15 16:21:48 +00:00
|
|
|
func rpcError(wf func(func(io.Writer)), req *request, code int, err error) {
|
|
|
|
wf(func(w io.Writer) {
|
|
|
|
if hw, ok := w.(http.ResponseWriter); ok {
|
|
|
|
hw.WriteHeader(500)
|
|
|
|
}
|
2019-07-12 17:12:38 +00:00
|
|
|
|
2019-07-22 18:13:41 +00:00
|
|
|
log.Warnf("rpc error: %s", err)
|
|
|
|
|
2019-07-15 16:21:48 +00:00
|
|
|
if req.ID == nil { // notification
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := response{
|
|
|
|
Jsonrpc: "2.0",
|
|
|
|
ID: *req.ID,
|
|
|
|
Error: &respError{
|
|
|
|
Code: code,
|
|
|
|
Message: err.Error(),
|
|
|
|
},
|
|
|
|
}
|
2019-07-12 17:12:38 +00:00
|
|
|
|
2019-07-22 18:13:41 +00:00
|
|
|
err = json.NewEncoder(w).Encode(resp)
|
|
|
|
if err != nil {
|
|
|
|
log.Warnf("failed to write rpc error: %s", err)
|
|
|
|
return
|
|
|
|
}
|
2019-07-15 16:21:48 +00:00
|
|
|
})
|
2019-07-12 17:12:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Register registers new RPC handler
|
|
|
|
//
|
|
|
|
// Handler is any value with methods defined
|
|
|
|
func (s *RPCServer) Register(namespace string, handler interface{}) {
|
|
|
|
s.methods.register(namespace, handler)
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ error = &respError{}
|