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  }