diff --git a/app/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/app/src/main/java/org/hyperledger/besu/RunnerBuilder.java index ce116018a35..9855e2f5bda 100644 --- a/app/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/app/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -39,7 +39,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.DefaultAuthenticationService; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.EngineAuthService; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.AuthenticatedJsonRpcProcessor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.health.HealthService; @@ -1142,7 +1142,8 @@ public Runner build() { new JsonRpcIpcService( vertx, jsonRpcIpcConfiguration.getPath(), - new JsonRpcExecutor(new BaseJsonRpcProcessor(), ipcMethodsFactory.methods()), + new JsonRpcExecutor( + new CombinedJsonRpcProcessor(metricsSystem), ipcMethodsFactory.methods()), Optional.of(subscriptionManager))); } else { jsonRpcIpcService = Optional.empty(); @@ -1471,11 +1472,11 @@ private WebSocketService createWebsocketService( if (authenticationService.isPresent()) { jsonRpcProcessor = new AuthenticatedJsonRpcProcessor( - new BaseJsonRpcProcessor(), + new CombinedJsonRpcProcessor(metricsSystem), authenticationService.get(), configuration.getRpcApisNoAuth()); } else { - jsonRpcProcessor = new BaseJsonRpcProcessor(); + jsonRpcProcessor = new CombinedJsonRpcProcessor(metricsSystem); } final JsonRpcExecutor jsonRpcExecutor = new JsonRpcExecutor(jsonRpcProcessor, websocketMethodsFactory.methods()); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java index 62d2c920313..e69de29bb2d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java @@ -1,147 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.handlers; - -import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; - -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; -import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; - -import java.io.IOException; -import java.util.Optional; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.json.Json; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.auth.User; -import io.vertx.ext.web.RoutingContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class AbstractJsonRpcExecutor { - private static final Logger LOG = LoggerFactory.getLogger(AbstractJsonRpcExecutor.class); - - protected static final String SPAN_CONTEXT = "span_context"; - final JsonRpcExecutor jsonRpcExecutor; - final Tracer tracer; - final RoutingContext ctx; - final JsonRpcConfiguration jsonRpcConfiguration; - - private static final ObjectMapper jsonObjectMapper = - new ObjectMapper() - .registerModule(new Jdk8Module()); // Handle JDK8 Optionals (de)serialization - - /** - * Creates a new AbstractJsonRpcExecutor. - * - * @param jsonRpcExecutor The executor used to process the JSON RPC requests. - * @param tracer The tracer used for monitoring and debugging purposes. - * @param ctx The context of the routing, containing information about the HTTP request and - * response. - * @param jsonRpcConfiguration The configuration for JSON RPC operations - */ - public AbstractJsonRpcExecutor( - final JsonRpcExecutor jsonRpcExecutor, - final Tracer tracer, - final RoutingContext ctx, - final JsonRpcConfiguration jsonRpcConfiguration) { - this.jsonRpcExecutor = jsonRpcExecutor; - this.tracer = tracer; - this.ctx = ctx; - this.jsonRpcConfiguration = jsonRpcConfiguration; - } - - abstract void execute() throws IOException; - - abstract String getRpcMethodName(final RoutingContext ctx); - - protected static JsonRpcResponse executeRequest( - final JsonRpcExecutor jsonRpcExecutor, - final Tracer tracer, - final JsonObject jsonRequest, - final RoutingContext ctx) { - final Optional user = ContextKey.AUTHENTICATED_USER.extractFrom(ctx, Optional::empty); - final Context spanContext = ctx.get(SPAN_CONTEXT); - return jsonRpcExecutor.execute( - user, - tracer, - spanContext, - () -> !ctx.response().closed(), - jsonRequest, - req -> req.mapTo(JsonRpcRequest.class)); - } - - protected static void handleJsonRpcError( - final RoutingContext routingContext, final Object id, final RpcErrorType error) { - final HttpServerResponse response = routingContext.response(); - if (!response.closed()) { - if (response.headWritten()) { - // Streaming already started — cannot change status code or headers. - // Reset the connection so the client sees a transport error rather than - // silently receiving truncated JSON. - response.reset(); - } else { - response - .setStatusCode(statusCodeFromError(error).code()) - .end(Json.encode(new JsonRpcErrorResponse(id, error))); - } - } - } - - private static HttpResponseStatus statusCodeFromError(final RpcErrorType error) { - return switch (error) { - case INVALID_REQUEST, PARSE_ERROR -> HttpResponseStatus.BAD_REQUEST; - case TIMEOUT_ERROR -> HttpResponseStatus.REQUEST_TIMEOUT; - case UNAUTHORIZED -> HttpResponseStatus.UNAUTHORIZED; - default -> HttpResponseStatus.OK; - }; - } - - protected HttpServerResponse prepareHttpResponse(final RoutingContext ctx) { - HttpServerResponse response = ctx.response(); - response = response.putHeader("Content-Type", APPLICATION_JSON); - return response; - } - - protected static ObjectMapper getJsonObjectMapper() { - return jsonObjectMapper; - } - - @FunctionalInterface - protected interface ExceptionThrowingSupplier { - T get() throws Exception; - } - - protected static void lazyTraceLogger( - final ExceptionThrowingSupplier logMessageSupplier) { - if (LOG.isTraceEnabled()) { - try { - LOG.trace(logMessageSupplier.get()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcArrayExecutor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcArrayExecutor.java deleted file mode 100644 index 0026f17c096..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcArrayExecutor.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.handlers; - -import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_REQUEST; - -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonResponseStreamer; -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; -import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; -import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import io.opentelemetry.api.trace.Tracer; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.RoutingContext; - -public class JsonRpcArrayExecutor extends AbstractJsonRpcExecutor { - public JsonRpcArrayExecutor( - final JsonRpcExecutor jsonRpcExecutor, - final Tracer tracer, - final RoutingContext ctx, - final JsonRpcConfiguration jsonRpcConfiguration) { - super(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration); - } - - /** - * Executes the JSON-RPC request(s) associated with the current routing context. - * - * @throws IOException if there is an error writing the response to the client - */ - @Override - void execute() throws IOException { - HttpServerResponse response = prepareHttpResponse(ctx); - final JsonArray batchJsonRequest = getRequestBodyAsJsonArray(ctx); - if (isBatchSizeValid(batchJsonRequest)) { - try (final JsonResponseStreamer streamer = - new JsonResponseStreamer(response, ctx.request().remoteAddress())) { - executeRpcRequestBatch(batchJsonRequest, streamer); - } - } else { - handleJsonRpcError(ctx, null, RpcErrorType.EXCEEDS_RPC_MAX_BATCH_SIZE); - } - } - - /** - * Executes a batch of RPC requests. - * - * @param rpcRequestBatch the batch of RPC requests. - * @param streamer the JsonResponseStreamer to use. - */ - public void executeRpcRequestBatch( - final JsonArray rpcRequestBatch, final JsonResponseStreamer streamer) throws IOException { - try (JsonGenerator generator = getJsonObjectMapper().getFactory().createGenerator(streamer)) { - generator.writeStartArray(); - for (int i = 0; i < rpcRequestBatch.size(); i++) { - JsonRpcResponse response = processMaybeRequest(rpcRequestBatch.getValue(i)); - if (response.getType() != RpcResponseType.NONE) { - generator.writeObject(response); - } - lazyTraceLogger(() -> getJsonObjectMapper().writeValueAsString(response)); - } - generator.writeEndArray(); - } - } - - /** - * Processes a single RPC request. - * - * @param maybeRequest the object that might be a request. - * @return the response from executing the request, or an error response if it wasn't a valid - * request. - */ - private JsonRpcResponse processMaybeRequest(final Object maybeRequest) { - if (maybeRequest instanceof JsonObject) { - return executeRequest((JsonObject) maybeRequest); - } else { - return createErrorResponse(); - } - } - - /** - * Executes a single RPC request. - * - * @param request the request to execute. - * @return the response from executing the request. - */ - private JsonRpcResponse executeRequest(final JsonObject request) { - return executeRequest(jsonRpcExecutor, tracer, request, ctx); - } - - /** - * Creates an error response for an invalid request. - * - * @return an error response. - */ - private JsonRpcResponse createErrorResponse() { - return new JsonRpcErrorResponse(null, INVALID_REQUEST); - } - - @Override - String getRpcMethodName(final RoutingContext ctx) { - return "JsonArray"; - } - - private boolean isBatchSizeValid(final JsonArray batchJsonRequest) { - return !(jsonRpcConfiguration.getMaxBatchSize() > 0 - && batchJsonRequest.size() > jsonRpcConfiguration.getMaxBatchSize()); - } - - private JsonArray getRequestBodyAsJsonArray(final RoutingContext ctx) { - final JsonArray batchJsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_ARRAY.name()); - lazyTraceLogger(batchJsonRequest::toString); - return batchJsonRequest; - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java index b0ae06904fa..d0801639750 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java @@ -14,19 +14,34 @@ */ package org.hyperledger.besu.ethereum.api.handlers; -import static org.hyperledger.besu.ethereum.api.handlers.AbstractJsonRpcExecutor.handleJsonRpcError; +import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_REQUEST; +import org.hyperledger.besu.ethereum.api.jsonrpc.JsonResponseStreamer; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; import java.io.IOException; import java.util.Optional; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import io.netty.handler.codec.http.HttpResponseStatus; import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.vertx.core.Handler; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.Json; +import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; +import io.vertx.ext.auth.User; import io.vertx.ext.web.RoutingContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +49,12 @@ public class JsonRpcExecutorHandler { private static final Logger LOG = LoggerFactory.getLogger(JsonRpcExecutorHandler.class); + private static final String SPAN_CONTEXT = "span_context"; + + private static final ObjectMapper jsonObjectMapper = + new ObjectMapper() + .registerModule(new Jdk8Module()); // Handle JDK8 Optionals (de)serialization + private JsonRpcExecutorHandler() {} public static Handler handler( @@ -64,29 +85,24 @@ public static Handler handler( ctx.put("timerId", timerId); try { - createExecutor(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration) - .ifPresentOrElse( - executor -> { - try { - executor.execute(); - } catch (IOException e) { - final String method = executor.getRpcMethodName(ctx); - final String requestBodyAsJson = - ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()).toString(); - LOG.error("{} - Error streaming JSON-RPC response", method, e); - LOG.atTrace() - .setMessage("{} - Error streaming JSON-RPC response") - .addArgument(requestBodyAsJson) - .log(); - handleErrorAndEndResponse(ctx, null, RpcErrorType.INTERNAL_ERROR); - } finally { - cancelTimer(ctx); - } - }, - () -> { - handleErrorAndEndResponse(ctx, null, RpcErrorType.PARSE_ERROR); - cancelTimer(ctx); - }); + if (isJsonObjectRequest(ctx)) { + executeSingleRequest(jsonRpcExecutor, tracer, ctx); + } else if (isJsonArrayRequest(ctx)) { + executeBatchRequest(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration); + } else { + handleErrorAndEndResponse(ctx, null, RpcErrorType.PARSE_ERROR); + cancelTimer(ctx); + } + } catch (final IOException e) { + final String requestBodyAsJson = + ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()).toString(); + LOG.error("Error streaming JSON-RPC response", e); + LOG.atTrace() + .setMessage("Error streaming JSON-RPC response") + .addArgument(requestBodyAsJson) + .log(); + handleErrorAndEndResponse(ctx, null, RpcErrorType.INTERNAL_ERROR); + cancelTimer(ctx); } catch (final RuntimeException e) { final String requestBodyAsJson = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()).toString(); @@ -104,41 +120,153 @@ public static Handler handler( }; } - private static Object getShortLogString(final String requestBodyAsJson) { - final int maxLogLength = 256; - return requestBodyAsJson == null || requestBodyAsJson.length() < maxLogLength - ? requestBodyAsJson - : requestBodyAsJson.substring(0, maxLogLength).concat("..."); + private static void executeSingleRequest( + final JsonRpcExecutor jsonRpcExecutor, final Tracer tracer, final RoutingContext ctx) + throws IOException { + HttpServerResponse response = ctx.response(); + response = response.putHeader("Content-Type", APPLICATION_JSON); + + final JsonObject jsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); + lazyTraceLogger(jsonRequest::toString); + final JsonRpcResponse jsonRpcResponse = + executeRequest(jsonRpcExecutor, tracer, jsonRequest, ctx); + handleSingleResponse(response, jsonRpcResponse, ctx); + cancelTimer(ctx); } - private static void cancelTimer(final RoutingContext ctx) { - Long timerId = ctx.get("timerId"); - if (timerId != null) { - ctx.vertx().cancelTimer(timerId); + private static void executeBatchRequest( + final JsonRpcExecutor jsonRpcExecutor, + final Tracer tracer, + final RoutingContext ctx, + final JsonRpcConfiguration jsonRpcConfiguration) + throws IOException { + HttpServerResponse response = ctx.response(); + response = response.putHeader("Content-Type", APPLICATION_JSON); + + final JsonArray batchJsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_ARRAY.name()); + lazyTraceLogger(batchJsonRequest::toString); + + if (isBatchSizeValid(batchJsonRequest, jsonRpcConfiguration)) { + try (final JsonResponseStreamer streamer = + new JsonResponseStreamer(response, ctx.request().remoteAddress())) { + executeBatch(jsonRpcExecutor, tracer, batchJsonRequest, streamer, ctx); + } + } else { + handleErrorAndEndResponse(ctx, null, RpcErrorType.EXCEEDS_RPC_MAX_BATCH_SIZE); } + cancelTimer(ctx); } - private static void handleErrorAndEndResponse( - final RoutingContext ctx, final Object id, final RpcErrorType errorType) { - if (!ctx.response().ended()) { - handleJsonRpcError(ctx, id, errorType); + private static void executeBatch( + final JsonRpcExecutor jsonRpcExecutor, + final Tracer tracer, + final JsonArray batchJsonRequest, + final JsonResponseStreamer streamer, + final RoutingContext ctx) + throws IOException { + try (JsonGenerator generator = getJsonObjectMapper().getFactory().createGenerator(streamer)) { + generator.writeStartArray(); + for (int i = 0; i < batchJsonRequest.size(); i++) { + JsonRpcResponse response = + processMaybeRequest(jsonRpcExecutor, tracer, batchJsonRequest.getValue(i), ctx); + if (response.getType() != RpcResponseType.NONE) { + generator.writeObject(response); + } + lazyTraceLogger(() -> getJsonObjectMapper().writeValueAsString(response)); + } + generator.writeEndArray(); } } - private static Optional createExecutor( + private static JsonRpcResponse processMaybeRequest( final JsonRpcExecutor jsonRpcExecutor, final Tracer tracer, - final RoutingContext ctx, - final JsonRpcConfiguration jsonRpcConfiguration) { - if (isJsonObjectRequest(ctx)) { - return Optional.of( - new JsonRpcObjectExecutor(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration)); + final Object maybeRequest, + final RoutingContext ctx) { + if (maybeRequest instanceof JsonObject) { + return executeRequest(jsonRpcExecutor, tracer, (JsonObject) maybeRequest, ctx); + } else { + return new JsonRpcErrorResponse(null, INVALID_REQUEST); } - if (isJsonArrayRequest(ctx)) { - return Optional.of( - new JsonRpcArrayExecutor(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration)); + } + + private static void handleSingleResponse( + final HttpServerResponse response, + final JsonRpcResponse jsonRpcResponse, + final RoutingContext ctx) + throws IOException { + response.setStatusCode(status(jsonRpcResponse).code()); + if (jsonRpcResponse.getType() == RpcResponseType.NONE) { + response.end(); + } else { + try (final JsonResponseStreamer streamer = + new JsonResponseStreamer(response, ctx.request().remoteAddress())) { + lazyTraceLogger(() -> getJsonObjectMapper().writeValueAsString(jsonRpcResponse)); + getJsonObjectMapper().writeValue(streamer, jsonRpcResponse); + } + } + } + + private static JsonRpcResponse executeRequest( + final JsonRpcExecutor jsonRpcExecutor, + final Tracer tracer, + final JsonObject jsonRequest, + final RoutingContext ctx) { + final Optional user = ContextKey.AUTHENTICATED_USER.extractFrom(ctx, Optional::empty); + final Context spanContext = ctx != null ? ctx.get(SPAN_CONTEXT) : Context.current(); + return jsonRpcExecutor.execute( + user, + tracer, + spanContext, + ctx != null ? () -> !ctx.response().closed() : () -> true, + jsonRequest, + req -> req.mapTo(JsonRpcRequest.class)); + } + + private static boolean isBatchSizeValid( + final JsonArray batchJsonRequest, final JsonRpcConfiguration jsonRpcConfiguration) { + return !(jsonRpcConfiguration.getMaxBatchSize() > 0 + && batchJsonRequest.size() > jsonRpcConfiguration.getMaxBatchSize()); + } + + private static HttpResponseStatus status(final JsonRpcResponse response) { + return switch (response.getType()) { + case UNAUTHORIZED -> HttpResponseStatus.UNAUTHORIZED; + case ERROR -> statusCodeFromError(((JsonRpcErrorResponse) response).getErrorType()); + default -> HttpResponseStatus.OK; + }; + } + + private static HttpResponseStatus statusCodeFromError(final RpcErrorType error) { + return switch (error) { + case INVALID_REQUEST, PARSE_ERROR -> HttpResponseStatus.BAD_REQUEST; + case TIMEOUT_ERROR -> HttpResponseStatus.REQUEST_TIMEOUT; + default -> HttpResponseStatus.OK; + }; + } + + private static void handleErrorAndEndResponse( + final RoutingContext ctx, final Object id, final RpcErrorType errorType) { + if (!ctx.response().ended()) { + final HttpServerResponse response = ctx.response(); + response + .setStatusCode(statusCodeFromError(errorType).code()) + .end(Json.encode(new JsonRpcErrorResponse(id, errorType))); + } + } + + private static Object getShortLogString(final String requestBodyAsJson) { + final int maxLogLength = 256; + return requestBodyAsJson == null || requestBodyAsJson.length() < maxLogLength + ? requestBodyAsJson + : requestBodyAsJson.substring(0, maxLogLength).concat("..."); + } + + private static void cancelTimer(final RoutingContext ctx) { + Long timerId = ctx.get("timerId"); + if (timerId != null) { + ctx.vertx().cancelTimer(timerId); } - return Optional.empty(); } private static boolean isJsonObjectRequest(final RoutingContext ctx) { @@ -161,4 +289,23 @@ private static long resolveTimeoutMillis( } return config.getHttpTimeoutSec() * 1000; } + + private static ObjectMapper getJsonObjectMapper() { + return jsonObjectMapper; + } + + @FunctionalInterface + private interface ExceptionThrowingSupplier { + T get() throws Exception; + } + + private static void lazyTraceLogger(final ExceptionThrowingSupplier logMessageSupplier) { + if (LOG.isTraceEnabled()) { + try { + LOG.trace(logMessageSupplier.get()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java index 3afec845791..e69de29bb2d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java @@ -1,166 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.handlers; - -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonResponseStreamer; -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; -import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; -import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; - -import java.io.IOException; -import java.util.Optional; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectWriter; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.auth.User; -import io.vertx.ext.web.RoutingContext; - -public class JsonRpcObjectExecutor extends AbstractJsonRpcExecutor { - private final ObjectWriter jsonObjectWriter = createObjectWriter(); - - public JsonRpcObjectExecutor( - final JsonRpcExecutor jsonRpcExecutor, - final Tracer tracer, - final RoutingContext ctx, - final JsonRpcConfiguration jsonRpcConfiguration) { - super(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration); - } - - @Override - void execute() throws IOException { - final HttpServerResponse response = prepareHttpResponse(ctx); - final JsonObject jsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); - - if (jsonRpcExecutor.isStreamingMethod(jsonRequest.getString("method"))) { - executeStreamingMethod(response, jsonRequest); - return; - } - - lazyTraceLogger(jsonRequest::toString); - final JsonRpcResponse jsonRpcResponse = - executeRequest(jsonRpcExecutor, tracer, jsonRequest, ctx); - handleJsonObjectResponse(response, jsonRpcResponse, ctx); - } - - private void executeStreamingMethod( - final HttpServerResponse response, final JsonObject jsonRequest) throws IOException { - // Do NOT set the status code eagerly — let JsonResponseStreamer flush headers - // on first write. This keeps the response uncommitted so that pre-stream - // errors (bad params, auth failures, missing blocks) can still produce a - // proper HTTP error with the correct status code. - final JsonResponseStreamer streamer = - new JsonResponseStreamer(response, ctx.request().remoteAddress()); - try { - final Optional user = ContextKey.AUTHENTICATED_USER.extractFrom(ctx, Optional::empty); - final Context spanContext = ctx.get(SPAN_CONTEXT); - final Optional preStreamError = - jsonRpcExecutor.executeStreaming( - user, - tracer, - spanContext, - () -> !ctx.response().closed(), - jsonRequest, - req -> req.mapTo(JsonRpcRequest.class), - streamer, - getJsonObjectMapper()); - if (preStreamError.isPresent()) { - // Validation failed before any data was written to the stream. - // The streamer's close() is a no-op (chunked never set), so we can - // send a proper error response with the correct HTTP status code. - handleJsonObjectResponse(response, preStreamError.get(), ctx); - return; - } - // Streaming completed — end the chunked response. - streamer.close(); - } catch (final Exception e) { - if (!response.headWritten()) { - // Headers not flushed yet — send a proper HTTP error response. - final Object id = jsonRequest.getValue("id"); - final RpcErrorType errorType = - e instanceof InvalidJsonRpcRequestException ijrp - ? ijrp.getRpcErrorType() - : RpcErrorType.INTERNAL_ERROR; - handleJsonRpcError(ctx, id, errorType); - } else if (!response.ended()) { - // Streaming started but failed mid-stream — reset the connection so the - // client sees a transport error rather than truncated JSON. - response.reset(); - } - if (e instanceof IOException ioe) { - throw ioe; - } - } - } - - @Override - String getRpcMethodName(final RoutingContext ctx) { - final JsonObject jsonObject = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); - return jsonObject.getString("method"); - } - - private void handleJsonObjectResponse( - final HttpServerResponse response, - final JsonRpcResponse jsonRpcResponse, - final RoutingContext ctx) - throws IOException { - response.setStatusCode(status(jsonRpcResponse).code()); - if (jsonRpcResponse.getType() == RpcResponseType.NONE) { - response.end(); - return; - } - - try (final JsonResponseStreamer streamer = - new JsonResponseStreamer(response, ctx.request().remoteAddress())) { - lazyTraceLogger(() -> getJsonObjectMapper().writeValueAsString(jsonRpcResponse)); - jsonObjectWriter.writeValue(streamer, jsonRpcResponse); - } - } - - private static HttpResponseStatus status(final JsonRpcResponse response) { - return switch (response.getType()) { - case UNAUTHORIZED -> HttpResponseStatus.UNAUTHORIZED; - case ERROR -> statusCodeFromError(((JsonRpcErrorResponse) response).getErrorType()); - default -> HttpResponseStatus.OK; - }; - } - - private ObjectWriter createObjectWriter() { - ObjectWriter writer = - jsonRpcConfiguration.isPrettyJsonEnabled() - ? getJsonObjectMapper().writerWithDefaultPrettyPrinter() - : getJsonObjectMapper().writer(); - return writer - .without(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM) - .with(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - } - - private static HttpResponseStatus statusCodeFromError(final RpcErrorType error) { - return switch (error) { - case INVALID_REQUEST, PARSE_ERROR -> HttpResponseStatus.BAD_REQUEST; - default -> HttpResponseStatus.OK; - }; - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/EngineJsonRpcService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/EngineJsonRpcService.java index 34595c94982..6c6b58633be 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/EngineJsonRpcService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/EngineJsonRpcService.java @@ -24,11 +24,9 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.AuthenticationUtils; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.DefaultAuthenticationService; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.AuthenticatedJsonRpcProcessor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcProcessor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.TimedJsonRpcProcessor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.TracedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.health.HealthService; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.Logging403ErrorHandler; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod; @@ -39,7 +37,6 @@ import org.hyperledger.besu.ethereum.api.tls.TlsClientAuthConfiguration; import org.hyperledger.besu.ethereum.api.tls.TlsConfiguration; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; -import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.metrics.opentelemetry.OpenTelemetrySystem; import org.hyperledger.besu.nat.NatMethod; import org.hyperledger.besu.nat.NatService; @@ -47,8 +44,6 @@ import org.hyperledger.besu.nat.core.domain.NetworkProtocol; import org.hyperledger.besu.nat.upnp.UpnpNatManager; import org.hyperledger.besu.plugin.services.MetricsSystem; -import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; -import org.hyperledger.besu.plugin.services.metrics.OperationTimer; import org.hyperledger.besu.util.ExceptionUtils; import org.hyperledger.besu.util.NetworkUtility; @@ -135,7 +130,6 @@ public String get(final @Nullable HttpServerRequest carrier, final String key) { private final Map rpcMethods; private final NatService natService; private final Path dataDir; - private final LabelledMetric requestTimer; private TracerProvider tracerProvider; private Tracer tracer; private final int maxActiveConnections; @@ -180,13 +174,7 @@ public EngineJsonRpcService( final HealthService livenessService, final HealthService readinessService) { this.dataDir = dataDir; - this.requestTimer = - metricsSystem.createLabelledTimer( - BesuMetricCategory.RPC, - "request_time", - "Time taken to process a JSON-RPC request", - "methodName"); - JsonRpcProcessor jsonRpcProcessor = new BaseJsonRpcProcessor(); + JsonRpcProcessor jsonRpcProcessor = new CombinedJsonRpcProcessor(metricsSystem); if (metricsSystem instanceof OpenTelemetrySystem) { this.tracerProvider = ((OpenTelemetrySystem) metricsSystem).getTracerProvider(); } @@ -467,9 +455,7 @@ private Router buildRouter() { HandlerFactory.jsonRpcExecutor( new JsonRpcExecutor( new AuthenticatedJsonRpcProcessor( - new TimedJsonRpcProcessor( - new TracedJsonRpcProcessor(new BaseJsonRpcProcessor(), metricsSystem), - requestTimer), + new CombinedJsonRpcProcessor(metricsSystem), authenticationService.get(), config.getNoAuthRpcApis()), rpcMethods), @@ -479,11 +465,7 @@ private Router buildRouter() { } else { mainRoute.blockingHandler( HandlerFactory.jsonRpcExecutor( - new JsonRpcExecutor( - new TimedJsonRpcProcessor( - new TracedJsonRpcProcessor(new BaseJsonRpcProcessor(), metricsSystem), - requestTimer), - rpcMethods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(metricsSystem), rpcMethods), tracer, config), false); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java index 666de8bd24b..3a265fa1532 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java @@ -22,10 +22,8 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.AuthenticationService; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.DefaultAuthenticationService; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.AuthenticatedJsonRpcProcessor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.TimedJsonRpcProcessor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.TracedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.health.HealthService; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.Logging403ErrorHandler; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; @@ -39,8 +37,6 @@ import org.hyperledger.besu.nat.core.domain.NetworkProtocol; import org.hyperledger.besu.nat.upnp.UpnpNatManager; import org.hyperledger.besu.plugin.services.MetricsSystem; -import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; -import org.hyperledger.besu.plugin.services.metrics.OperationTimer; import org.hyperledger.besu.util.ExceptionUtils; import org.hyperledger.besu.util.NetworkUtility; @@ -123,7 +119,6 @@ public String get(final @Nullable HttpServerRequest carrier, final String key) { private final Map rpcMethods; private final NatService natService; private final Path dataDir; - private final LabelledMetric requestTimer; private TracerProvider tracerProvider; private Tracer tracer; private final int maxActiveConnections; @@ -180,12 +175,6 @@ public JsonRpcHttpService( final HealthService livenessService, final HealthService readinessService) { this.dataDir = dataDir; - requestTimer = - metricsSystem.createLabelledTimer( - BesuMetricCategory.RPC, - "request_time", - "Time taken to process a JSON-RPC request", - "methodName"); metricsSystem.createIntegerGauge( BesuMetricCategory.RPC, @@ -345,9 +334,7 @@ private Router buildRouter() { HandlerFactory.jsonRpcExecutor( new JsonRpcExecutor( new AuthenticatedJsonRpcProcessor( - new TimedJsonRpcProcessor( - new TracedJsonRpcProcessor(new BaseJsonRpcProcessor(), metricsSystem), - requestTimer), + new CombinedJsonRpcProcessor(metricsSystem), authenticationService.get(), config.getNoAuthRpcApis()), rpcMethods), @@ -357,11 +344,7 @@ private Router buildRouter() { } else { mainRoute.blockingHandler( HandlerFactory.jsonRpcExecutor( - new JsonRpcExecutor( - new TimedJsonRpcProcessor( - new TracedJsonRpcProcessor(new BaseJsonRpcProcessor(), metricsSystem), - requestTimer), - rpcMethods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(metricsSystem), rpcMethods), tracer, config), false); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/BaseJsonRpcProcessor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/BaseJsonRpcProcessor.java index 2274de046ba..e69de29bb2d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/BaseJsonRpcProcessor.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/BaseJsonRpcProcessor.java @@ -1,77 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.jsonrpc.execution; - -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestId; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.StreamingJsonRpcMethod; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.opentelemetry.api.trace.Span; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BaseJsonRpcProcessor implements JsonRpcProcessor { - - private static final Logger LOG = LoggerFactory.getLogger(BaseJsonRpcProcessor.class); - - @Override - public JsonRpcResponse process( - final JsonRpcRequestId id, - final JsonRpcMethod method, - final Span metricSpan, - final JsonRpcRequestContext request) { - try { - return method.response(request); - } catch (final InvalidJsonRpcParameters e) { - LOG.debug( - "Invalid Params {} for method: {}, error: {}", - Arrays.toString(request.getRequest().getParams()), - method.getName(), - e.getRpcErrorType().getMessage(), - e); - return new JsonRpcErrorResponse(id, e.getRpcErrorType()); - } catch (final RuntimeException e) { - final JsonArray params = JsonObject.mapFrom(request.getRequest()).getJsonArray("params"); - LOG.error(String.format("Error processing method: %s %s", method.getName(), params), e); - return new JsonRpcErrorResponse(id, RpcErrorType.INTERNAL_ERROR); - } - } - - @Override - public void streamProcess( - final JsonRpcRequestId id, - final JsonRpcMethod method, - final Span metricSpan, - final JsonRpcRequestContext request, - final OutputStream out, - final ObjectMapper mapper) - throws IOException { - // Let exceptions propagate — the caller decides how to handle them based on - // whether the response headers have already been flushed. - ((StreamingJsonRpcMethod) method).streamResponse(request, out, mapper); - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/CombinedJsonRpcProcessor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/CombinedJsonRpcProcessor.java new file mode 100644 index 00000000000..e734569b6a7 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/CombinedJsonRpcProcessor.java @@ -0,0 +1,219 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.execution; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestId; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.metrics.BesuMetricCategory; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.Counter; +import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; +import org.hyperledger.besu.plugin.services.metrics.OperationTimer; +import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CombinedJsonRpcProcessor implements JsonRpcProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(CombinedJsonRpcProcessor.class); + + private final LabelledMetric requestTimer; + private final LabelledMetric rpcErrorsCounter; + + public CombinedJsonRpcProcessor(final MetricsSystem metricsSystem) { + this.requestTimer = + metricsSystem.createLabelledTimer( + BesuMetricCategory.RPC, + "request_time", + "Time to process JSON-RPC requests", + "methodName"); + this.rpcErrorsCounter = + metricsSystem.createLabelledCounter( + BesuMetricCategory.RPC, + "errors_count", + "Number of errors per RPC method and RPC error type", + "rpcMethod", + "errorType"); + } + + @Override + public JsonRpcResponse process( + final JsonRpcRequestId id, + final JsonRpcMethod method, + final Span metricSpan, + final JsonRpcRequestContext request) { + try (final OperationTimer.TimingContext ignored = + requestTimer.labels(request.getRequest().getMethod()).startTimer()) { + JsonRpcResponse response = executeMethod(id, method, request); + if (response.getType() == RpcResponseType.ERROR) { + recordError(method, response, metricSpan); + } + metricSpan.end(); + return response; + } + } + + @Override + @SuppressWarnings("UnusedVariable") // Parameters required by interface + public void streamProcess( + final JsonRpcRequestId id, + final JsonRpcMethod method, + final Span metricSpan, + final JsonRpcRequestContext request, + final OutputStream out, + final ObjectMapper mapper) + throws IOException { + try (final OperationTimer.TimingContext ignored = + requestTimer.labels(request.getRequest().getMethod()).startTimer()) { + executeMethodAndStream(id, method, request, out, mapper); + metricSpan.end(); + } + } + + @SuppressWarnings("UnusedVariable") // Parameters required by interface + private void executeMethodAndStream( + final JsonRpcRequestId id, + final JsonRpcMethod method, + final JsonRpcRequestContext request, + final OutputStream out, + final ObjectMapper mapper) + throws IOException { + // Default implementation throws UnsupportedOperationException + // Methods supporting streaming override this + throw new UnsupportedOperationException( + "Method " + method.getName() + " does not support streaming"); + } + + private JsonRpcResponse executeMethod( + final JsonRpcRequestId id, final JsonRpcMethod method, final JsonRpcRequestContext request) { + try { + return method.response(request); + } catch (final InvalidJsonRpcParameters e) { + LOG.debug( + "Invalid Params {} for method: {}, error: {}", + Arrays.toString(request.getRequest().getParams()), + method.getName(), + e.getRpcErrorType().getMessage(), + e); + return new JsonRpcErrorResponse(id, e.getRpcErrorType()); + } catch (final RuntimeException e) { + final JsonArray params = JsonObject.mapFrom(request.getRequest()).getJsonArray("params"); + LOG.error(String.format("Error processing method: %s %s", method.getName(), params), e); + return new JsonRpcErrorResponse(id, RpcErrorType.INTERNAL_ERROR); + } + } + + private void recordError( + final JsonRpcMethod method, final JsonRpcResponse response, final Span metricSpan) { + JsonRpcErrorResponse errorResponse = (JsonRpcErrorResponse) response; + RpcErrorType errorType = errorResponse.getErrorType(); + rpcErrorsCounter.labels(method.getName(), errorType.name()).inc(); + setSpanStatus(metricSpan, errorType); + } + + private void setSpanStatus(final Span span, final RpcErrorType errorType) { + switch (errorType) { + case INVALID_PARAMS: + case INVALID_ACCOUNT_PARAMS: + case INVALID_ADDRESS_HASH_PARAMS: + case INVALID_ADDRESS_PARAMS: + case INVALID_BLOB_COUNT: + case INVALID_BLOB_GAS_USED_PARAMS: + case INVALID_BLOCK_PARAMS: + case INVALID_BLOCK_COUNT_PARAMS: + case INVALID_BLOCK_HASH_PARAMS: + case INVALID_BLOCK_INDEX_PARAMS: + case INVALID_BLOCK_NUMBER_PARAMS: + case INVALID_CALL_PARAMS: + case INVALID_CONSOLIDATION_REQUEST_PARAMS: + case INVALID_DATA_PARAMS: + case INVALID_DEPOSIT_REQUEST_PARAMS: + case INVALID_ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_PARAMS: + case INVALID_ENGINE_FORKCHOICE_UPDATED_PARAMS: + case INVALID_ENGINE_FORKCHOICE_UPDATED_PAYLOAD_ATTRIBUTES: + case INVALID_ENGINE_NEW_PAYLOAD_PARAMS: + case INVALID_ENGINE_PREPARE_PAYLOAD_PARAMS: + case INVALID_ENODE_PARAMS: + case INVALID_EXCESS_BLOB_GAS_PARAMS: + case INVALID_EXECUTION_REQUESTS_PARAMS: + case INVALID_EXTRA_DATA_PARAMS: + case INVALID_FILTER_PARAMS: + case INVALID_HASH_RATE_PARAMS: + case INVALID_ID_PARAMS: + case INVALID_LOG_FILTER_PARAMS: + case INVALID_LOG_LEVEL_PARAMS: + case INVALID_MAX_RESULTS_PARAMS: + case INVALID_METHOD_PARAMS: + case INVALID_MIN_GAS_PRICE_PARAMS: + case INVALID_MIN_PRIORITY_FEE_PARAMS: + case INVALID_MIX_HASH_PARAMS: + case INVALID_NONCE_PARAMS: + case INVALID_PARENT_BEACON_BLOCK_ROOT_PARAMS: + case INVALID_PARAM_COUNT: + case INVALID_PAYLOAD_ID_PARAMS: + case INVALID_PENDING_TRANSACTIONS_PARAMS: + case INVALID_PLUGIN_NAME_PARAMS: + case INVALID_POSITION_PARAMS: + case INVALID_POW_HASH_PARAMS: + case INVALID_PRIVATE_FROM_PARAMS: + case INVALID_PRIVATE_FOR_PARAMS: + case INVALID_PROPOSAL_PARAMS: + case INVALID_REMOTE_CAPABILITIES_PARAMS: + case INVALID_REWARD_PERCENTILES_PARAMS: + case INVALID_REQUESTS_PARAMS: + case INVALID_SEALER_ID_PARAMS: + case INVALID_STORAGE_KEYS_PARAMS: + case INVALID_SUBSCRIPTION_PARAMS: + case INVALID_TARGET_GAS_LIMIT_PARAMS: + case INVALID_TIMESTAMP_PARAMS: + case INVALID_TRACE_CALL_MANY_PARAMS: + case INVALID_TRACE_NUMBERS_PARAMS: + case INVALID_TRACE_TYPE_PARAMS: + case INVALID_TRANSACTION_PARAMS: + case INVALID_TRANSACTION_HASH_PARAMS: + case INVALID_TRANSACTION_INDEX_PARAMS: + case INVALID_TRANSACTION_LIMIT_PARAMS: + case INVALID_TRANSACTION_TRACE_PARAMS: + case INVALID_VERSIONED_HASH_PARAMS: + case INVALID_VOTE_TYPE_PARAMS: + case INVALID_WITHDRAWALS_PARAMS: + span.setStatus(StatusCode.ERROR, "Invalid Params"); + break; + case UNAUTHORIZED: + span.setStatus(StatusCode.ERROR, "Unauthorized"); + break; + case INTERNAL_ERROR: + span.setStatus(StatusCode.ERROR, "Error processing JSON-RPC requestBody"); + break; + default: + span.setStatus(StatusCode.ERROR, "Unexpected error"); + } + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TimedJsonRpcProcessor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TimedJsonRpcProcessor.java index 585f8f194fe..e69de29bb2d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TimedJsonRpcProcessor.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TimedJsonRpcProcessor.java @@ -1,67 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.jsonrpc.execution; - -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestId; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; -import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; -import org.hyperledger.besu.plugin.services.metrics.OperationTimer; - -import java.io.IOException; -import java.io.OutputStream; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.opentelemetry.api.trace.Span; - -public class TimedJsonRpcProcessor implements JsonRpcProcessor { - - private final JsonRpcProcessor rpcProcessor; - private final LabelledMetric requestTimer; - - public TimedJsonRpcProcessor( - final JsonRpcProcessor rpcProcessor, final LabelledMetric requestTimer) { - this.rpcProcessor = rpcProcessor; - this.requestTimer = requestTimer; - } - - @Override - public JsonRpcResponse process( - final JsonRpcRequestId id, - final JsonRpcMethod method, - final Span metricSpan, - final JsonRpcRequestContext request) { - try (final OperationTimer.TimingContext ignored = - requestTimer.labels(request.getRequest().getMethod()).startTimer()) { - return rpcProcessor.process(id, method, metricSpan, request); - } - } - - @Override - public void streamProcess( - final JsonRpcRequestId id, - final JsonRpcMethod method, - final Span metricSpan, - final JsonRpcRequestContext request, - final OutputStream out, - final ObjectMapper mapper) - throws IOException { - try (final OperationTimer.TimingContext ignored = - requestTimer.labels(request.getRequest().getMethod()).startTimer()) { - rpcProcessor.streamProcess(id, method, metricSpan, request, out, mapper); - } - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java index 71c67f26337..e69de29bb2d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java @@ -1,169 +0,0 @@ -/* - * Copyright contributors to Hyperledger Besu. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.api.jsonrpc.execution; - -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestId; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; -import org.hyperledger.besu.metrics.BesuMetricCategory; -import org.hyperledger.besu.plugin.services.MetricsSystem; -import org.hyperledger.besu.plugin.services.metrics.Counter; -import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; -import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; - -import java.io.IOException; -import java.io.OutputStream; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.StatusCode; - -public class TracedJsonRpcProcessor implements JsonRpcProcessor { - - private final JsonRpcProcessor rpcProcessor; - protected final LabelledMetric rpcErrorsCounter; - - public TracedJsonRpcProcessor( - final JsonRpcProcessor rpcProcessor, final MetricsSystem metricsSystem) { - this.rpcProcessor = rpcProcessor; - this.rpcErrorsCounter = - metricsSystem.createLabelledCounter( - BesuMetricCategory.RPC, - "errors_count", - "Number of errors per RPC method and RPC error type", - "rpcMethod", - "errorType"); - } - - @Override - public JsonRpcResponse process( - final JsonRpcRequestId id, - final JsonRpcMethod method, - final Span metricSpan, - final JsonRpcRequestContext request) { - JsonRpcResponse jsonRpcResponse = rpcProcessor.process(id, method, metricSpan, request); - if (RpcResponseType.ERROR == jsonRpcResponse.getType()) { - JsonRpcErrorResponse errorResponse = (JsonRpcErrorResponse) jsonRpcResponse; - this.rpcErrorsCounter.labels(method.getName(), errorResponse.getErrorType().name()).inc(); - switch (errorResponse.getErrorType()) { - case INVALID_PARAMS: - case INVALID_ACCOUNT_PARAMS: - case INVALID_ADDRESS_HASH_PARAMS: - case INVALID_ADDRESS_PARAMS: - case INVALID_BLOB_COUNT: - case INVALID_BLOB_GAS_USED_PARAMS: - case INVALID_BLOCK_PARAMS: - case INVALID_BLOCK_COUNT_PARAMS: - case INVALID_BLOCK_HASH_PARAMS: - case INVALID_BLOCK_INDEX_PARAMS: - case INVALID_BLOCK_NUMBER_PARAMS: - case INVALID_CALL_PARAMS: - case INVALID_CONSOLIDATION_REQUEST_PARAMS: - case INVALID_DATA_PARAMS: - case INVALID_DEPOSIT_REQUEST_PARAMS: - case INVALID_ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_PARAMS: - case INVALID_ENGINE_FORKCHOICE_UPDATED_PARAMS: - case INVALID_ENGINE_FORKCHOICE_UPDATED_PAYLOAD_ATTRIBUTES: - case INVALID_ENGINE_NEW_PAYLOAD_PARAMS: - case INVALID_ENGINE_PREPARE_PAYLOAD_PARAMS: - case INVALID_ENODE_PARAMS: - case INVALID_EXCESS_BLOB_GAS_PARAMS: - case INVALID_EXECUTION_REQUESTS_PARAMS: - case INVALID_EXTRA_DATA_PARAMS: - case INVALID_FILTER_PARAMS: - case INVALID_HASH_RATE_PARAMS: - case INVALID_ID_PARAMS: - case INVALID_RETURN_COMPLETE_TRANSACTION_PARAMS: - case INVALID_LOG_FILTER_PARAMS: - case INVALID_LOG_LEVEL_PARAMS: - case INVALID_MAX_RESULTS_PARAMS: - case INVALID_METHOD_PARAMS: - case INVALID_MIN_GAS_PRICE_PARAMS: - case INVALID_MIN_PRIORITY_FEE_PARAMS: - case INVALID_MIX_HASH_PARAMS: - case INVALID_NONCE_PARAMS: - case INVALID_PARENT_BEACON_BLOCK_ROOT_PARAMS: - case INVALID_PARAM_COUNT: - case INVALID_PAYLOAD_ID_PARAMS: - case INVALID_PENDING_TRANSACTIONS_PARAMS: - case INVALID_PLUGIN_NAME_PARAMS: - case INVALID_POSITION_PARAMS: - case INVALID_POW_HASH_PARAMS: - case INVALID_PRIVATE_FROM_PARAMS: - case INVALID_PRIVATE_FOR_PARAMS: - case INVALID_PROPOSAL_PARAMS: - case INVALID_REMOTE_CAPABILITIES_PARAMS: - case INVALID_REWARD_PERCENTILES_PARAMS: - case INVALID_REQUESTS_PARAMS: - case INVALID_SEALER_ID_PARAMS: - case INVALID_STORAGE_KEYS_PARAMS: - case INVALID_SUBSCRIPTION_PARAMS: - case INVALID_TARGET_GAS_LIMIT_PARAMS: - case INVALID_TIMESTAMP_PARAMS: - case INVALID_TRACE_CALL_MANY_PARAMS: - case INVALID_TRACE_NUMBERS_PARAMS: - case INVALID_TRACE_TYPE_PARAMS: - case INVALID_TRANSACTION_PARAMS: - case INVALID_TRANSACTION_HASH_PARAMS: - case INVALID_TRANSACTION_ID_PARAMS: - case INVALID_TRANSACTION_INDEX_PARAMS: - case INVALID_TRANSACTION_LIMIT_PARAMS: - case INVALID_TRANSACTION_TRACE_PARAMS: - case INVALID_VERSIONED_HASH_PARAMS: - case INVALID_VOTE_TYPE_PARAMS: - case INVALID_WITHDRAWALS_PARAMS: - metricSpan.setStatus(StatusCode.ERROR, "Invalid Params"); - break; - case UNAUTHORIZED: - metricSpan.setStatus(StatusCode.ERROR, "Unauthorized"); - break; - case INTERNAL_ERROR: - metricSpan.setStatus(StatusCode.ERROR, "Error processing JSON-RPC requestBody"); - break; - default: - metricSpan.setStatus(StatusCode.ERROR, "Unexpected error"); - } - } - metricSpan.end(); - return jsonRpcResponse; - } - - @Override - public void streamProcess( - final JsonRpcRequestId id, - final JsonRpcMethod method, - final Span metricSpan, - final JsonRpcRequestContext request, - final OutputStream out, - final ObjectMapper mapper) - throws IOException { - try { - rpcProcessor.streamProcess(id, method, metricSpan, request, out, mapper); - } catch (final IOException | RuntimeException e) { - final String errorLabel = - e instanceof InvalidJsonRpcRequestException ijrp - ? ijrp.getRpcErrorType().name() - : "INTERNAL_ERROR"; - rpcErrorsCounter.labels(method.getName(), errorLabel).inc(); - metricSpan.setStatus(StatusCode.ERROR, "Error processing JSON-RPC requestBody"); - throw e; - } finally { - metricSpan.end(); - } - } -} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/ipc/JsonRpcIpcServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/ipc/JsonRpcIpcServiceTest.java index 985bc4f9f59..e905bff77c4 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/ipc/JsonRpcIpcServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/ipc/JsonRpcIpcServiceTest.java @@ -19,7 +19,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; @@ -85,7 +85,7 @@ void successfulExecution() { new JsonRpcIpcService( vertx, socketPath, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), Map.of("test_method", testMethod))); + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), Map.of("test_method", testMethod))); final String expectedResponse = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"TEST OK\"}\n"; assertSocketCall( @@ -107,7 +107,7 @@ void successfulBatchExecution() { vertx, socketPath, new JsonRpcExecutor( - new BaseJsonRpcProcessor(), + new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), Map.of("foo_method", fooMethod, "bar_method", barMethod))); assertSocketCall( @@ -128,7 +128,7 @@ void validJsonButNotRpcShouldReturnInvalidRequest() { new JsonRpcIpcService( vertx, socketPath, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), Collections.emptyMap())); + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), Collections.emptyMap())); final String expectedResponse = "{\"jsonrpc\":\"2.0\",\"id\":null,\"error\":{\"code\":-32600,\"message\":\"Invalid Request\"}}\n"; @@ -160,7 +160,7 @@ void concatenatedJsonRequestsShouldBeHandledIndependently() { vertx, socketPath, new JsonRpcExecutor( - new BaseJsonRpcProcessor(), + new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), Map.of("foo_method", fooMethod, "bar_method", barMethod, "baz_method", bazMethod))); // Simulate concurrent requests concatenated in a single buffer @@ -280,7 +280,7 @@ void subscriptionRequestSuccessful() { new JsonRpcIpcService( vertx, socketPath, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), methods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), methods), Optional.of(subscriptionManager)); final String request = "{\"id\":1,\"method\":\"eth_subscribe\",\"params\":[\"newHeads\"]}\n"; @@ -326,7 +326,7 @@ void unsubscribeRequestSuccessful() { new JsonRpcIpcService( vertx, socketPath, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), methods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), methods), Optional.of(subscriptionManager)); final String subscribeRequest = @@ -383,7 +383,7 @@ void batchRequestDoesNotSupportSubscriptions() { new JsonRpcIpcService( vertx, socketPath, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), methods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), methods), Optional.of(subscriptionManager)); final String batchRequest = diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketHostAllowlistTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketHostAllowlistTest.java index 3ac348c01a1..14c807dccbb 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketHostAllowlistTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketHostAllowlistTest.java @@ -20,7 +20,7 @@ import static org.mockito.Mockito.spy; import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketMethodsFactory; @@ -80,7 +80,7 @@ public void initServerAndClient() { spy( new WebSocketMessageHandler( vertx, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), websocketMethods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), websocketMethods), mock(EthScheduler.class), TimeoutOptions.defaultOptions().getTimeoutSeconds())); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketMessageHandlerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketMessageHandlerTest.java index 3fcd20324ee..026a56e517e 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketMessageHandlerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketMessageHandlerTest.java @@ -22,7 +22,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; @@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketRpcRequest; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.util.HashMap; import java.util.List; @@ -85,7 +86,7 @@ public void before() { handler = new WebSocketMessageHandler( vertx, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), methods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), methods), mock(EthScheduler.class), TimeoutOptions.defaultOptions().getTimeoutSeconds()); } @@ -160,7 +161,7 @@ public void handlerBatchRequestContainingErrorsShouldRespondWithBatchErrors() WebSocketMessageHandler handleBadCalls = new WebSocketMessageHandler( vertx, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), methods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), methods), mock(EthScheduler.class), TimeoutOptions.defaultOptions().getTimeoutSeconds()); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java index dd17945703a..6907f4f5230 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceLoginTest.java @@ -36,7 +36,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.DefaultAuthenticationService; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.AuthenticatedJsonRpcProcessor; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.filter.FilterManager; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; @@ -228,7 +228,7 @@ public void before() throws URISyntaxException { vertx, new JsonRpcExecutor( new AuthenticatedJsonRpcProcessor( - new BaseJsonRpcProcessor(), + new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), DefaultAuthenticationService.create(vertx, websocketConfiguration).get(), websocketConfiguration.getRpcApisNoAuth()), websocketMethods), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java index da4fd9458d0..935b634e71c 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTLSTest.java @@ -20,7 +20,7 @@ import static org.mockito.Mockito.spy; import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketMethodsFactory; @@ -73,7 +73,7 @@ public void setUp() { spy( new WebSocketMessageHandler( vertx, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), websocketMethods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), websocketMethods), mock(EthScheduler.class), TimeoutOptions.defaultOptions().getTimeoutSeconds())); } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTest.java index 94c543332bb..e1e987f0fd4 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketServiceTest.java @@ -24,7 +24,7 @@ import static org.mockito.Mockito.verifyNoInteractions; import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketMethodsFactory; @@ -89,7 +89,7 @@ public void before() { spy( new WebSocketMessageHandler( vertx, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), websocketMethods), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), websocketMethods), mock(EthScheduler.class), TimeoutOptions.defaultOptions().getTimeoutSeconds())); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthSubscribeIntegrationTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthSubscribeIntegrationTest.java index 086ea8aea77..93fa0b4996f 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthSubscribeIntegrationTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthSubscribeIntegrationTest.java @@ -21,7 +21,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; @@ -76,7 +76,7 @@ public void before() { webSocketMessageHandler = new WebSocketMessageHandler( vertx, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), webSocketMethodsFactory.methods()), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), webSocketMethodsFactory.methods()), Mockito.mock(EthScheduler.class), TimeoutOptions.defaultOptions().getTimeoutSeconds()); } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthUnsubscribeIntegrationTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthUnsubscribeIntegrationTest.java index 6aa6d727914..835bb5bd153 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthUnsubscribeIntegrationTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/methods/EthUnsubscribeIntegrationTest.java @@ -21,7 +21,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions; -import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.CombinedJsonRpcProcessor; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; @@ -70,7 +70,7 @@ public void before() { webSocketMessageHandler = new WebSocketMessageHandler( vertx, - new JsonRpcExecutor(new BaseJsonRpcProcessor(), webSocketMethodsFactory.methods()), + new JsonRpcExecutor(new CombinedJsonRpcProcessor(new NoOpMetricsSystem()), webSocketMethodsFactory.methods()), mock(EthScheduler.class), TimeoutOptions.defaultOptions().getTimeoutSeconds()); }