public Task Init(INethermindApi api)
        {
            _api = api;
            _healthChecksConfig = _api.Config <IHealthChecksConfig>();

            return(Task.CompletedTask);
        }
Example #2
0
        public Task Init(INethermindApi api)
        {
            _api = api;
            _healthChecksConfig = _api.Config <IHealthChecksConfig>();
            _jsonRpcConfig      = _api.Config <IJsonRpcConfig>();

            _logger = api.LogManager.GetClassLogger();

            return(Task.CompletedTask);
        }
Example #3
0
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IJsonRpcProcessor jsonRpcProcessor, IJsonRpcService jsonRpcService, IJsonRpcLocalStats jsonRpcLocalStats, IJsonSerializer jsonSerializer)
        {
            long SerializeTimeoutException(IJsonRpcService service, Stream resultStream)
            {
                JsonRpcErrorResponse?error = service.GetErrorResponse(ErrorCodes.Timeout, "Request was canceled due to enabled timeout.");

                return(jsonSerializer.Serialize(resultStream, error));
            }

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors("Cors");
            app.UseRouting();
            app.UseResponseCompression();

            IConfigProvider?   configProvider    = app.ApplicationServices.GetService <IConfigProvider>();
            IRpcAuthentication?rpcAuthentication = app.ApplicationServices.GetService <IRpcAuthentication>();

            if (configProvider == null)
            {
                throw new ApplicationException($"{nameof(IConfigProvider)} has not been loaded properly");
            }

            ILogManager?          logManager           = app.ApplicationServices.GetService <ILogManager>() ?? NullLogManager.Instance;
            ILogger               logger               = logManager.GetClassLogger();
            IInitConfig           initConfig           = configProvider.GetConfig <IInitConfig>();
            IJsonRpcConfig        jsonRpcConfig        = configProvider.GetConfig <IJsonRpcConfig>();
            IJsonRpcUrlCollection jsonRpcUrlCollection = app.ApplicationServices.GetRequiredService <IJsonRpcUrlCollection>();
            IHealthChecksConfig   healthChecksConfig   = configProvider.GetConfig <IHealthChecksConfig>();

            if (initConfig.WebSocketsEnabled)
            {
                app.UseWebSockets(new WebSocketOptions());
                app.UseWhen(ctx =>
                            ctx.WebSockets.IsWebSocketRequest &&
                            jsonRpcUrlCollection.TryGetValue(ctx.Connection.LocalPort, out JsonRpcUrl jsonRpcUrl) &&
                            jsonRpcUrl.RpcEndpoint.HasFlag(RpcEndpoint.Ws),
                            builder => builder.UseWebSocketsModules());
            }

            app.UseEndpoints(endpoints =>
            {
                if (healthChecksConfig.Enabled)
                {
                    try
                    {
                        endpoints.MapHealthChecks(healthChecksConfig.Slug, new HealthCheckOptions()
                        {
                            Predicate      = _ => true,
                            ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                        });
                        if (healthChecksConfig.UIEnabled)
                        {
                            endpoints.MapHealthChecksUI(setup => setup.AddCustomStylesheet(Path.Combine(AppDomain.CurrentDomain.BaseDirectory !, "nethermind.css")));
                        }
                    }
                    catch (Exception e)
                    {
                        if (logger.IsError)
                        {
                            logger.Error("Unable to initialize health checks. Check if you have Nethermind.HealthChecks.dll in your plugins folder.", e);
                        }
                    }
                }
            });

            app.Run(async(ctx) =>
            {
                if (ctx.Request.Method == "GET")
                {
                    await ctx.Response.WriteAsync("Nethermind JSON RPC");
                }

                if (ctx.Request.Method == "POST" &&
                    jsonRpcUrlCollection.TryGetValue(ctx.Connection.LocalPort, out JsonRpcUrl jsonRpcUrl) &&
                    jsonRpcUrl.RpcEndpoint.HasFlag(RpcEndpoint.Http))
                {
                    if (jsonRpcUrl.IsAuthenticated && !rpcAuthentication !.Authenticate(ctx.Request.Headers["Authorization"]))
                    {
                        var response             = jsonRpcService.GetErrorResponse(ErrorCodes.ParseError, "Authentication error");
                        ctx.Response.ContentType = "application/json";
                        ctx.Response.StatusCode  = StatusCodes.Status200OK;
                        jsonSerializer.Serialize(ctx.Response.Body, response);
                        await ctx.Response.CompleteAsync();
                        return;
                    }
                    Stopwatch stopwatch = Stopwatch.StartNew();
                    using CountingTextReader request = new(new StreamReader(ctx.Request.Body, Encoding.UTF8));
                    try
                    {
                        await foreach (JsonRpcResult result in jsonRpcProcessor.ProcessAsync(request, JsonRpcContext.Http(jsonRpcUrl)))
                        {
                            using (result)
                            {
                                Stream resultStream = jsonRpcConfig.BufferResponses ? new MemoryStream() : ctx.Response.Body;

                                long responseSize;
                                try
                                {
                                    ctx.Response.ContentType = "application/json";
                                    ctx.Response.StatusCode  = GetStatusCode(result);

                                    responseSize = result.IsCollection
                                        ? jsonSerializer.Serialize(resultStream, result.Responses)
                                        : jsonSerializer.Serialize(resultStream, result.Response);

                                    if (jsonRpcConfig.BufferResponses)
                                    {
                                        ctx.Response.ContentLength = responseSize = resultStream.Length;
                                        resultStream.Seek(0, SeekOrigin.Begin);
                                        await resultStream.CopyToAsync(ctx.Response.Body);
                                    }
                                }
                                catch (Exception e) when(e.InnerException is OperationCanceledException)
                                {
                                    responseSize = SerializeTimeoutException(jsonRpcService, resultStream);
                                }
                                catch (OperationCanceledException)
                                {
                                    responseSize = SerializeTimeoutException(jsonRpcService, resultStream);
                                }
                                finally
                                {
                                    await ctx.Response.CompleteAsync();

                                    if (jsonRpcConfig.BufferResponses)
                                    {
                                        await resultStream.DisposeAsync();
                                    }
                                }

                                long handlingTimeMicroseconds = stopwatch.ElapsedMicroseconds();
                                if (result.IsCollection)
                                {
                                    jsonRpcLocalStats.ReportCalls(result.Reports);
                                    jsonRpcLocalStats.ReportCall(new RpcReport("# collection serialization #", handlingTimeMicroseconds, true), handlingTimeMicroseconds, responseSize);
                                }
                                else
                                {
                                    jsonRpcLocalStats.ReportCall(result.Report, handlingTimeMicroseconds, responseSize);
                                }

                                Interlocked.Add(ref Metrics.JsonRpcBytesSentHttp, responseSize);
                            }

                            // There should be only one response because we don't expect multiple JSON tokens in the request
                            break;
                        }
                    }
                    catch (Microsoft.AspNetCore.Http.BadHttpRequestException e)
                    {
                        if (logger.IsDebug)
                        {
                            logger.Debug($"Couldn't read request.{Environment.NewLine}{e}");
                        }
                    }
                    finally
                    {
                        Interlocked.Add(ref Metrics.JsonRpcBytesReceivedHttp, ctx.Request.ContentLength ?? request.Length);
                    }
                }
            });
        }
Example #4
0
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IJsonRpcProcessor jsonRpcProcessor, IJsonRpcService jsonRpcService, IJsonRpcLocalStats jsonRpcLocalStats)
        {
            void SerializeTimeoutException(IJsonRpcService service, Stream resultStream)
            {
                JsonRpcErrorResponse?error = service.GetErrorResponse(ErrorCodes.Timeout, "Request was canceled due to enabled timeout.");

                _jsonSerializer.Serialize(resultStream, error);
            }

            _jsonSerializer = CreateJsonSerializer();

            foreach (JsonConverter converter in jsonRpcService.Converters)
            {
                _jsonSerializer.RegisterConverter(converter);
            }

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors("Cors");
            app.UseRouting();

            IConfigProvider     configProvider     = app.ApplicationServices.GetService <IConfigProvider>();
            IInitConfig         initConfig         = configProvider.GetConfig <IInitConfig>();
            IJsonRpcConfig      jsonRpcConfig      = configProvider.GetConfig <IJsonRpcConfig>();
            IHealthChecksConfig healthChecksConfig = configProvider.GetConfig <IHealthChecksConfig>();

            if (initConfig.WebSocketsEnabled)
            {
                app.UseWebSockets();
                app.UseWhen(ctx => ctx.WebSockets.IsWebSocketRequest &&
                            ctx.Connection.LocalPort == jsonRpcConfig.WebSocketsPort,
                            builder => builder.UseWebSocketsModules());
            }

            app.UseEndpoints(endpoints =>
            {
                if (healthChecksConfig.Enabled)
                {
                    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
                    {
                        Predicate      = _ => true,
                        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                    });
                    if (healthChecksConfig.UIEnabled)
                    {
                        endpoints.MapHealthChecksUI(setup => setup.AddCustomStylesheet(Path.Combine(AppDomain.CurrentDomain.BaseDirectory !, "nethermind.css")));
                    }
                }
            });

            app.Use(async(ctx, next) =>
            {
                if (ctx.Request.Method == "GET")
                {
                    await ctx.Response.WriteAsync("Nethermind JSON RPC");
                }
                if (ctx.Connection.LocalPort == jsonRpcConfig.Port && ctx.Request.Method == "POST")
                {
                    Stopwatch stopwatch        = Stopwatch.StartNew();
                    using StreamReader reader  = new StreamReader(ctx.Request.Body, Encoding.UTF8);
                    string request             = await reader.ReadToEndAsync();
                    using JsonRpcResult result = await jsonRpcProcessor.ProcessAsync(request);

                    ctx.Response.ContentType = "application/json";

                    Stream resultStream = jsonRpcConfig.BufferResponses ? new MemoryStream() : ctx.Response.Body;

                    try
                    {
                        if (result.IsCollection)
                        {
                            _jsonSerializer.Serialize(resultStream, result.Responses);
                        }
                        else
                        {
                            _jsonSerializer.Serialize(resultStream, result.Response);
                        }

                        if (jsonRpcConfig.BufferResponses)
                        {
                            ctx.Response.ContentLength = resultStream.Length;
                            resultStream.Seek(0, SeekOrigin.Begin);
                            await resultStream.CopyToAsync(ctx.Response.Body);
                        }
                    }
                    catch (Exception e) when(e.InnerException is OperationCanceledException)
                    {
                        SerializeTimeoutException(jsonRpcService, resultStream);
                    }
                    catch (OperationCanceledException)
                    {
                        SerializeTimeoutException(jsonRpcService, resultStream);
                    }
                    finally
                    {
                        await ctx.Response.CompleteAsync();

                        if (jsonRpcConfig.BufferResponses)
                        {
                            await resultStream.DisposeAsync();
                        }
                    }

                    if (result.IsCollection)
                    {
                        jsonRpcLocalStats.ReportCalls(result.Reports);
                        jsonRpcLocalStats.ReportCall(new RpcReport("# collection serialization #", stopwatch.ElapsedMicroseconds(), true));
                    }
                    else
                    {
                        jsonRpcLocalStats.ReportCall(result.Report, stopwatch.ElapsedMicroseconds());
                    }
                }
            });
        }