@@ -39,10 +39,15 @@ use hyper::{
3939use jsonrpsee_types:: error:: { Error , GenericTransportError } ;
4040use jsonrpsee_types:: v2:: request:: { JsonRpcInvalidRequest , JsonRpcRequest } ;
4141use jsonrpsee_types:: v2:: { error:: JsonRpcErrorCode , params:: RpcParams } ;
42- use jsonrpsee_utils:: { hyper_helpers:: read_response_to_body, server:: send_error} ;
42+ use jsonrpsee_utils:: {
43+ hyper_helpers:: read_response_to_body,
44+ server:: { send_error, RpcSender } ,
45+ } ;
4346use serde:: Serialize ;
47+ use serde_json:: value:: RawValue ;
4448use socket2:: { Domain , Socket , Type } ;
4549use std:: {
50+ cmp,
4651 net:: { SocketAddr , TcpListener } ,
4752 sync:: Arc ,
4853} ;
@@ -153,6 +158,30 @@ impl Server {
153158 Ok :: < _ , HyperError > ( service_fn ( move |request| {
154159 let methods = methods. clone ( ) ;
155160 let access_control = access_control. clone ( ) ;
161+
162+ // Look up the "method" (i.e. function pointer) from the registered methods and run it passing in
163+ // the params from the request. The result of the computation is sent back over the `tx` channel and
164+ // the result(s) are collected into a `String` and sent back over the wire.
165+ let execute =
166+ move |id : Option < & RawValue > , tx : RpcSender , method_name : & str , params : Option < & RawValue > | {
167+ if let Some ( method) = methods. get ( method_name) {
168+ let params = RpcParams :: new ( params. map ( |params| params. get ( ) ) ) ;
169+ // NOTE(niklasad1): connection ID is unused thus hardcoded to `0`.
170+ if let Err ( err) = ( method) ( id, params, & tx, 0 ) {
171+ log:: error!(
172+ "execution of method call '{}' failed: {:?}, request id={:?}" ,
173+ method_name,
174+ err,
175+ id
176+ ) ;
177+ }
178+ } else {
179+ send_error ( id, tx, JsonRpcErrorCode :: MethodNotFound . into ( ) ) ;
180+ }
181+ } ;
182+
183+ // Run some validation on the http request, then read the body and try to deserialize it into one of
184+ // two cases: a single RPC request or a batch of RPC requests.
156185 async move {
157186 if let Err ( e) = access_control_is_valid ( & access_control, & request) {
158187 return Ok :: < _ , HyperError > ( e) ;
@@ -175,31 +204,48 @@ impl Server {
175204
176205 // NOTE(niklasad1): it's a channel because it's needed for batch requests.
177206 let ( tx, mut rx) = mpsc:: unbounded ( ) ;
207+ // Is this a single request or a batch (or error)?
208+ let mut single = true ;
178209
179- match serde_json:: from_slice :: < JsonRpcRequest > ( & body) {
180- Ok ( req) => {
181- log:: debug!( "recv: {:?}" , req) ;
182- let params = RpcParams :: new ( req. params . map ( |params| params. get ( ) ) ) ;
183- if let Some ( method) = methods. get ( & * req. method ) {
184- // NOTE(niklasad1): connection ID is unused thus hardcoded to `0`.
185- if let Err ( err) = ( method) ( req. id , params, & tx, 0 ) {
186- log:: error!( "method_call: {} failed: {:?}" , req. method, err) ;
187- }
188- } else {
189- send_error ( req. id , & tx, JsonRpcErrorCode :: MethodNotFound . into ( ) ) ;
210+ // For reasons outlined [here](https://github.com/serde-rs/json/issues/497), `RawValue` can't be
211+ // used with untagged enums at the moment. This means we can't use an `SingleOrBatch` untagged
212+ // enum here and have to try each case individually: first the single request case, then the
213+ // batch case and lastly the error. For the worst case – unparseable input – we make three calls
214+ // to [`serde_json::from_slice`] which is pretty annoying.
215+ // Our [issue](https://github.com/paritytech/jsonrpsee/issues/296).
216+ if let Ok ( JsonRpcRequest { id, method : method_name, params, .. } ) =
217+ serde_json:: from_slice :: < JsonRpcRequest > ( & body)
218+ {
219+ execute ( id, & tx, & method_name, params) ;
220+ } else if let Ok ( batch) = serde_json:: from_slice :: < Vec < JsonRpcRequest > > ( & body) {
221+ if !batch. is_empty ( ) {
222+ single = false ;
223+ for JsonRpcRequest { id, method : method_name, params, .. } in batch {
224+ execute ( id, & tx, & method_name, params) ;
190225 }
226+ } else {
227+ send_error ( None , & tx, JsonRpcErrorCode :: InvalidRequest . into ( ) ) ;
191228 }
192- Err ( _e) => {
193- let ( id, code) = match serde_json:: from_slice :: < JsonRpcInvalidRequest > ( & body) {
194- Ok ( req) => ( req. id , JsonRpcErrorCode :: InvalidRequest ) ,
195- Err ( _) => ( None , JsonRpcErrorCode :: ParseError ) ,
196- } ;
197- send_error ( id, & tx, code. into ( ) ) ;
198- }
229+ } else {
230+ log:: error!(
231+ "[service_fn], Cannot parse request body={:?}" ,
232+ String :: from_utf8_lossy( & body[ ..cmp:: min( body. len( ) , 1024 ) ] )
233+ ) ;
234+ let ( id, code) = match serde_json:: from_slice :: < JsonRpcInvalidRequest > ( & body) {
235+ Ok ( req) => ( req. id , JsonRpcErrorCode :: InvalidRequest ) ,
236+ Err ( _) => ( None , JsonRpcErrorCode :: ParseError ) ,
237+ } ;
238+ send_error ( id, & tx, code. into ( ) ) ;
239+ }
240+ // Closes the receiving half of a channel without dropping it. This prevents any further
241+ // messages from being sent on the channel.
242+ rx. close ( ) ;
243+ let response = if single {
244+ rx. next ( ) . await . expect ( "Sender is still alive managed by us above; qed" )
245+ } else {
246+ collect_batch_responses ( rx) . await
199247 } ;
200-
201- let response = rx. next ( ) . await . expect ( "Sender is still alive managed by us above; qed" ) ;
202- log:: debug!( "send: {:?}" , response) ;
248+ log:: debug!( "[service_fn] sending back: {:?}" , & response[ ..cmp:: min( response. len( ) , 1024 ) ] ) ;
203249 Ok :: < _ , HyperError > ( response:: ok_response ( response) )
204250 }
205251 } ) )
@@ -211,6 +257,24 @@ impl Server {
211257 }
212258}
213259
260+ // Collect the results of all computations sent back on the ['Stream'] into a single `String` appropriately wrapped in
261+ // `[`/`]`.
262+ async fn collect_batch_responses ( rx : mpsc:: UnboundedReceiver < String > ) -> String {
263+ let mut buf = String :: with_capacity ( 2048 ) ;
264+ buf. push ( '[' ) ;
265+ let mut buf = rx
266+ . fold ( buf, |mut acc, response| async {
267+ acc = [ acc, response] . concat ( ) ;
268+ acc. push ( ',' ) ;
269+ acc
270+ } )
271+ . await ;
272+ // Remove trailing comma
273+ buf. pop ( ) ;
274+ buf. push ( ']' ) ;
275+ buf
276+ }
277+
214278// Checks to that access control of the received request is the same as configured.
215279fn access_control_is_valid (
216280 access_control : & AccessControl ,
0 commit comments