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