@@ -39,10 +39,46 @@ pub struct AppState<R> {
3939 pub logger : Logger ,
4040}
4141
42+ /// Dispatch a single JSON-RPC request to the appropriate method handler.
43+ ///
44+ /// Returns `None` for notifications (requests without an id), since the
45+ /// JSON-RPC 2.0 spec says notifications must not produce a response.
46+ async fn dispatch_request < R : SubgraphRegistrar > (
47+ state : & Arc < AppState < R > > ,
48+ request : JsonRpcRequest ,
49+ ) -> Option < JsonRpcResponse > {
50+ if !request. is_valid_version ( ) {
51+ return Some ( JsonRpcResponse :: invalid_request ( ) ) ;
52+ }
53+
54+ let id = request. id . clone ( ) . unwrap_or ( JsonRpcId :: Null ) ;
55+
56+ // Notifications (no id) should not produce a response per JSON-RPC 2.0 spec.
57+ // But since all our methods have side effects, we still execute them;
58+ // we just don't return the response.
59+ let is_notification = request. id . is_none ( ) ;
60+
61+ let response = match request. method . as_str ( ) {
62+ "subgraph_create" => handle_create ( state, & request, id) . await ,
63+ "subgraph_deploy" => handle_deploy ( state, & request, id) . await ,
64+ "subgraph_remove" => handle_remove ( state, & request, id) . await ,
65+ "subgraph_reassign" => handle_reassign ( state, & request, id) . await ,
66+ "subgraph_pause" => handle_pause ( state, & request, id) . await ,
67+ "subgraph_resume" => handle_resume ( state, & request, id) . await ,
68+ _ => JsonRpcResponse :: error ( id, JsonRpcError :: method_not_found ( ) ) ,
69+ } ;
70+
71+ if is_notification {
72+ None
73+ } else {
74+ Some ( response)
75+ }
76+ }
77+
4278/// Main JSON-RPC request handler.
4379///
44- /// Processes incoming JSON-RPC requests, dispatches to the appropriate method handler,
45- /// and returns the response .
80+ /// Supports both single requests and batch requests (JSON arrays).
81+ /// Per JSON-RPC 2.0 spec, an empty batch array returns an invalid request error .
4682pub async fn jsonrpc_handler < R : SubgraphRegistrar > (
4783 State ( state) : State < Arc < AppState < R > > > ,
4884 ConnectInfo ( remote_addr) : ConnectInfo < SocketAddr > ,
@@ -56,45 +92,64 @@ pub async fn jsonrpc_handler<R: SubgraphRegistrar>(
5692 . unwrap_or ( "unset" )
5793 }
5894
59- // Parse the JSON-RPC request
95+ let log_request = |method : & str , params : & Option < JsonValue > | {
96+ info ! (
97+ & state. logger,
98+ "JSON-RPC call" ;
99+ "method" => method,
100+ "params" => ?params,
101+ "remote_addr" => %remote_addr,
102+ "x_forwarded_for" => header( & headers, "x-forwarded-for" ) ,
103+ "x_real_ip" => header( & headers, "x-real-ip" ) ,
104+ "x_forwarded_proto" => header( & headers, "x-forwarded-proto" )
105+ ) ;
106+ } ;
107+
108+ // Try batch (JSON array) first, then single request.
109+ if let Ok ( requests) = serde_json:: from_str :: < Vec < JsonRpcRequest > > ( & body) {
110+ if requests. is_empty ( ) {
111+ let resp = serde_json:: to_value ( JsonRpcResponse :: invalid_request ( ) )
112+ . expect ( "failed to serialize response" ) ;
113+ return ( StatusCode :: OK , Json ( resp) ) ;
114+ }
115+
116+ info ! (
117+ & state. logger,
118+ "JSON-RPC batch request" ;
119+ "batch_size" => requests. len( ) ,
120+ "remote_addr" => %remote_addr,
121+ ) ;
122+
123+ let mut responses = Vec :: new ( ) ;
124+ for request in requests {
125+ log_request ( & request. method , & request. params ) ;
126+ if let Some ( resp) = dispatch_request ( & state, request) . await {
127+ responses. push ( resp) ;
128+ }
129+ }
130+
131+ let value = serde_json:: to_value ( responses) . expect ( "failed to serialize responses" ) ;
132+ return ( StatusCode :: OK , Json ( value) ) ;
133+ }
134+
135+ // Single request
60136 let request: JsonRpcRequest = match serde_json:: from_str ( & body) {
61137 Ok ( req) => req,
62138 Err ( _) => {
63- return ( StatusCode :: OK , Json ( JsonRpcResponse :: parse_error ( ) ) ) ;
139+ let resp = serde_json:: to_value ( JsonRpcResponse :: parse_error ( ) )
140+ . expect ( "failed to serialize response" ) ;
141+ return ( StatusCode :: OK , Json ( resp) ) ;
64142 }
65143 } ;
66144
67- // Validate JSON-RPC version
68- if !request. is_valid_version ( ) {
69- return ( StatusCode :: OK , Json ( JsonRpcResponse :: invalid_request ( ) ) ) ;
70- }
71-
72- let id = request. id . clone ( ) . unwrap_or ( JsonRpcId :: Null ) ;
73-
74- // Log the method call
75- info ! (
76- & state. logger,
77- "JSON-RPC call" ;
78- "method" => & request. method,
79- "params" => ?request. params,
80- "remote_addr" => %remote_addr,
81- "x_forwarded_for" => header( & headers, "x-forwarded-for" ) ,
82- "x_real_ip" => header( & headers, "x-real-ip" ) ,
83- "x_forwarded_proto" => header( & headers, "x-forwarded-proto" )
84- ) ;
145+ log_request ( & request. method , & request. params ) ;
85146
86- // Dispatch to the appropriate handler
87- let response = match request. method . as_str ( ) {
88- "subgraph_create" => handle_create ( & state, & request, id. clone ( ) ) . await ,
89- "subgraph_deploy" => handle_deploy ( & state, & request, id. clone ( ) ) . await ,
90- "subgraph_remove" => handle_remove ( & state, & request, id. clone ( ) ) . await ,
91- "subgraph_reassign" => handle_reassign ( & state, & request, id. clone ( ) ) . await ,
92- "subgraph_pause" => handle_pause ( & state, & request, id. clone ( ) ) . await ,
93- "subgraph_resume" => handle_resume ( & state, & request, id. clone ( ) ) . await ,
94- _ => JsonRpcResponse :: error ( id, JsonRpcError :: method_not_found ( ) ) ,
95- } ;
147+ let response = dispatch_request ( & state, request)
148+ . await
149+ . unwrap_or_else ( || JsonRpcResponse :: success ( JsonRpcId :: Null , JsonValue :: Null ) ) ;
96150
97- ( StatusCode :: OK , Json ( response) )
151+ let value = serde_json:: to_value ( response) . expect ( "failed to serialize response" ) ;
152+ ( StatusCode :: OK , Json ( value) )
98153}
99154
100155/// Parse parameters from a JSON-RPC request.
0 commit comments