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) }