"refactor(common): 重构序列化工具包,将serialize重命名为utils并添加bitset组件"
This commit is contained in:
183
common/utils/go-jsonrpc/server.go
Normal file
183
common/utils/go-jsonrpc/server.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
const (
|
||||
rpcParseError = -32700
|
||||
rpcInvalidRequest = -32600
|
||||
rpcMethodNotFound = -32601
|
||||
rpcInvalidParams = -32602
|
||||
)
|
||||
|
||||
// ConnectionType indicates the type of connection, this is set in the context and can be retrieved
|
||||
// with GetConnectionType.
|
||||
type ConnectionType string
|
||||
|
||||
const (
|
||||
// ConnectionTypeUnknown indicates that the connection type cannot be determined, likely because
|
||||
// it hasn't passed through an RPCServer.
|
||||
ConnectionTypeUnknown ConnectionType = "unknown"
|
||||
// ConnectionTypeHTTP indicates that the connection is an HTTP connection.
|
||||
ConnectionTypeHTTP ConnectionType = "http"
|
||||
// ConnectionTypeWS indicates that the connection is a WebSockets connection.
|
||||
ConnectionTypeWS ConnectionType = "websockets"
|
||||
)
|
||||
|
||||
var connectionTypeCtxKey = &struct{ name string }{"jsonrpc-connection-type"}
|
||||
|
||||
// GetConnectionType returns the connection type of the request if it was set by an RPCServer.
|
||||
// A connection type of ConnectionTypeUnknown means that the connection type was not set.
|
||||
func GetConnectionType(ctx context.Context) ConnectionType {
|
||||
if v := ctx.Value(connectionTypeCtxKey); v != nil {
|
||||
return v.(ConnectionType)
|
||||
}
|
||||
return ConnectionTypeUnknown
|
||||
}
|
||||
|
||||
// RPCServer provides a jsonrpc 2.0 http server handler
|
||||
type RPCServer struct {
|
||||
*handler
|
||||
reverseClientBuilder func(context.Context, *wsConn) (context.Context, error)
|
||||
|
||||
pingInterval time.Duration
|
||||
}
|
||||
|
||||
// NewServer creates new RPCServer instance
|
||||
func NewServer(opts ...ServerOption) *RPCServer {
|
||||
config := defaultServerConfig()
|
||||
for _, o := range opts {
|
||||
o(&config)
|
||||
}
|
||||
|
||||
return &RPCServer{
|
||||
handler: makeHandler(config),
|
||||
reverseClientBuilder: config.reverseClientBuilder,
|
||||
|
||||
pingInterval: config.pingInterval,
|
||||
}
|
||||
}
|
||||
|
||||
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.Errorw("upgrading connection", "error", err)
|
||||
// note that upgrader.Upgrade will set http error if there is an error
|
||||
return
|
||||
}
|
||||
|
||||
wc := &wsConn{
|
||||
conn: c,
|
||||
handler: s,
|
||||
pingInterval: s.pingInterval,
|
||||
exiting: make(chan struct{}),
|
||||
}
|
||||
|
||||
if s.reverseClientBuilder != nil {
|
||||
ctx, err = s.reverseClientBuilder(ctx, wc)
|
||||
if err != nil {
|
||||
log.Errorf("failed to build reverse client: %s", err)
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
lbl := pprof.Labels("jrpc-mode", "wsserver", "jrpc-remote", r.RemoteAddr, "jrpc-uuid", uuid.New().String())
|
||||
pprof.Do(ctx, lbl, func(ctx context.Context) {
|
||||
wc.handleWsConn(ctx)
|
||||
})
|
||||
|
||||
if err := c.Close(); err != nil {
|
||||
log.Errorw("closing websocket connection", "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") {
|
||||
ctx = context.WithValue(ctx, connectionTypeCtxKey, ConnectionTypeWS)
|
||||
s.handleWS(ctx, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, connectionTypeCtxKey, ConnectionTypeHTTP)
|
||||
s.handleReader(ctx, r.Body, w, rpcError)
|
||||
}
|
||||
|
||||
func (s *RPCServer) HandleRequest(ctx context.Context, r io.Reader, w io.Writer) {
|
||||
s.handleReader(ctx, r, w, rpcError)
|
||||
}
|
||||
|
||||
func rpcError(wf func(func(io.Writer)), req *request, code ErrorCode, err error) {
|
||||
log.Errorf("RPC Error: %s", err)
|
||||
wf(func(w io.Writer) {
|
||||
if hw, ok := w.(http.ResponseWriter); ok {
|
||||
if code == rpcInvalidRequest {
|
||||
hw.WriteHeader(http.StatusBadRequest)
|
||||
} else {
|
||||
hw.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
log.Warnf("rpc error: %s", err)
|
||||
|
||||
if req == nil {
|
||||
req = &request{}
|
||||
}
|
||||
|
||||
resp := response{
|
||||
Jsonrpc: "2.0",
|
||||
ID: req.ID,
|
||||
Error: &JSONRPCError{
|
||||
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)
|
||||
}
|
||||
|
||||
func (s *RPCServer) AliasMethod(alias, original string) {
|
||||
s.aliasedMethods[alias] = original
|
||||
}
|
||||
|
||||
var _ error = &JSONRPCError{}
|
||||
Reference in New Issue
Block a user