diff --git a/contrib/codecs/http/codec.go b/contrib/codecs/http/codec.go index cd84c8bc0ffb4376f8ab53f0d2047b19dc157682..3bb8a3d4f9abccba181855de73584b5d42ca4453 100644 --- a/contrib/codecs/http/codec.go +++ b/contrib/codecs/http/codec.go @@ -91,7 +91,7 @@ func (c *Codec) PeerInfo() jsonrpc.PeerInfo { func (r *Codec) doReadGet() (msg *serverutil.Bundle, err error) { method_up := r.r.URL.Query().Get("method") if method_up == "" { - method_up = r.r.URL.Path + method_up = strings.TrimPrefix(r.r.URL.Path, "/") } params, _ := url.QueryUnescape(r.r.URL.Query().Get("params")) param := []byte(params) diff --git a/contrib/handlers/argreflect/reflect_handler.go b/contrib/handlers/argreflect/reflect_handler.go index 242263fa8ad4b4c5dd066b7e85457eb9dbd79f27..58d7be9f47afbd175059eeec6ad4e4eb618a9924 100644 --- a/contrib/handlers/argreflect/reflect_handler.go +++ b/contrib/handlers/argreflect/reflect_handler.go @@ -2,12 +2,11 @@ package argreflect import ( "context" - "fmt" "reflect" - "runtime" "unicode" "gfx.cafe/open/jrpc/pkg/jsonrpc" + "github.com/davecgh/go-spew/spew" ) var ( @@ -40,6 +39,42 @@ func suitableCallbacks(receiver reflect.Value) map[string]jsonrpc.Handler { return callbacks } +func NewCallback(receiver, fn reflect.Value) jsonrpc.Handler { + return newCallback(receiver, fn) +} + +// newCallback turns fn (a function) into a handler. It returns nil if the function +// is unsuitable as an RPC callback. +func newCallback(receiver, fn reflect.Value) jsonrpc.Handler { + fntype := fn.Type() + c := &callback{fn: fn, rcvr: receiver, errPos: -1} + // Determine parameter types. They must all be exported or builtin types. + if x := c.makeArgTypes(); x != nil { + return x + } + + // Verify return types. The function must return at most one error + // and/or one other non-error value. + outs := make([]reflect.Type, fntype.NumOut()) + for i := 0; i < fntype.NumOut(); i++ { + outs[i] = fntype.Out(i) + } + if len(outs) > 2 { + return nil + } + // If an error is returned, it must be the last returned value. + switch { + case len(outs) == 1 && isErrorType(outs[0]): + c.errPos = 0 + case len(outs) == 2: + if isErrorType(outs[0]) || !isErrorType(outs[1]) { + return nil + } + c.errPos = 1 + } + return c +} + // callback is a method callback which was registered in the server type callback struct { fn reflect.Value // the function @@ -66,18 +101,6 @@ func (e *callback) ServeRPC(w jsonrpc.ResponseWriter, r *jsonrpc.Request) { fullargs = append(fullargs, reflect.ValueOf(r.Context())) } fullargs = append(fullargs, args...) - // Catch panic while running the callback. - defer func() { - if err := recover(); err != nil { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - // fmt.Sprintf(`crashed: method=%s err=%v crashed=%s\n`, r.Method, err, string(buf)) - // errRes := errors.New("method handler crashed: " + fmt.Sprint(err)) - w.Send(nil, fmt.Errorf("%s", err)) - return - } - }() // Run the callback. results := e.fn.Call(fullargs) if e.errPos >= 0 && !results[e.errPos].IsNil() { @@ -93,42 +116,11 @@ func (e *callback) ServeRPC(w jsonrpc.ResponseWriter, r *jsonrpc.Request) { w.Send(results[0].Interface(), nil) } -func NewCallback(receiver, fn reflect.Value) jsonrpc.Handler { - return newCallback(receiver, fn) -} - -// newCallback turns fn (a function) into a callback object. It returns nil if the function -// is unsuitable as an RPC callback. -func newCallback(receiver, fn reflect.Value) *callback { - fntype := fn.Type() - c := &callback{fn: fn, rcvr: receiver, errPos: -1} - // Determine parameter types. They must all be exported or builtin types. - c.makeArgTypes() - - // Verify return types. The function must return at most one error - // and/or one other non-error value. - outs := make([]reflect.Type, fntype.NumOut()) - for i := 0; i < fntype.NumOut(); i++ { - outs[i] = fntype.Out(i) - } - if len(outs) > 2 { - return nil - } - // If an error is returned, it must be the last returned value. - switch { - case len(outs) == 1 && isErrorType(outs[0]): - c.errPos = 0 - case len(outs) == 2: - if isErrorType(outs[0]) || !isErrorType(outs[1]) { - return nil - } - c.errPos = 1 - } - return c -} +var jrpcResponseWriterType = reflect.TypeOf((*jsonrpc.ResponseWriter)(nil)).Elem() +var jrpcRequestType = reflect.TypeOf(&jsonrpc.Request{}) // makeArgTypes composes the argTypes list. -func (c *callback) makeArgTypes() { +func (c *callback) makeArgTypes() jsonrpc.Handler { fntype := c.fn.Type() // Skip receiver and context.Context parameter (if present). firstArg := 0 @@ -139,11 +131,31 @@ func (c *callback) makeArgTypes() { c.hasCtx = true firstArg++ } + // Add all remaining parameters. c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg) for i := firstArg; i < fntype.NumIn(); i++ { c.argTypes[i-firstArg] = fntype.In(i) } + if len(c.argTypes) == 2 { + // special case to see if it is a valid jsonrpc.Handler + if c.argTypes[1].AssignableTo(jrpcRequestType) && c.argTypes[0].Implements(jrpcResponseWriterType) { + if !c.rcvr.IsValid() { + cb, ok := c.fn.Interface().(func(jsonrpc.ResponseWriter, *jsonrpc.Request)) + if !ok { + spew.Println("bad callback:", c) + panic("invalid callback registered") + } + return jsonrpc.HandlerFunc(cb) + } else { + return jsonrpc.HandlerFunc(func(w jsonrpc.ResponseWriter, r *jsonrpc.Request) { + c.fn.Call([]reflect.Value{c.rcvr, reflect.ValueOf(w), reflect.ValueOf(r)}) + }) + } + } + + } + return nil } // Does t satisfy the error interface? diff --git a/example/echo/main.go b/example/echo/main.go index 8e82aa56741f25137253587b5478f366cb20ec60..69af46e23469b1c90bdd41eaeedf9e9475d09e94 100644 --- a/example/echo/main.go +++ b/example/echo/main.go @@ -19,8 +19,23 @@ func main() { w.Send(r.Params, nil) }) + r.RegisterFunc("echo/register", func(w jsonrpc.ResponseWriter, r *jsonrpc.Request) { + w.Send(r.Params, nil) + }) + + server := &EchoServer{} + r.RegisterStruct("server", server) + log.Println("running on 8855") log.Println(http.ListenAndServe(":8855", codecs.HttpHandler(srv))) } +type EchoServer struct { +} + +func (e *EchoServer) Echo(w jsonrpc.ResponseWriter, r *jsonrpc.Request) { + w.Send(r.Params, nil) +} + // http://localhost:8855/?method=echo¶ms=[1,2,3] +// http://localhost:8855/?method=server/echo¶ms=[1,2,3]