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