Problem
For authorized JSON-RPC requests, RequestHandler parses the request body twice:
extractJsonRpcMethodFromBody (called from handle(), RequestHandler.java:189) — streaming parse via Jackson JsonParser to pull out only the method field, used to decide auth/rate-limiting policy.
parseRequestBodyAsJsonForRouting (called from determineTargetUrl, RequestHandler.java:501) — full objectMapper.readTree(...) to extract stateId/shardId.
The streaming parse is cheap and short-circuits early, so for rejected requests (auth/size failures) the redundancy doesn't matter — that path never runs the second parse. For authorized requests it does, and we pay both costs.
Why this wasn't fixed in #32
PR #32 adds Prometheus instrumentation; this is a pre-existing issue flagged by review there. Out of scope for that PR.
Suggested fix
Parse the body into a JsonNode once after the size check passes; pass the parsed tree (or null for non-JSON bodies) through handleRequestAsync → proxyRequest → determineTargetUrl. extractJsonRpcMethodFromBody becomes a tree lookup instead of a streaming parse.
Trade-off: streaming parse currently allocates very little when the method field appears early in the body (which is the common case for our clients). Switching to readTree allocates the whole tree even on the rejection path, where today we don't. So the right fix is conditional — only parse the tree once we know we'll need routing too. Concretely:
- Keep the streaming
extractJsonRpcMethodFromBody.
- Add a lazy
JsonNode holder built on first call inside determineTargetUrl.
- For methods whose routing comes from headers (
X-State-ID) or cookies, never parse the tree at all.
Acceptance criteria
- For authorized JSON-RPC requests with body-derived routing params, the body is parsed once.
- For requests rejected before the proxy step (auth fail / size fail / 405 etc.), allocation profile is no worse than today.
- No regression in
ProxyServerIntegrationTest, RateLimitingTest, ShardRoutingIntegrationTest, BftShardRoutingIntegrationTest.
Problem
For authorized JSON-RPC requests,
RequestHandlerparses the request body twice:extractJsonRpcMethodFromBody(called fromhandle(), RequestHandler.java:189) — streaming parse via JacksonJsonParserto pull out only themethodfield, used to decide auth/rate-limiting policy.parseRequestBodyAsJsonForRouting(called fromdetermineTargetUrl, RequestHandler.java:501) — fullobjectMapper.readTree(...)to extractstateId/shardId.The streaming parse is cheap and short-circuits early, so for rejected requests (auth/size failures) the redundancy doesn't matter — that path never runs the second parse. For authorized requests it does, and we pay both costs.
Why this wasn't fixed in #32
PR #32 adds Prometheus instrumentation; this is a pre-existing issue flagged by review there. Out of scope for that PR.
Suggested fix
Parse the body into a
JsonNodeonce after the size check passes; pass the parsed tree (ornullfor non-JSON bodies) throughhandleRequestAsync→proxyRequest→determineTargetUrl.extractJsonRpcMethodFromBodybecomes a tree lookup instead of a streaming parse.Trade-off: streaming parse currently allocates very little when the
methodfield appears early in the body (which is the common case for our clients). Switching toreadTreeallocates the whole tree even on the rejection path, where today we don't. So the right fix is conditional — only parse the tree once we know we'll need routing too. Concretely:extractJsonRpcMethodFromBody.JsonNodeholder built on first call insidedetermineTargetUrl.X-State-ID) or cookies, never parse the tree at all.Acceptance criteria
ProxyServerIntegrationTest,RateLimitingTest,ShardRoutingIntegrationTest,BftShardRoutingIntegrationTest.