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