@@ -39,10 +39,15 @@ use hyper::{
39
39
use jsonrpsee_types:: error:: { Error , GenericTransportError } ;
40
40
use jsonrpsee_types:: v2:: request:: { JsonRpcInvalidRequest , JsonRpcRequest } ;
41
41
use 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
+ } ;
43
46
use serde:: Serialize ;
47
+ use serde_json:: value:: RawValue ;
44
48
use socket2:: { Domain , Socket , Type } ;
45
49
use std:: {
50
+ cmp,
46
51
net:: { SocketAddr , TcpListener } ,
47
52
sync:: Arc ,
48
53
} ;
@@ -153,6 +158,30 @@ impl Server {
153
158
Ok :: < _ , HyperError > ( service_fn ( move |request| {
154
159
let methods = methods. clone ( ) ;
155
160
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.
156
185
async move {
157
186
if let Err ( e) = access_control_is_valid ( & access_control, & request) {
158
187
return Ok :: < _ , HyperError > ( e) ;
@@ -175,31 +204,48 @@ impl Server {
175
204
176
205
// NOTE(niklasad1): it's a channel because it's needed for batch requests.
177
206
let ( tx, mut rx) = mpsc:: unbounded ( ) ;
207
+ // Is this a single request or a batch (or error)?
208
+ let mut single = true ;
178
209
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) ;
190
225
}
226
+ } else {
227
+ send_error ( None , & tx, JsonRpcErrorCode :: InvalidRequest . into ( ) ) ;
191
228
}
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
199
247
} ;
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 ) ] ) ;
203
249
Ok :: < _ , HyperError > ( response:: ok_response ( response) )
204
250
}
205
251
} ) )
@@ -211,6 +257,24 @@ impl Server {
211
257
}
212
258
}
213
259
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
+
214
278
// Checks to that access control of the received request is the same as configured.
215
279
fn access_control_is_valid (
216
280
access_control : & AccessControl ,
0 commit comments