Line data Source code
1 : import 'dart:async';
2 : import 'dart:convert';
3 :
4 : import 'package:http/http.dart';
5 :
6 : // ignore: one_member_abstracts
7 :
8 : /// RPC Service base class.
9 : abstract class RpcService {
10 : /// Constructor.
11 4 : RpcService(this.url);
12 :
13 : /// Url.
14 : final String url;
15 :
16 : /// Performs an RPC request, asking the server to execute the function with
17 : /// the given name and the associated parameters, which need to be encodable
18 : /// with the [json] class of dart:convert.
19 : ///
20 : /// When the request is successful, an [RPCResponse] with the request id and
21 : /// the data from the server will be returned. If not, an RPCError will be
22 : /// thrown. Other errors might be thrown if an IO-Error occurs.
23 : Future<RPCResponse> call(String function, [List<dynamic>? params]);
24 : }
25 :
26 : /// Json RPC Service.
27 : class JsonRPC extends RpcService {
28 : /// Constructor.
29 8 : JsonRPC(String url, this.client) : super(url);
30 :
31 : /// Http client.
32 : final Client client;
33 :
34 : int _currentRequestId = 1;
35 :
36 : /// Performs an RPC request, asking the server to execute the function with
37 : /// the given name and the associated parameters, which need to be encodable
38 : /// with the [json] class of dart:convert.
39 : ///
40 : /// When the request is successful, an [RPCResponse] with the request id and
41 : /// the data from the server will be returned. If not, an RPCError will be
42 : /// thrown. Other errors might be thrown if an IO-Error occurs.
43 4 : @override
44 : Future<RPCResponse> call(String function, [List<dynamic>? params]) async {
45 4 : params ??= [];
46 :
47 4 : final requestPayload = {
48 : 'jsonrpc': '2.0',
49 : 'method': function,
50 : 'params': params,
51 8 : 'id': _currentRequestId++,
52 : };
53 :
54 8 : final response = await client.post(
55 8 : Uri.parse(url),
56 4 : headers: {'Content-Type': 'application/json'},
57 4 : body: json.encode(requestPayload),
58 : );
59 :
60 8 : if (response.statusCode != 200) {
61 6 : throw HttpError(response.statusCode, response.body);
62 : }
63 :
64 8 : final data = json.decode(response.body) as Map<String, dynamic>;
65 :
66 4 : if (data.containsKey('error')) {
67 1 : final error = data['error'];
68 :
69 1 : final code = error['code'] as int;
70 1 : final message = error['message'] as String;
71 1 : final errorData = error['data'];
72 :
73 1 : throw RPCError(code, message, errorData);
74 : }
75 :
76 4 : final id_d = data['id'];
77 4 : final id = id_d is int ? id_d : int.tryParse(id_d as String);
78 4 : final result = data['result'];
79 4 : return RPCResponse(id ?? -1, result);
80 : }
81 : }
82 :
83 : /// Response from the server to an rpc request. Contains the id of the request
84 : /// and the corresponding result as sent by the server.
85 : class RPCResponse {
86 : /// Constructor.
87 5 : const RPCResponse(this.id, this.result);
88 :
89 : /// Id.
90 : final int id;
91 :
92 : /// Result.
93 : final dynamic result;
94 : }
95 :
96 : /// Exception thrown when an the server returns an error code to an rpc request.
97 : class RPCError implements Exception {
98 : /// Constructor.
99 1 : const RPCError(this.errorCode, this.message, this.data);
100 :
101 : /// Error code.
102 : final int errorCode;
103 :
104 : /// Message.
105 : final String message;
106 :
107 : /// Data.
108 : final dynamic data;
109 :
110 1 : @override
111 : String toString() {
112 3 : return 'RPCError: got code $errorCode with msg "$message".';
113 : }
114 : }
115 :
116 : class HttpError implements Exception {
117 : /// Constructor.
118 2 : const HttpError(this.statusCode, this.message);
119 :
120 : /// Status code.
121 : final int statusCode;
122 :
123 : /// Message.
124 : final String message;
125 :
126 2 : @override
127 : String toString() {
128 6 : return 'HttpError: got code $statusCode with msg "$message".';
129 : }
130 : }
|