package jsonrpc
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/intel-go/fastjson"
|
|
)
|
|
|
|
const (
|
|
// Version is JSON-RPC 2.0.
|
|
Version = "2.0"
|
|
|
|
batchRequestKey = '['
|
|
contentTypeKey = "Content-Type"
|
|
contentTypeValue = "application/json"
|
|
)
|
|
|
|
type (
|
|
// A Request represents a JSON-RPC request received by the server.
|
|
Request struct {
|
|
ID *fastjson.RawMessage `json:"id"`
|
|
Version string `json:"jsonrpc"`
|
|
Method string `json:"method"`
|
|
Params *fastjson.RawMessage `json:"params"`
|
|
}
|
|
|
|
// A Response represents a JSON-RPC response returned by the server.
|
|
Response struct {
|
|
ID *fastjson.RawMessage `json:"id,omitempty"`
|
|
Version string `json:"jsonrpc"`
|
|
Result interface{} `json:"result,omitempty"`
|
|
Error *Error `json:"error,omitempty"`
|
|
}
|
|
)
|
|
|
|
// ParseRequest parses a HTTP request to JSON-RPC request.
|
|
func ParseRequest(r *http.Request) ([]*Request, bool, *Error) {
|
|
|
|
if !strings.HasPrefix(r.Header.Get(contentTypeKey), contentTypeValue) {
|
|
return nil, false, ErrInvalidRequest()
|
|
}
|
|
|
|
buf := bytes.NewBuffer(make([]byte, 0, r.ContentLength))
|
|
if _, err := buf.ReadFrom(r.Body); err != nil {
|
|
return nil, false, ErrInvalidRequest()
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
if buf.Len() == 0 {
|
|
return nil, false, ErrInvalidRequest()
|
|
}
|
|
|
|
f, _, err := buf.ReadRune()
|
|
if err != nil {
|
|
return nil, false, ErrInvalidRequest()
|
|
}
|
|
if err := buf.UnreadRune(); err != nil {
|
|
return nil, false, ErrInvalidRequest()
|
|
}
|
|
|
|
var rs []*Request
|
|
if f != batchRequestKey {
|
|
var req *Request
|
|
if err := fastjson.NewDecoder(buf).Decode(&req); err != nil {
|
|
return nil, false, ErrParse()
|
|
}
|
|
return append(rs, req), false, nil
|
|
}
|
|
|
|
if err := fastjson.NewDecoder(buf).Decode(&rs); err != nil {
|
|
return nil, false, ErrParse()
|
|
}
|
|
|
|
return rs, true, nil
|
|
}
|
|
|
|
// NewResponse generates a JSON-RPC response.
|
|
func NewResponse(r *Request) *Response {
|
|
return &Response{
|
|
Version: r.Version,
|
|
ID: r.ID,
|
|
}
|
|
}
|
|
|
|
// SendResponse writes JSON-RPC response.
|
|
func SendResponse(w http.ResponseWriter, resp []*Response, batch bool) error {
|
|
w.Header().Set(contentTypeKey, contentTypeValue)
|
|
if batch || len(resp) > 1 {
|
|
return fastjson.NewEncoder(w).Encode(resp)
|
|
} else if len(resp) == 1 {
|
|
return fastjson.NewEncoder(w).Encode(resp[0])
|
|
}
|
|
return nil
|
|
}
|