Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
JsonRpcServerRequest.cs
1using System;
3using System.Text;
4using System.Threading.Tasks;
5using Waher.Events;
6using Waher.Script;
9
11{
15 internal class JsonRpcServerRequest : IDisposable
16 {
17 public JsonRpcServerRequest?[]? BatchRequests = null;
18 public Dictionary<string, object?>? ResponseObject = null;
19 public Dictionary<string, object?>[]? ResponseArray = null;
20 public object? Response = null;
21 public Dictionary<string, object?>? ParametersObj = null;
22 public JsonRpcMethodInfo? MethodInfo = null;
23 public Array? ParametersArray = null;
24 public string JsonVersion = string.Empty;
25 public object? Id = null;
26 public int? ErrorCode = null;
27 public int StatusCode = 204;
28 public string StatusMessage = "No Content";
29 public string? ErrorMessage = null;
30 public object? Result = null;
31 public bool IsResult = false;
32 public bool IsError = false;
33
34 public void SetError(int ErrorCode, string ErrorMessage, int StatusCode,
35 string StatusMessage)
36 {
37 this.ErrorCode = ErrorCode;
38 this.ErrorMessage = ErrorMessage;
39 this.StatusCode = StatusCode;
40 this.StatusMessage = StatusMessage;
41 }
42
43 public void Dispose()
44 {
45 if (this.Result is IDisposable Disposable)
46 Disposable.Dispose();
47
48 if (!(this.BatchRequests is null))
49 {
50 int i, c = this.BatchRequests.Length;
51
52 for (i = 0; i < c; i++)
53 this.BatchRequests[i]?.Dispose();
54
55 this.BatchRequests = null;
56 }
57
58 this.Result = null;
59 }
60
68 public async Task<bool> BuildResponse(JsonRpcWebService WebService,
69 HttpRequest HttpRequest, HttpResponse HttpResponse)
70 {
71 bool HasSniffer = HttpRequest.Server.HasSniffers;
72
73 if (this.IsResult)
74 {
75 if (this.Id is null)
76 {
77 this.SetError(-32600, "Missing id attribute.",
78 BadRequestException.Code, BadRequestException.StatusMessage);
79 return false;
80 }
81
82 IJsonRpcClientRequest? Request = WebService.PopClientRequest(this.Id.ToString());
83 if (Request is null)
84 {
85 this.SetError(-32600, "Id attribute not recognized or obsolete.",
86 NotFoundException.Code, NotFoundException.StatusMessage);
87 return false;
88 }
89
90 await Request.ReportResult(this.Result);
91 return false;
92 }
93 else if (this.IsError && !(this.Id is null))
94 {
95 IJsonRpcClientRequest? Request = WebService.PopClientRequest(this.Id.ToString());
96 if (Request is null)
97 {
98 this.SetError(-32600, "Id attribute not recognized or obsolete.",
99 NotFoundException.Code, NotFoundException.StatusMessage);
100 return false;
101 }
102
103 await Request.ReportError(this.ErrorCode, this.ErrorMessage ?? "Unable to perform request.");
104 return false;
105 }
106
107 if (this.BatchRequests is null)
108 {
109 this.ResponseObject = new Dictionary<string, object?>();
110
111 if (!string.IsNullOrEmpty(this.JsonVersion))
112 this.ResponseObject["jsonrpc"] = this.JsonVersion;
113
114 if (!(this.Id is null))
115 this.ResponseObject["id"] = this.Id;
116
117 if (this.MethodInfo is null && !this.ErrorCode.HasValue)
118 {
119 this.SetError(-32600, "Missing method.",
120 BadRequestException.Code, BadRequestException.StatusMessage);
121 }
122
123 if (!this.ErrorCode.HasValue)
124 {
125 try
126 {
127 int i, c = this.MethodInfo!.NrArguments;
128 int NrParametersSet = 0;
129 object?[]? Parameters = null;
130
131 if (!(this.ParametersObj is null))
132 {
133 if (!this.MethodInfo.TryBuildRequest(
134 this.Id?.ToString() ?? string.Empty,
135 this.ParametersObj, null, out string? Reason,
136 out Parameters))
137 {
138 this.SetError(-32602, Reason,
139 BadRequestException.Code, BadRequestException.StatusMessage);
140 }
141 }
142 else if (!(this.ParametersArray is null))
143 {
144 if (this.ParametersArray.Length != c - this.MethodInfo.NrSpecialArguments)
145 {
146 this.SetError(-32602, "Invalid number of parameters.",
147 BadRequestException.Code, BadRequestException.StatusMessage);
148 }
149 else
150 {
151 Parameters = new object?[c];
152
153 for (i = 0; i < c; i++)
154 {
155 object? Value = this.ParametersArray.GetValue(i);
156 ProtectedMethodArgumentInfo ArgumentInfo = this.MethodInfo.Arguments[i];
157 Type ExpectedType = ArgumentInfo.Parameter.ParameterType;
158 Type ParameterType = Value?.GetType() ?? typeof(object);
159
160 if (ParameterType == ExpectedType)
161 Parameters[i] = Value;
162 else if (Expression.TryConvert(Value, ExpectedType, true, out object Converted))
163 Parameters[i] = Converted;
164 else if (ArgumentInfo.HasDefaultValue &&
165 Value is Dictionary<string, object?> Dictionary &&
166 Dictionary.Count == 0)
167 {
168 Parameters[i] = ArgumentInfo.DefaultValue;
169 }
170 else
171 {
172 this.SetError(-32602, "Parameter " + (i + 1).ToString() +
173 " has incorrect type: " + ParameterType.FullName +
174 ", Expected: " + ExpectedType.FullName,
175 BadRequestException.Code, BadRequestException.StatusMessage);
176 break;
177 }
178 }
179 }
180 }
181 else
182 {
183 Parameters = new object?[c];
184
185 if (NrParametersSet != c - this.MethodInfo.NrSpecialArguments)
186 {
187 foreach (ProtectedMethodArgumentInfo Argument in this.MethodInfo.Arguments)
188 {
189 if (!Argument.IsSpecialArgument &&
190 Argument.HasDefaultValue)
191 {
192 Parameters[Argument.Parameter.Position] = Argument.DefaultValue;
193 NrParametersSet++;
194 }
195 }
196 }
197
198 if (NrParametersSet != c - this.MethodInfo.NrSpecialArguments)
199 {
200 this.SetError(-32600, "Missing required parameters.",
201 BadRequestException.Code, BadRequestException.StatusMessage);
202 }
203 }
204
205 if (!this.ErrorCode.HasValue)
206 {
207 Parameters ??= new object?[c];
208
209 if (this.MethodInfo.NrSpecialArguments > 0)
210 {
211 if (this.MethodInfo.RequestArgument.HasValue)
212 Parameters[this.MethodInfo.RequestArgument.Value] = HttpRequest;
213
214 if (this.MethodInfo.ResponseArgument.HasValue)
215 Parameters[this.MethodInfo.ResponseArgument.Value] = HttpResponse;
216
217 if (this.MethodInfo.IdArgument.HasValue)
218 Parameters[this.MethodInfo.IdArgument.Value] = this.Id;
219 }
220
221 if (HasSniffer)
222 {
223 StringBuilder sb = new StringBuilder();
224 bool First = true;
225
226 sb.Append(this.MethodInfo.Method.Name);
227 sb.Append('(');
228
229 foreach (object? P in Parameters)
230 {
231 if (First)
232 First = false;
233 else
234 sb.Append(',');
235
236 sb.Append(Expression.ToExpressionString(P));
237 }
238
239 sb.Append(')');
240
241 HttpRequest.Server.Information(sb.ToString());
242 }
243
244 IUser User = HttpRequest.User;
245 bool Encrypted = HttpRequest.Encrypted;
246 int Strength = HttpRequest.CipherStrength;
247
248 if (this.MethodInfo.RequiresAuthentication && User is null)
249 {
250 HttpAuthenticationScheme[] Schemes = this.MethodInfo.AuthenticationMechanisms ?? Array.Empty<HttpAuthenticationScheme>();
251
252 foreach (HttpAuthenticationScheme Scheme in Schemes)
253 {
254 if (Scheme.RequireEncryption &&
255 (!Encrypted || Strength < Scheme.MinStrength))
256 {
257 continue;
258 }
259
260 if (Scheme.UserSessions && HttpRequest.Session is null)
261 HttpRequest.GetSessionFromCookie();
262
263 User = await Scheme.IsAuthenticated(HttpRequest);
264 if (!(User is null))
265 {
266 HttpRequest.User = User;
267 break;
268 }
269 }
270
271 if (User is null)
272 {
273 List<string> Challenges = new List<string>();
274
275 foreach (HttpAuthenticationScheme Scheme in Schemes)
276 {
277 if (Scheme.RequireEncryption &&
278 (!Encrypted || Strength < Scheme.MinStrength))
279 {
280 continue;
281 }
282
283 foreach (string Challenge in Scheme.GetChallenges(HttpRequest))
284 Challenges.Add(Challenge);
285 }
286
287 await HttpResponse.SendResponse(new UnauthorizedException(
288 Challenges.ToArray()));
289 return true;
290 }
291
292 foreach (string Privilege in this.MethodInfo.RequiredPrivileges)
293 {
294 if (!User.HasPrivilege(Privilege))
295 {
296 await HttpResponse.SendResponse(ForbiddenException.AccessDenied(
297 HttpRequest.Resource.ResourceName,
298 User.UserName, Privilege));
299 return true;
300 }
301 }
302 }
303
304 this.Result = await ScriptNode.WaitPossibleTask(
305 this.MethodInfo.Method.Invoke(WebService, Parameters));
306
307 if (HasSniffer)
308 {
309 if (this.Result is null)
310 HttpRequest.Server.Information("Result: null");
311 else if (Expression.IsVoid(this.Result.GetType()))
312 HttpRequest.Server.Information("Result: void");
313 else
314 {
315 HttpRequest.Server.Information("Result: " +
316 Expression.ToExpressionString(this.Result));
317 }
318 }
319
320 if (HttpResponse.ResponseSent)
321 return true;
322 }
323 }
324 catch (Exception ex)
325 {
326 if (HasSniffer)
327 HttpRequest.Server.Exception(ex);
328
329 this.SetError(-32603, Log.UnnestException(ex).Message,
330 InternalServerErrorException.Code, InternalServerErrorException.StatusMessage);
331 }
332 }
333
334 if (this.ErrorCode.HasValue)
335 {
336 this.ResponseObject["error"] = new Dictionary<string, object>()
337 {
338 { "code", this.ErrorCode.Value },
339 { "message", this.ErrorMessage ?? string.Empty }
340 };
341 }
342 else if (Expression.IsNullOrVoid(this.Result))
343 this.ResponseObject["result"] = new Dictionary<string, object>();
344 else
345 this.ResponseObject["result"] = this.Result;
346
347 this.Response = this.ResponseObject;
348
349 if (!(this.Id is null) && this.StatusCode == 204)
350 {
351 this.StatusCode = 200;
352 this.StatusMessage = "OK";
353 }
354 }
355 else
356 {
357 int i, c = this.BatchRequests.Length;
358 int j, d = 0;
359
360 for (i = 0; i < c; i++)
361 {
362 if (!(this.BatchRequests[i]!.Id is null))
363 d++;
364 }
365
366 this.ResponseArray = new Dictionary<string, object?>[d];
367
368 for (i = j = 0; i < c; i++)
369 {
370 JsonRpcServerRequest Request = this.BatchRequests[i]!;
371
372 if (await Request.BuildResponse(WebService, HttpRequest, HttpResponse))
373 return true;
374
375 if (!(Request.Id is null))
376 this.ResponseArray[j++] = Request.ResponseObject!;
377 }
378
379 this.Response = this.ResponseArray;
380
381 if (this.StatusCode == 204)
382 {
383 this.StatusCode = 200;
384 this.StatusMessage = "OK";
385 }
386 }
387
388 return false;
389 }
390 }
391}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
Class managing a script expression.
Definition: Expression.cs:41
static bool IsVoid(Type ResultType)
Checks if a result object type is equal to void (i.e. its type equal to System.Threading....
Definition: Expression.cs:4735
static bool TryConvert(object Value, Type DesiredType, bool AcceptInformationLoss, out object Result)
Tries to convert an object Value to an object of type DesiredType .
Definition: Expression.cs:5530
static bool IsNullOrVoid(object Result)
Checks if a result object value is equal to null or void (i.e. its type equal to System....
Definition: Expression.cs:4721
static string ToExpressionString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
Definition: Expression.cs:5052
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
static async Task< object > WaitPossibleTask(object Result)
Waits for any asynchronous process to terminate.
Definition: ScriptNode.cs:441
bool HasPrivilege(string Privilege)
If the object has a given privilege.
Basic interface for a user.
Definition: IUser.cs:7
string UserName
User Name.
Definition: IUser.cs:12
ParameterType
Type of parameter.
Definition: ParameterType.cs:7
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.