/// <summary> /// Parses all the requests from the json in the request /// </summary> /// <param name="jsonString">Json from the http request</param> /// <param name="isBulkRequest">If true, the request is a bulk request (even if there is only one)</param> /// <returns>List of Rpc requests that were parsed from the json</returns> public ParsingResult ParseRequests(Stream jsonStream) { this.logger.ParsingRequests(); List <RpcRequestParseResult>?rpcRequests = null; if (jsonStream == null || jsonStream.Length < 1) { throw new RpcException(RpcErrorCode.InvalidRequest, "Json request was empty"); } bool isBulkRequest = false; try { if (jsonStream.Length > int.MaxValue) { throw new RpcException(RpcErrorCode.ParseError, "Json body is too large to parse."); } byte[] jsonBytes = ArrayPool <byte> .Shared.Rent((int)jsonStream.Length); try { jsonStream.Read(jsonBytes, 0, (int)jsonStream.Length); var jsonReader = new Utf8JsonReader(jsonBytes); if (jsonReader.Read()) { switch (jsonReader.TokenType) { case JsonTokenType.StartObject: jsonReader.Read(); RpcRequestParseResult result = this.ParseResult(ref jsonReader, jsonBytes); rpcRequests = new List <RpcRequestParseResult> { result }; break; case JsonTokenType.StartArray: isBulkRequest = true; jsonReader.Read(); rpcRequests = new List <RpcRequestParseResult>(); while (jsonReader.TokenType != JsonTokenType.EndArray) { RpcRequestParseResult r = this.ParseResult(ref jsonReader, jsonBytes); rpcRequests.Add(r); jsonReader.Read(); } break; default: throw new RpcException(RpcErrorCode.InvalidRequest, "Json request was invalid"); } } } finally { ArrayPool <byte> .Shared.Return(jsonBytes, clearArray : false); } } catch (Exception ex) when(!(ex is RpcException)) { string errorMessage = "Unable to parse json request into an rpc format."; this.logger.LogException(ex, errorMessage); throw new RpcException(RpcErrorCode.InvalidRequest, errorMessage, ex); } if (rpcRequests == null || !rpcRequests.Any()) { throw new RpcException(RpcErrorCode.InvalidRequest, "No rpc json requests found"); } this.logger.ParsedRequests(rpcRequests.Count); var uniqueIds = new HashSet <RpcId>(); foreach (RpcRequestParseResult result in rpcRequests.Where(r => r.Id.HasValue)) { bool unique = uniqueIds.Add(result.Id); if (!unique) { throw new RpcException(RpcErrorCode.InvalidRequest, "Duplicate ids in batch requests are not allowed"); } } return(ParsingResult.FromResults(rpcRequests, isBulkRequest)); }
private RpcRequestParseResult ParseResult(ref Utf8JsonReader jsonReader, Memory <byte> bytes) { RpcId id = default; string? method = null; RpcParameters?parameters = null; string? rpcVersion = null; try { if (jsonReader.TokenType == JsonTokenType.StartObject) { jsonReader.Read(); } while (jsonReader.TokenType != JsonTokenType.EndObject) { string propertyName = jsonReader.GetString(); jsonReader.Read(); switch (propertyName) { case JsonRpcContants.IdPropertyName: switch (jsonReader.TokenType) { case JsonTokenType.String: id = new RpcId(jsonReader.GetString()); break; case JsonTokenType.Number: if (!jsonReader.TryGetInt64(out long longId)) { var idError = new RpcError(RpcErrorCode.ParseError, "Unable to parse rpc id as an integer"); return(RpcRequestParseResult.Fail(id, idError)); } id = new RpcId(longId); break; default: var error = new RpcError(RpcErrorCode.ParseError, "Unable to parse rpc id as a string or an integer"); return(RpcRequestParseResult.Fail(id, error)); } break; case JsonRpcContants.VersionPropertyName: rpcVersion = jsonReader.GetString(); break; case JsonRpcContants.MethodPropertyName: method = jsonReader.GetString(); break; case JsonRpcContants.ParamsPropertyName: RpcParameters ps; switch (jsonReader.TokenType) { case JsonTokenType.StartArray: jsonReader.Read(); var list = new List <IRpcParameter>(); while (jsonReader.TokenType != JsonTokenType.EndArray) { IRpcParameter parameter = this.GetParameter(ref jsonReader, bytes); list.Add(parameter); } //TODO array vs list? ps = new RpcParameters(list.ToArray()); break; case JsonTokenType.StartObject: jsonReader.Read(); var dict = new Dictionary <string, IRpcParameter>(); while (jsonReader.TokenType != JsonTokenType.EndObject) { string key = jsonReader.GetString(); jsonReader.Read(); IRpcParameter parameter = this.GetParameter(ref jsonReader, bytes); dict.Add(key, parameter); } ps = new RpcParameters(dict); break; default: return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The request parameter format is invalid."))); } parameters = ps; break; } jsonReader.Read(); } if (string.IsNullOrWhiteSpace(method)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The request method is required."))); } if (string.IsNullOrWhiteSpace(rpcVersion)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The jsonrpc version must be specified."))); } if (!string.Equals(rpcVersion, "2.0", StringComparison.OrdinalIgnoreCase)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, $"The jsonrpc version '{rpcVersion}' is not supported. Supported versions: '2.0'"))); } return(RpcRequestParseResult.Success(id, method !, parameters)); } catch (Exception ex) { RpcError error; if (ex is RpcException rpcException) { error = rpcException.ToRpcError(this.serverConfig.Value.ShowServerExceptions); } else { error = new RpcError(RpcErrorCode.ParseError, "Failed to parse request.", ex); } return(RpcRequestParseResult.Fail(id, error)); } }
/// <summary> /// Parses all the requests from the json in the request /// </summary> /// <param name="jsonString">Json from the http request</param> /// <param name="isBulkRequest">If true, the request is a bulk request (even if there is only one)</param> /// <returns>List of Rpc requests that were parsed from the json</returns> public ParsingResult ParseRequests(Stream jsonStream) { this.logger.ParsingRequests(); List <RpcRequestParseResult>?rpcRequests = null; if (jsonStream == null || jsonStream.Length < 1) { throw new RpcException(RpcErrorCode.InvalidRequest, "Json request was empty"); } bool isBulkRequest = false; try { if (jsonStream.Length > int.MaxValue) { throw new RpcException(RpcErrorCode.ParseError, "Json body is too large to parse."); } var jsonDocument = JsonDocument.Parse(jsonStream); switch (jsonDocument.RootElement.ValueKind) { case JsonValueKind.Object: RpcRequestParseResult result = this.ParseResult(jsonDocument.RootElement.EnumerateObject()); rpcRequests = new List <RpcRequestParseResult> { result }; break; case JsonValueKind.Array: isBulkRequest = true; rpcRequests = new List <RpcRequestParseResult>(); foreach (JsonElement element in jsonDocument.RootElement.EnumerateArray()) { RpcRequestParseResult r = this.ParseResult(element.EnumerateObject()); rpcRequests.Add(r); } break; default: throw new RpcException(RpcErrorCode.InvalidRequest, "Json request was invalid"); } } catch (Exception ex) when(!(ex is RpcException)) { string errorMessage = "Unable to parse json request into an rpc format."; this.logger.LogException(ex, errorMessage); throw new RpcException(RpcErrorCode.InvalidRequest, errorMessage, ex); } if (rpcRequests == null || !rpcRequests.Any()) { throw new RpcException(RpcErrorCode.InvalidRequest, "No rpc json requests found"); } this.logger.ParsedRequests(rpcRequests.Count); var uniqueIds = new HashSet <RpcId>(); foreach (RpcRequestParseResult result in rpcRequests.Where(r => r.Id.HasValue)) { bool unique = uniqueIds.Add(result.Id); if (!unique) { throw new RpcException(RpcErrorCode.InvalidRequest, "Duplicate ids in batch requests are not allowed"); } } return(ParsingResult.FromResults(rpcRequests, isBulkRequest)); }
private RpcRequestParseResult ParseResult(JsonElement.ObjectEnumerator objectEnumerator) { RpcId id = default; string? method = null; RpcParameters?parameters = null; string? rpcVersion = null; try { foreach (JsonProperty property in objectEnumerator) { switch (property.Name) { case JsonRpcContants.IdPropertyName: switch (property.Value.ValueKind) { case JsonValueKind.String: id = new RpcId(property.Value.GetString()); break; case JsonValueKind.Number: if (!property.Value.TryGetInt64(out long longId)) { var idError = new RpcError(RpcErrorCode.ParseError, "Unable to parse rpc id as an integer"); return(RpcRequestParseResult.Fail(id, idError)); } id = new RpcId(longId); break; default: var error = new RpcError(RpcErrorCode.ParseError, "Unable to parse rpc id as a string or an integer"); return(RpcRequestParseResult.Fail(id, error)); } break; case JsonRpcContants.VersionPropertyName: rpcVersion = property.Value.GetString(); break; case JsonRpcContants.MethodPropertyName: method = property.Value.GetString(); break; case JsonRpcContants.ParamsPropertyName: RpcParameters ps; switch (property.Value.ValueKind) { case JsonValueKind.Array: IRpcParameter[] items = property.Value .EnumerateArray() .Select(this.GetParameter) .Cast <IRpcParameter>() .ToArray(); //TODO array vs list? ps = new RpcParameters(items); break; case JsonValueKind.Object: Dictionary <string, IRpcParameter> dict = property.Value .EnumerateObject() .ToDictionary(j => j.Name, j => (IRpcParameter)this.GetParameter(j.Value)); ps = new RpcParameters(dict); break; default: return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The request parameter format is invalid."))); } parameters = ps; break; } } if (string.IsNullOrWhiteSpace(method)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The request method is required."))); } if (string.IsNullOrWhiteSpace(rpcVersion)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The jsonrpc version must be specified."))); } if (!string.Equals(rpcVersion, "2.0", StringComparison.OrdinalIgnoreCase)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, $"The jsonrpc version '{rpcVersion}' is not supported. Supported versions: '2.0'"))); } return(RpcRequestParseResult.Success(id, method !, parameters)); } catch (Exception ex) { RpcError error; if (ex is RpcException rpcException) { error = rpcException.ToRpcError(this.serverConfig.Value.ShowServerExceptions); } else { error = new RpcError(RpcErrorCode.ParseError, "Failed to parse request.", ex); } return(RpcRequestParseResult.Fail(id, error)); } }
/// <summary> /// Parses all the requests from the json in the request /// </summary> /// <param name="jsonString">Json from the http request</param> /// <param name="isBulkRequest">If true, the request is a bulk request (even if there is only one)</param> /// <returns>List of Rpc requests that were parsed from the json</returns> public ParsingResult ParseRequests(string jsonString) { this.logger?.LogDebug($"Attempting to parse Rpc request from the json string '{jsonString}'"); List <RpcRequestParseResult> rpcRequests; if (string.IsNullOrWhiteSpace(jsonString)) { throw new RpcException(RpcErrorCode.InvalidRequest, "Json request was empty"); } bool isBulkRequest; try { using (JsonReader jsonReader = new JsonTextReader(new StringReader(jsonString))) { //Fixes the date parsing issue https://github.com/JamesNK/Newtonsoft.Json/issues/862 jsonReader.DateParseHandling = DateParseHandling.None; JToken token = JToken.Load(jsonReader); switch (token.Type) { case JTokenType.Array: isBulkRequest = true; rpcRequests = ((JArray)token).Select(this.DeserializeRequest).ToList(); break; case JTokenType.Object: isBulkRequest = false; RpcRequestParseResult result = this.DeserializeRequest(token); rpcRequests = new List <RpcRequestParseResult> { result }; break; default: throw new RpcException(RpcErrorCode.ParseError, "Json body is not an array or an object."); } } } catch (Exception ex) when(!(ex is RpcException)) { string errorMessage = "Unable to parse json request into an rpc format."; this.logger?.LogException(ex, errorMessage); throw new RpcException(RpcErrorCode.InvalidRequest, errorMessage, ex); } if (rpcRequests == null || !rpcRequests.Any()) { throw new RpcException(RpcErrorCode.InvalidRequest, "No rpc json requests found"); } this.logger?.LogDebug($"Successfully parsed {rpcRequests.Count} Rpc request(s)"); var uniqueIds = new HashSet <RpcId>(); foreach (RpcRequestParseResult result in rpcRequests.Where(r => r.Request != null && r.Request.Id.HasValue)) { bool unique = uniqueIds.Add(result.Request.Id); if (!unique) { throw new RpcException(RpcErrorCode.InvalidRequest, "Duplicate ids in batch requests are not allowed"); } } return(ParsingResult.FromResults(rpcRequests, isBulkRequest)); }
private RpcRequestParseResult DeserializeRequest(JToken token) { RpcId id = null; JToken idToken = token[JsonRpcContants.IdPropertyName]; if (idToken != null) { switch (idToken.Type) { case JTokenType.Null: break; case JTokenType.Integer: case JTokenType.Float: id = new RpcId(idToken.Value <double>()); break; case JTokenType.String: case JTokenType.Guid: id = new RpcId(idToken.Value <string>()); break; default: //Throw exception here because we need an id for the response throw new RpcException(RpcErrorCode.ParseError, "Unable to parse rpc id as string or number."); } } try { string rpcVersion = token.Value <string>(JsonRpcContants.VersionPropertyName); if (string.IsNullOrWhiteSpace(rpcVersion)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The jsonrpc version must be specified."))); } if (!string.Equals(rpcVersion, "2.0", StringComparison.OrdinalIgnoreCase)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, $"The jsonrpc version '{rpcVersion}' is not supported. Supported versions: '2.0'"))); } string method = token.Value <string>(JsonRpcContants.MethodPropertyName); if (string.IsNullOrWhiteSpace(method)) { return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.InvalidRequest, "The request method is required."))); } RpcParameters parameters = default; JToken paramsToken = token[JsonRpcContants.ParamsPropertyName]; if (paramsToken != null) { switch (paramsToken.Type) { case JTokenType.Array: if (paramsToken.Any()) { parameters = RpcParameters.FromList(paramsToken.ToArray()); } break; case JTokenType.Object: if (paramsToken.Children().Any()) { Dictionary <string, object> dict = paramsToken.ToObject <Dictionary <string, JToken> >() .ToDictionary(kv => kv.Key, kv => (object)kv.Value); parameters = RpcParameters.FromDictionary(dict); } break; case JTokenType.Null: break; default: return(RpcRequestParseResult.Fail(id, new RpcError(RpcErrorCode.ParseError, "Parameters field could not be parsed."))); } } return(RpcRequestParseResult.Success(new RpcRequest(id, method, parameters))); } catch (Exception ex) { RpcError error; if (ex is RpcException rpcException) { error = rpcException.ToRpcError(this.serverConfig.Value.ShowServerExceptions); } else { error = new RpcError(RpcErrorCode.ParseError, "Failed to parse request.", ex); } return(RpcRequestParseResult.Fail(id, error)); } }