Commit 08483def authored by Rob Pike's avatar Rob Pike

rpc: allow non-struct args and reply (they must still be pointers)

R=rsc
CC=golang-dev
https://golang.org/cl/1722046
parent eb20ba6d
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
Only methods that satisfy these criteria will be made available for remote access; Only methods that satisfy these criteria will be made available for remote access;
other methods will be ignored: other methods will be ignored:
- the method name is publicly visible, that is, begins with an upper case letter. - the method receiver and name are publicly visible, that is, begin with an upper case letter.
- the method has two arguments, both pointers to publicly visible structs. - the method has two arguments, both pointers to publicly visible types.
- the method has return type os.Error. - the method has return type os.Error.
The method's first argument represents the arguments provided by the caller; the The method's first argument represents the arguments provided by the caller; the
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
NewClient on the connection. The convenience function Dial (DialHTTP) performs NewClient on the connection. The convenience function Dial (DialHTTP) performs
both steps for a raw network connection (an HTTP connection). The resulting both steps for a raw network connection (an HTTP connection). The resulting
Client object has two methods, Call and Go, that specify the service and method to Client object has two methods, Call and Go, that specify the service and method to
call, a structure containing the arguments, and a structure to receive the result call, a pointer containing the arguments, and a pointer to receive the result
parameters. parameters.
Call waits for the remote call to complete; Go launches the call asynchronously Call waits for the remote call to complete; Go launches the call asynchronously
...@@ -46,22 +46,23 @@ ...@@ -46,22 +46,23 @@
A, B int A, B int
} }
type Reply struct { type Quotient struct {
C int Quo, Rem int
} }
type Arith int type Arith int
func (t *Arith) Multiply(args *Args, reply *Reply) os.Error { func (t *Arith) Multiply(args *Args, reply *int) os.Error {
reply.C = args.A * args.B *reply = args.A * args.B
return nil return nil
} }
func (t *Arith) Divide(args *Args, reply *Reply) os.Error { func (t *Arith) Divide(args *Args, quo *Quotient) os.Error {
if args.B == 0 { if args.B == 0 {
return os.ErrorString("divide by zero") return os.ErrorString("divide by zero")
} }
reply.C = args.A / args.B quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil return nil
} }
...@@ -88,17 +89,18 @@ ...@@ -88,17 +89,18 @@
// Synchronous call // Synchronous call
args := &server.Args{7,8} args := &server.Args{7,8}
reply := new(server.Reply) var reply int
err = client.Call("Arith.Multiply", args, reply) err = client.Call("Arith.Multiply", args, &reply)
if err != nil { if err != nil {
log.Exit("arith error:", err) log.Exit("arith error:", err)
} }
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply.C) fmt.Printf("Arith: %d*%d=%d", args.A, args.B, *reply)
or or
// Asynchronous call // Asynchronous call
divCall := client.Go("Arith.Divide", args, reply, nil) quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, &quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc. // check errors, print, etc.
...@@ -193,7 +195,7 @@ func (server *serverType) register(rcvr interface{}) os.Error { ...@@ -193,7 +195,7 @@ func (server *serverType) register(rcvr interface{}) os.Error {
if sname == "" { if sname == "" {
log.Exit("rpc: no service name for type", s.typ.String()) log.Exit("rpc: no service name for type", s.typ.String())
} }
if !isPublic(sname) { if s.typ.PkgPath() != "" && !isPublic(sname) {
s := "rpc Register: type " + sname + " is not public" s := "rpc Register: type " + sname + " is not public"
log.Stderr(s) log.Stderr(s)
return os.ErrorString(s) return os.ErrorString(s)
...@@ -209,11 +211,10 @@ func (server *serverType) register(rcvr interface{}) os.Error { ...@@ -209,11 +211,10 @@ func (server *serverType) register(rcvr interface{}) os.Error {
method := s.typ.Method(m) method := s.typ.Method(m)
mtype := method.Type mtype := method.Type
mname := method.Name mname := method.Name
if !isPublic(mname) { if mtype.PkgPath() != "" && !isPublic(mname) {
continue continue
} }
// Method needs three ins: receiver, *args, *reply. // Method needs three ins: receiver, *args, *reply.
// The args and reply must be structs until gobs are more general.
if mtype.NumIn() != 3 { if mtype.NumIn() != 3 {
log.Stderr("method", mname, "has wrong number of ins:", mtype.NumIn()) log.Stderr("method", mname, "has wrong number of ins:", mtype.NumIn())
continue continue
...@@ -223,24 +224,16 @@ func (server *serverType) register(rcvr interface{}) os.Error { ...@@ -223,24 +224,16 @@ func (server *serverType) register(rcvr interface{}) os.Error {
log.Stderr(mname, "arg type not a pointer:", mtype.In(1)) log.Stderr(mname, "arg type not a pointer:", mtype.In(1))
continue continue
} }
if _, ok := argType.Elem().(*reflect.StructType); !ok {
log.Stderr(mname, "arg type not a pointer to a struct:", argType)
continue
}
replyType, ok := mtype.In(2).(*reflect.PtrType) replyType, ok := mtype.In(2).(*reflect.PtrType)
if !ok { if !ok {
log.Stderr(mname, "reply type not a pointer:", mtype.In(2)) log.Stderr(mname, "reply type not a pointer:", mtype.In(2))
continue continue
} }
if _, ok := replyType.Elem().(*reflect.StructType); !ok { if argType.Elem().PkgPath() != "" && !isPublic(argType.Elem().Name()) {
log.Stderr(mname, "reply type not a pointer to a struct:", replyType)
continue
}
if !isPublic(argType.Elem().Name()) {
log.Stderr(mname, "argument type not public:", argType) log.Stderr(mname, "argument type not public:", argType)
continue continue
} }
if !isPublic(replyType.Elem().Name()) { if replyType.Elem().PkgPath() != "" && !isPublic(replyType.Elem().Name()) {
log.Stderr(mname, "reply type not public:", replyType) log.Stderr(mname, "reply type not public:", replyType)
continue continue
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package rpc package rpc
import ( import (
"fmt"
"http" "http"
"log" "log"
"net" "net"
...@@ -48,6 +49,16 @@ func (t *Arith) Div(args *Args, reply *Reply) os.Error { ...@@ -48,6 +49,16 @@ func (t *Arith) Div(args *Args, reply *Reply) os.Error {
return nil return nil
} }
func (t *Arith) String(args *Args, reply *string) os.Error {
*reply = fmt.Sprintf("%d+%d=%d", args.A, args.B, args.A+args.B)
return nil
}
func (t *Arith) Scan(args *string, reply *Reply) (err os.Error) {
_, err = fmt.Sscan(*args, &reply.C)
return
}
func (t *Arith) Error(args *Args, reply *Reply) os.Error { func (t *Arith) Error(args *Args, reply *Reply) os.Error {
panic("ERROR") panic("ERROR")
} }
...@@ -136,6 +147,29 @@ func TestRPC(t *testing.T) { ...@@ -136,6 +147,29 @@ func TestRPC(t *testing.T) {
} else if err.String() != "divide by zero" { } else if err.String() != "divide by zero" {
t.Error("Div: expected divide by zero error; got", err) t.Error("Div: expected divide by zero error; got", err)
} }
// Non-struct argument
const Val = 12345
str := fmt.Sprint(Val)
reply = new(Reply)
err = client.Call("Arith.Scan", &str, reply)
if err != nil {
t.Errorf("Scan: expected no error but got string %q", err.String())
} else if reply.C != Val {
t.Errorf("Scan: expected %d got %d", Val, reply.C)
}
// Non-struct reply
args = &Args{27, 35}
str = ""
err = client.Call("Arith.String", args, &str)
if err != nil {
t.Errorf("String: expected no error but got string %q", err.String())
}
expect := fmt.Sprintf("%d+%d=%d", args.A, args.B, args.A+args.B)
if str != expect {
t.Errorf("String: expected %s got %s", expect, str)
}
} }
func TestHTTPRPC(t *testing.T) { func TestHTTPRPC(t *testing.T) {
...@@ -217,37 +251,44 @@ func TestCheckBadType(t *testing.T) { ...@@ -217,37 +251,44 @@ func TestCheckBadType(t *testing.T) {
} }
} }
type Bad int type ArgNotPointer int
type ReplyNotPointer int
type ArgNotPublic int
type ReplyNotPublic int
type local struct{} type local struct{}
func (t *Bad) ArgNotPointer(args Args, reply *Reply) os.Error { func (t *ArgNotPointer) ArgNotPointer(args Args, reply *Reply) os.Error {
return nil return nil
} }
func (t *Bad) ArgNotPointerToStruct(args *int, reply *Reply) os.Error { func (t *ReplyNotPointer) ReplyNotPointer(args *Args, reply Reply) os.Error {
return nil return nil
} }
func (t *Bad) ReplyNotPointer(args *Args, reply Reply) os.Error { func (t *ArgNotPublic) ArgNotPublic(args *local, reply *Reply) os.Error {
return nil return nil
} }
func (t *Bad) ReplyNotPointerToStruct(args *Args, reply *int) os.Error { func (t *ReplyNotPublic) ReplyNotPublic(args *Args, reply *local) os.Error {
return nil
}
func (t *Bad) ArgNotPublic(args *local, reply *Reply) os.Error {
return nil
}
func (t *Bad) ReplyNotPublic(args *Args, reply *local) os.Error {
return nil return nil
} }
// Check that registration handles lots of bad methods and a type with no suitable methods. // Check that registration handles lots of bad methods and a type with no suitable methods.
func TestRegistrationError(t *testing.T) { func TestRegistrationError(t *testing.T) {
err := Register(new(Bad)) err := Register(new(ArgNotPointer))
if err == nil {
t.Errorf("expected error registering ArgNotPointer")
}
err = Register(new(ReplyNotPointer))
if err == nil {
t.Errorf("expected error registering ReplyNotPointer")
}
err = Register(new(ArgNotPublic))
if err == nil {
t.Errorf("expected error registering ArgNotPublic")
}
err = Register(new(ReplyNotPublic))
if err == nil { if err == nil {
t.Errorf("expected error registering bad type") t.Errorf("expected error registering ReplyNotPublic")
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment