github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/rpc/client_test.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package rpc
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math/rand"
    23  	"net"
    24  	"net/http"
    25  	"net/http/httptest"
    26  	"os"
    27  	"reflect"
    28  	"runtime"
    29  	"sync"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/davecgh/go-spew/spew"
    34  	"github.com/ethereum/go-ethereum/log"
    35  )
    36  
    37  func TestClientRequest(t *testing.T) {
    38  	server := newTestServer("service", new(Service))
    39  	defer server.Stop()
    40  	client := DialInProc(server)
    41  	defer client.Close()
    42  
    43  	var resp Result
    44  	if err := client.Call(&resp, "service_echo", "hello", 10, &Args{"world"}); err != nil {
    45  		t.Fatal(err)
    46  	}
    47  	if !reflect.DeepEqual(resp, Result{"hello", 10, &Args{"world"}}) {
    48  		t.Errorf("incorrect result %#v", resp)
    49  	}
    50  }
    51  
    52  func TestClientBatchRequest(t *testing.T) {
    53  	server := newTestServer("service", new(Service))
    54  	defer server.Stop()
    55  	client := DialInProc(server)
    56  	defer client.Close()
    57  
    58  	batch := []BatchElem{
    59  		{
    60  			Method: "service_echo",
    61  			Args:   []interface{}{"hello", 10, &Args{"world"}},
    62  			Result: new(Result),
    63  		},
    64  		{
    65  			Method: "service_echo",
    66  			Args:   []interface{}{"hello2", 11, &Args{"world"}},
    67  			Result: new(Result),
    68  		},
    69  		{
    70  			Method: "no_such_method",
    71  			Args:   []interface{}{1, 2, 3},
    72  			Result: new(int),
    73  		},
    74  	}
    75  	if err := client.BatchCall(batch); err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	wantResult := []BatchElem{
    79  		{
    80  			Method: "service_echo",
    81  			Args:   []interface{}{"hello", 10, &Args{"world"}},
    82  			Result: &Result{"hello", 10, &Args{"world"}},
    83  		},
    84  		{
    85  			Method: "service_echo",
    86  			Args:   []interface{}{"hello2", 11, &Args{"world"}},
    87  			Result: &Result{"hello2", 11, &Args{"world"}},
    88  		},
    89  		{
    90  			Method: "no_such_method",
    91  			Args:   []interface{}{1, 2, 3},
    92  			Result: new(int),
    93  			Error:  &jsonError{Code: -32601, Message: "The method no_such_method_ does not exist/is not available"},
    94  		},
    95  	}
    96  	if !reflect.DeepEqual(batch, wantResult) {
    97  		t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult))
    98  	}
    99  }
   100  
   101  // func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) }
   102  func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) }
   103  func TestClientCancelHTTP(t *testing.T)      { testClientCancel("http", t) }
   104  func TestClientCancelIPC(t *testing.T)       { testClientCancel("ipc", t) }
   105  
   106  // This test checks that requests made through CallContext can be canceled by canceling
   107  // the context.
   108  func testClientCancel(transport string, t *testing.T) {
   109  	server := newTestServer("service", new(Service))
   110  	defer server.Stop()
   111  
   112  	// What we want to achieve is that the context gets canceled
   113  	// at various stages of request processing. The interesting cases
   114  	// are:
   115  	//  - cancel during dial
   116  	//  - cancel while performing a HTTP request
   117  	//  - cancel while waiting for a response
   118  	//
   119  	// To trigger those, the times are chosen such that connections
   120  	// are killed within the deadline for every other call (maxKillTimeout
   121  	// is 2x maxCancelTimeout).
   122  	//
   123  	// Once a connection is dead, there is a fair chance it won't connect
   124  	// successfully because the accept is delayed by 1s.
   125  	maxContextCancelTimeout := 300 * time.Millisecond
   126  	fl := &flakeyListener{
   127  		maxAcceptDelay: 1 * time.Second,
   128  		maxKillTimeout: 600 * time.Millisecond,
   129  	}
   130  
   131  	var client *Client
   132  	switch transport {
   133  	case "ws", "http":
   134  		c, hs := httpTestClient(server, transport, fl)
   135  		defer hs.Close()
   136  		client = c
   137  	case "ipc":
   138  		c, l := ipcTestClient(server, fl)
   139  		defer l.Close()
   140  		client = c
   141  	default:
   142  		panic("unknown transport: " + transport)
   143  	}
   144  
   145  	// These tests take a lot of time, run them all at once.
   146  	// You probably want to run with -parallel 1 or comment out
   147  	// the call to t.Parallel if you enable the logging.
   148  	t.Parallel()
   149  
   150  	// The actual test starts here.
   151  	var (
   152  		wg       sync.WaitGroup
   153  		nreqs    = 10
   154  		ncallers = 6
   155  	)
   156  	caller := func(index int) {
   157  		defer wg.Done()
   158  		for i := 0; i < nreqs; i++ {
   159  			var (
   160  				ctx     context.Context
   161  				cancel  func()
   162  				timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout)))
   163  			)
   164  			if index < ncallers/2 {
   165  				// For half of the callers, create a context without deadline
   166  				// and cancel it later.
   167  				ctx, cancel = context.WithCancel(context.Background())
   168  				time.AfterFunc(timeout, cancel)
   169  			} else {
   170  				// For the other half, create a context with a deadline instead. This is
   171  				// different because the context deadline is used to set the socket write
   172  				// deadline.
   173  				ctx, cancel = context.WithTimeout(context.Background(), timeout)
   174  			}
   175  			// Now perform a call with the context.
   176  			// The key thing here is that no call will ever complete successfully.
   177  			err := client.CallContext(ctx, nil, "service_sleep", 2*maxContextCancelTimeout)
   178  			if err != nil {
   179  				log.Debug(fmt.Sprint("got expected error:", err))
   180  			} else {
   181  				t.Errorf("no error for call with %v wait time", timeout)
   182  			}
   183  			cancel()
   184  		}
   185  	}
   186  	wg.Add(ncallers)
   187  	for i := 0; i < ncallers; i++ {
   188  		go caller(i)
   189  	}
   190  	wg.Wait()
   191  }
   192  
   193  func TestClientSubscribeInvalidArg(t *testing.T) {
   194  	server := newTestServer("service", new(Service))
   195  	defer server.Stop()
   196  	client := DialInProc(server)
   197  	defer client.Close()
   198  
   199  	check := func(shouldPanic bool, arg interface{}) {
   200  		defer func() {
   201  			err := recover()
   202  			if shouldPanic && err == nil {
   203  				t.Errorf("EthSubscribe should've panicked for %#v", arg)
   204  			}
   205  			if !shouldPanic && err != nil {
   206  				t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg)
   207  				buf := make([]byte, 1024*1024)
   208  				buf = buf[:runtime.Stack(buf, false)]
   209  				t.Error(err)
   210  				t.Error(string(buf))
   211  			}
   212  		}()
   213  		client.EthSubscribe(context.Background(), arg, "foo_bar")
   214  	}
   215  	check(true, nil)
   216  	check(true, 1)
   217  	check(true, (chan int)(nil))
   218  	check(true, make(<-chan int))
   219  	check(false, make(chan int))
   220  	check(false, make(chan<- int))
   221  }
   222  
   223  func TestClientSubscribe(t *testing.T) {
   224  	server := newTestServer("eth", new(NotificationTestService))
   225  	defer server.Stop()
   226  	client := DialInProc(server)
   227  	defer client.Close()
   228  
   229  	nc := make(chan int)
   230  	count := 10
   231  	sub, err := client.EthSubscribe(context.Background(), nc, "someSubscription", count, 0)
   232  	if err != nil {
   233  		t.Fatal("can't subscribe:", err)
   234  	}
   235  	for i := 0; i < count; i++ {
   236  		if val := <-nc; val != i {
   237  			t.Fatalf("value mismatch: got %d, want %d", val, i)
   238  		}
   239  	}
   240  
   241  	sub.Unsubscribe()
   242  	select {
   243  	case v := <-nc:
   244  		t.Fatal("received value after unsubscribe:", v)
   245  	case err := <-sub.Err():
   246  		if err != nil {
   247  			t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err)
   248  		}
   249  	case <-time.After(1 * time.Second):
   250  		t.Fatalf("subscription not closed within 1s after unsubscribe")
   251  	}
   252  }
   253  
   254  func TestClientSubscribeCustomNamespace(t *testing.T) {
   255  	namespace := "custom"
   256  	server := newTestServer(namespace, new(NotificationTestService))
   257  	defer server.Stop()
   258  	client := DialInProc(server)
   259  	defer client.Close()
   260  
   261  	nc := make(chan int)
   262  	count := 10
   263  	sub, err := client.Subscribe(context.Background(), namespace, nc, "someSubscription", count, 0)
   264  	if err != nil {
   265  		t.Fatal("can't subscribe:", err)
   266  	}
   267  	for i := 0; i < count; i++ {
   268  		if val := <-nc; val != i {
   269  			t.Fatalf("value mismatch: got %d, want %d", val, i)
   270  		}
   271  	}
   272  
   273  	sub.Unsubscribe()
   274  	select {
   275  	case v := <-nc:
   276  		t.Fatal("received value after unsubscribe:", v)
   277  	case err := <-sub.Err():
   278  		if err != nil {
   279  			t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err)
   280  		}
   281  	case <-time.After(1 * time.Second):
   282  		t.Fatalf("subscription not closed within 1s after unsubscribe")
   283  	}
   284  }
   285  
   286  // In this test, the connection drops while EthSubscribe is
   287  // waiting for a response.
   288  func TestClientSubscribeClose(t *testing.T) {
   289  	service := &NotificationTestService{
   290  		gotHangSubscriptionReq:  make(chan struct{}),
   291  		unblockHangSubscription: make(chan struct{}),
   292  	}
   293  	server := newTestServer("eth", service)
   294  	defer server.Stop()
   295  	client := DialInProc(server)
   296  	defer client.Close()
   297  
   298  	var (
   299  		nc   = make(chan int)
   300  		errc = make(chan error)
   301  		sub  *ClientSubscription
   302  		err  error
   303  	)
   304  	go func() {
   305  		sub, err = client.EthSubscribe(context.Background(), nc, "hangSubscription", 999)
   306  		errc <- err
   307  	}()
   308  
   309  	<-service.gotHangSubscriptionReq
   310  	client.Close()
   311  	service.unblockHangSubscription <- struct{}{}
   312  
   313  	select {
   314  	case err := <-errc:
   315  		if err == nil {
   316  			t.Errorf("EthSubscribe returned nil error after Close")
   317  		}
   318  		if sub != nil {
   319  			t.Error("EthSubscribe returned non-nil subscription after Close")
   320  		}
   321  	case <-time.After(1 * time.Second):
   322  		t.Fatalf("EthSubscribe did not return within 1s after Close")
   323  	}
   324  }
   325  
   326  // This test reproduces https://github.com/ethereum/go-ethereum/issues/17837 where the
   327  // client hangs during shutdown when Unsubscribe races with Client.Close.
   328  func TestClientCloseUnsubscribeRace(t *testing.T) {
   329  	service := &NotificationTestService{}
   330  	server := newTestServer("eth", service)
   331  	defer server.Stop()
   332  
   333  	for i := 0; i < 20; i++ {
   334  		client := DialInProc(server)
   335  		nc := make(chan int)
   336  		sub, err := client.EthSubscribe(context.Background(), nc, "someSubscription", 3, 1)
   337  		if err != nil {
   338  			t.Fatal(err)
   339  		}
   340  		go client.Close()
   341  		go sub.Unsubscribe()
   342  		select {
   343  		case <-sub.Err():
   344  		case <-time.After(5 * time.Second):
   345  			t.Fatal("subscription not closed within timeout")
   346  		}
   347  	}
   348  }
   349  
   350  // This test checks that Client doesn't lock up when a single subscriber
   351  // doesn't read subscription events.
   352  func TestClientNotificationStorm(t *testing.T) {
   353  	server := newTestServer("eth", new(NotificationTestService))
   354  	defer server.Stop()
   355  
   356  	doTest := func(count int, wantError bool) {
   357  		client := DialInProc(server)
   358  		defer client.Close()
   359  		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   360  		defer cancel()
   361  
   362  		// Subscribe on the server. It will start sending many notifications
   363  		// very quickly.
   364  		nc := make(chan int)
   365  		sub, err := client.EthSubscribe(ctx, nc, "someSubscription", count, 0)
   366  		if err != nil {
   367  			t.Fatal("can't subscribe:", err)
   368  		}
   369  		defer sub.Unsubscribe()
   370  
   371  		// Process each notification, try to run a call in between each of them.
   372  		for i := 0; i < count; i++ {
   373  			select {
   374  			case val := <-nc:
   375  				if val != i {
   376  					t.Fatalf("(%d/%d) unexpected value %d", i, count, val)
   377  				}
   378  			case err := <-sub.Err():
   379  				if wantError && err != ErrSubscriptionQueueOverflow {
   380  					t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow)
   381  				} else if !wantError {
   382  					t.Fatalf("(%d/%d) got unexpected error %q", i, count, err)
   383  				}
   384  				return
   385  			}
   386  			var r int
   387  			err := client.CallContext(ctx, &r, "eth_echo", i)
   388  			if err != nil {
   389  				if !wantError {
   390  					t.Fatalf("(%d/%d) call error: %v", i, count, err)
   391  				}
   392  				return
   393  			}
   394  		}
   395  	}
   396  
   397  	doTest(8000, false)
   398  	doTest(10000, true)
   399  }
   400  
   401  func TestClientHTTP(t *testing.T) {
   402  	server := newTestServer("service", new(Service))
   403  	defer server.Stop()
   404  
   405  	client, hs := httpTestClient(server, "http", nil)
   406  	defer hs.Close()
   407  	defer client.Close()
   408  
   409  	// Launch concurrent requests.
   410  	var (
   411  		results    = make([]Result, 100)
   412  		errc       = make(chan error)
   413  		wantResult = Result{"a", 1, new(Args)}
   414  	)
   415  	defer client.Close()
   416  	for i := range results {
   417  		i := i
   418  		go func() {
   419  			errc <- client.Call(&results[i], "service_echo",
   420  				wantResult.String, wantResult.Int, wantResult.Args)
   421  		}()
   422  	}
   423  
   424  	// Wait for all of them to complete.
   425  	timeout := time.NewTimer(5 * time.Second)
   426  	defer timeout.Stop()
   427  	for i := range results {
   428  		select {
   429  		case err := <-errc:
   430  			if err != nil {
   431  				t.Fatal(err)
   432  			}
   433  		case <-timeout.C:
   434  			t.Fatalf("timeout (got %d/%d) results)", i+1, len(results))
   435  		}
   436  	}
   437  
   438  	// Check results.
   439  	for i := range results {
   440  		if !reflect.DeepEqual(results[i], wantResult) {
   441  			t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult)
   442  		}
   443  	}
   444  }
   445  
   446  func TestClientReconnect(t *testing.T) {
   447  	startServer := func(addr string) (*Server, net.Listener) {
   448  		srv := newTestServer("service", new(Service))
   449  		l, err := net.Listen("tcp", addr)
   450  		if err != nil {
   451  			t.Fatal(err)
   452  		}
   453  		go http.Serve(l, srv.WebsocketHandler([]string{"*"}))
   454  		return srv, l
   455  	}
   456  
   457  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   458  	defer cancel()
   459  
   460  	// Start a server and corresponding client.
   461  	s1, l1 := startServer("127.0.0.1:0")
   462  	client, err := DialContext(ctx, "ws://"+l1.Addr().String())
   463  	if err != nil {
   464  		t.Fatal("can't dial", err)
   465  	}
   466  
   467  	// Perform a call. This should work because the server is up.
   468  	var resp Result
   469  	if err := client.CallContext(ctx, &resp, "service_echo", "", 1, nil); err != nil {
   470  		t.Fatal(err)
   471  	}
   472  
   473  	// Shut down the server and try calling again. It shouldn't work.
   474  	l1.Close()
   475  	s1.Stop()
   476  	if err := client.CallContext(ctx, &resp, "service_echo", "", 2, nil); err == nil {
   477  		t.Error("successful call while the server is down")
   478  		t.Logf("resp: %#v", resp)
   479  	}
   480  
   481  	// Allow for some cool down time so we can listen on the same address again.
   482  	time.Sleep(2 * time.Second)
   483  
   484  	// Start it up again and call again. The connection should be reestablished.
   485  	// We spawn multiple calls here to check whether this hangs somehow.
   486  	s2, l2 := startServer(l1.Addr().String())
   487  	defer l2.Close()
   488  	defer s2.Stop()
   489  
   490  	start := make(chan struct{})
   491  	errors := make(chan error, 20)
   492  	for i := 0; i < cap(errors); i++ {
   493  		go func() {
   494  			<-start
   495  			var resp Result
   496  			errors <- client.CallContext(ctx, &resp, "service_echo", "", 3, nil)
   497  		}()
   498  	}
   499  	close(start)
   500  	errcount := 0
   501  	for i := 0; i < cap(errors); i++ {
   502  		if err = <-errors; err != nil {
   503  			errcount++
   504  		}
   505  	}
   506  	t.Log("err:", err)
   507  	if errcount > 1 {
   508  		t.Errorf("expected one error after disconnect, got %d", errcount)
   509  	}
   510  }
   511  
   512  func newTestServer(serviceName string, service interface{}) *Server {
   513  	server := NewServer()
   514  	if err := server.RegisterName(serviceName, service); err != nil {
   515  		panic(err)
   516  	}
   517  	return server
   518  }
   519  
   520  func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) {
   521  	// Create the HTTP server.
   522  	var hs *httptest.Server
   523  	switch transport {
   524  	case "ws":
   525  		hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"}))
   526  	case "http":
   527  		hs = httptest.NewUnstartedServer(srv)
   528  	default:
   529  		panic("unknown HTTP transport: " + transport)
   530  	}
   531  	// Wrap the listener if required.
   532  	if fl != nil {
   533  		fl.Listener = hs.Listener
   534  		hs.Listener = fl
   535  	}
   536  	// Connect the client.
   537  	hs.Start()
   538  	client, err := Dial(transport + "://" + hs.Listener.Addr().String())
   539  	if err != nil {
   540  		panic(err)
   541  	}
   542  	return client, hs
   543  }
   544  
   545  func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) {
   546  	// Listen on a random endpoint.
   547  	endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63())
   548  	if runtime.GOOS == "windows" {
   549  		endpoint = `\\.\pipe\` + endpoint
   550  	} else {
   551  		endpoint = os.TempDir() + "/" + endpoint
   552  	}
   553  	l, err := ipcListen(endpoint)
   554  	if err != nil {
   555  		panic(err)
   556  	}
   557  	// Connect the listener to the server.
   558  	if fl != nil {
   559  		fl.Listener = l
   560  		l = fl
   561  	}
   562  	go srv.ServeListener(l)
   563  	// Connect the client.
   564  	client, err := Dial(endpoint)
   565  	if err != nil {
   566  		panic(err)
   567  	}
   568  	return client, l
   569  }
   570  
   571  // flakeyListener kills accepted connections after a random timeout.
   572  type flakeyListener struct {
   573  	net.Listener
   574  	maxKillTimeout time.Duration
   575  	maxAcceptDelay time.Duration
   576  }
   577  
   578  func (l *flakeyListener) Accept() (net.Conn, error) {
   579  	delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay)))
   580  	time.Sleep(delay)
   581  
   582  	c, err := l.Listener.Accept()
   583  	if err == nil {
   584  		timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout)))
   585  		time.AfterFunc(timeout, func() {
   586  			log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout))
   587  			c.Close()
   588  		})
   589  	}
   590  	return c, err
   591  }