github.com/codingfuture/orig-energi3@v0.8.4/rpc/subscription_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "net" 24 "sync" 25 "testing" 26 "time" 27 ) 28 29 type NotificationTestService struct { 30 mu sync.Mutex 31 unsubscribed chan string 32 gotHangSubscriptionReq chan struct{} 33 unblockHangSubscription chan struct{} 34 } 35 36 func (s *NotificationTestService) Echo(i int) int { 37 return i 38 } 39 40 func (s *NotificationTestService) Unsubscribe(subid string) { 41 if s.unsubscribed != nil { 42 s.unsubscribed <- subid 43 } 44 } 45 46 func (s *NotificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) { 47 notifier, supported := NotifierFromContext(ctx) 48 if !supported { 49 return nil, ErrNotificationsUnsupported 50 } 51 52 // by explicitly creating an subscription we make sure that the subscription id is send back to the client 53 // before the first subscription.Notify is called. Otherwise the events might be send before the response 54 // for the eth_subscribe method. 55 subscription := notifier.CreateSubscription() 56 57 go func() { 58 // test expects n events, if we begin sending event immediately some events 59 // will probably be dropped since the subscription ID might not be send to 60 // the client. 61 for i := 0; i < n; i++ { 62 if err := notifier.Notify(subscription.ID, val+i); err != nil { 63 return 64 } 65 } 66 67 select { 68 case <-notifier.Closed(): 69 case <-subscription.Err(): 70 } 71 if s.unsubscribed != nil { 72 s.unsubscribed <- string(subscription.ID) 73 } 74 }() 75 76 return subscription, nil 77 } 78 79 // HangSubscription blocks on s.unblockHangSubscription before 80 // sending anything. 81 func (s *NotificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) { 82 notifier, supported := NotifierFromContext(ctx) 83 if !supported { 84 return nil, ErrNotificationsUnsupported 85 } 86 87 s.gotHangSubscriptionReq <- struct{}{} 88 <-s.unblockHangSubscription 89 subscription := notifier.CreateSubscription() 90 91 go func() { 92 notifier.Notify(subscription.ID, val) 93 }() 94 return subscription, nil 95 } 96 97 func TestNotifications(t *testing.T) { 98 server := NewServer() 99 service := &NotificationTestService{unsubscribed: make(chan string)} 100 101 if err := server.RegisterName("eth", service); err != nil { 102 t.Fatalf("unable to register test service %v", err) 103 } 104 105 clientConn, serverConn := net.Pipe() 106 107 go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions) 108 109 out := json.NewEncoder(clientConn) 110 in := json.NewDecoder(clientConn) 111 112 n := 5 113 val := 12345 114 request := map[string]interface{}{ 115 "id": 1, 116 "method": "eth_subscribe", 117 "version": "2.0", 118 "params": []interface{}{"someSubscription", n, val}, 119 } 120 121 // create subscription 122 if err := out.Encode(request); err != nil { 123 t.Fatal(err) 124 } 125 126 var subid string 127 response := jsonSuccessResponse{Result: subid} 128 if err := in.Decode(&response); err != nil { 129 t.Fatal(err) 130 } 131 132 var ok bool 133 if _, ok = response.Result.(string); !ok { 134 t.Fatalf("expected subscription id, got %T", response.Result) 135 } 136 137 for i := 0; i < n; i++ { 138 var notification jsonNotification 139 if err := in.Decode(¬ification); err != nil { 140 t.Fatalf("%v", err) 141 } 142 143 if int(notification.Params.Result.(float64)) != val+i { 144 t.Fatalf("expected %d, got %d", val+i, notification.Params.Result) 145 } 146 } 147 148 clientConn.Close() // causes notification unsubscribe callback to be called 149 select { 150 case <-service.unsubscribed: 151 case <-time.After(1 * time.Second): 152 t.Fatal("Unsubscribe not called after one second") 153 } 154 } 155 156 func waitForMessages(t *testing.T, in *json.Decoder, successes chan<- jsonSuccessResponse, 157 failures chan<- jsonErrResponse, notifications chan<- jsonNotification, errors chan<- error) { 158 159 // read and parse server messages 160 for { 161 var rmsg json.RawMessage 162 if err := in.Decode(&rmsg); err != nil { 163 return 164 } 165 166 var responses []map[string]interface{} 167 if rmsg[0] == '[' { 168 if err := json.Unmarshal(rmsg, &responses); err != nil { 169 errors <- fmt.Errorf("Received invalid message: %s", rmsg) 170 return 171 } 172 } else { 173 var msg map[string]interface{} 174 if err := json.Unmarshal(rmsg, &msg); err != nil { 175 errors <- fmt.Errorf("Received invalid message: %s", rmsg) 176 return 177 } 178 responses = append(responses, msg) 179 } 180 181 for _, msg := range responses { 182 // determine what kind of msg was received and broadcast 183 // it to over the corresponding channel 184 if _, found := msg["result"]; found { 185 successes <- jsonSuccessResponse{ 186 Version: msg["jsonrpc"].(string), 187 Id: msg["id"], 188 Result: msg["result"], 189 } 190 continue 191 } 192 if _, found := msg["error"]; found { 193 params := msg["params"].(map[string]interface{}) 194 failures <- jsonErrResponse{ 195 Version: msg["jsonrpc"].(string), 196 Id: msg["id"], 197 Error: jsonError{int(params["subscription"].(float64)), params["message"].(string), params["data"]}, 198 } 199 continue 200 } 201 if _, found := msg["params"]; found { 202 params := msg["params"].(map[string]interface{}) 203 notifications <- jsonNotification{ 204 Version: msg["jsonrpc"].(string), 205 Method: msg["method"].(string), 206 Params: jsonSubscription{params["subscription"].(string), params["result"]}, 207 } 208 continue 209 } 210 errors <- fmt.Errorf("Received invalid message: %s", msg) 211 } 212 } 213 } 214 215 // TestSubscriptionMultipleNamespaces ensures that subscriptions can exists 216 // for multiple different namespaces. 217 func TestSubscriptionMultipleNamespaces(t *testing.T) { 218 var ( 219 namespaces = []string{"eth", "shh", "bzz"} 220 service = NotificationTestService{} 221 subCount = len(namespaces) * 2 222 notificationCount = 3 223 224 server = NewServer() 225 clientConn, serverConn = net.Pipe() 226 out = json.NewEncoder(clientConn) 227 in = json.NewDecoder(clientConn) 228 successes = make(chan jsonSuccessResponse) 229 failures = make(chan jsonErrResponse) 230 notifications = make(chan jsonNotification) 231 errors = make(chan error, 10) 232 ) 233 234 // setup and start server 235 for _, namespace := range namespaces { 236 if err := server.RegisterName(namespace, &service); err != nil { 237 t.Fatalf("unable to register test service %v", err) 238 } 239 } 240 241 go server.ServeCodec(NewJSONCodec(serverConn), OptionMethodInvocation|OptionSubscriptions) 242 defer server.Stop() 243 244 // wait for message and write them to the given channels 245 go waitForMessages(t, in, successes, failures, notifications, errors) 246 247 // create subscriptions one by one 248 for i, namespace := range namespaces { 249 request := map[string]interface{}{ 250 "id": i, 251 "method": fmt.Sprintf("%s_subscribe", namespace), 252 "version": "2.0", 253 "params": []interface{}{"someSubscription", notificationCount, i}, 254 } 255 256 if err := out.Encode(&request); err != nil { 257 t.Fatalf("Could not create subscription: %v", err) 258 } 259 } 260 261 // create all subscriptions in 1 batch 262 var requests []interface{} 263 for i, namespace := range namespaces { 264 requests = append(requests, map[string]interface{}{ 265 "id": i, 266 "method": fmt.Sprintf("%s_subscribe", namespace), 267 "version": "2.0", 268 "params": []interface{}{"someSubscription", notificationCount, i}, 269 }) 270 } 271 272 if err := out.Encode(&requests); err != nil { 273 t.Fatalf("Could not create subscription in batch form: %v", err) 274 } 275 276 timeout := time.After(30 * time.Second) 277 subids := make(map[string]string, subCount) 278 count := make(map[string]int, subCount) 279 allReceived := func() bool { 280 done := len(count) == subCount 281 for _, c := range count { 282 if c < notificationCount { 283 done = false 284 } 285 } 286 return done 287 } 288 289 for !allReceived() { 290 select { 291 case suc := <-successes: // subscription created 292 subids[namespaces[int(suc.Id.(float64))]] = suc.Result.(string) 293 case notification := <-notifications: 294 count[notification.Params.Subscription]++ 295 case err := <-errors: 296 t.Fatal(err) 297 case failure := <-failures: 298 t.Errorf("received error: %v", failure.Error) 299 case <-timeout: 300 for _, namespace := range namespaces { 301 subid, found := subids[namespace] 302 if !found { 303 t.Errorf("subscription for %q not created", namespace) 304 continue 305 } 306 if count, found := count[subid]; !found || count < notificationCount { 307 t.Errorf("didn't receive all notifications (%d<%d) in time for namespace %q", count, notificationCount, namespace) 308 } 309 } 310 t.Fatal("timed out") 311 } 312 } 313 }