From: Michael Fraenkel Date: Thu, 23 Mar 2017 19:08:43 +0000 (-0400) Subject: net/rpc: Create empty maps and slices as return type X-Git-Tag: go1.9beta1~1008 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0ca8701ea2a9d5972285fd7833b67f9f15a403c6;p=gostls13.git net/rpc: Create empty maps and slices as return type When a map or slice is used as a return type create an empty value rather than a nil value. Fixes #19588 Change-Id: I577fd74956172329745d614ac37d4db8f737efb8 Reviewed-on: https://go-review.googlesource.com/38474 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- diff --git a/src/net/rpc/jsonrpc/all_test.go b/src/net/rpc/jsonrpc/all_test.go index b811d3c0c7..bbb8eb0291 100644 --- a/src/net/rpc/jsonrpc/all_test.go +++ b/src/net/rpc/jsonrpc/all_test.go @@ -13,6 +13,7 @@ import ( "io/ioutil" "net" "net/rpc" + "reflect" "strings" "testing" ) @@ -55,8 +56,26 @@ func (t *Arith) Error(args *Args, reply *Reply) error { panic("ERROR") } +type BuiltinTypes struct{} + +func (BuiltinTypes) Map(i int, reply *map[int]int) error { + (*reply)[i] = i + return nil +} + +func (BuiltinTypes) Slice(i int, reply *[]int) error { + *reply = append(*reply, i) + return nil +} + +func (BuiltinTypes) Array(i int, reply *[1]int) error { + (*reply)[0] = i + return nil +} + func init() { rpc.Register(new(Arith)) + rpc.Register(BuiltinTypes{}) } func TestServerNoParams(t *testing.T) { @@ -182,6 +201,45 @@ func TestClient(t *testing.T) { } } +func TestBuiltinTypes(t *testing.T) { + cli, srv := net.Pipe() + go ServeConn(srv) + + client := NewClient(cli) + defer client.Close() + + // Map + arg := 7 + replyMap := map[int]int{} + err := client.Call("BuiltinTypes.Map", arg, &replyMap) + if err != nil { + t.Errorf("Map: expected no error but got string %q", err.Error()) + } + if replyMap[arg] != arg { + t.Errorf("Map: expected %d got %d", arg, replyMap[arg]) + } + + // Slice + replySlice := []int{} + err = client.Call("BuiltinTypes.Slice", arg, &replySlice) + if err != nil { + t.Errorf("Slice: expected no error but got string %q", err.Error()) + } + if e := []int{arg}; !reflect.DeepEqual(replySlice, e) { + t.Errorf("Slice: expected %v got %v", e, replySlice) + } + + // Array + replyArray := [1]int{} + err = client.Call("BuiltinTypes.Array", arg, &replyArray) + if err != nil { + t.Errorf("Array: expected no error but got string %q", err.Error()) + } + if e := [1]int{arg}; !reflect.DeepEqual(replyArray, e) { + t.Errorf("Array: expected %v got %v", e, replyArray) + } +} + func TestMalformedInput(t *testing.T) { cli, srv := net.Pipe() go cli.Write([]byte(`{id:1}`)) // invalid json diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go index 18ea629b0d..1bc570ba52 100644 --- a/src/net/rpc/server.go +++ b/src/net/rpc/server.go @@ -571,6 +571,13 @@ func (server *Server) readRequest(codec ServerCodec) (service *service, mtype *m } replyv = reflect.New(mtype.ReplyType.Elem()) + + switch mtype.ReplyType.Elem().Kind() { + case reflect.Map: + replyv.Elem().Set(reflect.MakeMap(mtype.ReplyType.Elem())) + case reflect.Slice: + replyv.Elem().Set(reflect.MakeSlice(mtype.ReplyType.Elem(), 0, 0)) + } return } diff --git a/src/net/rpc/server_test.go b/src/net/rpc/server_test.go index b94ea6f6ab..fb97f82a2f 100644 --- a/src/net/rpc/server_test.go +++ b/src/net/rpc/server_test.go @@ -11,6 +11,7 @@ import ( "log" "net" "net/http/httptest" + "reflect" "runtime" "strings" "sync" @@ -85,6 +86,24 @@ type Embed struct { hidden } +type BuiltinTypes struct{} + +func (BuiltinTypes) Map(args *Args, reply *map[int]int) error { + (*reply)[args.A] = args.B + return nil +} + +func (BuiltinTypes) Slice(args *Args, reply *[]int) error { + *reply = append(*reply, args.A, args.B) + return nil +} + +func (BuiltinTypes) Array(args *Args, reply *[2]int) error { + (*reply)[0] = args.A + (*reply)[1] = args.B + return nil +} + func listenTCP() (net.Listener, string) { l, e := net.Listen("tcp", "127.0.0.1:0") // any available address if e != nil { @@ -97,6 +116,7 @@ func startServer() { Register(new(Arith)) Register(new(Embed)) RegisterName("net.rpc.Arith", new(Arith)) + Register(BuiltinTypes{}) var l net.Listener l, serverAddr = listenTCP() @@ -326,6 +346,49 @@ func testHTTPRPC(t *testing.T, path string) { } } +func TestBuiltinTypes(t *testing.T) { + once.Do(startServer) + + client, err := DialHTTP("tcp", httpServerAddr) + if err != nil { + t.Fatal("dialing", err) + } + defer client.Close() + + // Map + args := &Args{7, 8} + replyMap := map[int]int{} + err = client.Call("BuiltinTypes.Map", args, &replyMap) + if err != nil { + t.Errorf("Map: expected no error but got string %q", err.Error()) + } + if replyMap[args.A] != args.B { + t.Errorf("Map: expected %d got %d", args.B, replyMap[args.A]) + } + + // Slice + args = &Args{7, 8} + replySlice := []int{} + err = client.Call("BuiltinTypes.Slice", args, &replySlice) + if err != nil { + t.Errorf("Slice: expected no error but got string %q", err.Error()) + } + if e := []int{args.A, args.B}; !reflect.DeepEqual(replySlice, e) { + t.Errorf("Slice: expected %v got %v", e, replySlice) + } + + // Array + args = &Args{7, 8} + replyArray := [2]int{} + err = client.Call("BuiltinTypes.Array", args, &replyArray) + if err != nil { + t.Errorf("Array: expected no error but got string %q", err.Error()) + } + if e := [2]int{args.A, args.B}; !reflect.DeepEqual(replyArray, e) { + t.Errorf("Array: expected %v got %v", e, replyArray) + } +} + // CodecEmulator provides a client-like api and a ServerCodec interface. // Can be used to test ServeRequest. type CodecEmulator struct {