rpc/protocol/json/server.go

200 lines
5.8 KiB
Go
Raw Normal View History

2017-10-25 14:52:47 +00:00
package json
import (
"encoding/json"
2017-10-26 07:21:35 +00:00
"io"
"git.loafle.net/commons_go/rpc/encode"
"git.loafle.net/commons_go/rpc/protocol"
2017-10-25 14:52:47 +00:00
)
var null = json.RawMessage([]byte("null"))
var Version = "2.0"
// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------
// 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"`
// 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"`
}
// serverResponse represents a JSON-RPC response returned by the server.
type serverResponse struct {
// JSON-RPC protocol.
Version string `json:"jsonrpc"`
// The Object that was returned by the invoked method. This must be null
// in case there was an error invoking the method.
// As per spec the member will be omitted if there was an error.
Result interface{} `json:"result,omitempty"`
// An Error object if there was an error invoking the method. It must be
// null if there was no error.
// As per spec the member will be omitted if there was no error.
Error *Error `json:"error,omitempty"`
// This must be the same id as the request it is responding to.
ID *json.RawMessage `json:"id"`
}
// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------
// NewcustomCodec returns a new JSON Codec based on passed encoder selector.
2017-10-26 07:21:35 +00:00
func NewCustomCodec(encSel encode.EncoderSelector) *Codec {
2017-10-25 14:52:47 +00:00
return &Codec{encSel: encSel}
}
// NewCodec returns a new JSON Codec.
func NewCodec() *Codec {
2017-10-26 07:21:35 +00:00
return NewCustomCodec(encode.DefaultEncoderSelector)
2017-10-25 14:52:47 +00:00
}
// Codec creates a CodecRequest to process each request.
type Codec struct {
2017-10-26 07:21:35 +00:00
encSel encode.EncoderSelector
2017-10-25 14:52:47 +00:00
}
// NewRequest returns a CodecRequest.
2017-10-26 07:21:35 +00:00
func (c *Codec) NewRequest(r io.Reader) protocol.CodecRequest {
2017-10-25 14:52:47 +00:00
return newCodecRequest(r, c.encSel.Select(r))
}
// ----------------------------------------------------------------------------
// CodecRequest
// ----------------------------------------------------------------------------
// newCodecRequest returns a new CodecRequest.
2017-10-26 07:21:35 +00:00
func newCodecRequest(r io.Reader, encoder encode.Encoder) protocol.CodecRequest {
2017-10-25 14:52:47 +00:00
// Decode the request body and check if RPC method is valid.
req := new(serverRequest)
2017-10-26 07:21:35 +00:00
err := json.NewDecoder(r).Decode(req)
2017-10-25 14:52:47 +00:00
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,
}
}
2017-10-26 07:21:35 +00:00
2017-10-25 14:52:47 +00:00
return &CodecRequest{request: req, err: err, encoder: encoder}
}
// CodecRequest decodes and encodes a single request.
type CodecRequest struct {
request *serverRequest
err error
2017-10-26 07:21:35 +00:00
encoder encode.Encoder
2017-10-25 14:52:47 +00:00
}
// Method returns the RPC method for the current request.
//
// The method uses a dotted notation as in "Service.Method".
func (c *CodecRequest) Method() (string, error) {
if c.err == nil {
return c.request.Method, nil
}
return "", c.err
}
// 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 (c *CodecRequest) ReadRequest(args interface{}) error {
if c.err == nil && c.request.Params != nil {
// Note: if c.request.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(*c.request.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.
params := [1]interface{}{args}
if err = json.Unmarshal(*c.request.Params, &params); err != nil {
c.err = &Error{
Code: E_INVALID_REQ,
Message: err.Error(),
Data: c.request.Params,
}
}
}
}
return c.err
}
// WriteResponse encodes the response and writes it to the ResponseWriter.
2017-10-26 07:21:35 +00:00
func (c *CodecRequest) WriteResponse(w io.Writer, reply interface{}) error {
2017-10-25 14:52:47 +00:00
res := &serverResponse{
Version: Version,
Result: reply,
2017-10-26 07:21:35 +00:00
ID: c.request.ID,
2017-10-25 14:52:47 +00:00
}
2017-10-26 07:21:35 +00:00
return c.writeServerResponse(w, res)
2017-10-25 14:52:47 +00:00
}
2017-10-26 07:21:35 +00:00
// WriteError encodes the response and writes it to the ResponseWriter.
func (c *CodecRequest) WriteError(w io.Writer, status int, err error) error {
2017-10-25 14:52:47 +00:00
jsonErr, ok := err.(*Error)
if !ok {
jsonErr = &Error{
Code: E_SERVER,
Message: err.Error(),
}
}
res := &serverResponse{
Version: Version,
Error: jsonErr,
2017-10-26 07:21:35 +00:00
ID: c.request.ID,
2017-10-25 14:52:47 +00:00
}
2017-10-26 07:21:35 +00:00
return c.writeServerResponse(w, res)
2017-10-25 14:52:47 +00:00
}
2017-10-26 07:21:35 +00:00
func (c *CodecRequest) writeServerResponse(w io.Writer, res *serverResponse) error {
// ID is null for notifications and they don't have a response.
if c.request.ID != nil {
2017-10-25 14:52:47 +00:00
encoder := json.NewEncoder(c.encoder.Encode(w))
err := encoder.Encode(res)
// Not sure in which case will this happen. But seems harmless.
if err != nil {
2017-10-26 07:21:35 +00:00
return err
2017-10-25 14:52:47 +00:00
}
}
2017-10-26 07:21:35 +00:00
return nil
2017-10-25 14:52:47 +00:00
}
type EmptyResponse struct {
}