Permalink
Browse files

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 <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information...
fraenkel authored and bradfitz committed Mar 23, 2017
1 parent 70ea0ec commit 0ca8701ea2a9d5972285fd7833b67f9f15a403c6
Showing with 128 additions and 0 deletions.
  1. +58 −0 src/net/rpc/jsonrpc/all_test.go
  2. +7 −0 src/net/rpc/server.go
  3. +63 −0 src/net/rpc/server_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
View
@@ -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
}
View
@@ -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 {

0 comments on commit 0ca8701

Please sign in to comment.