storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/rpc/json2/server.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Copyright 2012 The Gorilla Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 // Copyright 2020 MinIO, Inc. All rights reserved. 7 // forked from https://github.com/gorilla/rpc/v2 8 // modified to be used with MinIO under Apache 9 // 2.0 license that can be found in the LICENSE file. 10 11 package json2 12 13 import ( 14 "net/http" 15 16 jsoniter "github.com/json-iterator/go" 17 18 "storj.io/minio/pkg/rpc" 19 ) 20 21 var null = jsoniter.RawMessage([]byte("null")) 22 var Version = "2.0" 23 24 // ---------------------------------------------------------------------------- 25 // Request and Response 26 // ---------------------------------------------------------------------------- 27 28 // serverRequest represents a JSON-RPC request received by the server. 29 type serverRequest struct { 30 // JSON-RPC protocol. 31 Version string `json:"jsonrpc"` 32 33 // A String containing the name of the method to be invoked. 34 Method string `json:"method"` 35 36 // A Structured value to pass as arguments to the method. 37 Params *jsoniter.RawMessage `json:"params"` 38 39 // The request id. MUST be a string, number or null. 40 // Our implementation will not do type checking for id. 41 // It will be copied as it is. 42 ID *jsoniter.RawMessage `json:"id"` 43 } 44 45 // serverResponse represents a JSON-RPC response returned by the server. 46 type serverResponse struct { 47 // JSON-RPC protocol. 48 Version string `json:"jsonrpc"` 49 50 // The Object that was returned by the invoked method. This must be null 51 // in case there was an error invoking the method. 52 // As per spec the member will be omitted if there was an error. 53 Result interface{} `json:"result,omitempty"` 54 55 // An Error object if there was an error invoking the method. It must be 56 // null if there was no error. 57 // As per spec the member will be omitted if there was no error. 58 Error *Error `json:"error,omitempty"` 59 60 // This must be the same id as the request it is responding to. 61 ID *jsoniter.RawMessage `json:"id"` 62 } 63 64 // ---------------------------------------------------------------------------- 65 // Codec 66 // ---------------------------------------------------------------------------- 67 68 // NewCustomCodec returns a new JSON Codec based on passed encoder selector. 69 func NewCustomCodec(encSel rpc.EncoderSelector) *Codec { 70 return &Codec{encSel: encSel} 71 } 72 73 // NewCustomCodecWithErrorMapper returns a new JSON Codec based on the passed encoder selector 74 // and also accepts an errorMapper function. 75 // The errorMapper function will be called if the Service implementation returns an error, with that 76 // error as a param, replacing it by the value returned by this function. This function is intended 77 // to decouple your service implementation from the codec itself, making possible to return abstract 78 // errors in your service, and then mapping them here to the JSON-RPC error codes. 79 func NewCustomCodecWithErrorMapper(encSel rpc.EncoderSelector, errorMapper func(error) error) *Codec { 80 return &Codec{ 81 encSel: encSel, 82 errorMapper: errorMapper, 83 } 84 } 85 86 // NewCodec returns a new JSON Codec. 87 func NewCodec() *Codec { 88 return NewCustomCodec(rpc.DefaultEncoderSelector) 89 } 90 91 // Codec creates a CodecRequest to process each request. 92 type Codec struct { 93 encSel rpc.EncoderSelector 94 errorMapper func(error) error 95 } 96 97 // NewRequest returns a CodecRequest. 98 func (c *Codec) NewRequest(r *http.Request) rpc.CodecRequest { 99 return newCodecRequest(r, c.encSel.Select(r), c.errorMapper) 100 } 101 102 // ---------------------------------------------------------------------------- 103 // CodecRequest 104 // ---------------------------------------------------------------------------- 105 106 // newCodecRequest returns a new CodecRequest. 107 func newCodecRequest(r *http.Request, encoder rpc.Encoder, errorMapper func(error) error) rpc.CodecRequest { 108 // Decode the request body and check if RPC method is valid. 109 req := new(serverRequest) 110 var json = jsoniter.ConfigCompatibleWithStandardLibrary 111 err := json.NewDecoder(r.Body).Decode(req) 112 113 if err != nil { 114 err = &Error{ 115 Code: E_PARSE, 116 Message: err.Error(), 117 Data: req, 118 } 119 } else if req.Version != Version { 120 err = &Error{ 121 Code: E_INVALID_REQ, 122 Message: "jsonrpc must be " + Version, 123 Data: req, 124 } 125 } 126 127 r.Body.Close() 128 return &CodecRequest{request: req, err: err, encoder: encoder, errorMapper: errorMapper} 129 } 130 131 // CodecRequest decodes and encodes a single request. 132 type CodecRequest struct { 133 request *serverRequest 134 err error 135 encoder rpc.Encoder 136 errorMapper func(error) error 137 } 138 139 // Method returns the RPC method for the current request. 140 // 141 // The method uses a dotted notation as in "Service.Method". 142 func (c *CodecRequest) Method() (string, error) { 143 if c.err == nil { 144 return c.request.Method, nil 145 } 146 return "", c.err 147 } 148 149 // ReadRequest fills the request object for the RPC method. 150 // 151 // ReadRequest parses request parameters in two supported forms in 152 // accordance with http://www.jsonrpc.org/specification#parameter_structures 153 // 154 // by-position: params MUST be an Array, containing the 155 // values in the Server expected order. 156 // 157 // by-name: params MUST be an Object, with member names 158 // that match the Server expected parameter names. The 159 // absence of expected names MAY result in an error being 160 // generated. The names MUST match exactly, including 161 // case, to the method's expected parameters. 162 func (c *CodecRequest) ReadRequest(args interface{}) error { 163 if c.err == nil && c.request.Params != nil { 164 // Note: if c.request.Params is nil it's not an error, it's an optional member. 165 // JSON params structured object. Unmarshal to the args object. 166 var json = jsoniter.ConfigCompatibleWithStandardLibrary 167 if err := json.Unmarshal(*c.request.Params, args); err != nil { 168 // Clearly JSON params is not a structured object, 169 // fallback and attempt an unmarshal with JSON params as 170 // array value and RPC params is struct. Unmarshal into 171 // array containing the request struct. 172 params := [1]interface{}{args} 173 if err = json.Unmarshal(*c.request.Params, ¶ms); err != nil { 174 c.err = &Error{ 175 Code: E_INVALID_REQ, 176 Message: err.Error(), 177 Data: c.request.Params, 178 } 179 } 180 } 181 } 182 return c.err 183 } 184 185 // WriteResponse encodes the response and writes it to the ResponseWriter. 186 func (c *CodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}) { 187 res := &serverResponse{ 188 Version: Version, 189 Result: reply, 190 ID: c.request.ID, 191 } 192 c.writeServerResponse(w, res) 193 } 194 195 func (c *CodecRequest) WriteError(w http.ResponseWriter, status int, err error) { 196 err = c.tryToMapIfNotAnErrorAlready(err) 197 jsonErr, ok := err.(*Error) 198 if !ok { 199 jsonErr = &Error{ 200 Code: E_SERVER, 201 Message: err.Error(), 202 } 203 } 204 res := &serverResponse{ 205 Version: Version, 206 Error: jsonErr, 207 ID: c.request.ID, 208 } 209 c.writeServerResponse(w, res) 210 } 211 212 func (c CodecRequest) tryToMapIfNotAnErrorAlready(err error) error { 213 if _, ok := err.(*Error); ok || c.errorMapper == nil { 214 return err 215 } 216 return c.errorMapper(err) 217 } 218 219 func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, res *serverResponse) { 220 // ID is null for notifications and they don't have a response, unless we couldn't even parse the JSON, in that 221 // case we can't know whether it was intended to be a notification 222 if c.request.ID != nil || isParseErrorResponse(res) { 223 w.Header().Set("Content-Type", "application/json; charset=utf-8") 224 var json = jsoniter.ConfigCompatibleWithStandardLibrary 225 encoder := json.NewEncoder(c.encoder.Encode(w)) 226 err := encoder.Encode(res) 227 228 // Not sure in which case will this happen. But seems harmless. 229 if err != nil { 230 rpc.WriteError(w, http.StatusInternalServerError, err.Error()) 231 } 232 } 233 } 234 235 func isParseErrorResponse(res *serverResponse) bool { 236 return res != nil && res.Error != nil && res.Error.Code == E_PARSE 237 } 238 239 type EmptyResponse struct { 240 }