2020-01-14 12:02:38 +00:00
|
|
|
package request
|
|
|
|
|
|
|
|
import (
|
2020-10-26 17:22:20 +00:00
|
|
|
"bytes"
|
2020-01-14 12:02:38 +00:00
|
|
|
"encoding/json"
|
2020-10-26 17:22:20 +00:00
|
|
|
"errors"
|
2020-08-06 14:44:08 +00:00
|
|
|
"fmt"
|
2020-01-14 12:02:38 +00:00
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// JSONRPCVersion is the only JSON-RPC protocol version supported.
|
|
|
|
JSONRPCVersion = "2.0"
|
2020-10-26 17:22:20 +00:00
|
|
|
|
|
|
|
// maxBatchSize is the maximum number of request per batch.
|
|
|
|
maxBatchSize = 100
|
2020-01-14 12:02:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// RawParams is just a slice of abstract values, used to represent parameters
|
|
|
|
// passed from client to server.
|
|
|
|
type RawParams struct {
|
|
|
|
Values []interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRawParams creates RawParams from its parameters.
|
|
|
|
func NewRawParams(vals ...interface{}) RawParams {
|
|
|
|
p := RawParams{}
|
|
|
|
p.Values = make([]interface{}, len(vals))
|
|
|
|
for i := 0; i < len(p.Values); i++ {
|
|
|
|
p.Values[i] = vals[i]
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
// Raw represents JSON-RPC request.
|
|
|
|
type Raw struct {
|
|
|
|
JSONRPC string `json:"jsonrpc"`
|
|
|
|
Method string `json:"method"`
|
|
|
|
RawParams []interface{} `json:"params"`
|
|
|
|
ID int `json:"id"`
|
|
|
|
}
|
|
|
|
|
2020-10-26 17:22:20 +00:00
|
|
|
// Request contains standard JSON-RPC 2.0 request and batch of
|
|
|
|
// requests: http://www.jsonrpc.org/specification.
|
|
|
|
// It's used in server to represent incoming queries.
|
|
|
|
type Request struct {
|
|
|
|
In *In
|
|
|
|
Batch Batch
|
|
|
|
}
|
|
|
|
|
2020-01-14 12:02:38 +00:00
|
|
|
// In represents a standard JSON-RPC 2.0
|
2020-10-26 17:22:20 +00:00
|
|
|
// request: http://www.jsonrpc.org/specification#request_object.
|
2020-01-14 12:02:38 +00:00
|
|
|
type In struct {
|
|
|
|
JSONRPC string `json:"jsonrpc"`
|
|
|
|
Method string `json:"method"`
|
|
|
|
RawParams json.RawMessage `json:"params,omitempty"`
|
|
|
|
RawID json.RawMessage `json:"id,omitempty"`
|
|
|
|
}
|
|
|
|
|
2020-10-26 17:22:20 +00:00
|
|
|
// Batch represents a standard JSON-RPC 2.0
|
|
|
|
// batch: https://www.jsonrpc.org/specification#batch.
|
|
|
|
type Batch []In
|
|
|
|
|
2021-05-12 20:17:03 +00:00
|
|
|
// MarshalJSON implements json.Marshaler interface.
|
2020-10-26 17:22:20 +00:00
|
|
|
func (r Request) MarshalJSON() ([]byte, error) {
|
|
|
|
if r.In != nil {
|
|
|
|
return json.Marshal(r.In)
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
2020-10-26 17:22:20 +00:00
|
|
|
return json.Marshal(r.Batch)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
|
|
|
func (r *Request) UnmarshalJSON(data []byte) error {
|
|
|
|
var (
|
|
|
|
in *In
|
|
|
|
batch Batch
|
|
|
|
)
|
|
|
|
in = &In{}
|
|
|
|
err := json.Unmarshal(data, in)
|
|
|
|
if err == nil {
|
|
|
|
r.In = in
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
decoder := json.NewDecoder(bytes.NewReader(data))
|
|
|
|
t, err := decoder.Token() // read `[`
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if t != json.Delim('[') {
|
|
|
|
return fmt.Errorf("`[` expected, got %s", t)
|
|
|
|
}
|
|
|
|
count := 0
|
|
|
|
for decoder.More() {
|
|
|
|
if count > maxBatchSize {
|
|
|
|
return fmt.Errorf("the number of requests in batch shouldn't exceed %d", maxBatchSize)
|
|
|
|
}
|
|
|
|
in = &In{}
|
|
|
|
decodeErr := decoder.Decode(in)
|
|
|
|
if decodeErr != nil {
|
|
|
|
return decodeErr
|
|
|
|
}
|
|
|
|
batch = append(batch, *in)
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
if len(batch) == 0 {
|
|
|
|
return errors.New("empty request")
|
|
|
|
}
|
|
|
|
r.Batch = batch
|
|
|
|
return nil
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DecodeData decodes the given reader into the the request
|
|
|
|
// struct.
|
2020-10-26 17:22:20 +00:00
|
|
|
func (r *Request) DecodeData(data io.ReadCloser) error {
|
2020-01-14 12:02:38 +00:00
|
|
|
defer data.Close()
|
|
|
|
|
2020-10-26 17:22:20 +00:00
|
|
|
rawData := json.RawMessage{}
|
|
|
|
err := json.NewDecoder(data).Decode(&rawData)
|
2020-01-14 12:02:38 +00:00
|
|
|
if err != nil {
|
2020-08-06 16:09:57 +00:00
|
|
|
return fmt.Errorf("error parsing JSON payload: %w", err)
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
|
|
|
|
2020-10-26 17:22:20 +00:00
|
|
|
return r.UnmarshalJSON(rawData)
|
|
|
|
}
|
2020-01-14 12:02:38 +00:00
|
|
|
|
2020-10-26 17:22:20 +00:00
|
|
|
// NewRequest creates a new Request struct.
|
|
|
|
func NewRequest() *Request {
|
|
|
|
return &Request{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewIn creates a new In struct.
|
|
|
|
func NewIn() *In {
|
|
|
|
return &In{
|
|
|
|
JSONRPC: JSONRPCVersion,
|
|
|
|
}
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Params takes a slice of any type and attempts to bind
|
|
|
|
// the params to it.
|
|
|
|
func (r *In) Params() (*Params, error) {
|
|
|
|
params := Params{}
|
|
|
|
|
|
|
|
err := json.Unmarshal(r.RawParams, ¶ms)
|
|
|
|
if err != nil {
|
2020-08-06 16:09:57 +00:00
|
|
|
return nil, fmt.Errorf("error parsing params: %w", err)
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ¶ms, nil
|
|
|
|
}
|