github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/rpc/client_test.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:42</date>
    10  //</624450108569686016>
    11  
    12  
    13  package rpc
    14  
    15  import (
    16  	"context"
    17  	"fmt"
    18  	"math/rand"
    19  	"net"
    20  	"net/http"
    21  	"net/http/httptest"
    22  	"os"
    23  	"reflect"
    24  	"runtime"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/davecgh/go-spew/spew"
    30  	"github.com/ethereum/go-ethereum/log"
    31  )
    32  
    33  func TestClientRequest(t *testing.T) {
    34  	server := newTestServer("service", new(Service))
    35  	defer server.Stop()
    36  	client := DialInProc(server)
    37  	defer client.Close()
    38  
    39  	var resp Result
    40  	if err := client.Call(&resp, "service_echo", "hello", 10, &Args{"world"}); err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	if !reflect.DeepEqual(resp, Result{"hello", 10, &Args{"world"}}) {
    44  		t.Errorf("incorrect result %#v", resp)
    45  	}
    46  }
    47  
    48  func TestClientBatchRequest(t *testing.T) {
    49  	server := newTestServer("service", new(Service))
    50  	defer server.Stop()
    51  	client := DialInProc(server)
    52  	defer client.Close()
    53  
    54  	batch := []BatchElem{
    55  		{
    56  			Method: "service_echo",
    57  			Args:   []interface{}{"hello", 10, &Args{"world"}},
    58  			Result: new(Result),
    59  		},
    60  		{
    61  			Method: "service_echo",
    62  			Args:   []interface{}{"hello2", 11, &Args{"world"}},
    63  			Result: new(Result),
    64  		},
    65  		{
    66  			Method: "no_such_method",
    67  			Args:   []interface{}{1, 2, 3},
    68  			Result: new(int),
    69  		},
    70  	}
    71  	if err := client.BatchCall(batch); err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	wantResult := []BatchElem{
    75  		{
    76  			Method: "service_echo",
    77  			Args:   []interface{}{"hello", 10, &Args{"world"}},
    78  			Result: &Result{"hello", 10, &Args{"world"}},
    79  		},
    80  		{
    81  			Method: "service_echo",
    82  			Args:   []interface{}{"hello2", 11, &Args{"world"}},
    83  			Result: &Result{"hello2", 11, &Args{"world"}},
    84  		},
    85  		{
    86  			Method: "no_such_method",
    87  			Args:   []interface{}{1, 2, 3},
    88  			Result: new(int),
    89  			Error:  &jsonError{Code: -32601, Message: "The method no_such_method_ does not exist/is not available"},
    90  		},
    91  	}
    92  	if !reflect.DeepEqual(batch, wantResult) {
    93  		t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult))
    94  	}
    95  }
    96  
    97  //func testclientcancel inproc(t*testing.t)testclientcancel(“inproc”,t)
    98  func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) }
    99  func TestClientCancelHTTP(t *testing.T)      { testClientCancel("http", t) }
   100  func TestClientCancelIPC(t *testing.T)       { testClientCancel("ipc", t) }
   101  
   102  //此测试检查通过callContext发出的请求是否可以通过取消来取消
   103  //语境。
   104  func testClientCancel(transport string, t *testing.T) {
   105  	server := newTestServer("service", new(Service))
   106  	defer server.Stop()
   107  
   108  //我们想要实现的是取消上下文
   109  //在请求处理的各个阶段。有趣的案例
   110  //是:
   111  //-拨号时取消
   112  //-执行HTTP请求时取消
   113  //-等待响应时取消
   114  //
   115  //为了触发这些,时间的选择使得连接
   116  //在每个其他呼叫的截止时间内终止(maxkillTimeout
   117  //是2x maxCancelTimeout)。
   118  //
   119  //一旦连接断开,很有可能无法连接
   120  //成功,因为接受延迟了1秒。
   121  	maxContextCancelTimeout := 300 * time.Millisecond
   122  	fl := &flakeyListener{
   123  		maxAcceptDelay: 1 * time.Second,
   124  		maxKillTimeout: 600 * time.Millisecond,
   125  	}
   126  
   127  	var client *Client
   128  	switch transport {
   129  	case "ws", "http":
   130  		c, hs := httpTestClient(server, transport, fl)
   131  		defer hs.Close()
   132  		client = c
   133  	case "ipc":
   134  		c, l := ipcTestClient(server, fl)
   135  		defer l.Close()
   136  		client = c
   137  	default:
   138  		panic("unknown transport: " + transport)
   139  	}
   140  
   141  //这些测试需要很多时间,一次运行它们。
   142  //您可能希望使用-parallel 1或comment out运行
   143  //如果启用日志记录,则调用T.Parallel。
   144  	t.Parallel()
   145  
   146  //实际测试从这里开始。
   147  	var (
   148  		wg       sync.WaitGroup
   149  		nreqs    = 10
   150  		ncallers = 6
   151  	)
   152  	caller := func(index int) {
   153  		defer wg.Done()
   154  		for i := 0; i < nreqs; i++ {
   155  			var (
   156  				ctx     context.Context
   157  				cancel  func()
   158  				timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout)))
   159  			)
   160  			if index < ncallers/2 {
   161  //对于一半的调用者,创建一个没有截止日期的上下文
   162  //以后再取消。
   163  				ctx, cancel = context.WithCancel(context.Background())
   164  				time.AfterFunc(timeout, cancel)
   165  			} else {
   166  //对于另一半,创建一个带有截止日期的上下文。这是
   167  //不同,因为上下文期限用于设置套接字写入
   168  //截止日期。
   169  				ctx, cancel = context.WithTimeout(context.Background(), timeout)
   170  			}
   171  //现在使用上下文执行调用。
   172  //这里的关键是没有一个呼叫能成功完成。
   173  			err := client.CallContext(ctx, nil, "service_sleep", 2*maxContextCancelTimeout)
   174  			if err != nil {
   175  				log.Debug(fmt.Sprint("got expected error:", err))
   176  			} else {
   177  				t.Errorf("no error for call with %v wait time", timeout)
   178  			}
   179  			cancel()
   180  		}
   181  	}
   182  	wg.Add(ncallers)
   183  	for i := 0; i < ncallers; i++ {
   184  		go caller(i)
   185  	}
   186  	wg.Wait()
   187  }
   188  
   189  func TestClientSubscribeInvalidArg(t *testing.T) {
   190  	server := newTestServer("service", new(Service))
   191  	defer server.Stop()
   192  	client := DialInProc(server)
   193  	defer client.Close()
   194  
   195  	check := func(shouldPanic bool, arg interface{}) {
   196  		defer func() {
   197  			err := recover()
   198  			if shouldPanic && err == nil {
   199  				t.Errorf("EthSubscribe should've panicked for %#v", arg)
   200  			}
   201  			if !shouldPanic && err != nil {
   202  				t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg)
   203  				buf := make([]byte, 1024*1024)
   204  				buf = buf[:runtime.Stack(buf, false)]
   205  				t.Error(err)
   206  				t.Error(string(buf))
   207  			}
   208  		}()
   209  		client.EthSubscribe(context.Background(), arg, "foo_bar")
   210  	}
   211  	check(true, nil)
   212  	check(true, 1)
   213  	check(true, (chan int)(nil))
   214  	check(true, make(<-chan int))
   215  	check(false, make(chan int))
   216  	check(false, make(chan<- int))
   217  }
   218  
   219  func TestClientSubscribe(t *testing.T) {
   220  	server := newTestServer("eth", new(NotificationTestService))
   221  	defer server.Stop()
   222  	client := DialInProc(server)
   223  	defer client.Close()
   224  
   225  	nc := make(chan int)
   226  	count := 10
   227  	sub, err := client.EthSubscribe(context.Background(), nc, "someSubscription", count, 0)
   228  	if err != nil {
   229  		t.Fatal("can't subscribe:", err)
   230  	}
   231  	for i := 0; i < count; i++ {
   232  		if val := <-nc; val != i {
   233  			t.Fatalf("value mismatch: got %d, want %d", val, i)
   234  		}
   235  	}
   236  
   237  	sub.Unsubscribe()
   238  	select {
   239  	case v := <-nc:
   240  		t.Fatal("received value after unsubscribe:", v)
   241  	case err := <-sub.Err():
   242  		if err != nil {
   243  			t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err)
   244  		}
   245  	case <-time.After(1 * time.Second):
   246  		t.Fatalf("subscription not closed within 1s after unsubscribe")
   247  	}
   248  }
   249  
   250  func TestClientSubscribeCustomNamespace(t *testing.T) {
   251  	namespace := "custom"
   252  	server := newTestServer(namespace, new(NotificationTestService))
   253  	defer server.Stop()
   254  	client := DialInProc(server)
   255  	defer client.Close()
   256  
   257  	nc := make(chan int)
   258  	count := 10
   259  	sub, err := client.Subscribe(context.Background(), namespace, nc, "someSubscription", count, 0)
   260  	if err != nil {
   261  		t.Fatal("can't subscribe:", err)
   262  	}
   263  	for i := 0; i < count; i++ {
   264  		if val := <-nc; val != i {
   265  			t.Fatalf("value mismatch: got %d, want %d", val, i)
   266  		}
   267  	}
   268  
   269  	sub.Unsubscribe()
   270  	select {
   271  	case v := <-nc:
   272  		t.Fatal("received value after unsubscribe:", v)
   273  	case err := <-sub.Err():
   274  		if err != nil {
   275  			t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err)
   276  		}
   277  	case <-time.After(1 * time.Second):
   278  		t.Fatalf("subscription not closed within 1s after unsubscribe")
   279  	}
   280  }
   281  
   282  //在这个测试中,当ethsubscribe
   283  //正在等待响应。
   284  func TestClientSubscribeClose(t *testing.T) {
   285  	service := &NotificationTestService{
   286  		gotHangSubscriptionReq:  make(chan struct{}),
   287  		unblockHangSubscription: make(chan struct{}),
   288  	}
   289  	server := newTestServer("eth", service)
   290  	defer server.Stop()
   291  	client := DialInProc(server)
   292  	defer client.Close()
   293  
   294  	var (
   295  		nc   = make(chan int)
   296  		errc = make(chan error)
   297  		sub  *ClientSubscription
   298  		err  error
   299  	)
   300  	go func() {
   301  		sub, err = client.EthSubscribe(context.Background(), nc, "hangSubscription", 999)
   302  		errc <- err
   303  	}()
   304  
   305  	<-service.gotHangSubscriptionReq
   306  	client.Close()
   307  	service.unblockHangSubscription <- struct{}{}
   308  
   309  	select {
   310  	case err := <-errc:
   311  		if err == nil {
   312  			t.Errorf("EthSubscribe returned nil error after Close")
   313  		}
   314  		if sub != nil {
   315  			t.Error("EthSubscribe returned non-nil subscription after Close")
   316  		}
   317  	case <-time.After(1 * time.Second):
   318  		t.Fatalf("EthSubscribe did not return within 1s after Close")
   319  	}
   320  }
   321  
   322  //此测试复制了https://github.com/ethereum/go-ethereum/issues/17837,其中
   323  //当unsubscribe与client.close竞争时,客户机在关闭期间挂起。
   324  func TestClientCloseUnsubscribeRace(t *testing.T) {
   325  	service := &NotificationTestService{}
   326  	server := newTestServer("eth", service)
   327  	defer server.Stop()
   328  
   329  	for i := 0; i < 20; i++ {
   330  		client := DialInProc(server)
   331  		nc := make(chan int)
   332  		sub, err := client.EthSubscribe(context.Background(), nc, "someSubscription", 3, 1)
   333  		if err != nil {
   334  			t.Fatal(err)
   335  		}
   336  		go client.Close()
   337  		go sub.Unsubscribe()
   338  		select {
   339  		case <-sub.Err():
   340  		case <-time.After(5 * time.Second):
   341  			t.Fatal("subscription not closed within timeout")
   342  		}
   343  	}
   344  }
   345  
   346  //此测试检查当单个订阅服务器
   347  //不读取订阅事件。
   348  func TestClientNotificationStorm(t *testing.T) {
   349  	server := newTestServer("eth", new(NotificationTestService))
   350  	defer server.Stop()
   351  
   352  	doTest := func(count int, wantError bool) {
   353  		client := DialInProc(server)
   354  		defer client.Close()
   355  		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   356  		defer cancel()
   357  
   358  //订阅服务器。它将开始发送许多通知
   359  //很快。
   360  		nc := make(chan int)
   361  		sub, err := client.EthSubscribe(ctx, nc, "someSubscription", count, 0)
   362  		if err != nil {
   363  			t.Fatal("can't subscribe:", err)
   364  		}
   365  		defer sub.Unsubscribe()
   366  
   367  //处理每个通知,尝试在它们之间运行一个调用。
   368  		for i := 0; i < count; i++ {
   369  			select {
   370  			case val := <-nc:
   371  				if val != i {
   372  					t.Fatalf("(%d/%d) unexpected value %d", i, count, val)
   373  				}
   374  			case err := <-sub.Err():
   375  				if wantError && err != ErrSubscriptionQueueOverflow {
   376  					t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow)
   377  				} else if !wantError {
   378  					t.Fatalf("(%d/%d) got unexpected error %q", i, count, err)
   379  				}
   380  				return
   381  			}
   382  			var r int
   383  			err := client.CallContext(ctx, &r, "eth_echo", i)
   384  			if err != nil {
   385  				if !wantError {
   386  					t.Fatalf("(%d/%d) call error: %v", i, count, err)
   387  				}
   388  				return
   389  			}
   390  		}
   391  	}
   392  
   393  	doTest(8000, false)
   394  	doTest(10000, true)
   395  }
   396  
   397  func TestClientHTTP(t *testing.T) {
   398  	server := newTestServer("service", new(Service))
   399  	defer server.Stop()
   400  
   401  	client, hs := httpTestClient(server, "http", nil)
   402  	defer hs.Close()
   403  	defer client.Close()
   404  
   405  //启动并发请求。
   406  	var (
   407  		results    = make([]Result, 100)
   408  		errc       = make(chan error)
   409  		wantResult = Result{"a", 1, new(Args)}
   410  	)
   411  	defer client.Close()
   412  	for i := range results {
   413  		i := i
   414  		go func() {
   415  			errc <- client.Call(&results[i], "service_echo",
   416  				wantResult.String, wantResult.Int, wantResult.Args)
   417  		}()
   418  	}
   419  
   420  //等待所有任务完成。
   421  	timeout := time.NewTimer(5 * time.Second)
   422  	defer timeout.Stop()
   423  	for i := range results {
   424  		select {
   425  		case err := <-errc:
   426  			if err != nil {
   427  				t.Fatal(err)
   428  			}
   429  		case <-timeout.C:
   430  			t.Fatalf("timeout (got %d/%d) results)", i+1, len(results))
   431  		}
   432  	}
   433  
   434  //检查结果。
   435  	for i := range results {
   436  		if !reflect.DeepEqual(results[i], wantResult) {
   437  			t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult)
   438  		}
   439  	}
   440  }
   441  
   442  func TestClientReconnect(t *testing.T) {
   443  	startServer := func(addr string) (*Server, net.Listener) {
   444  		srv := newTestServer("service", new(Service))
   445  		l, err := net.Listen("tcp", addr)
   446  		if err != nil {
   447  			t.Fatal(err)
   448  		}
   449  		go http.Serve(l, srv.WebsocketHandler([]string{"*"}))
   450  		return srv, l
   451  	}
   452  
   453  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   454  	defer cancel()
   455  
   456  //启动服务器和相应的客户机。
   457  	s1, l1 := startServer("127.0.0.1:0")
   458  client, err := DialContext(ctx, "ws://“+l1.addr().string())
   459  	if err != nil {
   460  		t.Fatal("can't dial", err)
   461  	}
   462  
   463  //打个电话。这应该可以工作,因为服务器已启动。
   464  	var resp Result
   465  	if err := client.CallContext(ctx, &resp, "service_echo", "", 1, nil); err != nil {
   466  		t.Fatal(err)
   467  	}
   468  
   469  //关闭服务器,然后再次尝试呼叫。它不应该起作用。
   470  	l1.Close()
   471  	s1.Stop()
   472  	if err := client.CallContext(ctx, &resp, "service_echo", "", 2, nil); err == nil {
   473  		t.Error("successful call while the server is down")
   474  		t.Logf("resp: %#v", resp)
   475  	}
   476  
   477  //留出一些冷却时间,以便我们可以再次收听相同的地址。
   478  	time.Sleep(2 * time.Second)
   479  
   480  //重新启动然后再打电话。应重新建立连接。
   481  //我们在这里生成多个调用来检查这个是否以某种方式挂起。
   482  	s2, l2 := startServer(l1.Addr().String())
   483  	defer l2.Close()
   484  	defer s2.Stop()
   485  
   486  	start := make(chan struct{})
   487  	errors := make(chan error, 20)
   488  	for i := 0; i < cap(errors); i++ {
   489  		go func() {
   490  			<-start
   491  			var resp Result
   492  			errors <- client.CallContext(ctx, &resp, "service_echo", "", 3, nil)
   493  		}()
   494  	}
   495  	close(start)
   496  	errcount := 0
   497  	for i := 0; i < cap(errors); i++ {
   498  		if err = <-errors; err != nil {
   499  			errcount++
   500  		}
   501  	}
   502  	t.Log("err:", err)
   503  	if errcount > 1 {
   504  		t.Errorf("expected one error after disconnect, got %d", errcount)
   505  	}
   506  }
   507  
   508  func newTestServer(serviceName string, service interface{}) *Server {
   509  	server := NewServer()
   510  	if err := server.RegisterName(serviceName, service); err != nil {
   511  		panic(err)
   512  	}
   513  	return server
   514  }
   515  
   516  func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) {
   517  //创建HTTP服务器。
   518  	var hs *httptest.Server
   519  	switch transport {
   520  	case "ws":
   521  		hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"}))
   522  	case "http":
   523  		hs = httptest.NewUnstartedServer(srv)
   524  	default:
   525  		panic("unknown HTTP transport: " + transport)
   526  	}
   527  //根据需要包装侦听器。
   528  	if fl != nil {
   529  		fl.Listener = hs.Listener
   530  		hs.Listener = fl
   531  	}
   532  //连接客户端。
   533  	hs.Start()
   534  client, err := Dial(transport + "://“+hs.listener.addr().string())
   535  	if err != nil {
   536  		panic(err)
   537  	}
   538  	return client, hs
   539  }
   540  
   541  func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) {
   542  //在随机端点上侦听。
   543  	endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63())
   544  	if runtime.GOOS == "windows" {
   545  		endpoint = `\\.\pipe\` + endpoint
   546  	} else {
   547  		endpoint = os.TempDir() + "/" + endpoint
   548  	}
   549  	l, err := ipcListen(endpoint)
   550  	if err != nil {
   551  		panic(err)
   552  	}
   553  //将侦听器连接到服务器。
   554  	if fl != nil {
   555  		fl.Listener = l
   556  		l = fl
   557  	}
   558  	go srv.ServeListener(l)
   559  //连接客户端。
   560  	client, err := Dial(endpoint)
   561  	if err != nil {
   562  		panic(err)
   563  	}
   564  	return client, l
   565  }
   566  
   567  //flakeylistener在随机超时后终止接受的连接。
   568  type flakeyListener struct {
   569  	net.Listener
   570  	maxKillTimeout time.Duration
   571  	maxAcceptDelay time.Duration
   572  }
   573  
   574  func (l *flakeyListener) Accept() (net.Conn, error) {
   575  	delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay)))
   576  	time.Sleep(delay)
   577  
   578  	c, err := l.Listener.Accept()
   579  	if err == nil {
   580  		timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout)))
   581  		time.AfterFunc(timeout, func() {
   582  			log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout))
   583  			c.Close()
   584  		})
   585  	}
   586  	return c, err
   587  }
   588