How to read cached request body multiple times in spring-cloud-gateway – the right way

When using spring cloud gateway it’s possible to cache the request body as DataBuffer either by using a global filter or by activating it per route via an EnableBodyCachingEvent.

As soon as a Predicate or Filter reads from this DataBuffer it’s internal read position gets modified. (i.e when using the DataBuffer::asInputStream).
Subsequent filters share the same read position as well as the final proxy handler that delegates to an upstream service.

When some stage does not reset the read position to 0, the upstream http call will fail because either the read position is already at the end and therefor nothing more can be read (which probably results in a timeout error because the http call has a Content-Length defined that can’t be read) or the data read is corrupt (wrong position).

A common usecase for us is to implement some kind of schema validation filter.
This requires to read the body, perform a schema validation and in case of success delegate to the upstream.

Our code currently looks something like this:

    @Override
    public GatewayFilter apply(ValidateExchangeConfig config) {
        return (ServerWebExchange exchange, GatewayFilterChain chain) ->
                validateRequest(exchange, config)
                        .doFinally(signalType -> cleanupRequestBodyCache(signalType, exchange))
                        .then(chain.filter(exchange));
    }

    private static void cleanupRequestBodyCache(SignalType signalType, ServerWebExchange exchange) {
        Optional.<DataBuffer>ofNullable(exchange.getAttribute(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR))
                .ifPresent(dataBuffer -> {
                    switch (signalType) {
                        case ON_ERROR -> DataBufferUtils.release(dataBuffer); // release on validation error
                        // maybe some other cases
                        default -> dataBuffer.readPosition(0);  // reset to allow further processing
                    }
                });
    }

Shouldn’t there a way to reset the readPosition at least as soon as the request is proxied to the upstream service?

So the question is: whats the appropriate way to reset the DataBuffer’s readPosition before delegating to an upstream service?

I’m wondering that this – for us very common case – is not addressed somewhere globally.

  • Reading the body multiple times is not a common use case we support explicitly. I would advise reading it once and transforming it once and cache that. The various methods on ServerWebExchangeUtils.cache*() are the supported way of caching.

    – 

Leave a Comment