github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/rpc/subscription_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  //</624450109765062656>
    11  
    12  
    13  package rpc
    14  
    15  import (
    16  	"context"
    17  	"encoding/json"
    18  	"fmt"
    19  	"net"
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  )
    24  
    25  type NotificationTestService struct {
    26  	mu                      sync.Mutex
    27  	unsubscribed            chan string
    28  	gotHangSubscriptionReq  chan struct{}
    29  	unblockHangSubscription chan struct{}
    30  }
    31  
    32  func (s *NotificationTestService) Echo(i int) int {
    33  	return i
    34  }
    35  
    36  func (s *NotificationTestService) Unsubscribe(subid string) {
    37  	if s.unsubscribed != nil {
    38  		s.unsubscribed <- subid
    39  	}
    40  }
    41  
    42  func (s *NotificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) {
    43  	notifier, supported := NotifierFromContext(ctx)
    44  	if !supported {
    45  		return nil, ErrNotificationsUnsupported
    46  	}
    47  
    48  //通过显式创建订阅,我们确保将订阅ID发送回客户端
    49  //在第一次订阅之前。调用notify。否则,事件可能会在响应之前发送
    50  //对于eth-subscribe方法。
    51  	subscription := notifier.CreateSubscription()
    52  
    53  	go func() {
    54  //测试需要n个事件,如果我们立即开始发送事件,则某些事件
    55  //可能会删除,因为订阅ID可能不会发送到
    56  //客户。
    57  		for i := 0; i < n; i++ {
    58  			if err := notifier.Notify(subscription.ID, val+i); err != nil {
    59  				return
    60  			}
    61  		}
    62  
    63  		select {
    64  		case <-notifier.Closed():
    65  		case <-subscription.Err():
    66  		}
    67  		if s.unsubscribed != nil {
    68  			s.unsubscribed <- string(subscription.ID)
    69  		}
    70  	}()
    71  
    72  	return subscription, nil
    73  }
    74  
    75  //在s.unblockhangsubscription上挂起订阅块
    76  //发送任何东西。
    77  func (s *NotificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) {
    78  	notifier, supported := NotifierFromContext(ctx)
    79  	if !supported {
    80  		return nil, ErrNotificationsUnsupported
    81  	}
    82  
    83  	s.gotHangSubscriptionReq <- struct{}{}
    84  	<-s.unblockHangSubscription
    85  	subscription := notifier.CreateSubscription()
    86  
    87  	go func() {
    88  		notifier.Notify(subscription.ID, val)
    89  	}()
    90  	return subscription, nil
    91  }
    92  
    93  func TestNotifications(t *testing.T) {
    94  	server := NewServer()
    95  	service := &NotificationTestService{unsubscribed: make(chan string)}
    96  
    97  	if err := server.RegisterName("eth", service); err != nil {
    98  		t.Fatalf("unable to register test service %v", err)
    99  	}
   100  
   101  	clientConn, serverConn := net.Pipe()
   102  
   103  	go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions)
   104  
   105  	out := json.NewEncoder(clientConn)
   106  	in := json.NewDecoder(clientConn)
   107  
   108  	n := 5
   109  	val := 12345
   110  	request := map[string]interface{}{
   111  		"id":      1,
   112  		"method":  "eth_subscribe",
   113  		"version": "2.0",
   114  		"params":  []interface{}{"someSubscription", n, val},
   115  	}
   116  
   117  //创建订阅
   118  	if err := out.Encode(request); err != nil {
   119  		t.Fatal(err)
   120  	}
   121  
   122  	var subid string
   123  	response := jsonSuccessResponse{Result: subid}
   124  	if err := in.Decode(&response); err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	var ok bool
   129  	if _, ok = response.Result.(string); !ok {
   130  		t.Fatalf("expected subscription id, got %T", response.Result)
   131  	}
   132  
   133  	for i := 0; i < n; i++ {
   134  		var notification jsonNotification
   135  		if err := in.Decode(&notification); err != nil {
   136  			t.Fatalf("%v", err)
   137  		}
   138  
   139  		if int(notification.Params.Result.(float64)) != val+i {
   140  			t.Fatalf("expected %d, got %d", val+i, notification.Params.Result)
   141  		}
   142  	}
   143  
   144  clientConn.Close() //导致调用通知取消订阅回调
   145  	select {
   146  	case <-service.unsubscribed:
   147  	case <-time.After(1 * time.Second):
   148  		t.Fatal("Unsubscribe not called after one second")
   149  	}
   150  }
   151  
   152  func waitForMessages(t *testing.T, in *json.Decoder, successes chan<- jsonSuccessResponse,
   153  	failures chan<- jsonErrResponse, notifications chan<- jsonNotification, errors chan<- error) {
   154  
   155  //读取和分析服务器消息
   156  	for {
   157  		var rmsg json.RawMessage
   158  		if err := in.Decode(&rmsg); err != nil {
   159  			return
   160  		}
   161  
   162  		var responses []map[string]interface{}
   163  		if rmsg[0] == '[' {
   164  			if err := json.Unmarshal(rmsg, &responses); err != nil {
   165  				errors <- fmt.Errorf("Received invalid message: %s", rmsg)
   166  				return
   167  			}
   168  		} else {
   169  			var msg map[string]interface{}
   170  			if err := json.Unmarshal(rmsg, &msg); err != nil {
   171  				errors <- fmt.Errorf("Received invalid message: %s", rmsg)
   172  				return
   173  			}
   174  			responses = append(responses, msg)
   175  		}
   176  
   177  		for _, msg := range responses {
   178  //确定接收和广播的消息类型
   179  //通过相应的通道
   180  			if _, found := msg["result"]; found {
   181  				successes <- jsonSuccessResponse{
   182  					Version: msg["jsonrpc"].(string),
   183  					Id:      msg["id"],
   184  					Result:  msg["result"],
   185  				}
   186  				continue
   187  			}
   188  			if _, found := msg["error"]; found {
   189  				params := msg["params"].(map[string]interface{})
   190  				failures <- jsonErrResponse{
   191  					Version: msg["jsonrpc"].(string),
   192  					Id:      msg["id"],
   193  					Error:   jsonError{int(params["subscription"].(float64)), params["message"].(string), params["data"]},
   194  				}
   195  				continue
   196  			}
   197  			if _, found := msg["params"]; found {
   198  				params := msg["params"].(map[string]interface{})
   199  				notifications <- jsonNotification{
   200  					Version: msg["jsonrpc"].(string),
   201  					Method:  msg["method"].(string),
   202  					Params:  jsonSubscription{params["subscription"].(string), params["result"]},
   203  				}
   204  				continue
   205  			}
   206  			errors <- fmt.Errorf("Received invalid message: %s", msg)
   207  		}
   208  	}
   209  }
   210  
   211  //testsubscriptionmultiplename空间确保订阅可以存在
   212  //对于多个不同的命名空间。
   213  func TestSubscriptionMultipleNamespaces(t *testing.T) {
   214  	var (
   215  		namespaces        = []string{"eth", "shh", "bzz"}
   216  		service           = NotificationTestService{}
   217  		subCount          = len(namespaces) * 2
   218  		notificationCount = 3
   219  
   220  		server                 = NewServer()
   221  		clientConn, serverConn = net.Pipe()
   222  		out                    = json.NewEncoder(clientConn)
   223  		in                     = json.NewDecoder(clientConn)
   224  		successes              = make(chan jsonSuccessResponse)
   225  		failures               = make(chan jsonErrResponse)
   226  		notifications          = make(chan jsonNotification)
   227  		errors                 = make(chan error, 10)
   228  	)
   229  
   230  //安装并启动服务器
   231  	for _, namespace := range namespaces {
   232  		if err := server.RegisterName(namespace, &service); err != nil {
   233  			t.Fatalf("unable to register test service %v", err)
   234  		}
   235  	}
   236  
   237  	go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions)
   238  	defer server.Stop()
   239  
   240  //等待消息并将其写入给定的通道
   241  	go waitForMessages(t, in, successes, failures, notifications, errors)
   242  
   243  //逐个创建订阅
   244  	for i, namespace := range namespaces {
   245  		request := map[string]interface{}{
   246  			"id":      i,
   247  			"method":  fmt.Sprintf("%s_subscribe", namespace),
   248  			"version": "2.0",
   249  			"params":  []interface{}{"someSubscription", notificationCount, i},
   250  		}
   251  
   252  		if err := out.Encode(&request); err != nil {
   253  			t.Fatalf("Could not create subscription: %v", err)
   254  		}
   255  	}
   256  
   257  //在一批中创建所有订阅
   258  	var requests []interface{}
   259  	for i, namespace := range namespaces {
   260  		requests = append(requests, map[string]interface{}{
   261  			"id":      i,
   262  			"method":  fmt.Sprintf("%s_subscribe", namespace),
   263  			"version": "2.0",
   264  			"params":  []interface{}{"someSubscription", notificationCount, i},
   265  		})
   266  	}
   267  
   268  	if err := out.Encode(&requests); err != nil {
   269  		t.Fatalf("Could not create subscription in batch form: %v", err)
   270  	}
   271  
   272  	timeout := time.After(30 * time.Second)
   273  	subids := make(map[string]string, subCount)
   274  	count := make(map[string]int, subCount)
   275  	allReceived := func() bool {
   276  		done := len(count) == subCount
   277  		for _, c := range count {
   278  			if c < notificationCount {
   279  				done = false
   280  			}
   281  		}
   282  		return done
   283  	}
   284  
   285  	for !allReceived() {
   286  		select {
   287  case suc := <-successes: //已创建订阅
   288  			subids[namespaces[int(suc.Id.(float64))]] = suc.Result.(string)
   289  		case notification := <-notifications:
   290  			count[notification.Params.Subscription]++
   291  		case err := <-errors:
   292  			t.Fatal(err)
   293  		case failure := <-failures:
   294  			t.Errorf("received error: %v", failure.Error)
   295  		case <-timeout:
   296  			for _, namespace := range namespaces {
   297  				subid, found := subids[namespace]
   298  				if !found {
   299  					t.Errorf("subscription for %q not created", namespace)
   300  					continue
   301  				}
   302  				if count, found := count[subid]; !found || count < notificationCount {
   303  					t.Errorf("didn't receive all notifications (%d<%d) in time for namespace %q", count, notificationCount, namespace)
   304  				}
   305  			}
   306  			t.Fatal("timed out")
   307  		}
   308  	}
   309  }
   310