| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	vsn                      = "2.0" | 
					
						
							| 
									
										
										
										
											2017-04-06 08:56:41 +02:00
										 |  |  | 	serviceMethodSeparator   = "_" | 
					
						
							|  |  |  | 	subscribeMethodSuffix    = "_subscribe" | 
					
						
							|  |  |  | 	unsubscribeMethodSuffix  = "_unsubscribe" | 
					
						
							|  |  |  | 	notificationMethodSuffix = "_subscription" | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	defaultWriteTimeout = 10 * time.Second // used if context has no deadline | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | var null = json.RawMessage("null") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type subscriptionResult struct { | 
					
						
							|  |  |  | 	ID     string          `json:"subscription"` | 
					
						
							|  |  |  | 	Result json.RawMessage `json:"result,omitempty"` | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | // A value of this type can a JSON-RPC request, notification, successful response or | 
					
						
							|  |  |  | // error response. Which one it is depends on the fields. | 
					
						
							|  |  |  | type jsonrpcMessage struct { | 
					
						
							|  |  |  | 	Version string          `json:"jsonrpc,omitempty"` | 
					
						
							|  |  |  | 	ID      json.RawMessage `json:"id,omitempty"` | 
					
						
							|  |  |  | 	Method  string          `json:"method,omitempty"` | 
					
						
							|  |  |  | 	Params  json.RawMessage `json:"params,omitempty"` | 
					
						
							|  |  |  | 	Error   *jsonError      `json:"error,omitempty"` | 
					
						
							|  |  |  | 	Result  json.RawMessage `json:"result,omitempty"` | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | func (msg *jsonrpcMessage) isNotification() bool { | 
					
						
							|  |  |  | 	return msg.ID == nil && msg.Method != "" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | func (msg *jsonrpcMessage) isCall() bool { | 
					
						
							|  |  |  | 	return msg.hasValidID() && msg.Method != "" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | func (msg *jsonrpcMessage) isResponse() bool { | 
					
						
							|  |  |  | 	return msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | func (msg *jsonrpcMessage) hasValidID() bool { | 
					
						
							|  |  |  | 	return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | func (msg *jsonrpcMessage) isSubscribe() bool { | 
					
						
							|  |  |  | 	return strings.HasSuffix(msg.Method, subscribeMethodSuffix) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (msg *jsonrpcMessage) isUnsubscribe() bool { | 
					
						
							|  |  |  | 	return strings.HasSuffix(msg.Method, unsubscribeMethodSuffix) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (msg *jsonrpcMessage) namespace() string { | 
					
						
							|  |  |  | 	elem := strings.SplitN(msg.Method, serviceMethodSeparator, 2) | 
					
						
							|  |  |  | 	return elem[0] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (msg *jsonrpcMessage) String() string { | 
					
						
							|  |  |  | 	b, _ := json.Marshal(msg) | 
					
						
							|  |  |  | 	return string(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage { | 
					
						
							|  |  |  | 	resp := errorMessage(err) | 
					
						
							|  |  |  | 	resp.ID = msg.ID | 
					
						
							|  |  |  | 	return resp | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { | 
					
						
							|  |  |  | 	enc, err := json.Marshal(result) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// TODO: wrap with 'internal server error' | 
					
						
							|  |  |  | 		return msg.errorResponse(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func errorMessage(err error) *jsonrpcMessage { | 
					
						
							|  |  |  | 	msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{ | 
					
						
							|  |  |  | 		Code:    defaultErrorCode, | 
					
						
							|  |  |  | 		Message: err.Error(), | 
					
						
							|  |  |  | 	}} | 
					
						
							|  |  |  | 	ec, ok := err.(Error) | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		msg.Error.Code = ec.ErrorCode() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-08 10:09:49 +02:00
										 |  |  | 	de, ok := err.(DataError) | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		msg.Error.Data = de.ErrorData() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	return msg | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type jsonError struct { | 
					
						
							|  |  |  | 	Code    int         `json:"code"` | 
					
						
							|  |  |  | 	Message string      `json:"message"` | 
					
						
							|  |  |  | 	Data    interface{} `json:"data,omitempty"` | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 10:09:49 +02:00
										 |  |  | func (err *jsonError) ErrorData() interface{} { | 
					
						
							|  |  |  | 	return err.Data | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | // Conn is a subset of the methods of net.Conn which are sufficient for ServerCodec. | 
					
						
							|  |  |  | type Conn interface { | 
					
						
							|  |  |  | 	io.ReadWriteCloser | 
					
						
							|  |  |  | 	SetWriteDeadline(time.Time) error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 12:22:39 +02:00
										 |  |  | type deadlineCloser interface { | 
					
						
							|  |  |  | 	io.Closer | 
					
						
							|  |  |  | 	SetWriteDeadline(time.Time) error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | // ConnRemoteAddr wraps the RemoteAddr operation, which returns a description | 
					
						
							|  |  |  | // of the peer address of a connection. If a Conn also implements ConnRemoteAddr, this | 
					
						
							|  |  |  | // description is used in log messages. | 
					
						
							|  |  |  | type ConnRemoteAddr interface { | 
					
						
							|  |  |  | 	RemoteAddr() string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // jsonCodec reads and writes JSON-RPC messages to the underlying connection. It also has | 
					
						
							|  |  |  | // support for parsing arguments and serializing (result) objects. | 
					
						
							|  |  |  | type jsonCodec struct { | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | 	remote  string | 
					
						
							|  |  |  | 	closer  sync.Once                 // close closed channel once | 
					
						
							|  |  |  | 	closeCh chan interface{}          // closed on Close | 
					
						
							|  |  |  | 	decode  func(v interface{}) error // decoder to allow multiple transports | 
					
						
							|  |  |  | 	encMu   sync.Mutex                // guards the encoder | 
					
						
							|  |  |  | 	encode  func(v interface{}) error // encoder to allow multiple transports | 
					
						
							|  |  |  | 	conn    deadlineCloser | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewFuncCodec creates a codec which uses the given functions to read and write. If conn | 
					
						
							|  |  |  | // implements ConnRemoteAddr, log messages will use it to include the remote address of | 
					
						
							|  |  |  | // the connection. | 
					
						
							|  |  |  | func NewFuncCodec(conn deadlineCloser, encode, decode func(v interface{}) error) ServerCodec { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	codec := &jsonCodec{ | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | 		closeCh: make(chan interface{}), | 
					
						
							|  |  |  | 		encode:  encode, | 
					
						
							|  |  |  | 		decode:  decode, | 
					
						
							|  |  |  | 		conn:    conn, | 
					
						
							| 
									
										
										
										
											2018-03-13 13:23:44 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	if ra, ok := conn.(ConnRemoteAddr); ok { | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | 		codec.remote = ra.RemoteAddr() | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return codec | 
					
						
							| 
									
										
										
										
											2018-03-13 13:23:44 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | // NewCodec creates a codec on the given connection. If conn implements ConnRemoteAddr, log | 
					
						
							|  |  |  | // messages will use it to include the remote address of the connection. | 
					
						
							|  |  |  | func NewCodec(conn Conn) ServerCodec { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	enc := json.NewEncoder(conn) | 
					
						
							|  |  |  | 	dec := json.NewDecoder(conn) | 
					
						
							| 
									
										
										
										
											2018-03-13 13:23:44 +02:00
										 |  |  | 	dec.UseNumber() | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | 	return NewFuncCodec(conn, enc.Encode, dec.Decode) | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-13 13:23:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | func (c *jsonCodec) remoteAddr() string { | 
					
						
							|  |  |  | 	return c.remote | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-28 16:27:58 +02:00
										 |  |  | func (c *jsonCodec) readBatch() (messages []*jsonrpcMessage, batch bool, err error) { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	// Decode the next JSON object in the input stream. | 
					
						
							|  |  |  | 	// This verifies basic syntax, etc. | 
					
						
							|  |  |  | 	var rawmsg json.RawMessage | 
					
						
							|  |  |  | 	if err := c.decode(&rawmsg); err != nil { | 
					
						
							|  |  |  | 		return nil, false, err | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-28 16:27:58 +02:00
										 |  |  | 	messages, batch = parseMessage(rawmsg) | 
					
						
							|  |  |  | 	for i, msg := range messages { | 
					
						
							|  |  |  | 		if msg == nil { | 
					
						
							|  |  |  | 			// Message is JSON 'null'. Replace with zero value so it | 
					
						
							|  |  |  | 			// will be treated like any other invalid message. | 
					
						
							|  |  |  | 			messages[i] = new(jsonrpcMessage) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return messages, batch, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | func (c *jsonCodec) writeJSON(ctx context.Context, v interface{}) error { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	c.encMu.Lock() | 
					
						
							|  |  |  | 	defer c.encMu.Unlock() | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	deadline, ok := ctx.Deadline() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		deadline = time.Now().Add(defaultWriteTimeout) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	c.conn.SetWriteDeadline(deadline) | 
					
						
							|  |  |  | 	return c.encode(v) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | func (c *jsonCodec) close() { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	c.closer.Do(func() { | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | 		close(c.closeCh) | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		c.conn.Close() | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2016-03-14 09:38:54 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | // Closed returns a channel which will be closed when Close is called | 
					
						
							| 
									
										
										
										
											2019-11-18 09:40:59 +01:00
										 |  |  | func (c *jsonCodec) closed() <-chan interface{} { | 
					
						
							|  |  |  | 	return c.closeCh | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | // parseMessage parses raw bytes as a (batch of) JSON-RPC message(s). There are no error | 
					
						
							|  |  |  | // checks in this function because the raw message has already been syntax-checked when it | 
					
						
							|  |  |  | // is called. Any non-JSON-RPC messages in the input return the zero value of | 
					
						
							|  |  |  | // jsonrpcMessage. | 
					
						
							|  |  |  | func parseMessage(raw json.RawMessage) ([]*jsonrpcMessage, bool) { | 
					
						
							|  |  |  | 	if !isBatch(raw) { | 
					
						
							|  |  |  | 		msgs := []*jsonrpcMessage{{}} | 
					
						
							|  |  |  | 		json.Unmarshal(raw, &msgs[0]) | 
					
						
							|  |  |  | 		return msgs, false | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	dec := json.NewDecoder(bytes.NewReader(raw)) | 
					
						
							|  |  |  | 	dec.Token() // skip '[' | 
					
						
							|  |  |  | 	var msgs []*jsonrpcMessage | 
					
						
							|  |  |  | 	for dec.More() { | 
					
						
							|  |  |  | 		msgs = append(msgs, new(jsonrpcMessage)) | 
					
						
							|  |  |  | 		dec.Decode(&msgs[len(msgs)-1]) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	return msgs, true | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | // isBatch returns true when the first non-whitespace characters is '[' | 
					
						
							|  |  |  | func isBatch(raw json.RawMessage) bool { | 
					
						
							|  |  |  | 	for _, c := range raw { | 
					
						
							|  |  |  | 		// skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) | 
					
						
							|  |  |  | 		if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 		return c == '[' | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	return false | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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. | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, error) { | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 	dec := json.NewDecoder(bytes.NewReader(rawArgs)) | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	var args []reflect.Value | 
					
						
							|  |  |  | 	tok, err := dec.Token() | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case err == io.EOF || tok == nil && err == nil: | 
					
						
							|  |  |  | 		// "params" is optional and may be empty. Also allow "params":null even though it's | 
					
						
							|  |  |  | 		// not in the spec because our own client used to send it. | 
					
						
							|  |  |  | 	case err != nil: | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	case tok == json.Delim('['): | 
					
						
							|  |  |  | 		// Read argument array. | 
					
						
							|  |  |  | 		if args, err = parseArgumentArray(dec, types); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, errors.New("non-array args") | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	// Set any missing args to nil. | 
					
						
							|  |  |  | 	for i := len(args); i < len(types); i++ { | 
					
						
							|  |  |  | 		if types[i].Kind() != reflect.Ptr { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("missing value for required argument %d", i) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		args = append(args, reflect.Zero(types[i])) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return args, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func parseArgumentArray(dec *json.Decoder, types []reflect.Type) ([]reflect.Value, error) { | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 	args := make([]reflect.Value, 0, len(types)) | 
					
						
							|  |  |  | 	for i := 0; dec.More(); i++ { | 
					
						
							|  |  |  | 		if i >= len(types) { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 			return args, fmt.Errorf("too many arguments, want at most %d", len(types)) | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		argval := reflect.New(types[i]) | 
					
						
							|  |  |  | 		if err := dec.Decode(argval.Interface()); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 			return args, fmt.Errorf("invalid argument %d: %v", i, err) | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if argval.IsNil() && types[i].Kind() != reflect.Ptr { | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 			return args, fmt.Errorf("missing value for required argument %d", i) | 
					
						
							| 
									
										
										
										
											2016-12-12 12:20:30 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		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. | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | 	_, err := dec.Token() | 
					
						
							|  |  |  | 	return args, err | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 13:47:34 +01:00
										 |  |  | // parseSubscriptionName extracts the subscription name from an encoded argument array. | 
					
						
							|  |  |  | func parseSubscriptionName(rawArgs json.RawMessage) (string, error) { | 
					
						
							|  |  |  | 	dec := json.NewDecoder(bytes.NewReader(rawArgs)) | 
					
						
							|  |  |  | 	if tok, _ := dec.Token(); tok != json.Delim('[') { | 
					
						
							|  |  |  | 		return "", errors.New("non-array args") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	v, _ := dec.Token() | 
					
						
							|  |  |  | 	method, ok := v.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return "", errors.New("expected subscription name as first argument") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return method, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } |