lotus/rpclib/rpcServer.go

196 lines
3.6 KiB
Go
Raw Normal View History

package rpclib
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"reflect"
)
type rpcHandler struct {
paramReceivers []reflect.Type
nParams int
receiver reflect.Value
handlerFunc reflect.Value
hasCtx int
errOut int
valOut int
}
type RPCServer struct {
methods map[string]rpcHandler
}
func NewServer() *RPCServer {
return &RPCServer{
methods: map[string]rpcHandler{},
}
}
type param struct {
data []byte // from unmarshal
v reflect.Value // to marshal
}
func (p *param) UnmarshalJSON(raw []byte) error {
p.data = make([]byte, len(raw))
copy(p.data, raw)
return nil
}
func (p *param) MarshalJSON() ([]byte, error) {
return json.Marshal(p.v.Interface())
}
type request struct {
Jsonrpc string `json:"jsonrpc"`
Id *int64 `json:"id,omitempty"`
Method string `json:"method"`
Params []param `json:"params"`
}
type respError struct {
Code int `json:"code"`
Message string `json:"message"`
}
type response struct {
Jsonrpc string `json:"jsonrpc"`
Result interface{} `json:"result,omitempty"`
Id int64 `json:"id"`
Error *respError `json:"error,omitempty"`
}
func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var req request
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(500)
return
}
handler, ok := s.methods[req.Method]
if !ok {
w.WriteHeader(500)
return
}
if len(req.Params) != handler.nParams {
w.WriteHeader(500)
return
}
callParams := make([]reflect.Value, 1+handler.hasCtx+handler.nParams)
callParams[0] = handler.receiver
if handler.hasCtx == 1 {
callParams[1] = reflect.ValueOf(r.Context())
}
for i := 0; i < handler.nParams; i++ {
rp := reflect.New(handler.paramReceivers[i])
if err := json.NewDecoder(bytes.NewReader(req.Params[i].data)).Decode(rp.Interface()); err != nil {
w.WriteHeader(500)
fmt.Println(err)
return
}
callParams[i+1+handler.hasCtx] = reflect.ValueOf(rp.Elem().Interface())
}
callResult := handler.handlerFunc.Call(callParams)
if req.Id == nil {
return // notification
}
resp := response{
Jsonrpc: "2.0",
Id: *req.Id,
}
if handler.errOut != -1 {
err := callResult[handler.errOut].Interface()
if err != nil {
resp.Error = &respError{
Code: 1,
Message: err.(error).Error(),
}
}
}
if handler.valOut != -1 {
resp.Result = callResult[handler.valOut].Interface()
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
w.WriteHeader(500)
return
}
}
func (s *RPCServer) Register(r interface{}) {
val := reflect.ValueOf(r)
//TODO: expect ptr
name := val.Type().Elem().Name()
for i := 0; i < val.NumMethod(); i++ {
method := val.Type().Method(i)
funcType := method.Func.Type()
hasCtx := 0
if funcType.NumIn() >= 2 && funcType.In(1) == contextType {
hasCtx = 1
}
ins := funcType.NumIn() - 1 - hasCtx
recvs := make([]reflect.Type, ins)
for i := 0; i < ins; i++ {
recvs[i] = method.Type.In(i + 1 + hasCtx)
}
valOut, errOut, _ := processFuncOut(funcType)
s.methods[name+"."+method.Name] = rpcHandler{
paramReceivers: recvs,
nParams: ins,
handlerFunc: method.Func,
receiver: val,
hasCtx: hasCtx,
errOut: errOut,
valOut: valOut,
}
}
}
func processFuncOut(funcType reflect.Type) (valOut int, errOut int, n int) {
errOut = -1
valOut = -1
n = funcType.NumOut()
switch n {
case 0:
case 1:
if funcType.Out(0) == errorType {
errOut = 0
} else {
valOut = 0
}
case 2:
valOut = 0
errOut = 1
if funcType.Out(1) != errorType {
panic("expected error as second return value")
}
default:
panic("too many error values")
}
return
}