mirror of
https://github.com/minio/minio.git
synced 2026-02-06 19:00:14 -05:00
rpc/v2/json2 code has a bug where it treats all jsonrpc 2.0 request params like an 'object'. In accordance with the spec it could be both 'object' or an 'array'. Handle both cases.
190 lines
5.4 KiB
Go
190 lines
5.4 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package json2
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/rpc/v2"
|
|
)
|
|
|
|
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.
|
|
func NewCustomCodec(encSel rpc.EncoderSelector) *Codec {
|
|
return &Codec{encSel: encSel}
|
|
}
|
|
|
|
// NewCodec returns a new JSON Codec.
|
|
func NewCodec() *Codec {
|
|
return NewCustomCodec(rpc.DefaultEncoderSelector)
|
|
}
|
|
|
|
// Codec creates a CodecRequest to process each request.
|
|
type Codec struct {
|
|
encSel rpc.EncoderSelector
|
|
}
|
|
|
|
// NewRequest returns a CodecRequest.
|
|
func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest {
|
|
return newCodecRequest(r, c.encSel.Select(r))
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// CodecRequest
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// newCodecRequest returns a new CodecRequest.
|
|
func newCodecRequest(r *http.Request, encoder rpc.Encoder) rpc.CodecRequest {
|
|
// Decode the request body and check if RPC method is valid.
|
|
req := new(serverRequest)
|
|
err := json.NewDecoder(r.Body).Decode(req)
|
|
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,
|
|
}
|
|
}
|
|
r.Body.Close()
|
|
return &CodecRequest{request: req, err: err, encoder: encoder}
|
|
}
|
|
|
|
// CodecRequest decodes and encodes a single request.
|
|
type CodecRequest struct {
|
|
request *serverRequest
|
|
err error
|
|
encoder rpc.Encoder
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// ReadRe<quest fills the request object for the RPC method.
|
|
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 {
|
|
// Structed unmarshalling failed, attempt JSON params as
|
|
// array value. Unmarshal into array containing the
|
|
// request struct.
|
|
params := [1]interface{}{args}
|
|
if err = json.Unmarshal(*c.request.Params, ¶ms); 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.
|
|
func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) {
|
|
res := &serverResponse{
|
|
Version: Version,
|
|
Result: reply,
|
|
Id: c.request.Id,
|
|
}
|
|
c.writeServerResponse(w, res)
|
|
}
|
|
|
|
func (c *CodecRequest) WriteError(w http.ResponseWriter, status int, err error) {
|
|
jsonErr, ok := err.(*Error)
|
|
if !ok {
|
|
jsonErr = &Error{
|
|
Code: E_SERVER,
|
|
Message: err.Error(),
|
|
}
|
|
}
|
|
res := &serverResponse{
|
|
Version: Version,
|
|
Error: jsonErr,
|
|
Id: c.request.Id,
|
|
}
|
|
c.writeServerResponse(w, res)
|
|
}
|
|
|
|
func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, res *serverResponse) {
|
|
// Id is null for notifications and they don't have a response.
|
|
if c.request.Id != nil {
|
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
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 {
|
|
rpc.WriteError(w, 400, err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
type EmptyResponse struct {
|
|
}
|