| 
									
										
										
										
											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 ( | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | 	jsonrpcVersion         = "2.0" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	serviceMethodSeparator = "_" | 
					
						
							|  |  |  | 	subscribeMethod        = "eth_subscribe" | 
					
						
							|  |  |  | 	unsubscribeMethod      = "eth_unsubscribe" | 
					
						
							|  |  |  | 	notificationMethod     = "eth_subscription" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | type jsonRequest struct { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	Method  string          `json:"method"` | 
					
						
							|  |  |  | 	Version string          `json:"jsonrpc"` | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	Id      json.RawMessage `json:"id,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-02-02 19:06:43 +02:00
										 |  |  | 	Payload json.RawMessage `json:"params,omitempty"` | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | type jsonSuccessResponse struct { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	Version string      `json:"jsonrpc"` | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	Id      interface{} `json:"id,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-01-26 18:18:56 +01:00
										 |  |  | 	Result  interface{} `json:"result"` | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | type jsonError struct { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	Code    int         `json:"code"` | 
					
						
							|  |  |  | 	Message string      `json:"message"` | 
					
						
							|  |  |  | 	Data    interface{} `json:"data,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | type jsonErrResponse struct { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	Version string      `json:"jsonrpc"` | 
					
						
							|  |  |  | 	Id      interface{} `json:"id,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | 	Error   jsonError   `json:"error"` | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type jsonSubscription struct { | 
					
						
							|  |  |  | 	Subscription string      `json:"subscription"` | 
					
						
							|  |  |  | 	Result       interface{} `json:"result,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type jsonNotification struct { | 
					
						
							|  |  |  | 	Version string           `json:"jsonrpc"` | 
					
						
							|  |  |  | 	Method  string           `json:"method"` | 
					
						
							|  |  |  | 	Params  jsonSubscription `json:"params"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | // jsonCodec reads and writes JSON-RPC messages to the underlying connection. It | 
					
						
							|  |  |  | // also has support for parsing arguments and serializing (result) objects. | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | type jsonCodec struct { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	closer sync.Once          // close closed channel once | 
					
						
							|  |  |  | 	closed chan interface{}   // closed on Close | 
					
						
							|  |  |  | 	decMu  sync.Mutex         // guards d | 
					
						
							|  |  |  | 	d      *json.Decoder      // decodes incoming requests | 
					
						
							|  |  |  | 	encMu  sync.Mutex         // guards e | 
					
						
							|  |  |  | 	e      *json.Encoder      // encodes responses | 
					
						
							|  |  |  | 	rw     io.ReadWriteCloser // connection | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func (err *jsonError) Error() string { | 
					
						
							|  |  |  | 	if err.Message == "" { | 
					
						
							|  |  |  | 		return fmt.Sprintf("json-rpc error %d", err.Code) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err.Message | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (err *jsonError) ErrorCode() int { | 
					
						
							|  |  |  | 	return err.Code | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // NewJSONCodec creates a new RPC server codec with support for JSON-RPC 2.0 | 
					
						
							|  |  |  | func NewJSONCodec(rwc io.ReadWriteCloser) ServerCodec { | 
					
						
							|  |  |  | 	d := json.NewDecoder(rwc) | 
					
						
							|  |  |  | 	d.UseNumber() | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	return &jsonCodec{closed: make(chan interface{}), d: d, e: json.NewEncoder(rwc), rw: rwc} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isBatch returns true when the first non-whitespace characters is '[' | 
					
						
							|  |  |  | func isBatch(msg json.RawMessage) bool { | 
					
						
							|  |  |  | 	for _, c := range msg { | 
					
						
							|  |  |  | 		// skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) | 
					
						
							|  |  |  | 		if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return c == '[' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | // ReadRequestHeaders will read new requests without parsing the arguments. It will | 
					
						
							|  |  |  | // return a collection of requests, an indication if these requests are in batch | 
					
						
							|  |  |  | // form or an error when the incoming message could not be read/parsed. | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func (c *jsonCodec) ReadRequestHeaders() ([]rpcRequest, bool, Error) { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	c.decMu.Lock() | 
					
						
							|  |  |  | 	defer c.decMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	var incomingMsg json.RawMessage | 
					
						
							|  |  |  | 	if err := c.d.Decode(&incomingMsg); err != nil { | 
					
						
							|  |  |  | 		return nil, false, &invalidRequestError{err.Error()} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if isBatch(incomingMsg) { | 
					
						
							|  |  |  | 		return parseBatchRequest(incomingMsg) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return parseRequest(incomingMsg) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | // checkReqId returns an error when the given reqId isn't valid for RPC method calls. | 
					
						
							|  |  |  | // valid id's are strings, numbers or null | 
					
						
							|  |  |  | func checkReqId(reqId json.RawMessage) error { | 
					
						
							|  |  |  | 	if len(reqId) == 0 { | 
					
						
							|  |  |  | 		return fmt.Errorf("missing request id") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if _, err := strconv.ParseFloat(string(reqId), 64); err == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var str string | 
					
						
							|  |  |  | 	if err := json.Unmarshal(reqId, &str); err == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Errorf("invalid request id") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // parseRequest will parse a single request from the given RawMessage. It will return | 
					
						
							|  |  |  | // the parsed request, an indication if the request was a batch or an error when | 
					
						
							|  |  |  | // the request could not be parsed. | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func parseRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) { | 
					
						
							|  |  |  | 	var in jsonRequest | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if err := json.Unmarshal(incomingMsg, &in); err != nil { | 
					
						
							|  |  |  | 		return nil, false, &invalidMessageError{err.Error()} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	if err := checkReqId(in.Id); err != nil { | 
					
						
							|  |  |  | 		return nil, false, &invalidMessageError{err.Error()} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	// subscribe are special, they will always use `subscribeMethod` as first param in the payload | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if in.Method == subscribeMethod { | 
					
						
							| 
									
										
										
										
											2017-01-06 15:52:03 +01:00
										 |  |  | 		reqs := []rpcRequest{{id: &in.Id, isPubSub: true}} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		if len(in.Payload) > 0 { | 
					
						
							|  |  |  | 			// first param must be subscription name | 
					
						
							|  |  |  | 			var subscribeMethod [1]string | 
					
						
							|  |  |  | 			if err := json.Unmarshal(in.Payload, &subscribeMethod); err != nil { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 				log.Debug(fmt.Sprintf("Unable to parse subscription method: %v\n", err)) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				return nil, false, &invalidRequestError{"Unable to parse subscription request"} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// all subscriptions are made on the eth service | 
					
						
							|  |  |  | 			reqs[0].service, reqs[0].method = "eth", subscribeMethod[0] | 
					
						
							|  |  |  | 			reqs[0].params = in.Payload | 
					
						
							|  |  |  | 			return reqs, false, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil, false, &invalidRequestError{"Unable to parse subscription request"} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if in.Method == unsubscribeMethod { | 
					
						
							| 
									
										
										
										
											2017-01-06 15:52:03 +01:00
										 |  |  | 		return []rpcRequest{{id: &in.Id, isPubSub: true, | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			method: unsubscribeMethod, params: in.Payload}}, false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	elems := strings.Split(in.Method, serviceMethodSeparator) | 
					
						
							|  |  |  | 	if len(elems) != 2 { | 
					
						
							|  |  |  | 		return nil, false, &methodNotFoundError{in.Method, ""} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:42:44 +02:00
										 |  |  | 	// regular RPC call | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if len(in.Payload) == 0 { | 
					
						
							| 
									
										
										
										
											2017-01-06 15:52:03 +01:00
										 |  |  | 		return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id}}, false, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 15:52:03 +01:00
										 |  |  | 	return []rpcRequest{{service: elems[0], method: elems[1], id: &in.Id, params: in.Payload}}, false, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // parseBatchRequest will parse a batch request into a collection of requests from the given RawMessage, an indication | 
					
						
							|  |  |  | // if the request was a batch or an error when the request could not be read. | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func parseBatchRequest(incomingMsg json.RawMessage) ([]rpcRequest, bool, Error) { | 
					
						
							|  |  |  | 	var in []jsonRequest | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if err := json.Unmarshal(incomingMsg, &in); err != nil { | 
					
						
							|  |  |  | 		return nil, false, &invalidMessageError{err.Error()} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	requests := make([]rpcRequest, len(in)) | 
					
						
							|  |  |  | 	for i, r := range in { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 		if err := checkReqId(r.Id); err != nil { | 
					
						
							|  |  |  | 			return nil, false, &invalidMessageError{err.Error()} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 		id := &in[i].Id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// subscribe are special, they will always use `subscribeMethod` as first param in the payload | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		if r.Method == subscribeMethod { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 			requests[i] = rpcRequest{id: id, isPubSub: true} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			if len(r.Payload) > 0 { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 				// first param must be subscription name | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				var subscribeMethod [1]string | 
					
						
							|  |  |  | 				if err := json.Unmarshal(r.Payload, &subscribeMethod); err != nil { | 
					
						
							| 
									
										
										
										
											2017-02-22 14:10:07 +02:00
										 |  |  | 					log.Debug(fmt.Sprintf("Unable to parse subscription method: %v\n", err)) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 					return nil, false, &invalidRequestError{"Unable to parse subscription request"} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// all subscriptions are made on the eth service | 
					
						
							|  |  |  | 				requests[i].service, requests[i].method = "eth", subscribeMethod[0] | 
					
						
							|  |  |  | 				requests[i].params = r.Payload | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return nil, true, &invalidRequestError{"Unable to parse (un)subscribe request arguments"} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if r.Method == unsubscribeMethod { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 			requests[i] = rpcRequest{id: id, isPubSub: true, method: unsubscribeMethod, params: r.Payload} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(r.Payload) == 0 { | 
					
						
							| 
									
										
										
										
											2016-07-12 17:42:44 +02:00
										 |  |  | 			requests[i] = rpcRequest{id: id, params: nil} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			requests[i] = rpcRequest{id: id, params: r.Payload} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if elem := strings.Split(r.Method, serviceMethodSeparator); len(elem) == 2 { | 
					
						
							|  |  |  | 			requests[i].service, requests[i].method = elem[0], elem[1] | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2016-07-12 17:42:44 +02:00
										 |  |  | 			requests[i].err = &methodNotFoundError{r.Method, ""} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return requests, true, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-17 15:39:55 +01:00
										 |  |  | // ParseRequestArguments tries to parse the given params (json.RawMessage) with the given | 
					
						
							|  |  |  | // types. It returns the parsed values or an error when the parsing failed. | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func (c *jsonCodec) ParseRequestArguments(argTypes []reflect.Type, params interface{}) ([]reflect.Value, Error) { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if args, ok := params.(json.RawMessage); !ok { | 
					
						
							|  |  |  | 		return nil, &invalidParamsError{"Invalid params supplied"} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return parsePositionalArguments(args, argTypes) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | // parsePositionalArguments tries to parse the given args to an array of values with the | 
					
						
							|  |  |  | // given types. It returns the parsed values or an error when the args could not be | 
					
						
							|  |  |  | // parsed. Missing optional arguments are returned as reflect.Zero values. | 
					
						
							|  |  |  | func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, Error) { | 
					
						
							|  |  |  | 	// Read beginning of the args array. | 
					
						
							|  |  |  | 	dec := json.NewDecoder(bytes.NewReader(rawArgs)) | 
					
						
							|  |  |  | 	if tok, _ := dec.Token(); tok != json.Delim('[') { | 
					
						
							|  |  |  | 		return nil, &invalidParamsError{"non-array args"} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 	// Read args. | 
					
						
							|  |  |  | 	args := make([]reflect.Value, 0, len(types)) | 
					
						
							|  |  |  | 	for i := 0; dec.More(); i++ { | 
					
						
							|  |  |  | 		if i >= len(types) { | 
					
						
							|  |  |  | 			return nil, &invalidParamsError{fmt.Sprintf("too many arguments, want at most %d", len(types))} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		argval := reflect.New(types[i]) | 
					
						
							|  |  |  | 		if err := dec.Decode(argval.Interface()); err != nil { | 
					
						
							|  |  |  | 			return nil, &invalidParamsError{fmt.Sprintf("invalid argument %d: %v", i, err)} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if argval.IsNil() && types[i].Kind() != reflect.Ptr { | 
					
						
							|  |  |  | 			return nil, &invalidParamsError{fmt.Sprintf("missing value for required argument %d", i)} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		args = append(args, argval.Elem()) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 	// Read end of args array. | 
					
						
							|  |  |  | 	if _, err := dec.Token(); err != nil { | 
					
						
							|  |  |  | 		return nil, &invalidParamsError{err.Error()} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 	// Set any missing args to nil. | 
					
						
							|  |  |  | 	for i := len(args); i < len(types); i++ { | 
					
						
							|  |  |  | 		if types[i].Kind() != reflect.Ptr { | 
					
						
							|  |  |  | 			return nil, &invalidParamsError{fmt.Sprintf("missing value for required argument %d", i)} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 		args = append(args, reflect.Zero(types[i])) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 	return args, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateResponse will create a JSON-RPC success response with the given id and reply as result. | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | func (c *jsonCodec) CreateResponse(id interface{}, reply interface{}) interface{} { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	if isHexNum(reflect.TypeOf(reply)) { | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | 		return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: fmt.Sprintf(`%#x`, reply)} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | 	return &jsonSuccessResponse{Version: jsonrpcVersion, Id: id, Result: reply} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateErrorResponse will create a JSON-RPC error response with the given id and error. | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func (c *jsonCodec) CreateErrorResponse(id interface{}, err Error) interface{} { | 
					
						
							|  |  |  | 	return &jsonErrResponse{Version: jsonrpcVersion, Id: id, Error: jsonError{Code: err.ErrorCode(), Message: err.Error()}} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateErrorResponseWithInfo will create a JSON-RPC error response with the given id and error. | 
					
						
							|  |  |  | // info is optional and contains additional information about the error. When an empty string is passed it is ignored. | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | func (c *jsonCodec) CreateErrorResponseWithInfo(id interface{}, err Error, info interface{}) interface{} { | 
					
						
							|  |  |  | 	return &jsonErrResponse{Version: jsonrpcVersion, Id: id, | 
					
						
							|  |  |  | 		Error: jsonError{Code: err.ErrorCode(), Message: err.Error(), Data: info}} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CreateNotification will create a JSON-RPC notification with the given subscription id and event as params. | 
					
						
							|  |  |  | func (c *jsonCodec) CreateNotification(subid string, event interface{}) interface{} { | 
					
						
							|  |  |  | 	if isHexNum(reflect.TypeOf(event)) { | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | 		return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod, | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			Params: jsonSubscription{Subscription: subid, Result: fmt.Sprintf(`%#x`, event)}} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 17:47:15 +02:00
										 |  |  | 	return &jsonNotification{Version: jsonrpcVersion, Method: notificationMethod, | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		Params: jsonSubscription{Subscription: subid, Result: event}} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write message to client | 
					
						
							|  |  |  | func (c *jsonCodec) Write(res interface{}) error { | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 	c.encMu.Lock() | 
					
						
							|  |  |  | 	defer c.encMu.Unlock() | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	return c.e.Encode(res) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close the underlying connection | 
					
						
							|  |  |  | func (c *jsonCodec) Close() { | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	c.closer.Do(func() { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		close(c.closed) | 
					
						
							|  |  |  | 		c.rw.Close() | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Closed returns a channel which will be closed when Close is called | 
					
						
							|  |  |  | func (c *jsonCodec) Closed() <-chan interface{} { | 
					
						
							|  |  |  | 	return c.closed | 
					
						
							|  |  |  | } |