github.com/MetalBlockchain/subnet-evm@v0.4.9/rpc/client_test.go (about)

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