github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/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  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"math/rand"
    25  	"net"
    26  	"net/http"
    27  	"net/http/httptest"
    28  	"os"
    29  	"reflect"
    30  	"runtime"
    31  	"strings"
    32  	"sync"
    33  	"testing"
    34  	"time"
    35  
    36  	"github.com/davecgh/go-spew/spew"
    37  	"github.com/ethereum/go-ethereum/log"
    38  )
    39  
    40  func TestClientRequest(t *testing.T) {
    41  	server := newTestServer()
    42  	defer server.Stop()
    43  	client := DialInProc(server)
    44  	defer client.Close()
    45  
    46  	var resp echoResult
    47  	if err := client.Call(&resp, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
    48  		t.Fatal(err)
    49  	}
    50  	if !reflect.DeepEqual(resp, echoResult{"hello", 10, &echoArgs{"world"}}) {
    51  		t.Errorf("incorrect result %#v", resp)
    52  	}
    53  }
    54  
    55  func TestClientResponseType(t *testing.T) {
    56  	server := newTestServer()
    57  	defer server.Stop()
    58  	client := DialInProc(server)
    59  	defer client.Close()
    60  
    61  	if err := client.Call(nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
    62  		t.Errorf("Passing nil as result should be fine, but got an error: %v", err)
    63  	}
    64  	var resultVar echoResult
    65  	// Note: passing the var, not a ref
    66  	err := client.Call(resultVar, "test_echo", "hello", 10, &echoArgs{"world"})
    67  	if err == nil {
    68  		t.Error("Passing a var as result should be an error")
    69  	}
    70  }
    71  
    72  // This test checks calling a method that returns 'null'.
    73  func TestClientNullResponse(t *testing.T) {
    74  	server := newTestServer()
    75  	defer server.Stop()
    76  
    77  	client := DialInProc(server)
    78  	defer client.Close()
    79  
    80  	var result json.RawMessage
    81  	if err := client.Call(&result, "test_null"); err != nil {
    82  		t.Fatal(err)
    83  	}
    84  	if result == nil {
    85  		t.Fatal("Expected non-nil result")
    86  	}
    87  	if !reflect.DeepEqual(result, json.RawMessage("null")) {
    88  		t.Errorf("Expected null, got %s", result)
    89  	}
    90  }
    91  
    92  // This test checks that server-returned errors with code and data come out of Client.Call.
    93  func TestClientErrorData(t *testing.T) {
    94  	server := newTestServer()
    95  	defer server.Stop()
    96  	client := DialInProc(server)
    97  	defer client.Close()
    98  
    99  	var resp interface{}
   100  	err := client.Call(&resp, "test_returnError")
   101  	if err == nil {
   102  		t.Fatal("expected error")
   103  	}
   104  
   105  	// Check code.
   106  	// The method handler returns an error value which implements the rpc.Error
   107  	// interface, i.e. it has a custom error code. The server returns this error code.
   108  	expectedCode := testError{}.ErrorCode()
   109  	if e, ok := err.(Error); !ok {
   110  		t.Fatalf("client did not return rpc.Error, got %#v", e)
   111  	} else if e.ErrorCode() != expectedCode {
   112  		t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), expectedCode)
   113  	}
   114  
   115  	// Check data.
   116  	if e, ok := err.(DataError); !ok {
   117  		t.Fatalf("client did not return rpc.DataError, got %#v", e)
   118  	} else if e.ErrorData() != (testError{}.ErrorData()) {
   119  		t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData())
   120  	}
   121  }
   122  
   123  func TestClientBatchRequest(t *testing.T) {
   124  	server := newTestServer()
   125  	defer server.Stop()
   126  	client := DialInProc(server)
   127  	defer client.Close()
   128  
   129  	batch := []BatchElem{
   130  		{
   131  			Method: "test_echo",
   132  			Args:   []interface{}{"hello", 10, &echoArgs{"world"}},
   133  			Result: new(echoResult),
   134  		},
   135  		{
   136  			Method: "test_echo",
   137  			Args:   []interface{}{"hello2", 11, &echoArgs{"world"}},
   138  			Result: new(echoResult),
   139  		},
   140  		{
   141  			Method: "no_such_method",
   142  			Args:   []interface{}{1, 2, 3},
   143  			Result: new(int),
   144  		},
   145  	}
   146  	if err := client.BatchCall(batch); err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	wantResult := []BatchElem{
   150  		{
   151  			Method: "test_echo",
   152  			Args:   []interface{}{"hello", 10, &echoArgs{"world"}},
   153  			Result: &echoResult{"hello", 10, &echoArgs{"world"}},
   154  		},
   155  		{
   156  			Method: "test_echo",
   157  			Args:   []interface{}{"hello2", 11, &echoArgs{"world"}},
   158  			Result: &echoResult{"hello2", 11, &echoArgs{"world"}},
   159  		},
   160  		{
   161  			Method: "no_such_method",
   162  			Args:   []interface{}{1, 2, 3},
   163  			Result: new(int),
   164  			Error:  &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"},
   165  		},
   166  	}
   167  	if !reflect.DeepEqual(batch, wantResult) {
   168  		t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult))
   169  	}
   170  }
   171  
   172  // This checks that, for HTTP connections, the length of batch responses is validated to
   173  // match the request exactly.
   174  func TestClientBatchRequest_len(t *testing.T) {
   175  	b, err := json.Marshal([]jsonrpcMessage{
   176  		{Version: "2.0", ID: json.RawMessage("1"), Result: json.RawMessage(`"0x1"`)},
   177  		{Version: "2.0", ID: json.RawMessage("2"), Result: json.RawMessage(`"0x2"`)},
   178  	})
   179  	if err != nil {
   180  		t.Fatal("failed to encode jsonrpc message:", err)
   181  	}
   182  	s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   183  		_, err := rw.Write(b)
   184  		if err != nil {
   185  			t.Error("failed to write response:", err)
   186  		}
   187  	}))
   188  	t.Cleanup(s.Close)
   189  
   190  	t.Run("too-few", func(t *testing.T) {
   191  		client, err := Dial(s.URL)
   192  		if err != nil {
   193  			t.Fatal("failed to dial test server:", err)
   194  		}
   195  		defer client.Close()
   196  
   197  		batch := []BatchElem{
   198  			{Method: "foo", Result: new(string)},
   199  			{Method: "bar", Result: new(string)},
   200  			{Method: "baz", Result: new(string)},
   201  		}
   202  		ctx, cancelFn := context.WithTimeout(context.Background(), time.Second)
   203  		defer cancelFn()
   204  
   205  		if err := client.BatchCallContext(ctx, batch); err != nil {
   206  			t.Fatal("error:", err)
   207  		}
   208  		for i, elem := range batch[:2] {
   209  			if elem.Error != nil {
   210  				t.Errorf("expected no error for batch element %d, got %q", i, elem.Error)
   211  			}
   212  		}
   213  		for i, elem := range batch[2:] {
   214  			if elem.Error != ErrMissingBatchResponse {
   215  				t.Errorf("wrong error %q for batch element %d", elem.Error, i+2)
   216  			}
   217  		}
   218  	})
   219  
   220  	t.Run("too-many", func(t *testing.T) {
   221  		client, err := Dial(s.URL)
   222  		if err != nil {
   223  			t.Fatal("failed to dial test server:", err)
   224  		}
   225  		defer client.Close()
   226  
   227  		batch := []BatchElem{
   228  			{Method: "foo", Result: new(string)},
   229  		}
   230  		ctx, cancelFn := context.WithTimeout(context.Background(), time.Second)
   231  		defer cancelFn()
   232  
   233  		if err := client.BatchCallContext(ctx, batch); err != nil {
   234  			t.Fatal("error:", err)
   235  		}
   236  		for i, elem := range batch[:1] {
   237  			if elem.Error != nil {
   238  				t.Errorf("expected no error for batch element %d, got %q", i, elem.Error)
   239  			}
   240  		}
   241  		for i, elem := range batch[1:] {
   242  			if elem.Error != ErrMissingBatchResponse {
   243  				t.Errorf("wrong error %q for batch element %d", elem.Error, i+2)
   244  			}
   245  		}
   246  	})
   247  }
   248  
   249  // This checks that the client can handle the case where the server doesn't
   250  // respond to all requests in a batch.
   251  func TestClientBatchRequestLimit(t *testing.T) {
   252  	server := newTestServer()
   253  	defer server.Stop()
   254  	server.SetBatchLimits(2, 100000)
   255  	client := DialInProc(server)
   256  	defer client.Close()
   257  
   258  	batch := []BatchElem{
   259  		{Method: "foo"},
   260  		{Method: "bar"},
   261  		{Method: "baz"},
   262  	}
   263  	err := client.BatchCall(batch)
   264  	if err != nil {
   265  		t.Fatal("unexpected error:", err)
   266  	}
   267  
   268  	// Check that the first response indicates an error with batch size.
   269  	var err0 Error
   270  	if !errors.As(batch[0].Error, &err0) {
   271  		t.Log("error zero:", batch[0].Error)
   272  		t.Fatalf("batch elem 0 has wrong error type: %T", batch[0].Error)
   273  	} else {
   274  		if err0.ErrorCode() != -32600 || err0.Error() != errMsgBatchTooLarge {
   275  			t.Fatalf("wrong error on batch elem zero: %v", err0)
   276  		}
   277  	}
   278  
   279  	// Check that remaining response batch elements are reported as absent.
   280  	for i, elem := range batch[1:] {
   281  		if elem.Error != ErrMissingBatchResponse {
   282  			t.Fatalf("batch elem %d has unexpected error: %v", i+1, elem.Error)
   283  		}
   284  	}
   285  }
   286  
   287  func TestClientNotify(t *testing.T) {
   288  	server := newTestServer()
   289  	defer server.Stop()
   290  	client := DialInProc(server)
   291  	defer client.Close()
   292  
   293  	if err := client.Notify(context.Background(), "test_echo", "hello", 10, &echoArgs{"world"}); err != nil {
   294  		t.Fatal(err)
   295  	}
   296  }
   297  
   298  // func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) }
   299  func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) }
   300  func TestClientCancelHTTP(t *testing.T)      { testClientCancel("http", t) }
   301  func TestClientCancelIPC(t *testing.T)       { testClientCancel("ipc", t) }
   302  
   303  // This test checks that requests made through CallContext can be canceled by canceling
   304  // the context.
   305  func testClientCancel(transport string, t *testing.T) {
   306  	// These tests take a lot of time, run them all at once.
   307  	// You probably want to run with -parallel 1 or comment out
   308  	// the call to t.Parallel if you enable the logging.
   309  	t.Parallel()
   310  
   311  	server := newTestServer()
   312  	defer server.Stop()
   313  
   314  	// What we want to achieve is that the context gets canceled
   315  	// at various stages of request processing. The interesting cases
   316  	// are:
   317  	//  - cancel during dial
   318  	//  - cancel while performing a HTTP request
   319  	//  - cancel while waiting for a response
   320  	//
   321  	// To trigger those, the times are chosen such that connections
   322  	// are killed within the deadline for every other call (maxKillTimeout
   323  	// is 2x maxCancelTimeout).
   324  	//
   325  	// Once a connection is dead, there is a fair chance it won't connect
   326  	// successfully because the accept is delayed by 1s.
   327  	maxContextCancelTimeout := 300 * time.Millisecond
   328  	fl := &flakeyListener{
   329  		maxAcceptDelay: 1 * time.Second,
   330  		maxKillTimeout: 600 * time.Millisecond,
   331  	}
   332  
   333  	var client *Client
   334  	switch transport {
   335  	case "ws", "http":
   336  		c, hs := httpTestClient(server, transport, fl)
   337  		defer hs.Close()
   338  		client = c
   339  	case "ipc":
   340  		c, l := ipcTestClient(server, fl)
   341  		defer l.Close()
   342  		client = c
   343  	default:
   344  		panic("unknown transport: " + transport)
   345  	}
   346  	defer client.Close()
   347  
   348  	// The actual test starts here.
   349  	var (
   350  		wg       sync.WaitGroup
   351  		nreqs    = 10
   352  		ncallers = 10
   353  	)
   354  	caller := func(index int) {
   355  		defer wg.Done()
   356  		for i := 0; i < nreqs; i++ {
   357  			var (
   358  				ctx     context.Context
   359  				cancel  func()
   360  				timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout)))
   361  			)
   362  			if index < ncallers/2 {
   363  				// For half of the callers, create a context without deadline
   364  				// and cancel it later.
   365  				ctx, cancel = context.WithCancel(context.Background())
   366  				time.AfterFunc(timeout, cancel)
   367  			} else {
   368  				// For the other half, create a context with a deadline instead. This is
   369  				// different because the context deadline is used to set the socket write
   370  				// deadline.
   371  				ctx, cancel = context.WithTimeout(context.Background(), timeout)
   372  			}
   373  
   374  			// Now perform a call with the context.
   375  			// The key thing here is that no call will ever complete successfully.
   376  			err := client.CallContext(ctx, nil, "test_block")
   377  			switch {
   378  			case err == nil:
   379  				_, hasDeadline := ctx.Deadline()
   380  				t.Errorf("no error for call with %v wait time (deadline: %v)", timeout, hasDeadline)
   381  				// default:
   382  				//	t.Logf("got expected error with %v wait time: %v", timeout, err)
   383  			}
   384  			cancel()
   385  		}
   386  	}
   387  	wg.Add(ncallers)
   388  	for i := 0; i < ncallers; i++ {
   389  		go caller(i)
   390  	}
   391  	wg.Wait()
   392  }
   393  
   394  func TestClientSubscribeInvalidArg(t *testing.T) {
   395  	server := newTestServer()
   396  	defer server.Stop()
   397  	client := DialInProc(server)
   398  	defer client.Close()
   399  
   400  	check := func(shouldPanic bool, arg interface{}) {
   401  		defer func() {
   402  			err := recover()
   403  			if shouldPanic && err == nil {
   404  				t.Errorf("EthSubscribe should've panicked for %#v", arg)
   405  			}
   406  			if !shouldPanic && err != nil {
   407  				t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg)
   408  				buf := make([]byte, 1024*1024)
   409  				buf = buf[:runtime.Stack(buf, false)]
   410  				t.Error(err)
   411  				t.Error(string(buf))
   412  			}
   413  		}()
   414  		client.EthSubscribe(context.Background(), arg, "foo_bar")
   415  	}
   416  	check(true, nil)
   417  	check(true, 1)
   418  	check(true, (chan int)(nil))
   419  	check(true, make(<-chan int))
   420  	check(false, make(chan int))
   421  	check(false, make(chan<- int))
   422  }
   423  
   424  func TestClientSubscribe(t *testing.T) {
   425  	server := newTestServer()
   426  	defer server.Stop()
   427  	client := DialInProc(server)
   428  	defer client.Close()
   429  
   430  	nc := make(chan int)
   431  	count := 10
   432  	sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", count, 0)
   433  	if err != nil {
   434  		t.Fatal("can't subscribe:", err)
   435  	}
   436  	for i := 0; i < count; i++ {
   437  		if val := <-nc; val != i {
   438  			t.Fatalf("value mismatch: got %d, want %d", val, i)
   439  		}
   440  	}
   441  
   442  	sub.Unsubscribe()
   443  	select {
   444  	case v := <-nc:
   445  		t.Fatal("received value after unsubscribe:", v)
   446  	case err := <-sub.Err():
   447  		if err != nil {
   448  			t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err)
   449  		}
   450  	case <-time.After(1 * time.Second):
   451  		t.Fatalf("subscription not closed within 1s after unsubscribe")
   452  	}
   453  }
   454  
   455  // In this test, the connection drops while Subscribe is waiting for a response.
   456  func TestClientSubscribeClose(t *testing.T) {
   457  	server := newTestServer()
   458  	service := &notificationTestService{
   459  		gotHangSubscriptionReq:  make(chan struct{}),
   460  		unblockHangSubscription: make(chan struct{}),
   461  	}
   462  	if err := server.RegisterName("nftest2", service); err != nil {
   463  		t.Fatal(err)
   464  	}
   465  
   466  	defer server.Stop()
   467  	client := DialInProc(server)
   468  	defer client.Close()
   469  
   470  	var (
   471  		nc   = make(chan int)
   472  		errc = make(chan error, 1)
   473  		sub  *ClientSubscription
   474  		err  error
   475  	)
   476  	go func() {
   477  		sub, err = client.Subscribe(context.Background(), "nftest2", nc, "hangSubscription", 999)
   478  		errc <- err
   479  	}()
   480  
   481  	<-service.gotHangSubscriptionReq
   482  	client.Close()
   483  	service.unblockHangSubscription <- struct{}{}
   484  
   485  	select {
   486  	case err := <-errc:
   487  		if err == nil {
   488  			t.Errorf("Subscribe returned nil error after Close")
   489  		}
   490  		if sub != nil {
   491  			t.Error("Subscribe returned non-nil subscription after Close")
   492  		}
   493  	case <-time.After(1 * time.Second):
   494  		t.Fatalf("Subscribe did not return within 1s after Close")
   495  	}
   496  }
   497  
   498  // This test reproduces https://github.com/ethereum/go-ethereum/issues/17837 where the
   499  // client hangs during shutdown when Unsubscribe races with Client.Close.
   500  func TestClientCloseUnsubscribeRace(t *testing.T) {
   501  	server := newTestServer()
   502  	defer server.Stop()
   503  
   504  	for i := 0; i < 20; i++ {
   505  		client := DialInProc(server)
   506  		nc := make(chan int)
   507  		sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", 3, 1)
   508  		if err != nil {
   509  			t.Fatal(err)
   510  		}
   511  		go client.Close()
   512  		go sub.Unsubscribe()
   513  		select {
   514  		case <-sub.Err():
   515  		case <-time.After(5 * time.Second):
   516  			t.Fatal("subscription not closed within timeout")
   517  		}
   518  	}
   519  }
   520  
   521  // unsubscribeRecorder collects the subscription IDs of *_unsubscribe calls.
   522  type unsubscribeRecorder struct {
   523  	ServerCodec
   524  	unsubscribes map[string]bool
   525  }
   526  
   527  func (r *unsubscribeRecorder) readBatch() ([]*jsonrpcMessage, bool, error) {
   528  	if r.unsubscribes == nil {
   529  		r.unsubscribes = make(map[string]bool)
   530  	}
   531  
   532  	msgs, batch, err := r.ServerCodec.readBatch()
   533  	for _, msg := range msgs {
   534  		if msg.isUnsubscribe() {
   535  			var params []string
   536  			if err := json.Unmarshal(msg.Params, &params); err != nil {
   537  				panic("unsubscribe decode error: " + err.Error())
   538  			}
   539  			r.unsubscribes[params[0]] = true
   540  		}
   541  	}
   542  	return msgs, batch, err
   543  }
   544  
   545  // This checks that Client calls the _unsubscribe method on the server when Unsubscribe is
   546  // called on a subscription.
   547  func TestClientSubscriptionUnsubscribeServer(t *testing.T) {
   548  	t.Parallel()
   549  
   550  	// Create the server.
   551  	srv := NewServer()
   552  	srv.RegisterName("nftest", new(notificationTestService))
   553  	p1, p2 := net.Pipe()
   554  	recorder := &unsubscribeRecorder{ServerCodec: NewCodec(p1)}
   555  	go srv.ServeCodec(recorder, OptionMethodInvocation|OptionSubscriptions)
   556  	defer srv.Stop()
   557  
   558  	// Create the client on the other end of the pipe.
   559  	cfg := new(clientConfig)
   560  	client, _ := newClient(context.Background(), cfg, func(context.Context) (ServerCodec, error) {
   561  		return NewCodec(p2), nil
   562  	})
   563  	defer client.Close()
   564  
   565  	// Create the subscription.
   566  	ch := make(chan int)
   567  	sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 1, 1)
   568  	if err != nil {
   569  		t.Fatal(err)
   570  	}
   571  
   572  	// Unsubscribe and check that unsubscribe was called.
   573  	sub.Unsubscribe()
   574  	if !recorder.unsubscribes[sub.subid] {
   575  		t.Fatal("client did not call unsubscribe method")
   576  	}
   577  	if _, open := <-sub.Err(); open {
   578  		t.Fatal("subscription error channel not closed after unsubscribe")
   579  	}
   580  }
   581  
   582  // This checks that the subscribed channel can be closed after Unsubscribe.
   583  // It is the reproducer for https://github.com/ethereum/go-ethereum/issues/22322
   584  func TestClientSubscriptionChannelClose(t *testing.T) {
   585  	t.Parallel()
   586  
   587  	var (
   588  		srv     = NewServer()
   589  		httpsrv = httptest.NewServer(srv.WebsocketHandler(nil))
   590  		wsURL   = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
   591  	)
   592  	defer srv.Stop()
   593  	defer httpsrv.Close()
   594  
   595  	srv.RegisterName("nftest", new(notificationTestService))
   596  	client, _ := Dial(wsURL)
   597  	defer client.Close()
   598  
   599  	for i := 0; i < 100; i++ {
   600  		ch := make(chan int, 100)
   601  		sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 100, 1)
   602  		if err != nil {
   603  			t.Fatal(err)
   604  		}
   605  		sub.Unsubscribe()
   606  		close(ch)
   607  	}
   608  }
   609  
   610  // This test checks that Client doesn't lock up when a single subscriber
   611  // doesn't read subscription events.
   612  func TestClientNotificationStorm(t *testing.T) {
   613  	server := newTestServer()
   614  	defer server.Stop()
   615  
   616  	doTest := func(count int, wantError bool) {
   617  		client := DialInProc(server)
   618  		defer client.Close()
   619  		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   620  		defer cancel()
   621  
   622  		// Subscribe on the server. It will start sending many notifications
   623  		// very quickly.
   624  		nc := make(chan int)
   625  		sub, err := client.Subscribe(ctx, "nftest", nc, "someSubscription", count, 0)
   626  		if err != nil {
   627  			t.Fatal("can't subscribe:", err)
   628  		}
   629  		defer sub.Unsubscribe()
   630  
   631  		// Process each notification, try to run a call in between each of them.
   632  		for i := 0; i < count; i++ {
   633  			select {
   634  			case val := <-nc:
   635  				if val != i {
   636  					t.Fatalf("(%d/%d) unexpected value %d", i, count, val)
   637  				}
   638  			case err := <-sub.Err():
   639  				if wantError && err != ErrSubscriptionQueueOverflow {
   640  					t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow)
   641  				} else if !wantError {
   642  					t.Fatalf("(%d/%d) got unexpected error %q", i, count, err)
   643  				}
   644  				return
   645  			}
   646  			var r int
   647  			err := client.CallContext(ctx, &r, "nftest_echo", i)
   648  			if err != nil {
   649  				if !wantError {
   650  					t.Fatalf("(%d/%d) call error: %v", i, count, err)
   651  				}
   652  				return
   653  			}
   654  		}
   655  		if wantError {
   656  			t.Fatalf("didn't get expected error")
   657  		}
   658  	}
   659  
   660  	doTest(8000, false)
   661  	doTest(24000, true)
   662  }
   663  
   664  func TestClientSetHeader(t *testing.T) {
   665  	var gotHeader bool
   666  	srv := newTestServer()
   667  	httpsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   668  		if r.Header.Get("test") == "ok" {
   669  			gotHeader = true
   670  		}
   671  		srv.ServeHTTP(w, r)
   672  	}))
   673  	defer httpsrv.Close()
   674  	defer srv.Stop()
   675  
   676  	client, err := Dial(httpsrv.URL)
   677  	if err != nil {
   678  		t.Fatal(err)
   679  	}
   680  	defer client.Close()
   681  
   682  	client.SetHeader("test", "ok")
   683  	if _, err := client.SupportedModules(); err != nil {
   684  		t.Fatal(err)
   685  	}
   686  	if !gotHeader {
   687  		t.Fatal("client did not set custom header")
   688  	}
   689  
   690  	// Check that Content-Type can be replaced.
   691  	client.SetHeader("content-type", "application/x-garbage")
   692  	_, err = client.SupportedModules()
   693  	if err == nil {
   694  		t.Fatal("no error for invalid content-type header")
   695  	} else if !strings.Contains(err.Error(), "Unsupported Media Type") {
   696  		t.Fatalf("error is not related to content-type: %q", err)
   697  	}
   698  }
   699  
   700  func TestClientHTTP(t *testing.T) {
   701  	server := newTestServer()
   702  	defer server.Stop()
   703  
   704  	client, hs := httpTestClient(server, "http", nil)
   705  	defer hs.Close()
   706  	defer client.Close()
   707  
   708  	// Launch concurrent requests.
   709  	var (
   710  		results    = make([]echoResult, 100)
   711  		errc       = make(chan error, len(results))
   712  		wantResult = echoResult{"a", 1, new(echoArgs)}
   713  	)
   714  	for i := range results {
   715  		i := i
   716  		go func() {
   717  			errc <- client.Call(&results[i], "test_echo", wantResult.String, wantResult.Int, wantResult.Args)
   718  		}()
   719  	}
   720  
   721  	// Wait for all of them to complete.
   722  	timeout := time.NewTimer(5 * time.Second)
   723  	defer timeout.Stop()
   724  	for i := range results {
   725  		select {
   726  		case err := <-errc:
   727  			if err != nil {
   728  				t.Fatal(err)
   729  			}
   730  		case <-timeout.C:
   731  			t.Fatalf("timeout (got %d/%d) results)", i+1, len(results))
   732  		}
   733  	}
   734  
   735  	// Check results.
   736  	for i := range results {
   737  		if !reflect.DeepEqual(results[i], wantResult) {
   738  			t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult)
   739  		}
   740  	}
   741  }
   742  
   743  func TestClientReconnect(t *testing.T) {
   744  	startServer := func(addr string) (*Server, net.Listener) {
   745  		srv := newTestServer()
   746  		l, err := net.Listen("tcp", addr)
   747  		if err != nil {
   748  			t.Fatal("can't listen:", err)
   749  		}
   750  		go http.Serve(l, srv.WebsocketHandler([]string{"*"}))
   751  		return srv, l
   752  	}
   753  
   754  	ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
   755  	defer cancel()
   756  
   757  	// Start a server and corresponding client.
   758  	s1, l1 := startServer("127.0.0.1:0")
   759  	client, err := DialContext(ctx, "ws://"+l1.Addr().String())
   760  	if err != nil {
   761  		t.Fatal("can't dial", err)
   762  	}
   763  	defer client.Close()
   764  
   765  	// Perform a call. This should work because the server is up.
   766  	var resp echoResult
   767  	if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil {
   768  		t.Fatal(err)
   769  	}
   770  
   771  	// Shut down the server and allow for some cool down time so we can listen on the same
   772  	// address again.
   773  	l1.Close()
   774  	s1.Stop()
   775  	time.Sleep(2 * time.Second)
   776  
   777  	// Try calling again. It shouldn't work.
   778  	if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil {
   779  		t.Error("successful call while the server is down")
   780  		t.Logf("resp: %#v", resp)
   781  	}
   782  
   783  	// Start it up again and call again. The connection should be reestablished.
   784  	// We spawn multiple calls here to check whether this hangs somehow.
   785  	s2, l2 := startServer(l1.Addr().String())
   786  	defer l2.Close()
   787  	defer s2.Stop()
   788  
   789  	start := make(chan struct{})
   790  	errors := make(chan error, 20)
   791  	for i := 0; i < cap(errors); i++ {
   792  		go func() {
   793  			<-start
   794  			var resp echoResult
   795  			errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil)
   796  		}()
   797  	}
   798  	close(start)
   799  	errcount := 0
   800  	for i := 0; i < cap(errors); i++ {
   801  		if err = <-errors; err != nil {
   802  			errcount++
   803  		}
   804  	}
   805  	t.Logf("%d errors, last error: %v", errcount, err)
   806  	if errcount > 1 {
   807  		t.Errorf("expected one error after disconnect, got %d", errcount)
   808  	}
   809  }
   810  
   811  func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) {
   812  	// Create the HTTP server.
   813  	var hs *httptest.Server
   814  	switch transport {
   815  	case "ws":
   816  		hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"}))
   817  	case "http":
   818  		hs = httptest.NewUnstartedServer(srv)
   819  	default:
   820  		panic("unknown HTTP transport: " + transport)
   821  	}
   822  	// Wrap the listener if required.
   823  	if fl != nil {
   824  		fl.Listener = hs.Listener
   825  		hs.Listener = fl
   826  	}
   827  	// Connect the client.
   828  	hs.Start()
   829  	client, err := Dial(transport + "://" + hs.Listener.Addr().String())
   830  	if err != nil {
   831  		panic(err)
   832  	}
   833  	return client, hs
   834  }
   835  
   836  func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) {
   837  	// Listen on a random endpoint.
   838  	endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63())
   839  	if runtime.GOOS == "windows" {
   840  		endpoint = `\\.\pipe\` + endpoint
   841  	} else {
   842  		endpoint = os.TempDir() + "/" + endpoint
   843  	}
   844  	l, err := ipcListen(endpoint)
   845  	if err != nil {
   846  		panic(err)
   847  	}
   848  	// Connect the listener to the server.
   849  	if fl != nil {
   850  		fl.Listener = l
   851  		l = fl
   852  	}
   853  	go srv.ServeListener(l)
   854  	// Connect the client.
   855  	client, err := Dial(endpoint)
   856  	if err != nil {
   857  		panic(err)
   858  	}
   859  	return client, l
   860  }
   861  
   862  // flakeyListener kills accepted connections after a random timeout.
   863  type flakeyListener struct {
   864  	net.Listener
   865  	maxKillTimeout time.Duration
   866  	maxAcceptDelay time.Duration
   867  }
   868  
   869  func (l *flakeyListener) Accept() (net.Conn, error) {
   870  	delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay)))
   871  	time.Sleep(delay)
   872  
   873  	c, err := l.Listener.Accept()
   874  	if err == nil {
   875  		timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout)))
   876  		time.AfterFunc(timeout, func() {
   877  			log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout))
   878  			c.Close()
   879  		})
   880  	}
   881  	return c, err
   882  }