239 lines
6.3 KiB
Go
239 lines
6.3 KiB
Go
|
package json
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"io"
|
||
|
"sync"
|
||
|
|
||
|
"git.loafle.net/commons_go/rpc/codec"
|
||
|
"git.loafle.net/commons_go/rpc/protocol"
|
||
|
)
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
// Request
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
// serverRequest represents a JSON-RPC request received by the server.
|
||
|
type serverRequest struct {
|
||
|
// JSON-RPC protocol.
|
||
|
Version string `json:"jsonrpc"`
|
||
|
|
||
|
// A String containing the name of the method to be invoked.
|
||
|
Method string `json:"method"`
|
||
|
|
||
|
// A Structured value to pass as arguments to the method.
|
||
|
Params *json.RawMessage `json:"params,omitempty"`
|
||
|
|
||
|
// The request id. MUST be a string, number or null.
|
||
|
// Our implementation will not do type checking for id.
|
||
|
// It will be copied as it is.
|
||
|
ID *json.RawMessage `json:"id,omitempty"`
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
// ServerRequestCodec
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
// newRequestCodec returns a new ServerRequestCodec.
|
||
|
func newServerRequestCodec(r io.Reader, codec codec.Codec) (protocol.ServerRequestCodec, error) {
|
||
|
// Decode the request body and check if RPC method is valid.
|
||
|
req := retainServerRequest()
|
||
|
err := json.NewDecoder(r).Decode(req)
|
||
|
if err == io.ErrUnexpectedEOF || err == io.EOF {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err != nil {
|
||
|
err = &Error{
|
||
|
Code: E_PARSE,
|
||
|
Message: err.Error(),
|
||
|
Data: req,
|
||
|
}
|
||
|
}
|
||
|
if req.Version != Version {
|
||
|
err = &Error{
|
||
|
Code: E_INVALID_REQ,
|
||
|
Message: "jsonrpc must be " + Version,
|
||
|
Data: req,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return retainServerRequestCodec(req, err, codec), nil
|
||
|
}
|
||
|
|
||
|
// ServerRequestCodec decodes and encodes a single request.
|
||
|
type ServerRequestCodec struct {
|
||
|
req *serverRequest
|
||
|
err error
|
||
|
codec codec.Codec
|
||
|
}
|
||
|
|
||
|
// Close is callback function that end of request.
|
||
|
func (src *ServerRequestCodec) Close() {
|
||
|
if nil != src.req {
|
||
|
releaseServerRequest(src.req)
|
||
|
}
|
||
|
releaseServerRequestCodec(src)
|
||
|
}
|
||
|
|
||
|
// Method returns the RPC method for the current request.
|
||
|
//
|
||
|
// The method uses a dotted notation as in "Service.Method".
|
||
|
func (src *ServerRequestCodec) Method() string {
|
||
|
return src.req.Method
|
||
|
}
|
||
|
|
||
|
// ReadRequest fills the request object for the RPC method.
|
||
|
//
|
||
|
// ReadRequest parses request parameters in two supported forms in
|
||
|
// accordance with http://www.jsonrpc.org/specification#parameter_structures
|
||
|
//
|
||
|
// by-position: params MUST be an Array, containing the
|
||
|
// values in the Server expected order.
|
||
|
//
|
||
|
// by-name: params MUST be an Object, with member names
|
||
|
// that match the Server expected parameter names. The
|
||
|
// absence of expected names MAY result in an error being
|
||
|
// generated. The names MUST match exactly, including
|
||
|
// case, to the method's expected parameters.
|
||
|
func (src *ServerRequestCodec) ReadParams(args []interface{}) error {
|
||
|
if src.err == nil && src.req.Params != nil {
|
||
|
// Note: if src.req.Params is nil it's not an error, it's an optional member.
|
||
|
// JSON params structured object. Unmarshal to the args object.
|
||
|
|
||
|
// if err := json.Unmarshal(*src.req.Params, &args); err != nil {
|
||
|
// // Clearly JSON params is not a structured object,
|
||
|
// // fallback and attempt an unmarshal with JSON params as
|
||
|
// // array value and RPC params is struct. Unmarshal into
|
||
|
// // array containing the request struct.
|
||
|
// src.err = &Error{
|
||
|
// Code: E_INVALID_REQ,
|
||
|
// Message: err.Error(),
|
||
|
// Data: src.req.Params,
|
||
|
// }
|
||
|
// }
|
||
|
raws := make([]json.RawMessage, len(args))
|
||
|
if err := json.Unmarshal(*src.req.Params, &raws); err != nil {
|
||
|
src.err = &Error{
|
||
|
Code: E_INVALID_REQ,
|
||
|
Message: err.Error(),
|
||
|
Data: src.req.Params,
|
||
|
}
|
||
|
return src.err
|
||
|
}
|
||
|
|
||
|
for indexI := 0; indexI < len(args); indexI++ {
|
||
|
raw := raws[indexI]
|
||
|
arg := args[indexI]
|
||
|
if err := json.Unmarshal(raw, &arg); err != nil {
|
||
|
src.err = &Error{
|
||
|
Code: E_INVALID_REQ,
|
||
|
Message: err.Error(),
|
||
|
Data: src.req.Params,
|
||
|
}
|
||
|
return src.err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return src.err
|
||
|
}
|
||
|
|
||
|
func (src *ServerRequestCodec) Params() ([]string, error) {
|
||
|
if src.err == nil && src.req.Params != nil {
|
||
|
var results []string
|
||
|
|
||
|
if err := json.Unmarshal(*src.req.Params, &results); err != nil {
|
||
|
src.err = &Error{
|
||
|
Code: E_INVALID_REQ,
|
||
|
Message: err.Error(),
|
||
|
Data: src.req.Params,
|
||
|
}
|
||
|
return nil, src.err
|
||
|
}
|
||
|
|
||
|
return results, nil
|
||
|
}
|
||
|
return nil, src.err
|
||
|
}
|
||
|
|
||
|
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||
|
func (src *ServerRequestCodec) WriteResponse(w io.Writer, reply interface{}) error {
|
||
|
res := retainServerResponse(reply, nil, src.req.ID)
|
||
|
return src.writeServerResponse(w, res)
|
||
|
}
|
||
|
|
||
|
// WriteError encodes the response and writes it to the ResponseWriter.
|
||
|
func (src *ServerRequestCodec) WriteError(w io.Writer, status int, err error) error {
|
||
|
jsonErr, ok := err.(*Error)
|
||
|
if !ok {
|
||
|
jsonErr = &Error{
|
||
|
Code: E_SERVER,
|
||
|
Message: err.Error(),
|
||
|
}
|
||
|
}
|
||
|
res := retainServerResponse(nil, jsonErr, src.req.ID)
|
||
|
return src.writeServerResponse(w, res)
|
||
|
}
|
||
|
|
||
|
func (src *ServerRequestCodec) writeServerResponse(w io.Writer, res *serverResponse) error {
|
||
|
defer func() {
|
||
|
releaseServerResponse(res)
|
||
|
}()
|
||
|
// ID is null for notifications and they don't have a response.
|
||
|
if src.req.ID != nil {
|
||
|
msg := retainServerMessage(protocol.MessageTypeResponse, res)
|
||
|
defer func() {
|
||
|
releaseServerMessage(msg)
|
||
|
}()
|
||
|
encoder := json.NewEncoder(src.codec.Encode(w))
|
||
|
// Not sure in which case will this happen. But seems harmless.
|
||
|
if err := encoder.Encode(msg); nil != err {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var serverRequestCodecPool sync.Pool
|
||
|
|
||
|
func retainServerRequestCodec(req *serverRequest, err error, codec codec.Codec) *ServerRequestCodec {
|
||
|
var src *ServerRequestCodec
|
||
|
v := serverRequestCodecPool.Get()
|
||
|
if v == nil {
|
||
|
src = &ServerRequestCodec{}
|
||
|
} else {
|
||
|
src = v.(*ServerRequestCodec)
|
||
|
}
|
||
|
|
||
|
src.req = req
|
||
|
src.err = err
|
||
|
src.codec = codec
|
||
|
|
||
|
return src
|
||
|
}
|
||
|
|
||
|
func releaseServerRequestCodec(src *ServerRequestCodec) {
|
||
|
src.req = nil
|
||
|
src.err = nil
|
||
|
src.codec = nil
|
||
|
|
||
|
serverRequestCodecPool.Put(src)
|
||
|
}
|
||
|
|
||
|
var serverRequestPool sync.Pool
|
||
|
|
||
|
func retainServerRequest() *serverRequest {
|
||
|
v := serverRequestPool.Get()
|
||
|
if v == nil {
|
||
|
return &serverRequest{}
|
||
|
}
|
||
|
return v.(*serverRequest)
|
||
|
}
|
||
|
|
||
|
func releaseServerRequest(sr *serverRequest) {
|
||
|
sr.Method = ""
|
||
|
sr.Params = nil
|
||
|
sr.ID = nil
|
||
|
|
||
|
serverRequestPool.Put(sr)
|
||
|
}
|