@@ -20,6 +20,20 @@ import (
2020 "github.com/filecoin-project/go-jsonrpc/metrics"
2121)
2222
23+ type RawParams json.RawMessage
24+
25+ var rtRawParams = reflect .TypeOf (RawParams {})
26+
27+ // todo is there a better way to tell 'struct with any number of fields'?
28+ func DecodeParams [T any ](p RawParams ) (T , error ) {
29+ var t T
30+ err := json .Unmarshal (p , & t )
31+
32+ // todo also handle list-encoding automagically (json.Unmarshal doesn't do that, does it?)
33+
34+ return t , err
35+ }
36+
2337// methodHandler is a handler for a single method
2438type methodHandler struct {
2539 paramReceivers []reflect.Type
@@ -28,7 +42,8 @@ type methodHandler struct {
2842 receiver reflect.Value
2943 handlerFunc reflect.Value
3044
31- hasCtx int
45+ hasCtx int
46+ hasRawParams bool
3247
3348 errOut int
3449 valOut int
@@ -40,7 +55,7 @@ type request struct {
4055 Jsonrpc string `json:"jsonrpc"`
4156 ID interface {} `json:"id,omitempty"`
4257 Method string `json:"method"`
43- Params [] param `json:"params"`
58+ Params json. RawMessage `json:"params"`
4459 Meta map [string ]string `json:"meta,omitempty"`
4560}
4661
@@ -135,9 +150,16 @@ func (s *handler) register(namespace string, r interface{}) {
135150 hasCtx = 1
136151 }
137152
153+ hasRawParams := false
138154 ins := funcType .NumIn () - 1 - hasCtx
139155 recvs := make ([]reflect.Type , ins )
140156 for i := 0 ; i < ins ; i ++ {
157+ if hasRawParams && i > 0 {
158+ panic ("raw params must be the last parameter" )
159+ }
160+ if funcType .In (i + 1 + hasCtx ) == rtRawParams {
161+ hasRawParams = true
162+ }
141163 recvs [i ] = method .Type .In (i + 1 + hasCtx )
142164 }
143165
@@ -150,7 +172,8 @@ func (s *handler) register(namespace string, r interface{}) {
150172 handlerFunc : method .Func ,
151173 receiver : val ,
152174
153- hasCtx : hasCtx ,
175+ hasCtx : hasCtx ,
176+ hasRawParams : hasRawParams ,
154177
155178 errOut : errOut ,
156179 valOut : valOut ,
@@ -291,13 +314,6 @@ func (s *handler) handle(ctx context.Context, req request, w func(func(io.Writer
291314 }
292315 }
293316
294- if len (req .Params ) != handler .nParams {
295- rpcError (w , & req , rpcInvalidParams , fmt .Errorf ("wrong param count (method '%s'): %d != %d" , req .Method , len (req .Params ), handler .nParams ))
296- stats .Record (ctx , metrics .RPCRequestError .M (1 ))
297- done (false )
298- return
299- }
300-
301317 outCh := handler .valOut != - 1 && handler .handlerFunc .Type ().Out (handler .valOut ).Kind () == reflect .Chan
302318 defer done (outCh )
303319
@@ -313,30 +329,54 @@ func (s *handler) handle(ctx context.Context, req request, w func(func(io.Writer
313329 callParams [1 ] = reflect .ValueOf (ctx )
314330 }
315331
316- for i := 0 ; i < handler .nParams ; i ++ {
317- var rp reflect. Value
318-
319- typ := handler . paramReceivers [ i ]
320- dec , found := s . paramDecoders [ typ ]
321- if ! found {
322- rp = reflect . New ( typ )
323- if err := json . NewDecoder ( bytes . NewReader ( req . Params [ i ]. data )). Decode ( rp . Interface ()); err != nil {
324- rpcError ( w , & req , rpcParseError , xerrors . Errorf ( "unmarshaling params for '%s' ( param: %T): %w" , req . Method , rp . Interface (), err ))
325- stats . Record ( ctx , metrics . RPCRequestError . M ( 1 ) )
326- return
327- }
328- rp = rp . Elem ( )
329- } else {
330- var err error
331- rp , err = dec ( ctx , req . Params [ i ]. data )
332- if err != nil {
333- rpcError (w , & req , rpcParseError , xerrors .Errorf ("decoding params for '%s' (param : %d; custom decoder): %w " , req .Method , i , err ))
334- stats .Record (ctx , metrics .RPCRequestError .M (1 ))
335- return
336- }
332+ if handler .hasRawParams {
333+ // When hasRawParams is true, there is only one parameter and it is a
334+ // json.RawMessage.
335+
336+ callParams [ 1 + handler . hasCtx ] = reflect . ValueOf ( RawParams ( req . Params ))
337+ } else {
338+ // "normal" param list; no good way to do named params in Golang
339+
340+ var ps [] param
341+ err := json . Unmarshal ( req . Params , & ps )
342+ if err != nil {
343+ rpcError ( w , & req , rpcParseError , xerrors . Errorf ( "unmarshaling param array: %w" , err ))
344+ stats . Record ( ctx , metrics . RPCRequestError . M ( 1 ) )
345+ return
346+ }
347+
348+ if len ( ps ) != handler . nParams {
349+ rpcError (w , & req , rpcInvalidParams , fmt .Errorf ("wrong param count (method '%s') : %d != %d " , req .Method , len ( ps ), handler . nParams ))
350+ stats .Record (ctx , metrics .RPCRequestError .M (1 ))
351+ done ( false )
352+ return
337353 }
338354
339- callParams [i + 1 + handler .hasCtx ] = reflect .ValueOf (rp .Interface ())
355+ for i := 0 ; i < handler .nParams ; i ++ {
356+ var rp reflect.Value
357+
358+ typ := handler .paramReceivers [i ]
359+ dec , found := s .paramDecoders [typ ]
360+ if ! found {
361+ rp = reflect .New (typ )
362+ if err := json .NewDecoder (bytes .NewReader (ps [i ].data )).Decode (rp .Interface ()); err != nil {
363+ rpcError (w , & req , rpcParseError , xerrors .Errorf ("unmarshaling params for '%s' (param: %T): %w" , req .Method , rp .Interface (), err ))
364+ stats .Record (ctx , metrics .RPCRequestError .M (1 ))
365+ return
366+ }
367+ rp = rp .Elem ()
368+ } else {
369+ var err error
370+ rp , err = dec (ctx , ps [i ].data )
371+ if err != nil {
372+ rpcError (w , & req , rpcParseError , xerrors .Errorf ("decoding params for '%s' (param: %d; custom decoder): %w" , req .Method , i , err ))
373+ stats .Record (ctx , metrics .RPCRequestError .M (1 ))
374+ return
375+ }
376+ }
377+
378+ callParams [i + 1 + handler .hasCtx ] = reflect .ValueOf (rp .Interface ())
379+ }
340380 }
341381
342382 // /////////////////
0 commit comments