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  }