func (*reflectSuite) TestValueOf(c *gc.C) { v := rpcreflect.ValueOf(reflect.ValueOf(nil)) c.Check(v.IsValid(), jc.IsFalse) c.Check(func() { v.MethodCaller("foo", "bar") }, gc.PanicMatches, "MethodCaller called on invalid Value") root := &Root{} v = rpcreflect.ValueOf(reflect.ValueOf(root)) c.Check(v.IsValid(), jc.IsTrue) c.Check(v.GoValue().Interface(), gc.Equals, root) }
func (*reflectSuite) TestMethodCaller(c *gc.C) { // MethodCaller is actually extensively tested because it's // used in the implementation of the rpc server, // so just a simple sanity check test here. root := &Root{ simple: make(map[string]*SimpleMethods), } root.simple["a99"] = &SimpleMethods{root: root, id: "a99"} v := rpcreflect.ValueOf(reflect.ValueOf(root)) m, err := v.MethodCaller("foo", "bar") c.Assert(err, gc.ErrorMatches, `unknown object type "foo"`) c.Assert(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Assert(m, gc.DeepEquals, rpcreflect.MethodCaller{}) m, err = v.MethodCaller("SimpleMethods", "bar") c.Assert(err, gc.ErrorMatches, "no such request - method SimpleMethods.bar is not implemented") c.Assert(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Assert(m, gc.DeepEquals, rpcreflect.MethodCaller{}) m, err = v.MethodCaller("SimpleMethods", "Call1r1e") c.Assert(err, gc.IsNil) c.Assert(m.ParamsType, gc.Equals, reflect.TypeOf(stringVal{})) c.Assert(m.ResultType, gc.Equals, reflect.TypeOf(stringVal{})) ret, err := m.Call("a99", reflect.ValueOf(stringVal{"foo"})) c.Assert(err, gc.IsNil) c.Assert(ret.Interface(), gc.Equals, stringVal{"Call1r1e ret"}) }
// Serve serves RPC requests on the connection by invoking methods on // root. Note that it does not start the connection running, // though it may be called once the connection is already started. // // The server executes each client request by calling a method on root // to obtain an object to act on; then it invokes an method on that // object with the request parameters, possibly returning some result. // // Methods on the root value are of the form: // // M(id string) (O, error) // // where M is an exported name, conventionally naming the object type, // id is some identifier for the object and O is the type of the // returned object. // // Methods defined on O may defined in one of the following forms, where // T and R must be struct types. // // Method() // Method() R // Method() (R, error) // Method() error // Method(T) // Method(T) R // Method(T) (R, error) // Method(T) error // // If transformErrors is non-nil, it will be called on all returned // non-nil errors, for example to transform the errors into ServerErrors // with specified codes. There will be a panic if transformErrors // returns nil. // // Serve may be called at any time on a connection to change the // set of methods being served by the connection. This will have // no effect on calls that are currently being services. // If root is nil, the connection will serve no methods. func (conn *Conn) Serve(root interface{}, transformErrors func(error) error) { rootValue := rpcreflect.ValueOf(reflect.ValueOf(root)) if rootValue.IsValid() && transformErrors == nil { transformErrors = func(err error) error { return err } } conn.mutex.Lock() defer conn.mutex.Unlock() conn.rootValue = rootValue conn.transformErrors = transformErrors }