Commit 0ca8701e authored by Michael Fraenkel's avatar Michael Fraenkel Committed by Brad Fitzpatrick

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: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 70ea0ec3
......@@ -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
......
......@@ -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
}
......
......@@ -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 {
......
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