github.com/theQRL/go-zond@v0.1.1/rpc/subscription.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 "container/list" 21 "context" 22 crand "crypto/rand" 23 "encoding/binary" 24 "encoding/hex" 25 "encoding/json" 26 "errors" 27 "math/rand" 28 "reflect" 29 "strings" 30 "sync" 31 "time" 32 ) 33 34 var ( 35 // ErrNotificationsUnsupported is returned by the client when the connection doesn't 36 // support notifications. You can use this error value to check for subscription 37 // support like this: 38 // 39 // sub, err := client.EthSubscribe(ctx, channel, "newHeads", true) 40 // if errors.Is(err, rpc.ErrNotificationsUnsupported) { 41 // // Server does not support subscriptions, fall back to polling. 42 // } 43 // 44 ErrNotificationsUnsupported = notificationsUnsupportedError{} 45 46 // ErrSubscriptionNotFound is returned when the notification for the given id is not found 47 ErrSubscriptionNotFound = errors.New("subscription not found") 48 ) 49 50 var globalGen = randomIDGenerator() 51 52 // ID defines a pseudo random number that is used to identify RPC subscriptions. 53 type ID string 54 55 // NewID returns a new, random ID. 56 func NewID() ID { 57 return globalGen() 58 } 59 60 // randomIDGenerator returns a function generates a random IDs. 61 func randomIDGenerator() func() ID { 62 var buf = make([]byte, 8) 63 var seed int64 64 if _, err := crand.Read(buf); err == nil { 65 seed = int64(binary.BigEndian.Uint64(buf)) 66 } else { 67 seed = int64(time.Now().Nanosecond()) 68 } 69 70 var ( 71 mu sync.Mutex 72 rng = rand.New(rand.NewSource(seed)) 73 ) 74 return func() ID { 75 mu.Lock() 76 defer mu.Unlock() 77 id := make([]byte, 16) 78 rng.Read(id) 79 return encodeID(id) 80 } 81 } 82 83 func encodeID(b []byte) ID { 84 id := hex.EncodeToString(b) 85 id = strings.TrimLeft(id, "0") 86 if id == "" { 87 id = "0" // ID's are RPC quantities, no leading zero's and 0 is 0x0. 88 } 89 return ID("0x" + id) 90 } 91 92 type notifierKey struct{} 93 94 // NotifierFromContext returns the Notifier value stored in ctx, if any. 95 func NotifierFromContext(ctx context.Context) (*Notifier, bool) { 96 n, ok := ctx.Value(notifierKey{}).(*Notifier) 97 return n, ok 98 } 99 100 // Notifier is tied to a RPC connection that supports subscriptions. 101 // Server callbacks use the notifier to send notifications. 102 type Notifier struct { 103 h *handler 104 namespace string 105 106 mu sync.Mutex 107 sub *Subscription 108 buffer []json.RawMessage 109 callReturned bool 110 activated bool 111 } 112 113 // CreateSubscription returns a new subscription that is coupled to the 114 // RPC connection. By default subscriptions are inactive and notifications 115 // are dropped until the subscription is marked as active. This is done 116 // by the RPC server after the subscription ID is send to the client. 117 func (n *Notifier) CreateSubscription() *Subscription { 118 n.mu.Lock() 119 defer n.mu.Unlock() 120 121 if n.sub != nil { 122 panic("can't create multiple subscriptions with Notifier") 123 } else if n.callReturned { 124 panic("can't create subscription after subscribe call has returned") 125 } 126 n.sub = &Subscription{ID: n.h.idgen(), namespace: n.namespace, err: make(chan error, 1)} 127 return n.sub 128 } 129 130 // Notify sends a notification to the client with the given data as payload. 131 // If an error occurs the RPC connection is closed and the error is returned. 132 func (n *Notifier) Notify(id ID, data interface{}) error { 133 enc, err := json.Marshal(data) 134 if err != nil { 135 return err 136 } 137 138 n.mu.Lock() 139 defer n.mu.Unlock() 140 141 if n.sub == nil { 142 panic("can't Notify before subscription is created") 143 } else if n.sub.ID != id { 144 panic("Notify with wrong ID") 145 } 146 if n.activated { 147 return n.send(n.sub, enc) 148 } 149 n.buffer = append(n.buffer, enc) 150 return nil 151 } 152 153 // Closed returns a channel that is closed when the RPC connection is closed. 154 // Deprecated: use subscription error channel 155 func (n *Notifier) Closed() <-chan interface{} { 156 return n.h.conn.closed() 157 } 158 159 // takeSubscription returns the subscription (if one has been created). No subscription can 160 // be created after this call. 161 func (n *Notifier) takeSubscription() *Subscription { 162 n.mu.Lock() 163 defer n.mu.Unlock() 164 n.callReturned = true 165 return n.sub 166 } 167 168 // activate is called after the subscription ID was sent to client. Notifications are 169 // buffered before activation. This prevents notifications being sent to the client before 170 // the subscription ID is sent to the client. 171 func (n *Notifier) activate() error { 172 n.mu.Lock() 173 defer n.mu.Unlock() 174 175 for _, data := range n.buffer { 176 if err := n.send(n.sub, data); err != nil { 177 return err 178 } 179 } 180 n.activated = true 181 return nil 182 } 183 184 func (n *Notifier) send(sub *Subscription, data json.RawMessage) error { 185 params, _ := json.Marshal(&subscriptionResult{ID: string(sub.ID), Result: data}) 186 ctx := context.Background() 187 188 msg := &jsonrpcMessage{ 189 Version: vsn, 190 Method: n.namespace + notificationMethodSuffix, 191 Params: params, 192 } 193 return n.h.conn.writeJSON(ctx, msg, false) 194 } 195 196 // A Subscription is created by a notifier and tied to that notifier. The client can use 197 // this subscription to wait for an unsubscribe request for the client, see Err(). 198 type Subscription struct { 199 ID ID 200 namespace string 201 err chan error // closed on unsubscribe 202 } 203 204 // Err returns a channel that is closed when the client send an unsubscribe request. 205 func (s *Subscription) Err() <-chan error { 206 return s.err 207 } 208 209 // MarshalJSON marshals a subscription as its ID. 210 func (s *Subscription) MarshalJSON() ([]byte, error) { 211 return json.Marshal(s.ID) 212 } 213 214 // ClientSubscription is a subscription established through the Client's Subscribe or 215 // EthSubscribe methods. 216 type ClientSubscription struct { 217 client *Client 218 etype reflect.Type 219 channel reflect.Value 220 namespace string 221 subid string 222 223 // The in channel receives notification values from client dispatcher. 224 in chan json.RawMessage 225 226 // The error channel receives the error from the forwarding loop. 227 // It is closed by Unsubscribe. 228 err chan error 229 errOnce sync.Once 230 231 // Closing of the subscription is requested by sending on 'quit'. This is handled by 232 // the forwarding loop, which closes 'forwardDone' when it has stopped sending to 233 // sub.channel. Finally, 'unsubDone' is closed after unsubscribing on the server side. 234 quit chan error 235 forwardDone chan struct{} 236 unsubDone chan struct{} 237 } 238 239 // This is the sentinel value sent on sub.quit when Unsubscribe is called. 240 var errUnsubscribed = errors.New("unsubscribed") 241 242 func newClientSubscription(c *Client, namespace string, channel reflect.Value) *ClientSubscription { 243 sub := &ClientSubscription{ 244 client: c, 245 namespace: namespace, 246 etype: channel.Type().Elem(), 247 channel: channel, 248 in: make(chan json.RawMessage), 249 quit: make(chan error), 250 forwardDone: make(chan struct{}), 251 unsubDone: make(chan struct{}), 252 err: make(chan error, 1), 253 } 254 return sub 255 } 256 257 // Err returns the subscription error channel. The intended use of Err is to schedule 258 // resubscription when the client connection is closed unexpectedly. 259 // 260 // The error channel receives a value when the subscription has ended due to an error. The 261 // received error is nil if Close has been called on the underlying client and no other 262 // error has occurred. 263 // 264 // The error channel is closed when Unsubscribe is called on the subscription. 265 func (sub *ClientSubscription) Err() <-chan error { 266 return sub.err 267 } 268 269 // Unsubscribe unsubscribes the notification and closes the error channel. 270 // It can safely be called more than once. 271 func (sub *ClientSubscription) Unsubscribe() { 272 sub.errOnce.Do(func() { 273 select { 274 case sub.quit <- errUnsubscribed: 275 <-sub.unsubDone 276 case <-sub.unsubDone: 277 } 278 close(sub.err) 279 }) 280 } 281 282 // deliver is called by the client's message dispatcher to send a notification value. 283 func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) { 284 select { 285 case sub.in <- result: 286 return true 287 case <-sub.forwardDone: 288 return false 289 } 290 } 291 292 // close is called by the client's message dispatcher when the connection is closed. 293 func (sub *ClientSubscription) close(err error) { 294 select { 295 case sub.quit <- err: 296 case <-sub.forwardDone: 297 } 298 } 299 300 // run is the forwarding loop of the subscription. It runs in its own goroutine and 301 // is launched by the client's handler after the subscription has been created. 302 func (sub *ClientSubscription) run() { 303 defer close(sub.unsubDone) 304 305 unsubscribe, err := sub.forward() 306 307 // The client's dispatch loop won't be able to execute the unsubscribe call if it is 308 // blocked in sub.deliver() or sub.close(). Closing forwardDone unblocks them. 309 close(sub.forwardDone) 310 311 // Call the unsubscribe method on the server. 312 if unsubscribe { 313 sub.requestUnsubscribe() 314 } 315 316 // Send the error. 317 if err != nil { 318 if err == ErrClientQuit { 319 // ErrClientQuit gets here when Client.Close is called. This is reported as a 320 // nil error because it's not an error, but we can't close sub.err here. 321 err = nil 322 } 323 sub.err <- err 324 } 325 } 326 327 // forward is the forwarding loop. It takes in RPC notifications and sends them 328 // on the subscription channel. 329 func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { 330 cases := []reflect.SelectCase{ 331 {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)}, 332 {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.in)}, 333 {Dir: reflect.SelectSend, Chan: sub.channel}, 334 } 335 buffer := list.New() 336 337 for { 338 var chosen int 339 var recv reflect.Value 340 if buffer.Len() == 0 { 341 // Idle, omit send case. 342 chosen, recv, _ = reflect.Select(cases[:2]) 343 } else { 344 // Non-empty buffer, send the first queued item. 345 cases[2].Send = reflect.ValueOf(buffer.Front().Value) 346 chosen, recv, _ = reflect.Select(cases) 347 } 348 349 switch chosen { 350 case 0: // <-sub.quit 351 if !recv.IsNil() { 352 err = recv.Interface().(error) 353 } 354 if err == errUnsubscribed { 355 // Exiting because Unsubscribe was called, unsubscribe on server. 356 return true, nil 357 } 358 return false, err 359 360 case 1: // <-sub.in 361 val, err := sub.unmarshal(recv.Interface().(json.RawMessage)) 362 if err != nil { 363 return true, err 364 } 365 if buffer.Len() == maxClientSubscriptionBuffer { 366 return true, ErrSubscriptionQueueOverflow 367 } 368 buffer.PushBack(val) 369 370 case 2: // sub.channel<- 371 cases[2].Send = reflect.Value{} // Don't hold onto the value. 372 buffer.Remove(buffer.Front()) 373 } 374 } 375 } 376 377 func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) { 378 val := reflect.New(sub.etype) 379 err := json.Unmarshal(result, val.Interface()) 380 return val.Elem().Interface(), err 381 } 382 383 func (sub *ClientSubscription) requestUnsubscribe() error { 384 var result interface{} 385 return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid) 386 }