@@ -58,43 +58,62 @@ struct JsonRpcResponse<T> {
58
58
/// Result returned from the RPC method, if successful.
59
59
result : Option < T > ,
60
60
/// Error object returned by the RPC server, if the call failed.
61
- error : Option < Value > ,
61
+ error : Option < JsonRpcError > ,
62
+ }
63
+
64
+ /// Represents the JSON-RPC response error received from the endpoint
65
+ #[ derive( Deserialize , Debug , thiserror:: Error ) ]
66
+ #[ error( "JsonRpcError code {code}: {message}" ) ]
67
+ pub struct JsonRpcError {
68
+ /// error code
69
+ code : i32 ,
70
+ /// human-readable error message
71
+ message : String ,
72
+ /// data can be any JSON value or omitted
73
+ data : Option < Value > ,
62
74
}
63
75
64
76
/// Represents a JSON-RPC error encountered during a transport operation.
65
- #[ derive( Debug , Clone , Deserialize , Serialize ) ]
77
+ #[ derive( Debug , thiserror :: Error ) ]
66
78
pub enum RpcError {
67
- /// Represents a network-level error, such as connection failures or timeouts.
68
- Network ( String ) ,
69
- /// Indicates that the request could not be encoded properly
70
- Encode ( String ) ,
71
- /// Indicates that the response could not be decoded properly.
72
- Decode ( String ) ,
79
+ // Serde decoding error
80
+ #[ error( "JSON decoding error: {0}" ) ]
81
+ DecodeJson ( serde_json:: Error ) ,
82
+ // Serde encoding error
83
+ #[ error( "JSON encoding error: {0}" ) ]
84
+ EncodeJson ( serde_json:: Error ) ,
85
+ /// Indicates that the response doesn't contain a json payload
86
+ #[ error( "Invalid JSON payload error" ) ]
87
+ InvalidJsonPayload ,
88
+ // RPC Id mismatch between request and response
89
+ #[ error( "Id Mismatch! Request: {0}, Response: {1}" ) ]
90
+ MismatchedId ( String , String ) ,
91
+ // Stacks common network error
92
+ #[ error( "Stacks Net error: {0}" ) ]
93
+ NetworkStacksCommon ( #[ from] stacks_common:: types:: net:: Error ) ,
94
+ // Stacks network lib error
95
+ #[ error( "IO error: {0}" ) ]
96
+ NetworkIO ( #[ from] io:: Error ) ,
97
+ // Stacks lib network error
98
+ #[ error( "Stacks Net error: {0}" ) ]
99
+ NetworkStacksLib ( #[ from] stacks:: net:: Error ) ,
73
100
/// Represents an error returned by the RPC service itself.
74
- Service ( String ) ,
101
+ #[ error( "Service JSON error: {0}" ) ]
102
+ Service ( JsonRpcError ) ,
103
+ // URL missing host error
104
+ #[ error( "URL missing host error: {0}" ) ]
105
+ UrlMissingHost ( Url ) ,
106
+ // URL missing port error
107
+ #[ error( "URL missing port error: {0}" ) ]
108
+ UrlMissingPort ( Url ) ,
109
+ // URL parse error
110
+ #[ error( "URL error: {0}" ) ]
111
+ UrlParse ( #[ from] url:: ParseError ) ,
75
112
}
76
113
77
114
/// Alias for results returned from RPC operations using `RpcTransport`.
78
115
pub type RpcResult < T > = Result < T , RpcError > ;
79
116
80
- impl From < url:: ParseError > for RpcError {
81
- fn from ( e : url:: ParseError ) -> Self {
82
- Self :: Network ( format ! ( "Url Error: {e:?}" ) )
83
- }
84
- }
85
-
86
- impl From < stacks_common:: types:: net:: Error > for RpcError {
87
- fn from ( e : stacks_common:: types:: net:: Error ) -> Self {
88
- Self :: Network ( format ! ( "Net Error: {e:?}" ) )
89
- }
90
- }
91
-
92
- impl From < io:: Error > for RpcError {
93
- fn from ( e : io:: Error ) -> Self {
94
- Self :: Network ( format ! ( "IO Error: {e:?}" ) )
95
- }
96
- }
97
-
98
117
/// Represents supported authentication mechanisms for RPC requests.
99
118
#[ derive( Debug , Clone ) ]
100
119
pub enum RpcAuth {
@@ -136,10 +155,10 @@ impl RpcTransport {
136
155
let url_obj = Url :: parse ( & url) ?;
137
156
let host = url_obj
138
157
. host_str ( )
139
- . ok_or ( RpcError :: Network ( format ! ( "Missing host in url: {url}" ) ) ) ?;
158
+ . ok_or ( RpcError :: UrlMissingHost ( url_obj . clone ( ) ) ) ?;
140
159
let port = url_obj
141
160
. port_or_known_default ( )
142
- . ok_or ( RpcError :: Network ( format ! ( "Missing port in url: {url}" ) ) ) ?;
161
+ . ok_or ( RpcError :: UrlMissingHost ( url_obj . clone ( ) ) ) ?;
143
162
144
163
let peer: PeerHost = format ! ( "{host}:{port}" ) . parse ( ) ?;
145
164
let path = url_obj. path ( ) . to_string ( ) ;
@@ -177,20 +196,15 @@ impl RpcTransport {
177
196
params : Value :: Array ( params) ,
178
197
} ;
179
198
180
- let json_payload = serde_json:: to_value ( payload)
181
- . map_err ( |e| RpcError :: Encode ( format ! ( "Failed to encode request as JSON: {e:?}" ) ) ) ?;
199
+ let json_payload = serde_json:: to_value ( payload) . map_err ( RpcError :: EncodeJson ) ?;
182
200
183
201
let mut request = StacksHttpRequest :: new_for_peer (
184
202
self . peer . clone ( ) ,
185
203
"POST" . to_string ( ) ,
186
204
self . path . clone ( ) ,
187
205
HttpRequestContents :: new ( ) . payload_json ( json_payload) ,
188
- )
189
- . map_err ( |e| {
190
- RpcError :: Encode ( format ! (
191
- "Failed to encode infallible data as HTTP request {e:?}"
192
- ) )
193
- } ) ?;
206
+ ) ?;
207
+
194
208
request. add_header ( "Connection" . into ( ) , "close" . into ( ) ) ;
195
209
196
210
if let Some ( auth_header) = self . auth_header ( ) {
@@ -203,27 +217,24 @@ impl RpcTransport {
203
217
let response = send_http_request ( & host, port, request, self . timeout ) ?;
204
218
let json_response = match response. destruct ( ) . 1 {
205
219
HttpResponsePayload :: JSON ( js) => Ok ( js) ,
206
- _ => Err ( RpcError :: Decode ( "Did not get a JSON response" . to_string ( ) ) ) ,
220
+ _ => Err ( RpcError :: InvalidJsonPayload ) ,
207
221
} ?;
208
222
209
- let parsed_response: JsonRpcResponse < T > = serde_json :: from_value ( json_response )
210
- . map_err ( |e| RpcError :: Decode ( format ! ( "Json Parse Error: {e:?}" ) ) ) ?;
223
+ let parsed_response: JsonRpcResponse < T > =
224
+ serde_json :: from_value ( json_response ) . map_err ( RpcError :: DecodeJson ) ?;
211
225
212
226
if id != parsed_response. id {
213
- return Err ( RpcError :: Decode ( format ! (
214
- "Invalid response: mismatched 'id': expected '{}', got '{}'" ,
215
- id, parsed_response. id
216
- ) ) ) ;
227
+ return Err ( RpcError :: MismatchedId ( id. to_string ( ) , parsed_response. id ) ) ;
217
228
}
218
229
219
230
if let Some ( error) = parsed_response. error {
220
- return Err ( RpcError :: Service ( format ! ( "{:#}" , error) ) ) ;
231
+ return Err ( RpcError :: Service ( error) ) ;
221
232
}
222
233
223
234
if let Some ( result) = parsed_response. result {
224
235
Ok ( result)
225
236
} else {
226
- Ok ( serde_json:: from_value ( Value :: Null ) . unwrap ( ) )
237
+ Ok ( serde_json:: from_value ( Value :: Null ) . map_err ( RpcError :: DecodeJson ) ? )
227
238
}
228
239
}
229
240
0 commit comments