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