181 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			181 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|   | // Copyright 2018 The go-ethereum Authors | ||
|  | // This file is part of the go-ethereum library. | ||
|  | // | ||
|  | // The go-ethereum library is free software: you can redistribute it and/or modify | ||
|  | // it under the terms of the GNU Lesser General Public License as published by | ||
|  | // the Free Software Foundation, either version 3 of the License, or | ||
|  | // (at your option) any later version. | ||
|  | // | ||
|  | // The go-ethereum library is distributed in the hope that it will be useful, | ||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
|  | // GNU Lesser General Public License for more details. | ||
|  | // | ||
|  | // You should have received a copy of the GNU Lesser General Public License | ||
|  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | ||
|  | 
 | ||
|  | package rpc | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"context" | ||
|  | 	"encoding/binary" | ||
|  | 	"errors" | ||
|  | 	"sync" | ||
|  | 	"time" | ||
|  | ) | ||
|  | 
 | ||
|  | func newTestServer() *Server { | ||
|  | 	server := NewServer() | ||
|  | 	server.idgen = sequentialIDGenerator() | ||
|  | 	if err := server.RegisterName("test", new(testService)); err != nil { | ||
|  | 		panic(err) | ||
|  | 	} | ||
|  | 	if err := server.RegisterName("nftest", new(notificationTestService)); err != nil { | ||
|  | 		panic(err) | ||
|  | 	} | ||
|  | 	return server | ||
|  | } | ||
|  | 
 | ||
|  | func sequentialIDGenerator() func() ID { | ||
|  | 	var ( | ||
|  | 		mu      sync.Mutex | ||
|  | 		counter uint64 | ||
|  | 	) | ||
|  | 	return func() ID { | ||
|  | 		mu.Lock() | ||
|  | 		defer mu.Unlock() | ||
|  | 		counter++ | ||
|  | 		id := make([]byte, 8) | ||
|  | 		binary.BigEndian.PutUint64(id, counter) | ||
|  | 		return encodeID(id) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | type testService struct{} | ||
|  | 
 | ||
|  | type Args struct { | ||
|  | 	S string | ||
|  | } | ||
|  | 
 | ||
|  | type Result struct { | ||
|  | 	String string | ||
|  | 	Int    int | ||
|  | 	Args   *Args | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) NoArgsRets() {} | ||
|  | 
 | ||
|  | func (s *testService) Echo(str string, i int, args *Args) Result { | ||
|  | 	return Result{str, i, args} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args *Args) Result { | ||
|  | 	return Result{str, i, args} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) Sleep(ctx context.Context, duration time.Duration) { | ||
|  | 	time.Sleep(duration) | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) Rets() (string, error) { | ||
|  | 	return "", nil | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) InvalidRets1() (error, string) { | ||
|  | 	return nil, "" | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) InvalidRets2() (string, string) { | ||
|  | 	return "", "" | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) InvalidRets3() (string, string, error) { | ||
|  | 	return "", "", nil | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) CallMeBack(ctx context.Context, method string, args []interface{}) (interface{}, error) { | ||
|  | 	c, ok := ClientFromContext(ctx) | ||
|  | 	if !ok { | ||
|  | 		return nil, errors.New("no client") | ||
|  | 	} | ||
|  | 	var result interface{} | ||
|  | 	err := c.Call(&result, method, args...) | ||
|  | 	return result, err | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) CallMeBackLater(ctx context.Context, method string, args []interface{}) error { | ||
|  | 	c, ok := ClientFromContext(ctx) | ||
|  | 	if !ok { | ||
|  | 		return errors.New("no client") | ||
|  | 	} | ||
|  | 	go func() { | ||
|  | 		<-ctx.Done() | ||
|  | 		var result interface{} | ||
|  | 		c.Call(&result, method, args...) | ||
|  | 	}() | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | func (s *testService) Subscription(ctx context.Context) (*Subscription, error) { | ||
|  | 	return nil, nil | ||
|  | } | ||
|  | 
 | ||
|  | type notificationTestService struct { | ||
|  | 	unsubscribed            chan string | ||
|  | 	gotHangSubscriptionReq  chan struct{} | ||
|  | 	unblockHangSubscription chan struct{} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *notificationTestService) Echo(i int) int { | ||
|  | 	return i | ||
|  | } | ||
|  | 
 | ||
|  | func (s *notificationTestService) Unsubscribe(subid string) { | ||
|  | 	if s.unsubscribed != nil { | ||
|  | 		s.unsubscribed <- subid | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *notificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) { | ||
|  | 	notifier, supported := NotifierFromContext(ctx) | ||
|  | 	if !supported { | ||
|  | 		return nil, ErrNotificationsUnsupported | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// By explicitly creating an subscription we make sure that the subscription id is send | ||
|  | 	// back to the client before the first subscription.Notify is called. Otherwise the | ||
|  | 	// events might be send before the response for the *_subscribe method. | ||
|  | 	subscription := notifier.CreateSubscription() | ||
|  | 	go func() { | ||
|  | 		for i := 0; i < n; i++ { | ||
|  | 			if err := notifier.Notify(subscription.ID, val+i); err != nil { | ||
|  | 				return | ||
|  | 			} | ||
|  | 		} | ||
|  | 		select { | ||
|  | 		case <-notifier.Closed(): | ||
|  | 		case <-subscription.Err(): | ||
|  | 		} | ||
|  | 		if s.unsubscribed != nil { | ||
|  | 			s.unsubscribed <- string(subscription.ID) | ||
|  | 		} | ||
|  | 	}() | ||
|  | 	return subscription, nil | ||
|  | } | ||
|  | 
 | ||
|  | // HangSubscription blocks on s.unblockHangSubscription before sending anything. | ||
|  | func (s *notificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) { | ||
|  | 	notifier, supported := NotifierFromContext(ctx) | ||
|  | 	if !supported { | ||
|  | 		return nil, ErrNotificationsUnsupported | ||
|  | 	} | ||
|  | 	s.gotHangSubscriptionReq <- struct{}{} | ||
|  | 	<-s.unblockHangSubscription | ||
|  | 	subscription := notifier.CreateSubscription() | ||
|  | 
 | ||
|  | 	go func() { | ||
|  | 		notifier.Notify(subscription.ID, val) | ||
|  | 	}() | ||
|  | 	return subscription, nil | ||
|  | } |