rpc/protocol/json/client.go
crusader c03a9b4fe9 ing
2017-11-01 18:15:48 +09:00

200 lines
5.0 KiB
Go

package json
import (
"encoding/json"
"fmt"
"io"
"sync"
"git.loafle.net/commons_go/rpc/encode"
"git.loafle.net/commons_go/rpc/protocol"
)
// ----------------------------------------------------------------------------
// Request and Response
// ----------------------------------------------------------------------------
// clientRequest represents a JSON-RPC request sent by a client.
type clientRequest struct {
// JSON-RPC protocol.
Version string `json:"jsonrpc"`
// A String containing the name of the method to be invoked.
Method string `json:"method"`
// Object to pass as request parameter to the method.
Params interface{} `json:"params"`
// The request id. This can be of any type. It is used to match the
// response with the request that it is replying to.
ID interface{} `json:"id"`
}
// clientResponse represents a JSON-RPC response returned to a client.
type clientResponse struct {
Version string `json:"jsonrpc"`
Result interface{} `json:"result"`
Error error `json:"error"`
ID interface{} `json:"id"`
}
// clientRequest represents a JSON-RPC request sent by a client.
type clientNotify struct {
// JSON-RPC protocol.
Version string `json:"jsonrpc"`
// A String containing the name of the method to be invoked.
Method string `json:"method"`
// Object to pass as request parameter to the method.
Params *json.RawMessage `json:"params"`
}
// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------
// NewCustomClientCodec returns a new JSON Codec based on passed encoder selector.
func NewCustomClientCodec(encSel encode.EncoderSelector) *ClientCodec {
return &ClientCodec{encSel: encSel}
}
// NewClientCodec returns a new JSON Codec.
func NewClientCodec() *ClientCodec {
return NewCustomClientCodec(encode.DefaultEncoderSelector)
}
// ClientCodec creates a ClientCodecRequest to process each request.
type ClientCodec struct {
encSel encode.EncoderSelector
}
func (cc *ClientCodec) Write(w io.Writer, method string, args interface{}, id interface{}) error {
req := retainClientRequest(method, args, id)
defer func() {
if nil != req {
releaseClientRequest(req)
}
}()
encoder := json.NewEncoder(cc.encSel.SelectByWriter(w).Encode(w))
if err := encoder.Encode(req); nil != err {
return err
}
return nil
}
// NewResponse returns a ClientCodecResponse.
func (cc *ClientCodec) NewResponseOrNotify(r io.Reader) (protocol.ClientCodecResponseOrNotify, error) {
return newClientCodecResponseOrNotify(r, cc.encSel.SelectByReader(r))
}
// newCodecRequest returns a new ServerCodecRequest.
func newClientCodecResponseOrNotify(r io.Reader, encoder encode.Encoder) (protocol.ClientCodecResponseOrNotify, error) {
// Decode the request body and check if RPC method is valid.
var raw json.RawMessage
dec := json.NewDecoder(r)
err := dec.Decode(&raw)
if err == io.ErrUnexpectedEOF || err == io.EOF {
return nil, err
}
if err != nil {
err = &Error{
Code: E_PARSE,
Message: err.Error(),
Data: raw,
}
}
ccrn := retainClientCodecResponseOrNotify()
if res, err := newClientCodecResponse(raw); nil != err {
notify, err := newClientCodecNotify(raw)
if nil != err {
releaseClientCodecResponseOrNotify(ccrn)
return nil, fmt.Errorf("Is not response or notification [%v]", raw)
}
ccrn.notify = notify
} else {
ccrn.response = res
}
return ccrn, nil
}
type ClientCodecResponseOrNotify struct {
notify protocol.ClientCodecNotify
response protocol.ClientCodecResponse
}
func (ccrn *ClientCodecResponseOrNotify) IsResponse() bool {
return nil != ccrn.response
}
func (ccrn *ClientCodecResponseOrNotify) IsNotify() bool {
return nil != ccrn.notify
}
func (ccrn *ClientCodecResponseOrNotify) GetResponse() protocol.ClientCodecResponse {
return ccrn.response
}
func (ccrn *ClientCodecResponseOrNotify) GetNotify() protocol.ClientCodecNotify {
return ccrn.notify
}
func (ccrn *ClientCodecResponseOrNotify) Complete() {
if nil != ccrn.notify {
ccrn.notify.Complete()
}
if nil != ccrn.response {
ccrn.response.Complete()
}
releaseClientCodecResponseOrNotify(ccrn)
}
var clientRequestPool sync.Pool
func retainClientRequest(method string, params interface{}, id interface{}) *clientRequest {
var cr *clientRequest
v := clientRequestPool.Get()
if v == nil {
cr = &clientRequest{}
} else {
cr = v.(*clientRequest)
}
cr.Version = Version
cr.Method = method
cr.Params = params
cr.ID = id
return cr
}
func releaseClientRequest(cr *clientRequest) {
cr.Version = ""
cr.Method = ""
cr.Params = nil
cr.ID = nil
clientRequestPool.Put(cr)
}
var clientCodecResponseOrNotifyPool sync.Pool
func retainClientCodecResponseOrNotify() *ClientCodecResponseOrNotify {
v := clientCodecResponseOrNotifyPool.Get()
if v == nil {
return &ClientCodecResponseOrNotify{}
}
return v.(*ClientCodecResponseOrNotify)
}
func releaseClientCodecResponseOrNotify(cr *ClientCodecResponseOrNotify) {
clientCodecResponseOrNotifyPool.Put(cr)
}