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