<commit_before>﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using NBitcoin.DataEncoders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Features.RPC.Exceptions;
using Stratis.Bitcoin.Utilities;
using TracerAttributes;

namespace Stratis.Bitcoin.Features.RPC
{
    public class RPCMiddleware
    {
        private readonly RequestDelegate next;

        private readonly IRPCAuthorization authorization;

        private readonly ILogger logger;

        private readonly IHttpContextFactory httpContextFactory;
        private readonly DataFolder dataFolder;

        public const string ContentType = "application/json; charset=utf-8";

        public RPCMiddleware(RequestDelegate next, IRPCAuthorization authorization, ILoggerFactory loggerFactory, IHttpContextFactory httpContextFactory, DataFolder dataFolder)
        {
            Guard.NotNull(next, nameof(next));
            Guard.NotNull(authorization, nameof(authorization));

            this.next = next;
            this.authorization = authorization;
            this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
            this.httpContextFactory = httpContextFactory;
            this.dataFolder = dataFolder;
        }

        public async Task InvokeAsync(HttpContext httpContext)
        {
            Guard.NotNull(httpContext, nameof(httpContext));

            if (!this.Authorized(httpContext))
            {
                httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                return;
            }

            Exception ex = null;
            try
            {
                string request = await this.ReadRequestAsync(httpContext.Request).ConfigureAwait(false);

                this.logger.LogDebug("RPC request: {0}", string.IsNullOrEmpty(request) ? request : JToken.Parse(request).ToString(Formatting.Indented));

                JToken token = string.IsNullOrEmpty(request) ? null : JToken.Parse(request);
                JToken response;

                if (token is JArray)
                {
                    // Batch request, invoke each request and accumulate responses into JArray.
                    response = await this.InvokeBatchAsync(httpContext, token as JArray);
                }
                else if (token is JObject)
                {
                    // Single request, invoke single request and return single response object.
                    response = await this.InvokeSingleAsync(httpContext, token as JObject);
                }
                else
                {
                    throw new NotImplementedException("The request is not supported.");
                }

                httpContext.Response.ContentType = ContentType;

                // Write the response body.
                using (StreamWriter streamWriter = new StreamWriter(httpContext.Response.Body, Encoding.Default, 1024, true))
                using (JsonTextWriter textWriter = new JsonTextWriter(streamWriter))
                {
                    await response.WriteToAsync(textWriter);
                }
            }
            catch (Exception exx)
            {
                ex = exx;
            }

            await this.HandleRpcInvokeExceptionAsync(httpContext, ex);
        }

        [NoTrace]
        private async Task<string> ReadRequestAsync(HttpRequest request)
        {
            if (request.ContentLength == null || request.ContentLength == 0)
            {
                return string.Empty;
            }

            // Allows streams to be read multiple times.
            request.EnableRewind();

            // Read the request.
            byte[] requestBuffer = new byte[request.ContentLength.Value];
            await request.Body.ReadAsync(requestBuffer, 0, requestBuffer.Length).ConfigureAwait(false);

            string requestBody = Encoding.UTF8.GetString(requestBuffer);

            request.Body.Position = 0;

            return requestBody;
        }

        private async Task HandleRpcInvokeExceptionAsync(HttpContext httpContext, Exception ex)
        {
            if (ex is ArgumentException || ex is FormatException)
            {
                JObject response = CreateError(RPCErrorCode.RPC_MISC_ERROR, "Argument error: " + ex.Message);
                httpContext.Response.ContentType = ContentType;<commit_msg>[RPC] Fix for decode rpc (#4064)<commit_after>﻿using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using NBitcoin.DataEncoders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Features.RPC.Exceptions;
using Stratis.Bitcoin.Utilities;
using TracerAttributes;

namespace Stratis.Bitcoin.Features.RPC
{
    public class RPCMiddleware
    {
        private readonly RequestDelegate next;

        private readonly IRPCAuthorization authorization;

        private readonly ILogger logger;

        private readonly IHttpContextFactory httpContextFactory;
        private readonly DataFolder dataFolder;

        public const string ContentType = "application/json; charset=utf-8";

        public RPCMiddleware(RequestDelegate next, IRPCAuthorization authorization, ILoggerFactory loggerFactory, IHttpContextFactory httpContextFactory, DataFolder dataFolder)
        {
            Guard.NotNull(next, nameof(next));
            Guard.NotNull(authorization, nameof(authorization));

            this.next = next;
            this.authorization = authorization;
            this.logger = loggerFactory.CreateLogger(this.GetType().FullName);
            this.httpContextFactory = httpContextFactory;
            this.dataFolder = dataFolder;
        }

        public async Task InvokeAsync(HttpContext httpContext)
        {
            Guard.NotNull(httpContext, nameof(httpContext));

            if (!this.Authorized(httpContext))
            {
                httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                return;
            }

            Exception ex = null;
            try
            {
                string request = await this.ReadRequestAsync(httpContext.Request).ConfigureAwait(false);

                this.logger.LogDebug("RPC request: {0}", string.IsNullOrEmpty(request) ? request : JToken.Parse(request).ToString(Formatting.Indented));

                JToken token = string.IsNullOrEmpty(request) ? null : JToken.Parse(request);
                JToken response;

                if (token is JArray)
                {
                    // Batch request, invoke each request and accumulate responses into JArray.
                    response = await this.InvokeBatchAsync(httpContext, token as JArray);
                }
                else if (token is JObject)
                {
                    // Single request, invoke single request and return single response object.
                    response = await this.InvokeSingleAsync(httpContext, token as JObject);
                }
                else
                {
                    throw new NotImplementedException("The request is not supported.");
                }

                httpContext.Response.ContentType = ContentType;

                // Write the response body.
                using (StreamWriter streamWriter = new StreamWriter(httpContext.Response.Body, Encoding.Default, 1024, true))
                using (JsonTextWriter textWriter = new JsonTextWriter(streamWriter))
                {
                    await response.WriteToAsync(textWriter);
                }
            }
            catch (Exception exx)
            {
                ex = exx;
            }

            await this.HandleRpcInvokeExceptionAsync(httpContext, ex);
        }

        [NoTrace]
        private async Task<string> ReadRequestAsync(HttpRequest request)
        {
            if (request.ContentLength == null || request.ContentLength == 0)
            {
                return string.Empty;
            }

            // Allows streams to be read multiple times.
            request.EnableRewind();

            // Read the request.
            var builder = new StringBuilder();
            byte[] requestBuffer = new byte[request.ContentLength.Value];

            while (true)
            {
                var remainingBytesCount = await request.Body.ReadAsync(requestBuffer, 0, requestBuffer.Length).ConfigureAwait(false);
                if (remainingBytesCount == 0) break;

                var encodedString = Encoding.UTF8.GetString(requestBuffer, 0, remainingBytesCount);
                builder.Append(encodedString);
            }

            request.Body.Position = 0;

            return builder.ToString();
        }

        private async Task HandleRpcInvokeExceptionAsync(HttpContext httpContext, Exception ex)
        {
            if (ex is ArgumentException || ex is FormatException)
            {
                JObject response = CreateError(RPCErrorCode.RPC_MISC_ERROR, "Argument error: " + ex.Message);
                httpContext.Response.ContentType = ContentType;
{'fraction_non_alphanumeric': 0.07532956685499058, 'fraction_numerical': 0.0033479807491106927, 'mean_word_length': 2.056923568915894, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3101164', 'n_tokens_mistral': 2312, 'n_tokens_neox': 2213, 'n_words': 543}