// - for each of these servers: // * TCP server without context // * TCP server with context // * HTTP server with context // - call these methods: // * Sum() // * Name() // * NameCtx() // * TODO batch call all func TestContext(t *testing.T) { // Server provide a TCP transport without context. serverTCPNoCtx, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } defer serverTCPNoCtx.Close() go func() { for { conn, err := serverTCPNoCtx.Accept() if err != nil { return } go jsonrpc2.ServeConn(conn) } }() // Server provide a TCP transport with context. serverTCP, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } defer serverTCP.Close() go func() { for { conn, err := serverTCP.Accept() if err != nil { return } ctx := context.WithValue(context.Background(), remoteAddrContextKey, conn.RemoteAddr()) go jsonrpc2.ServeConnContext(ctx, conn) } }() // Server provide a HTTP transport with context. http.Handle("/", jsonrpc2.HTTPHandler(nil)) serverHTTP, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } defer serverHTTP.Close() go http.Serve(serverHTTP, nil) // Client use TCP transport without context. clientTCPNoCtx, err := jsonrpc2.Dial("tcp", serverTCPNoCtx.Addr().String()) if err != nil { t.Fatal(err) } defer clientTCPNoCtx.Close() // Client use TCP transport with context. clientTCP, err := jsonrpc2.Dial("tcp", serverTCP.Addr().String()) if err != nil { t.Fatal(err) } defer clientTCP.Close() // Client use HTTP transport with context. clientHTTP := jsonrpc2.NewHTTPClient("http://" + serverHTTP.Addr().String() + "/") defer clientHTTP.Close() cases := []struct { client *jsonrpc2.Client clientName string method string arg interface{} want interface{} }{ {clientTCPNoCtx, "clientTCPNoCtx", "CtxSvc.Sum", [2]int{3, 5}, 8.0}, {clientTCP, "clientTCP", "CtxSvc.Sum", [2]int{3, 5}, 8.0}, {clientHTTP, "clientHTTP", "CtxSvc.Sum", [2]int{3, 5}, 8.0}, {clientTCPNoCtx, "clientTCPNoCtx", "CtxSvc.Name", NameArg{"First", "Last"}, map[string]interface{}{ "Name": "First Last", }}, {clientTCP, "clientTCP", "CtxSvc.Name", NameArg{"First", "Last"}, map[string]interface{}{ "Name": "First Last", }}, {clientHTTP, "clientHTTP", "CtxSvc.Name", NameArg{"First", "Last"}, map[string]interface{}{ "Name": "First Last", }}, {clientTCPNoCtx, "clientTCPNoCtx", "CtxSvc.NameCtx", NameArg{"First", "Last"}, map[string]interface{}{ "Name": "First Last", "TCPRemoteAddr": "", "HTTPRemoteAddr": "", }}, {clientTCP, "clientTCP", "CtxSvc.NameCtx", NameArg{"First", "Last"}, map[string]interface{}{ "Name": "First Last", "TCPRemoteAddr": "127.0.0.1", "HTTPRemoteAddr": "", }}, {clientHTTP, "clientHTTP", "CtxSvc.NameCtx", NameArg{"First", "Last"}, map[string]interface{}{ "Name": "First Last", "TCPRemoteAddr": "", "HTTPRemoteAddr": "127.0.0.1", }}, } for _, v := range cases { var res interface{} err := v.client.Call(v.method, v.arg, &res) if err != nil { t.Errorf("%s.Call(%q) = %v", v.clientName, v.method, err) } if !reflect.DeepEqual(v.want, res) { t.Errorf("%s.Call(%q):\n\n\texp: %#v\n\n\tgot: %#v\n\n", v.clientName, v.method, v.want, res) } } }
func Example() { // Server export an object of type ExampleSvc. rpc.Register(&ExampleSvc{}) // Server provide a TCP transport. lnTCP, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { panic(err) } defer lnTCP.Close() go func() { for { conn, err := lnTCP.Accept() if err != nil { return } ctx := context.WithValue(context.Background(), RemoteAddrContextKey, conn.RemoteAddr()) go jsonrpc2.ServeConnContext(ctx, conn) } }() // Server provide a HTTP transport on /rpc endpoint. http.Handle("/rpc", jsonrpc2.HTTPHandler(nil)) lnHTTP, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { panic(err) } defer lnHTTP.Close() go http.Serve(lnHTTP, nil) // Client use TCP transport. clientTCP, err := jsonrpc2.Dial("tcp", lnTCP.Addr().String()) if err != nil { panic(err) } defer clientTCP.Close() // Client use HTTP transport. clientHTTP := jsonrpc2.NewHTTPClient("http://" + lnHTTP.Addr().String() + "/rpc") defer clientHTTP.Close() // Custom client use HTTP transport. clientCustomHTTP := jsonrpc2.NewCustomHTTPClient( "http://"+lnHTTP.Addr().String()+"/rpc", jsonrpc2.DoerFunc(func(req *http.Request) (*http.Response, error) { // Setup custom HTTP client. client := &http.Client{} // Modify request as needed. req.Header.Set("Content-Type", "application/json-rpc") return client.Do(req) }), ) defer clientCustomHTTP.Close() var reply int // Synchronous call using positional params and TCP. err = clientTCP.Call("ExampleSvc.Sum", [2]int{3, 5}, &reply) fmt.Printf("Sum(3,5)=%d\n", reply) // Synchronous call using positional params and HTTP. err = clientHTTP.Call("ExampleSvc.SumAll", []int{3, 5, -2}, &reply) fmt.Printf("SumAll(3,5,-2)=%d\n", reply) // Asynchronous call using named params and TCP. startCall := clientTCP.Go("ExampleSvc.MapLen", map[string]int{"a": 10, "b": 20, "c": 30}, &reply, nil) replyCall := <-startCall.Done fmt.Printf("MapLen({a:10,b:20,c:30})=%d\n", *replyCall.Reply.(*int)) // Notification using named params and HTTP. clientHTTP.Notify("ExampleSvc.FullName", NameArg{"First", "Last"}) // Synchronous call using named params and TCP with context. clientTCP.Call("ExampleSvc.FullName2", NameArg{"First", "Last"}, nil) // Synchronous call using named params and HTTP with context. clientHTTP.Call("ExampleSvc.FullName3", NameArg{"First", "Last"}, nil) // Correct error handling. err = clientTCP.Call("ExampleSvc.Err1", nil, nil) if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF { fmt.Printf("Err1(): %q\n", err) } else if err != nil { rpcerr := jsonrpc2.ServerError(err) fmt.Printf("Err1(): code=%d msg=%q data=%v\n", rpcerr.Code, rpcerr.Message, rpcerr.Data) } err = clientCustomHTTP.Call("ExampleSvc.Err2", nil, nil) if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF { fmt.Printf("Err2(): %q\n", err) } else if err != nil { rpcerr := jsonrpc2.ServerError(err) fmt.Printf("Err2(): code=%d msg=%q data=%v\n", rpcerr.Code, rpcerr.Message, rpcerr.Data) } err = clientHTTP.Call("ExampleSvc.Err3", nil, nil) if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF { fmt.Printf("Err3(): %q\n", err) } else if err != nil { rpcerr := jsonrpc2.ServerError(err) fmt.Printf("Err3(): code=%d msg=%q data=%v\n", rpcerr.Code, rpcerr.Message, rpcerr.Data) } // Output: // Sum(3,5)=8 // SumAll(3,5,-2)=6 // MapLen({a:10,b:20,c:30})=3 // FullName2(): Remote IP is 127.0.0.1 // FullName3(): Remote IP is 127.0.0.1 // Err1(): code=-32000 msg="some issue" data=<nil> // Err2(): code=-32603 msg="bad HTTP Status: 415 Unsupported Media Type" data=<nil> // Err3(): code=42 msg="some issue" data=[one two] }