Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
JsonRpcClient.cs
1using System;
4using System.Security.Cryptography.X509Certificates;
5using System.Text;
6using System.Threading.Tasks;
7using Waher.Content;
12using Waher.Security;
13
15{
20 {
21 private X509Certificate? certificate;
22 private readonly Uri endpoint;
23 private long id;
24
30 public JsonRpcClient(string Endpoint, params ISniffer[] Sniffers)
31 : this(new Uri(Endpoint), null, Sniffers)
32 {
33 }
34
40 public JsonRpcClient(Uri Endpoint, params ISniffer[] Sniffers)
41 : this(Endpoint, null, Sniffers)
42 {
43 this.endpoint = Endpoint;
44 }
45
52 public JsonRpcClient(string Endpoint, X509Certificate? Certificate, params ISniffer[] Sniffers)
53 : this(new Uri(Endpoint), Certificate, Sniffers)
54 {
55 }
56
63 public JsonRpcClient(Uri Endpoint, X509Certificate? Certificate, params ISniffer[] Sniffers)
64 : base(true, Sniffers)
65 {
66 this.endpoint = Endpoint;
67 this.certificate = Certificate;
68 }
69
74 public void UpdateCertificate(X509Certificate Certificate)
75 {
76 this.certificate = Certificate;
77 }
78
85 public Task<object?> Request(string Method, Dictionary<string, object> Parameters)
86 {
87 return this.Request(JsonRpcHttpMethod.POST, JsonRpcVersion.JsonRpcV2,
88 Method, Parameters);
89 }
90
96 public Task<object?> Request(string Method)
97 {
98 return this.Request(JsonRpcHttpMethod.POST, JsonRpcVersion.JsonRpcV2, Method);
99 }
100
107 public Task<object?> Request(string Method, IEnumerable Parameters)
108 {
109 return this.Request(JsonRpcHttpMethod.POST, JsonRpcVersion.JsonRpcV2,
110 Method, Parameters);
111 }
112
120 public Task<object?> Request(JsonRpcHttpMethod HttpMethod, string Method,
121 Dictionary<string, object> Parameters)
122 {
123 return this.Request(HttpMethod, JsonRpcVersion.JsonRpcV2, Method, Parameters);
124 }
125
132 public Task<object?> Request(JsonRpcHttpMethod HttpMethod, string Method)
133 {
134 return this.Request(HttpMethod, JsonRpcVersion.JsonRpcV2, Method);
135 }
136
144 public Task<object?> Request(JsonRpcHttpMethod HttpMethod, string Method,
145 IEnumerable Parameters)
146 {
147 return this.Request(HttpMethod, JsonRpcVersion.JsonRpcV2, Method, Parameters);
148 }
149
158 public Task<object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version,
159 string Method, Dictionary<string, object> Parameters)
160 {
161 return this.Request(HttpMethod, new JsonRpcRequest(Version, Method, Parameters));
162 }
163
171 public Task<object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version,
172 string Method)
173 {
174 return this.Request(HttpMethod, new JsonRpcRequest(Version, Method));
175 }
176
185 public Task<object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version,
186 string Method, IEnumerable Parameters)
187 {
188 return this.Request(HttpMethod, new JsonRpcRequest(Version, Method, Parameters));
189 }
190
196 public Task<object?> Request(JsonRpcRequest Request)
197 {
198 return this.Request(JsonRpcHttpMethod.POST, Request);
199 }
200
207 public async Task<object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcRequest Request)
208 {
209 Dictionary<string, object> Payload = Request.BuildRequest(this.id++);
210 ContentResponse Response = await this.SendNotification(HttpMethod, Payload);
211 Response.AssertOk();
212
213 if (!(Response.Decoded is Dictionary<string, object> JsonResponse))
214 {
215 throw new JsonRpcException("Unexpected response received of type " +
216 Response.Decoded?.GetType().FullName);
217 }
218
219 JsonRpcResult Result = new JsonRpcResult(JsonResponse);
220
221 if (!Result.HasResult)
222 throw new JsonRpcException("Unexpected response received: Result property missing.");
223
224 if (!Result.Id.HasValue)
225 throw new JsonRpcException("Unexpected response received: Id property missing.");
226
227 if (Payload.TryGetValue("id", out object PrevIdObj) &&
228 PrevIdObj is long PrevId &&
229 PrevId != Result.Id.Value)
230 {
231 throw new JsonRpcException("Response did not match request.");
232 }
233
234 return Result.Result;
235 }
236
242 public Task Notify(string Method, Dictionary<string, object> Parameters)
243 {
244 return this.Notify(JsonRpcHttpMethod.POST, JsonRpcVersion.JsonRpcV2,
245 Method, Parameters);
246 }
247
252 public Task Notify(string Method)
253 {
254 return this.Notify(JsonRpcHttpMethod.POST, JsonRpcVersion.JsonRpcV2, Method);
255 }
256
262 public Task Notify(string Method, IEnumerable Parameters)
263 {
264 return this.Notify(JsonRpcHttpMethod.POST, JsonRpcVersion.JsonRpcV2,
265 Method, Parameters);
266 }
267
274 public Task Notify(JsonRpcHttpMethod HttpMethod, string Method,
275 Dictionary<string, object> Parameters)
276 {
277 return this.Notify(HttpMethod, JsonRpcVersion.JsonRpcV2, Method, Parameters);
278 }
279
285 public Task Notify(JsonRpcHttpMethod HttpMethod, string Method)
286 {
287 return this.Notify(HttpMethod, JsonRpcVersion.JsonRpcV2, Method);
288 }
289
296 public Task Notify(JsonRpcHttpMethod HttpMethod, string Method,
297 IEnumerable Parameters)
298 {
299 return this.Notify(HttpMethod, JsonRpcVersion.JsonRpcV2, Method, Parameters);
300 }
301
309 public Task Notify(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version,
310 string Method, Dictionary<string, object> Parameters)
311 {
312 return this.Notify(HttpMethod, new JsonRpcRequest(Version, Method, Parameters));
313 }
314
321 public Task Notify(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version,
322 string Method)
323 {
324 return this.Notify(HttpMethod, new JsonRpcRequest(Version, Method));
325 }
326
334 public Task Notify(JsonRpcHttpMethod HttpMethod,
335 JsonRpcVersion Version, string Method, IEnumerable Parameters)
336 {
337 return this.Notify(HttpMethod, new JsonRpcRequest(Version, Method, Parameters));
338 }
339
345 {
346 return this.Notify(JsonRpcHttpMethod.POST, Request);
347 }
348
354 public async Task Notify(JsonRpcHttpMethod HttpMethod, JsonRpcRequest Request)
355 {
356 ContentResponse Response = await this.SendNotification(HttpMethod, Request.BuildRequest(null));
357 Response.AssertOk();
358 }
359
360 private async Task<ContentResponse> SendNotification(JsonRpcHttpMethod HttpMethod,
361 Dictionary<string, object> Payload)
362 {
363 if (this.HasSniffers)
364 this.TransmitText(JSON.Encode(Payload, true));
365
366 ContentResponse Response;
367
368 if (HttpMethod == JsonRpcHttpMethod.GET)
369 {
370 StringBuilder sb = new StringBuilder();
371 bool First = true;
372
373 sb.Append(this.endpoint.OriginalString);
374
375 foreach (KeyValuePair<string, object> P in Payload)
376 {
377 if (First)
378 {
379 First = false;
380 sb.Append('?');
381 }
382 else
383 sb.Append('&');
384
385 sb.Append(P.Key);
386 sb.Append('=');
387
388 if (P.Value is string s)
389 sb.Append(System.Net.WebUtility.UrlEncode(s));
390 else
391 sb.Append(System.Net.WebUtility.UrlEncode(JSON.Encode(P.Value, false)));
392 }
393
394 Response = await InternetContent.GetAsync(
395 new Uri(sb.ToString()), this.certificate,
396 new KeyValuePair<string, string>("Accept", JsonCodec.JsonRpcContentType));
397 }
398 else
399 {
400 Response = await InternetContent.PostAsync(
401 this.endpoint, Payload, this.certificate,
402 new KeyValuePair<string, string>("Accept", JsonCodec.JsonRpcContentType));
403 }
404
405 if (Response.HasError)
406 {
407 if (!(Response.Error is WebException ex) || ex.Content is null)
408 {
409 if (this.HasSniffers)
410 this.Error(Response.Error.Message);
411 }
412 else
413 {
414 if (this.HasSniffers)
415 this.Error(JSON.Encode(ex.Content, true));
416
417 Response = this.CheckJsonRpcError(Response);
418 }
419 }
420 else if (this.HasSniffers)
421 this.ReceiveText(JSON.Encode(Response.Decoded, true));
422
423 return Response;
424 }
425
426 private ContentResponse CheckJsonRpcError(ContentResponse Response)
427 {
428 if (Response.HasError &&
429 Response.Error is WebException ex &&
430 ex.Content is Dictionary<string, object> Result &&
431 Result.TryGetValue("error", out object ErrorObj) &&
432 ErrorObj is Dictionary<string, object> Error &&
433 Error.TryGetValue("code", out object Obj) && Obj is int ErrorCode &&
434 Error.TryGetValue("message", out Obj) && Obj is string ErrorMessage)
435 {
436 return new ContentResponse(GetError(ErrorCode, ErrorMessage));
437 }
438 else
439 return Response;
440 }
441
442 internal static JsonRpcError GetError(int ErrorCode, string ErrorMessage)
443 {
444 switch (ErrorCode)
445 {
446 case -32700:
447 return new JsonRpcParseError(ErrorCode, ErrorMessage, null);
448
449 case -32600:
450 return new JsonRpcInvalidRequestError(ErrorCode, ErrorMessage, null);
451
452 case -32601:
453 return new JsonRpcMethodNotFoundError(ErrorCode, ErrorMessage, null);
454
455 case -32602:
456 return new JsonRpcInvalidParametersError(ErrorCode, ErrorMessage, null);
457
458 case -32603:
459 return new JsonRpcInternalError(ErrorCode, ErrorMessage, null);
460
461 default:
462 if (ErrorCode <= -32000 && ErrorCode >= -32099)
463 return new JsonRpcServerError(ErrorCode, ErrorMessage, null);
464 else
465 return new JsonRpcError(ErrorCode, ErrorMessage, null);
466 }
467 }
468
475 public async Task<JsonRpcResult[]> BatchProcess(params JsonRpcRequest[] Requests)
476 {
477 int i, c = Requests.Length;
478 Dictionary<string, object>[] Payloads = new Dictionary<string, object>[c];
479
480 for (i = 0; i < c; i++)
481 {
482 JsonRpcRequest Request = Requests[i];
483 Payloads[i] = Request.BuildRequest(Request.IsRequest ? this.id++ : (long?)null);
484 }
485
486 if (this.HasSniffers)
487 this.TransmitText(JSON.Encode(Payloads, true));
488
490 this.endpoint, Payloads, this.certificate,
491 new KeyValuePair<string, string>("Accept", JsonCodec.JsonRpcContentType));
492
493 if (this.HasSniffers)
494 {
495 if (Response.HasError)
496 {
497 if (!(Response.Error is WebException ex) || ex.Content is null)
498 this.Error(Response.Error.Message);
499 else
500 this.Error(JSON.Encode(ex.Content, true));
501 }
502 else
503 this.ReceiveText(JSON.Encode(Response.Decoded, true));
504 }
505
506 Response = this.CheckJsonRpcError(Response);
507 Response.AssertOk();
508
509 if (!(Response.Decoded is Array A))
510 {
511 throw new JsonRpcException("Unexpected response received of type " +
512 Response.Decoded?.GetType().FullName);
513 }
514
515 c = A.Length;
516 JsonRpcResult[] Results = new JsonRpcResult[c];
517
518 for (i = 0; i < c; i++)
519 {
520 if (!(A.GetValue(i) is Dictionary<string, object> ItemResponse))
521 {
522 throw new JsonRpcException("Unexpected item response received of type " +
523 A.GetValue(i)?.GetType().FullName);
524 }
525
526 Results[i] = new JsonRpcResult(ItemResponse);
527 }
528
529 return Results;
530 }
531 }
532}
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
void AssertOk()
Asserts response is OK.
Exception class for web exceptions.
Definition: WebException.cs:11
object Content
Decoded content.
Definition: WebException.cs:55
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
static Task< ContentResponse > GetAsync(Uri Uri, params KeyValuePair< string, string >[] Headers)
Gets a resource, given its URI.
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:537
const string JsonRpcContentType
application/json-rpc
Definition: JsonCodec.cs:25
Simple base class for classes implementing communication protocols.
void TransmitText(string Text)
Called when text has been transmitted.
void ReceiveText(string Text)
Called when text has been received.
ISniffer[] Sniffers
Registered sniffers.
bool HasSniffers
If there are sniffers registered on the object.
void Error(string Error)
Called to inform the viewer of an error state.
Base class for JSON-RPC error responses.
Definition: JsonRpcError.cs:7
async Task< JsonRpcResult[]> BatchProcess(params JsonRpcRequest[] Requests)
Processes a batch of requests.
Task Notify(JsonRpcHttpMethod HttpMethod, string Method, Dictionary< string, object > Parameters)
Sends a JSON-RPC notification.
Task Notify(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version, string Method)
Sends a JSON-RPC notification.
Task< object?> Request(JsonRpcHttpMethod HttpMethod, string Method)
Performs a JSON-RPC request.
Task< object?> Request(string Method)
Performs a JSON-RPC request.
Task Notify(JsonRpcHttpMethod HttpMethod, string Method, IEnumerable Parameters)
Sends a JSON-RPC notification.
Task Notify(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version, string Method, Dictionary< string, object > Parameters)
Sends a JSON-RPC notification.
Task< object?> Request(JsonRpcRequest Request)
Performs a JSON-RPC request.
JsonRpcClient(Uri Endpoint, params ISniffer[] Sniffers)
A JSON-RPC client
Task< object?> Request(JsonRpcHttpMethod HttpMethod, string Method, IEnumerable Parameters)
Performs a JSON-RPC request.
Task Notify(string Method, Dictionary< string, object > Parameters)
Sends a JSON-RPC notification.
Task< object?> Request(string Method, IEnumerable Parameters)
Performs a JSON-RPC request.
async Task Notify(JsonRpcHttpMethod HttpMethod, JsonRpcRequest Request)
Sends a JSON-RPC notification.
JsonRpcClient(string Endpoint, params ISniffer[] Sniffers)
A JSON-RPC client
Task< object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version, string Method)
Performs a JSON-RPC request.
Task Notify(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version, string Method, IEnumerable Parameters)
Sends a JSON-RPC notification.
Task Notify(JsonRpcRequest Request)
Sends a JSON-RPC notification.
JsonRpcClient(Uri Endpoint, X509Certificate? Certificate, params ISniffer[] Sniffers)
A JSON-RPC client
async Task< object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcRequest Request)
Performs a JSON-RPC request.
Task< object?> Request(JsonRpcHttpMethod HttpMethod, string Method, Dictionary< string, object > Parameters)
Performs a JSON-RPC request.
Task Notify(string Method)
Sends a JSON-RPC notification.
Task< object?> Request(string Method, Dictionary< string, object > Parameters)
Performs a JSON-RPC request.
void UpdateCertificate(X509Certificate Certificate)
Updates the certificate used in mTLS negotiation.
Task< object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version, string Method, IEnumerable Parameters)
Performs a JSON-RPC request.
Task< object?> Request(JsonRpcHttpMethod HttpMethod, JsonRpcVersion Version, string Method, Dictionary< string, object > Parameters)
Performs a JSON-RPC request.
Task Notify(JsonRpcHttpMethod HttpMethod, string Method)
Sends a JSON-RPC notification.
JsonRpcClient(string Endpoint, X509Certificate? Certificate, params ISniffer[] Sniffers)
A JSON-RPC client
Task Notify(string Method, IEnumerable Parameters)
Sends a JSON-RPC notification.
Information about a JSON-RPC request.
Information about a JSON-RPC result.
object? Result
Result of operation, if HasResult is true.
long? Id
ID matching the request, if any.
bool HasResult
If response has a result in Result, this property is true.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:10
Interface for Mutual TLS (mTLS) Clients or TLS servers.
JsonRpcHttpMethod
HTTP method to use when making a JSON-RPC call.
JsonRpcVersion
JSON-RPC version