github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/rpc/jsonrpc/jsonrpc_test.go (about)

     1  package jsonrpc
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	crand "crypto/rand"
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"os"
    11  	"os/exec"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/go-kit/kit/log/term"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	tmbytes "github.com/tendermint/tendermint/libs/bytes"
    20  	"github.com/tendermint/tendermint/libs/log"
    21  	tmrand "github.com/tendermint/tendermint/libs/rand"
    22  
    23  	client "github.com/tendermint/tendermint/rpc/jsonrpc/client"
    24  	server "github.com/tendermint/tendermint/rpc/jsonrpc/server"
    25  	types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
    26  )
    27  
    28  // Client and Server should work over tcp or unix sockets
    29  const (
    30  	tcpAddr = "tcp://127.0.0.1:47768"
    31  
    32  	unixSocket = "/tmp/rpc_test.sock"
    33  	unixAddr   = "unix://" + unixSocket
    34  
    35  	websocketEndpoint = "/websocket/endpoint"
    36  
    37  	testVal = "acbd"
    38  )
    39  
    40  type ResultEcho struct {
    41  	Value string `json:"value"`
    42  }
    43  
    44  type ResultEchoInt struct {
    45  	Value int `json:"value"`
    46  }
    47  
    48  type ResultEchoBytes struct {
    49  	Value []byte `json:"value"`
    50  }
    51  
    52  type ResultEchoDataBytes struct {
    53  	Value tmbytes.HexBytes `json:"value"`
    54  }
    55  
    56  // Define some routes
    57  var Routes = map[string]*server.RPCFunc{
    58  	"echo":            server.NewRPCFunc(EchoResult, "arg"),
    59  	"echo_ws":         server.NewWSRPCFunc(EchoWSResult, "arg"),
    60  	"echo_bytes":      server.NewRPCFunc(EchoBytesResult, "arg"),
    61  	"echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"),
    62  	"echo_int":        server.NewRPCFunc(EchoIntResult, "arg"),
    63  }
    64  
    65  func EchoResult(ctx *types.Context, v string) (*ResultEcho, error) {
    66  	return &ResultEcho{v}, nil
    67  }
    68  
    69  func EchoWSResult(ctx *types.Context, v string) (*ResultEcho, error) {
    70  	return &ResultEcho{v}, nil
    71  }
    72  
    73  func EchoIntResult(ctx *types.Context, v int) (*ResultEchoInt, error) {
    74  	return &ResultEchoInt{v}, nil
    75  }
    76  
    77  func EchoBytesResult(ctx *types.Context, v []byte) (*ResultEchoBytes, error) {
    78  	return &ResultEchoBytes{v}, nil
    79  }
    80  
    81  func EchoDataBytesResult(ctx *types.Context, v tmbytes.HexBytes) (*ResultEchoDataBytes, error) {
    82  	return &ResultEchoDataBytes{v}, nil
    83  }
    84  
    85  func TestMain(m *testing.M) {
    86  	setup()
    87  	code := m.Run()
    88  	os.Exit(code)
    89  }
    90  
    91  var colorFn = func(keyvals ...interface{}) term.FgBgColor {
    92  	for i := 0; i < len(keyvals)-1; i += 2 {
    93  		if keyvals[i] == "socket" {
    94  			if keyvals[i+1] == "tcp" {
    95  				return term.FgBgColor{Fg: term.DarkBlue}
    96  			} else if keyvals[i+1] == "unix" {
    97  				return term.FgBgColor{Fg: term.DarkCyan}
    98  			}
    99  		}
   100  	}
   101  	return term.FgBgColor{}
   102  }
   103  
   104  // launch unix and tcp servers
   105  func setup() {
   106  	logger := log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn)
   107  
   108  	cmd := exec.Command("rm", "-f", unixSocket)
   109  	err := cmd.Start()
   110  	if err != nil {
   111  		panic(err)
   112  	}
   113  	if err = cmd.Wait(); err != nil {
   114  		panic(err)
   115  	}
   116  
   117  	tcpLogger := logger.With("socket", "tcp")
   118  	mux := http.NewServeMux()
   119  	server.RegisterRPCFuncs(mux, Routes, tcpLogger)
   120  	wm := server.NewWebsocketManager(Routes, server.ReadWait(5*time.Second), server.PingPeriod(1*time.Second))
   121  	wm.SetLogger(tcpLogger)
   122  	mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
   123  	config := server.DefaultConfig()
   124  	listener1, err := server.Listen(tcpAddr, config)
   125  	if err != nil {
   126  		panic(err)
   127  	}
   128  	go server.Serve(listener1, mux, tcpLogger, config)
   129  
   130  	unixLogger := logger.With("socket", "unix")
   131  	mux2 := http.NewServeMux()
   132  	server.RegisterRPCFuncs(mux2, Routes, unixLogger)
   133  	wm = server.NewWebsocketManager(Routes)
   134  	wm.SetLogger(unixLogger)
   135  	mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
   136  	listener2, err := server.Listen(unixAddr, config)
   137  	if err != nil {
   138  		panic(err)
   139  	}
   140  	go server.Serve(listener2, mux2, unixLogger, config)
   141  
   142  	// wait for servers to start
   143  	time.Sleep(time.Second * 2)
   144  }
   145  
   146  func echoViaHTTP(cl client.Caller, val string) (string, error) {
   147  	params := map[string]interface{}{
   148  		"arg": val,
   149  	}
   150  	result := new(ResultEcho)
   151  	if _, err := cl.Call("echo", params, result); err != nil {
   152  		return "", err
   153  	}
   154  	return result.Value, nil
   155  }
   156  
   157  func echoIntViaHTTP(cl client.Caller, val int) (int, error) {
   158  	params := map[string]interface{}{
   159  		"arg": val,
   160  	}
   161  	result := new(ResultEchoInt)
   162  	if _, err := cl.Call("echo_int", params, result); err != nil {
   163  		return 0, err
   164  	}
   165  	return result.Value, nil
   166  }
   167  
   168  func echoBytesViaHTTP(cl client.Caller, bytes []byte) ([]byte, error) {
   169  	params := map[string]interface{}{
   170  		"arg": bytes,
   171  	}
   172  	result := new(ResultEchoBytes)
   173  	if _, err := cl.Call("echo_bytes", params, result); err != nil {
   174  		return []byte{}, err
   175  	}
   176  	return result.Value, nil
   177  }
   178  
   179  func echoDataBytesViaHTTP(cl client.Caller, bytes tmbytes.HexBytes) (tmbytes.HexBytes, error) {
   180  	params := map[string]interface{}{
   181  		"arg": bytes,
   182  	}
   183  	result := new(ResultEchoDataBytes)
   184  	if _, err := cl.Call("echo_data_bytes", params, result); err != nil {
   185  		return []byte{}, err
   186  	}
   187  	return result.Value, nil
   188  }
   189  
   190  func testWithHTTPClient(t *testing.T, cl client.HTTPClient) {
   191  	val := testVal
   192  	got, err := echoViaHTTP(cl, val)
   193  	require.Nil(t, err)
   194  	assert.Equal(t, got, val)
   195  
   196  	val2 := randBytes(t)
   197  	got2, err := echoBytesViaHTTP(cl, val2)
   198  	require.Nil(t, err)
   199  	assert.Equal(t, got2, val2)
   200  
   201  	val3 := tmbytes.HexBytes(randBytes(t))
   202  	got3, err := echoDataBytesViaHTTP(cl, val3)
   203  	require.Nil(t, err)
   204  	assert.Equal(t, got3, val3)
   205  
   206  	val4 := tmrand.Intn(10000)
   207  	got4, err := echoIntViaHTTP(cl, val4)
   208  	require.Nil(t, err)
   209  	assert.Equal(t, got4, val4)
   210  }
   211  
   212  func echoViaWS(cl *client.WSClient, val string) (string, error) {
   213  	params := map[string]interface{}{
   214  		"arg": val,
   215  	}
   216  	err := cl.Call(context.Background(), "echo", params)
   217  	if err != nil {
   218  		return "", err
   219  	}
   220  
   221  	msg := <-cl.ResponsesCh
   222  	if msg.Error != nil {
   223  		return "", err
   224  
   225  	}
   226  	result := new(ResultEcho)
   227  	err = json.Unmarshal(msg.Result, result)
   228  	if err != nil {
   229  		return "", nil
   230  	}
   231  	return result.Value, nil
   232  }
   233  
   234  func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) {
   235  	params := map[string]interface{}{
   236  		"arg": bytes,
   237  	}
   238  	err := cl.Call(context.Background(), "echo_bytes", params)
   239  	if err != nil {
   240  		return []byte{}, err
   241  	}
   242  
   243  	msg := <-cl.ResponsesCh
   244  	if msg.Error != nil {
   245  		return []byte{}, msg.Error
   246  
   247  	}
   248  	result := new(ResultEchoBytes)
   249  	err = json.Unmarshal(msg.Result, result)
   250  	if err != nil {
   251  		return []byte{}, nil
   252  	}
   253  	return result.Value, nil
   254  }
   255  
   256  func testWithWSClient(t *testing.T, cl *client.WSClient) {
   257  	val := testVal
   258  	got, err := echoViaWS(cl, val)
   259  	require.Nil(t, err)
   260  	assert.Equal(t, got, val)
   261  
   262  	val2 := randBytes(t)
   263  	got2, err := echoBytesViaWS(cl, val2)
   264  	require.Nil(t, err)
   265  	assert.Equal(t, got2, val2)
   266  }
   267  
   268  //-------------
   269  
   270  func TestServersAndClientsBasic(t *testing.T) {
   271  	serverAddrs := [...]string{tcpAddr, unixAddr}
   272  	for _, addr := range serverAddrs {
   273  		cl1, err := client.NewURI(addr)
   274  		require.Nil(t, err)
   275  		fmt.Printf("=== testing server on %s using URI client", addr)
   276  		testWithHTTPClient(t, cl1)
   277  
   278  		cl2, err := client.New(addr)
   279  		require.Nil(t, err)
   280  		fmt.Printf("=== testing server on %s using JSONRPC client", addr)
   281  		testWithHTTPClient(t, cl2)
   282  
   283  		cl3, err := client.NewWS(addr, websocketEndpoint)
   284  		require.Nil(t, err)
   285  		cl3.SetLogger(log.TestingLogger())
   286  		err = cl3.Start()
   287  		require.Nil(t, err)
   288  		fmt.Printf("=== testing server on %s using WS client", addr)
   289  		testWithWSClient(t, cl3)
   290  		err = cl3.Stop()
   291  		require.NoError(t, err)
   292  	}
   293  }
   294  
   295  func TestHexStringArg(t *testing.T) {
   296  	cl, err := client.NewURI(tcpAddr)
   297  	require.Nil(t, err)
   298  	// should NOT be handled as hex
   299  	val := "0xabc"
   300  	got, err := echoViaHTTP(cl, val)
   301  	require.Nil(t, err)
   302  	assert.Equal(t, got, val)
   303  }
   304  
   305  func TestQuotedStringArg(t *testing.T) {
   306  	cl, err := client.NewURI(tcpAddr)
   307  	require.Nil(t, err)
   308  	// should NOT be unquoted
   309  	val := "\"abc\""
   310  	got, err := echoViaHTTP(cl, val)
   311  	require.Nil(t, err)
   312  	assert.Equal(t, got, val)
   313  }
   314  
   315  func TestWSNewWSRPCFunc(t *testing.T) {
   316  	cl, err := client.NewWS(tcpAddr, websocketEndpoint)
   317  	require.Nil(t, err)
   318  	cl.SetLogger(log.TestingLogger())
   319  	err = cl.Start()
   320  	require.Nil(t, err)
   321  	t.Cleanup(func() {
   322  		if err := cl.Stop(); err != nil {
   323  			t.Error(err)
   324  		}
   325  	})
   326  
   327  	val := testVal
   328  	params := map[string]interface{}{
   329  		"arg": val,
   330  	}
   331  	err = cl.Call(context.Background(), "echo_ws", params)
   332  	require.Nil(t, err)
   333  
   334  	msg := <-cl.ResponsesCh
   335  	if msg.Error != nil {
   336  		t.Fatal(err)
   337  	}
   338  	result := new(ResultEcho)
   339  	err = json.Unmarshal(msg.Result, result)
   340  	require.Nil(t, err)
   341  	got := result.Value
   342  	assert.Equal(t, got, val)
   343  }
   344  
   345  func TestWSHandlesArrayParams(t *testing.T) {
   346  	cl, err := client.NewWS(tcpAddr, websocketEndpoint)
   347  	require.Nil(t, err)
   348  	cl.SetLogger(log.TestingLogger())
   349  	err = cl.Start()
   350  	require.Nil(t, err)
   351  	t.Cleanup(func() {
   352  		if err := cl.Stop(); err != nil {
   353  			t.Error(err)
   354  		}
   355  	})
   356  
   357  	val := testVal
   358  	params := []interface{}{val}
   359  	err = cl.CallWithArrayParams(context.Background(), "echo_ws", params)
   360  	require.Nil(t, err)
   361  
   362  	msg := <-cl.ResponsesCh
   363  	if msg.Error != nil {
   364  		t.Fatalf("%+v", err)
   365  	}
   366  	result := new(ResultEcho)
   367  	err = json.Unmarshal(msg.Result, result)
   368  	require.Nil(t, err)
   369  	got := result.Value
   370  	assert.Equal(t, got, val)
   371  }
   372  
   373  // TestWSClientPingPong checks that a client & server exchange pings
   374  // & pongs so connection stays alive.
   375  func TestWSClientPingPong(t *testing.T) {
   376  	cl, err := client.NewWS(tcpAddr, websocketEndpoint)
   377  	require.Nil(t, err)
   378  	cl.SetLogger(log.TestingLogger())
   379  	err = cl.Start()
   380  	require.Nil(t, err)
   381  	t.Cleanup(func() {
   382  		if err := cl.Stop(); err != nil {
   383  			t.Error(err)
   384  		}
   385  	})
   386  
   387  	time.Sleep(6 * time.Second)
   388  }
   389  
   390  func randBytes(t *testing.T) []byte {
   391  	n := tmrand.Intn(10) + 2
   392  	buf := make([]byte, n)
   393  	_, err := crand.Read(buf)
   394  	require.Nil(t, err)
   395  	return bytes.Replace(buf, []byte("="), []byte{100}, -1)
   396  }