Пример #1
0
        /// <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));
        }
Пример #2
0
        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));
            }
        }
Пример #3
0
        /// <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));
        }
Пример #4
0
        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));
            }
        }
Пример #5
0
        /// <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));
        }
Пример #6
0
        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));
            }
        }