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); } } }); }
public void Method_resolution_is_scoped_to_url_enabled_modules() { _moduleProvider.Register(new SingletonModulePool <INetRpcModule>(Substitute.For <INetRpcModule>(), true)); _moduleProvider.Register(new SingletonModulePool <IProofRpcModule>(Substitute.For <IProofRpcModule>(), true)); JsonRpcUrl url = new JsonRpcUrl("http", "127.0.0.1", 8888, RpcEndpoint.Http, new[] { "net" }); ModuleResolution inScopeResolution = _moduleProvider.Check("net_version", JsonRpcContext.Http(url)); Assert.AreEqual(ModuleResolution.Enabled, inScopeResolution); ModuleResolution outOfScopeResolution = _moduleProvider.Check("proof_call", JsonRpcContext.Http(url)); Assert.AreEqual(ModuleResolution.Disabled, outOfScopeResolution); ModuleResolution fallbackResolution = _moduleProvider.Check("proof_call", new JsonRpcContext(RpcEndpoint.Http)); Assert.AreEqual(ModuleResolution.Enabled, fallbackResolution); }