| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // Copyright 2015 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/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | package rpc | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 	"golang.org/x/net/context" | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 	"gopkg.in/fatih/set.v0" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	notificationBufferSize = 10000 // max buffered notifications before codec is closed | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-11 11:49:44 +03:00
										 |  |  | 	MetadataApi     = "rpc" | 
					
						
							|  |  |  | 	DefaultIPCApis  = "admin,debug,eth,miner,net,personal,shh,txpool,web3" | 
					
						
							| 
									
										
										
										
											2016-02-09 13:24:42 +02:00
										 |  |  | 	DefaultHTTPApis = "eth,net,web3" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // CodecOption specifies which type of messages this codec supports | 
					
						
							|  |  |  | type CodecOption int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// OptionMethodInvocation is an indication that the codec supports RPC method calls | 
					
						
							|  |  |  | 	OptionMethodInvocation CodecOption = 1 << iota | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// OptionSubscriptions is an indication that the codec suports RPC notifications | 
					
						
							|  |  |  | 	OptionSubscriptions = 1 << iota // support pub sub | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // NewServer will create a new server instance with no registered handlers. | 
					
						
							|  |  |  | func NewServer() *Server { | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 	server := &Server{ | 
					
						
							|  |  |  | 		services:      make(serviceRegistry), | 
					
						
							|  |  |  | 		subscriptions: make(subscriptionRegistry), | 
					
						
							|  |  |  | 		codecs:        set.New(), | 
					
						
							|  |  |  | 		run:           1, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// register a default service which will provide meta information about the RPC service such as the services and | 
					
						
							|  |  |  | 	// methods it offers. | 
					
						
							|  |  |  | 	rpcService := &RPCService{server} | 
					
						
							| 
									
										
										
										
											2016-05-11 11:49:44 +03:00
										 |  |  | 	server.RegisterName(MetadataApi, rpcService) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return server | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RPCService gives meta information about the server. | 
					
						
							|  |  |  | // e.g. gives information about the loaded modules. | 
					
						
							|  |  |  | type RPCService struct { | 
					
						
							|  |  |  | 	server *Server | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Modules returns the list of RPC services with their version number | 
					
						
							|  |  |  | func (s *RPCService) Modules() map[string]string { | 
					
						
							|  |  |  | 	modules := make(map[string]string) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	for name := range s.server.services { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		modules[name] = "1.0" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return modules | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-21 05:52:38 +01:00
										 |  |  | // RegisterName will create a service for the given rcvr type under the given name. When no methods on the given rcvr | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // match the criteria to be either a RPC method or a subscription an error is returned. Otherwise a new service is | 
					
						
							|  |  |  | // created and added to the service collection this server instance serves. | 
					
						
							|  |  |  | func (s *Server) RegisterName(name string, rcvr interface{}) error { | 
					
						
							|  |  |  | 	if s.services == nil { | 
					
						
							|  |  |  | 		s.services = make(serviceRegistry) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	svc := new(service) | 
					
						
							|  |  |  | 	svc.typ = reflect.TypeOf(rcvr) | 
					
						
							|  |  |  | 	rcvrVal := reflect.ValueOf(rcvr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if name == "" { | 
					
						
							|  |  |  | 		return fmt.Errorf("no service name for type %s", svc.typ.String()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !isExported(reflect.Indirect(rcvrVal).Type().Name()) { | 
					
						
							|  |  |  | 		return fmt.Errorf("%s is not exported", reflect.Indirect(rcvrVal).Type().Name()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// already a previous service register under given sname, merge methods/subscriptions | 
					
						
							|  |  |  | 	if regsvc, present := s.services[name]; present { | 
					
						
							|  |  |  | 		methods, subscriptions := suitableCallbacks(rcvrVal, svc.typ) | 
					
						
							|  |  |  | 		if len(methods) == 0 && len(subscriptions) == 0 { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 			return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, m := range methods { | 
					
						
							|  |  |  | 			regsvc.callbacks[formatName(m.method.Name)] = m | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, s := range subscriptions { | 
					
						
							|  |  |  | 			regsvc.subscriptions[formatName(s.method.Name)] = s | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	svc.name = name | 
					
						
							|  |  |  | 	svc.callbacks, svc.subscriptions = suitableCallbacks(rcvrVal, svc.typ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(svc.callbacks) == 0 && len(svc.subscriptions) == 0 { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		return fmt.Errorf("Service %T doesn't have any suitable methods/subscriptions to expose", rcvr) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s.services[svc.name] = svc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // hasOption returns true if option is included in options, otherwise false | 
					
						
							|  |  |  | func hasOption(option CodecOption, options []CodecOption) bool { | 
					
						
							|  |  |  | 	for _, o := range options { | 
					
						
							|  |  |  | 		if option == o { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | // serveRequest will reads requests from the codec, calls the RPC callback and | 
					
						
							|  |  |  | // writes the response to the given codec. | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | // If singleShot is true it will process a single request, otherwise it will handle | 
					
						
							|  |  |  | // requests until the codec returns an error when reading a request (in most cases | 
					
						
							|  |  |  | // an EOF). It executes requests in parallel when singleShot is false. | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | func (s *Server) serveRequest(codec ServerCodec, singleShot bool, options CodecOption) error { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err := recover(); err != nil { | 
					
						
							|  |  |  | 			const size = 64 << 10 | 
					
						
							|  |  |  | 			buf := make([]byte, size) | 
					
						
							|  |  |  | 			buf = buf[:runtime.Stack(buf, false)] | 
					
						
							|  |  |  | 			glog.Errorln(string(buf)) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		s.codecsMu.Lock() | 
					
						
							|  |  |  | 		s.codecs.Remove(codec) | 
					
						
							|  |  |  | 		s.codecsMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 	ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	// if the codec supports notification include a notifier that callbacks can use | 
					
						
							|  |  |  | 	// to send notification to clients. It is thight to the codec/connection. If the | 
					
						
							|  |  |  | 	// connection is closed the notifier will stop and cancels all active subscriptions. | 
					
						
							|  |  |  | 	if options&OptionSubscriptions == OptionSubscriptions { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		ctx = context.WithValue(ctx, notifierKey{}, newNotifier(codec)) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 	s.codecsMu.Lock() | 
					
						
							|  |  |  | 	if atomic.LoadInt32(&s.run) != 1 { // server stopped | 
					
						
							|  |  |  | 		s.codecsMu.Unlock() | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 		return &shutdownError{} | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	s.codecs.Add(codec) | 
					
						
							|  |  |  | 	s.codecsMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 	// test if the server is ordered to stop | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 	for atomic.LoadInt32(&s.run) == 1 { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		reqs, batch, err := s.readRequest(codec) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-12 17:42:44 +02:00
										 |  |  | 			glog.V(logger.Debug).Infof("read error %v\n", err) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			codec.Write(codec.CreateErrorResponse(nil, err)) | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 		// check if server is ordered to shutdown and return an error | 
					
						
							|  |  |  | 		// telling the client that his request failed. | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 		if atomic.LoadInt32(&s.run) != 1 { | 
					
						
							|  |  |  | 			err = &shutdownError{} | 
					
						
							|  |  |  | 			if batch { | 
					
						
							|  |  |  | 				resps := make([]interface{}, len(reqs)) | 
					
						
							|  |  |  | 				for i, r := range reqs { | 
					
						
							|  |  |  | 					resps[i] = codec.CreateErrorResponse(&r.id, err) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				codec.Write(resps) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				codec.Write(codec.CreateErrorResponse(&reqs[0].id, err)) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 		if singleShot && batch { | 
					
						
							|  |  |  | 			s.execBatch(ctx, codec, reqs) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} else if singleShot && !batch { | 
					
						
							|  |  |  | 			s.exec(ctx, codec, reqs[0]) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} else if !singleShot && batch { | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 			go s.execBatch(ctx, codec, reqs) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 			go s.exec(ctx, codec, reqs[0]) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServeCodec reads incoming requests from codec, calls the appropriate callback and writes the | 
					
						
							|  |  |  | // response back using the given codec. It will block until the codec is closed or the server is | 
					
						
							|  |  |  | // stopped. In either case the codec is closed. | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) { | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | 	defer codec.Close() | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	s.serveRequest(codec, false, options) | 
					
						
							| 
									
										
										
										
											2016-02-24 11:19:00 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServeSingleRequest reads and processes a single RPC request from the given codec. It will not | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // close the codec unless a non-recoverable error has occurred. Note, this method will return after | 
					
						
							|  |  |  | // a single request has been processed! | 
					
						
							|  |  |  | func (s *Server) ServeSingleRequest(codec ServerCodec, options CodecOption) { | 
					
						
							|  |  |  | 	s.serveRequest(codec, true, options) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | // Stop will stop reading new requests, wait for stopPendingRequestTimeout to allow pending requests to finish, | 
					
						
							| 
									
										
										
										
											2016-06-21 05:52:38 +01:00
										 |  |  | // close all codecs which will cancel pending requests/subscriptions. | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | func (s *Server) Stop() { | 
					
						
							|  |  |  | 	if atomic.CompareAndSwapInt32(&s.run, 1, 0) { | 
					
						
							|  |  |  | 		glog.V(logger.Debug).Infoln("RPC Server shutdown initiatied") | 
					
						
							| 
									
										
										
										
											2016-07-12 17:34:59 +02:00
										 |  |  | 		s.codecsMu.Lock() | 
					
						
							|  |  |  | 		defer s.codecsMu.Unlock() | 
					
						
							|  |  |  | 		s.codecs.Each(func(c interface{}) bool { | 
					
						
							|  |  |  | 			c.(ServerCodec).Close() | 
					
						
							|  |  |  | 			return true | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // createSubscription will call the subscription callback and returns the subscription id or error. | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | func (s *Server) createSubscription(ctx context.Context, c ServerCodec, req *serverRequest) (ID, error) { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	// subscription have as first argument the context following optional arguments | 
					
						
							|  |  |  | 	args := []reflect.Value{req.callb.rcvr, reflect.ValueOf(ctx)} | 
					
						
							|  |  |  | 	args = append(args, req.args...) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	reply := req.callb.method.Func.Call(args) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	if !reply[1].IsNil() { // subscription creation failed | 
					
						
							|  |  |  | 		return "", reply[1].Interface().(error) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	return reply[0].Interface().(*Subscription).ID, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // handle executes a request and returns the response from the callback. | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | func (s *Server) handle(ctx context.Context, codec ServerCodec, req *serverRequest) (interface{}, func()) { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if req.err != nil { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		return codec.CreateErrorResponse(&req.id, req.err), nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	if req.isUnsubscribe { // cancel subscription, first param must be the subscription id | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		if len(req.args) >= 1 && req.args[0].Kind() == reflect.String { | 
					
						
							| 
									
										
										
										
											2016-04-15 18:05:24 +02:00
										 |  |  | 			notifier, supported := NotifierFromContext(ctx) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 			if !supported { // interface doesn't support subscriptions (e.g. http) | 
					
						
							|  |  |  | 				return codec.CreateErrorResponse(&req.id, &callbackError{ErrNotificationsUnsupported.Error()}), nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 			subid := ID(req.args[0].String()) | 
					
						
							|  |  |  | 			if err := notifier.unsubscribe(subid); err != nil { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 				return codec.CreateErrorResponse(&req.id, &callbackError{err.Error()}), nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			return codec.CreateResponse(req.id, true), nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		return codec.CreateErrorResponse(&req.id, &invalidParamsError{"Expected subscription id as first argument"}), nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if req.callb.isSubscribe { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		subid, err := s.createSubscription(ctx, codec, req) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 			return codec.CreateErrorResponse(&req.id, &callbackError{err.Error()}), nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-21 05:52:38 +01:00
										 |  |  | 		// active the subscription after the sub id was successfully sent to the client | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		activateSub := func() { | 
					
						
							| 
									
										
										
										
											2016-04-15 18:05:24 +02:00
										 |  |  | 			notifier, _ := NotifierFromContext(ctx) | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 			notifier.activate(subid) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return codec.CreateResponse(req.id, subid), activateSub | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	// regular RPC call, prepare arguments | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if len(req.args) != len(req.callb.argTypes) { | 
					
						
							|  |  |  | 		rpcErr := &invalidParamsError{fmt.Sprintf("%s%s%s expects %d parameters, got %d", | 
					
						
							|  |  |  | 			req.svcname, serviceMethodSeparator, req.callb.method.Name, | 
					
						
							|  |  |  | 			len(req.callb.argTypes), len(req.args))} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		return codec.CreateErrorResponse(&req.id, rpcErr), nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	arguments := []reflect.Value{req.callb.rcvr} | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | 	if req.callb.hasCtx { | 
					
						
							|  |  |  | 		arguments = append(arguments, reflect.ValueOf(ctx)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if len(req.args) > 0 { | 
					
						
							|  |  |  | 		arguments = append(arguments, req.args...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	// execute RPC method and return result | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	reply := req.callb.method.Func.Call(arguments) | 
					
						
							|  |  |  | 	if len(reply) == 0 { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		return codec.CreateResponse(req.id, nil), nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if req.callb.errPos >= 0 { // test if method returned an error | 
					
						
							|  |  |  | 		if !reply[req.callb.errPos].IsNil() { | 
					
						
							|  |  |  | 			e := reply[req.callb.errPos].Interface().(error) | 
					
						
							|  |  |  | 			res := codec.CreateErrorResponse(&req.id, &callbackError{e.Error()}) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 			return res, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	return codec.CreateResponse(req.id, reply[0].Interface()), nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // exec executes the given request and writes the result back using the codec. | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | func (s *Server) exec(ctx context.Context, codec ServerCodec, req *serverRequest) { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	var response interface{} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	var callback func() | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if req.err != nil { | 
					
						
							|  |  |  | 		response = codec.CreateErrorResponse(&req.id, req.err) | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		response, callback = s.handle(ctx, codec, req) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if err := codec.Write(response); err != nil { | 
					
						
							|  |  |  | 		glog.V(logger.Error).Infof("%v\n", err) | 
					
						
							|  |  |  | 		codec.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// when request was a subscribe request this allows these subscriptions to be actived | 
					
						
							|  |  |  | 	if callback != nil { | 
					
						
							|  |  |  | 		callback() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // execBatch executes the given requests and writes the result back using the codec. | 
					
						
							|  |  |  | // It will only write the response back when the last request is processed. | 
					
						
							| 
									
										
										
										
											2015-12-09 18:28:07 +01:00
										 |  |  | func (s *Server) execBatch(ctx context.Context, codec ServerCodec, requests []*serverRequest) { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	responses := make([]interface{}, len(requests)) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	var callbacks []func() | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	for i, req := range requests { | 
					
						
							|  |  |  | 		if req.err != nil { | 
					
						
							|  |  |  | 			responses[i] = codec.CreateErrorResponse(&req.id, req.err) | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 			var callback func() | 
					
						
							|  |  |  | 			if responses[i], callback = s.handle(ctx, codec, req); callback != nil { | 
					
						
							|  |  |  | 				callbacks = append(callbacks, callback) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := codec.Write(responses); err != nil { | 
					
						
							|  |  |  | 		glog.V(logger.Error).Infof("%v\n", err) | 
					
						
							|  |  |  | 		codec.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// when request holds one of more subscribe requests this allows these subscriptions to be actived | 
					
						
							|  |  |  | 	for _, c := range callbacks { | 
					
						
							|  |  |  | 		c() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | // readRequest requests the next (batch) request from the codec. It will return the collection | 
					
						
							|  |  |  | // of requests, an indication if the request was a batch, the invalid request identifier and an | 
					
						
							|  |  |  | // error when the request could not be read/parsed. | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func (s *Server) readRequest(codec ServerCodec) ([]*serverRequest, bool, Error) { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	reqs, batch, err := codec.ReadRequestHeaders() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, batch, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	requests := make([]*serverRequest, len(reqs)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// verify requests | 
					
						
							|  |  |  | 	for i, r := range reqs { | 
					
						
							|  |  |  | 		var ok bool | 
					
						
							|  |  |  | 		var svc *service | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:42:44 +02:00
										 |  |  | 		if r.err != nil { | 
					
						
							|  |  |  | 			requests[i] = &serverRequest{id: r.id, err: r.err} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		if r.isPubSub && r.method == unsubscribeMethod { | 
					
						
							|  |  |  | 			requests[i] = &serverRequest{id: r.id, isUnsubscribe: true} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 			argTypes := []reflect.Type{reflect.TypeOf("")} // expect subscription id as first arg | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			if args, err := codec.ParseRequestArguments(argTypes, r.params); err == nil { | 
					
						
							|  |  |  | 				requests[i].args = args | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				requests[i].err = &invalidParamsError{err.Error()} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		if svc, ok = s.services[r.service]; !ok { // rpc method isn't available | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{r.service, r.method}} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		if r.isPubSub { // eth_subscribe, r.method contains the subscription method name | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			if callb, ok := svc.subscriptions[r.method]; ok { | 
					
						
							|  |  |  | 				requests[i] = &serverRequest{id: r.id, svcname: svc.name, callb: callb} | 
					
						
							|  |  |  | 				if r.params != nil && len(callb.argTypes) > 0 { | 
					
						
							|  |  |  | 					argTypes := []reflect.Type{reflect.TypeOf("")} | 
					
						
							|  |  |  | 					argTypes = append(argTypes, callb.argTypes...) | 
					
						
							|  |  |  | 					if args, err := codec.ParseRequestArguments(argTypes, r.params); err == nil { | 
					
						
							|  |  |  | 						requests[i].args = args[1:] // first one is service.method name which isn't an actual argument | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						requests[i].err = &invalidParamsError{err.Error()} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{subscribeMethod, r.method}} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 		if callb, ok := svc.callbacks[r.method]; ok { // lookup RPC method | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			requests[i] = &serverRequest{id: r.id, svcname: svc.name, callb: callb} | 
					
						
							|  |  |  | 			if r.params != nil && len(callb.argTypes) > 0 { | 
					
						
							|  |  |  | 				if args, err := codec.ParseRequestArguments(callb.argTypes, r.params); err == nil { | 
					
						
							|  |  |  | 					requests[i].args = args | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					requests[i].err = &invalidParamsError{err.Error()} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		requests[i] = &serverRequest{id: r.id, err: &methodNotFoundError{r.service, r.method}} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return requests, batch, nil | 
					
						
							|  |  |  | } |