rpc/protocol/json/client.go

200 lines
5.0 KiB
Go
Raw Normal View History

2017-10-26 07:21:35 +00:00
package json
import (
"encoding/json"
2017-10-31 09:25:44 +00:00
"fmt"
2017-10-26 07:21:35 +00:00
"io"
2017-10-31 09:25:44 +00:00
"sync"
"git.loafle.net/commons_go/rpc/encode"
"git.loafle.net/commons_go/rpc/protocol"
2017-10-26 07:21:35 +00:00
)
// ----------------------------------------------------------------------------
// 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.
2017-10-31 09:25:44 +00:00
ID interface{} `json:"id"`
2017-10-26 07:21:35 +00:00
}
// clientResponse represents a JSON-RPC response returned to a client.
type clientResponse struct {
Version string `json:"jsonrpc"`
Result *json.RawMessage `json:"result"`
2017-10-31 09:25:44 +00:00
Error interface{} `json:"error"`
ID interface{} `json:"id"`
2017-10-26 07:21:35 +00:00
}
2017-10-31 09:25:44 +00:00
// 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"`
2017-10-26 07:21:35 +00:00
}
2017-10-31 09:25:44 +00:00
// ----------------------------------------------------------------------------
// Codec
// ----------------------------------------------------------------------------
// NewCustomClientCodec returns a new JSON Codec based on passed encoder selector.
func NewCustomClientCodec(encSel encode.EncoderSelector) *ClientCodec {
return &ClientCodec{encSel: encSel}
2017-10-26 07:21:35 +00:00
}
2017-10-31 09:25:44 +00:00
// 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 {
2017-10-26 07:21:35 +00:00
return err
}
2017-10-31 09:25:44 +00:00
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,
2017-10-26 07:21:35 +00:00
}
}
2017-10-31 09:25:44 +00:00
ccrn := retainClientCodecResponseOrNotify()
2017-11-01 08:13:23 +00:00
if res, err := newClientCodecResponse(raw); nil != err {
notify, err := newClientCodecNotify(raw)
2017-10-31 09:25:44 +00:00
if nil != err {
releaseClientCodecResponseOrNotify(ccrn)
return nil, fmt.Errorf("Is not response or notification [%v]", raw)
}
ccrn.notify = notify
} else {
ccrn.response = res
2017-10-26 07:21:35 +00:00
}
2017-10-31 09:25:44 +00:00
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 {
2017-11-01 07:55:51 +00:00
var cr *clientRequest
2017-10-31 09:25:44 +00:00
v := clientRequestPool.Get()
if v == nil {
2017-11-01 07:55:51 +00:00
cr = &clientRequest{}
} else {
cr = v.(*clientRequest)
2017-10-31 09:25:44 +00:00
}
2017-11-01 07:55:51 +00:00
2017-11-01 08:03:17 +00:00
cr.Version = Version
2017-10-31 09:25:44 +00:00
cr.Method = method
cr.Params = params
cr.ID = id
2017-11-01 07:55:51 +00:00
2017-10-31 09:25:44 +00:00
return cr
}
func releaseClientRequest(cr *clientRequest) {
2017-11-01 08:03:17 +00:00
cr.Version = ""
2017-10-31 09:25:44 +00:00
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)
2017-10-26 07:21:35 +00:00
}