Class EchoMixin

java.lang.Object
org.apache.juneau.rest.server.ops.EchoMixin

public class EchoMixin extends Object
Mixin that serves a request-echo / round-trip introspection endpoint at /echo/* (configurable).

Sibling of AdminMixin (/admin/*) and RouteIndexMixin (/options). All three classes live in the org.apache.juneau.rest.server.ops ops/introspection mixin pack.

Compose into a host resource via @Rest(mixins=EchoMixin.class); the /echo/* URL becomes available alongside the host's own endpoints with no further wiring.

Configurable mount path:

The default mount /echo/* can be overridden via the SVL variable ${juneau.echo.path:echo} — set via system property (-Djuneau.echo.path=introspect), environment variable (JUNEAU_ECHO_PATH=introspect), or Config key (juneau.echo.path = introspect) to change the runtime mount without subclassing. Resolution happens once at RestContext construction time (SVL resolution in @RestOp(path)).

Override accepts bare token (echo), leading slash (/echo), trailing slash (echo/), or wildcard suffix (/echo/*) — all resolve to the same mount.

Migration note (10.0.0): Earlier development snapshots of this mixin mounted at both /echo/* and /debug/echo/* as historical aliases on a single op. That dual default has been collapsed to a single SVL-configurable mount as part of the "single path per op" principle. Deployers who relied on the /debug/echo/* alias must now either set -Djuneau.echo.path=debug/echo or compose a second instance with the override.

Mixin-only deployment:

This resource is designed for composition via @Rest(mixins=...). The mount path is pinned at the op level by @RestOp(path="/{juneau.echo.path:echo}/*") on echo(org.apache.juneau.rest.server.RestRequest, org.apache.juneau.rest.server.RestResponse, java.lang.String); a class-level @Rest(paths=...) declaration would be silently ignored under the mixin pattern (see @Rest(paths) Javadoc).

Composition example:

@Rest( path="/api", mixins=EchoMixin.class ) public class ApiResource extends RestServlet { } // Enable the endpoint (default OFF) and, optionally, tighten the body cap or adjust // redacted headers via a @Bean factory: @Bean EchoMixin echo() { return EchoMixin.create() .enabled() .bodyLimit(64 * 1024L) .redactHeader("X-Internal-Trace") .build(); }

Enablement:

The endpoint is disabled by default. Reachability is controlled by an explicit, non-logging switch and is independent of the JUL logger level — raising a resource's (or operation's) logger to FINE/FINEST never exposes /echo/*. When the endpoint is disabled the handler returns 404 Not Found (so the existence of the endpoint isn't disclosed).

Enablement is tri-state, resolved per request:

  • An explicit builder value (true/false) always wins.
  • When the builder value is left unset (null, the default), the handler resolves the SVL expression ${juneau.echo.enabled:false} through RestRequest.getVarResolverSession() — i.e. the system property -Djuneau.echo.enabled=true or the relaxed environment variable JUNEAU_ECHO_ENABLED=true. A per-resource Config key is not consulted for this switch.

GLOBAL blast-radius warning: juneau.echo.enabled=true is a process-wide system-property / environment switch. It enables /echo/* on every host in the JVM that mixes in EchoMixin — including hosts the operator did not intend to open — and, because it is resolved per request, setting it flips this security switch mid-process without a restart. It is a convenience for single-app deployments and local testing, not the recommended production posture.

Recommended production posture: leave echo OFF by default and enable it per host explicitly via a @Bean EchoMixin factory that calls EchoMixin.Builder.enabled(), gated behind an authorization guard chain. Host-level @Rest(guards=...) inherit onto the mixed-in /echo/* operation, so declaring guards at the host authorizes the endpoint.

Sensitive-header redaction:

Token-bearing headers must never be reflected back to the caller (would defeat any auth scheme in front of the endpoint). The default redacted list is — case-insensitively — Authorization, Cookie, Set-Cookie, Proxy-Authorization, and X-API-Key. Each redacted header surfaces in the echo body with the literal value "[REDACTED]" so the caller can see the header was present without leaking its value. Override the default list via EchoMixin.Builder.redactedHeaders(String...) (replaces) or extend via EchoMixin.Builder.redactHeader(String) (additive).

REDACTED and DEFAULT_REDACTED_HEADERS are re-exports of RedactedHeaders.REDACTED and RedactedHeaders.DEFAULT respectively; masking is performed by delegating to RedactedHeaders.redact(String, String, java.util.Collection).

Body capture and truncation:

The handler reads up to bodyLimit bytes (default "1048576" = 1 MB) of the inbound body and emits it as a UTF-8 string in the content field of the JSON response. When the body exceeds the cap, the captured portion is truncated and the truncated flag is set to true so callers can see the response is incomplete.

Response shape:

{ "method": "POST", "path": "/echo/foo/bar", "queryString": "x=1", "pathRemainder": "foo/bar", "headers": { "User-Agent": "curl", "Authorization": "[REDACTED]" }, "queryParams": { "x": "1" }, "attributes": { }, "contentLength": 5, "content": "hello", "truncated": false }

The endpoint is excluded from generated Swagger / OpenAPI specs via @OpSwagger(ignore=true).

See Also:
Since:
10.0.0
  • Field Details

  • Constructor Details

  • Method Details

    • create

      public static EchoMixin.Builder create()
      Creates a new builder.
      Returns:
      A new builder.
    • echo

      public void echo(RestRequest req, RestResponse res, @Path("/*") String remainder) throws IOException
      [* /echo/*] — emit an introspection echo of the inbound request.

      Returns 404 Not Found when the endpoint is not enabled for the current request. Reachability is independent of the JUL logger level (see the class-level Javadoc for the enablement rules and the global-fallback blast-radius warning).

      Parameters:
      req - The current REST request.
      res - The current REST response.
      remainder - The path remainder after the mount prefix (multi-segment, may be empty).
      Throws:
      IOException - If an I/O error occurs while reading the request or writing the response.
      NotFound - When the endpoint resolves to disabled for the current request.
    • getBodyLimit

      public long getBodyLimit()
      Returns the configured body-capture cap (test/inspection helper).
      Returns:
      The body capture cap, in bytes.
    • getRedactedHeadersLower

      Returns the redacted-header set as lowercased, immutable strings (test/inspection helper).
      Returns:
      The redacted-header set.
    • getEnabled

      public Boolean getEnabled()
      Returns the explicit enablement tri-state configured on the builder (test/inspection helper).
      Returns:
      Boolean.TRUE/Boolean.FALSE when explicitly configured, or null when unset (deferred to the ${juneau.echo.enabled:false} deployment fallback).