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(¬ification); 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