Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modifyResponseBodyFilterFactory for some reason not executing #3670

Open
dreamstar-enterprises opened this issue Jan 15, 2025 · 0 comments
Open

Comments

@dreamstar-enterprises
Copy link

dreamstar-enterprises commented Jan 15, 2025

Hi, I'm not sure why the rewrite function here simply won't execute.

/**
 * Gateway filter that enriches the /user-info endpoint response with session timing information.
 *
 * Process flow:
 * 1. Intercepts responses from the /user-info endpoint
 * 2. Attempts to retrieve session creation time from Redis session hash
 * 3. If successful, adds session timing information to the JSON response:
 *    - sessionCreatedAt: When the session was created
 *    - sessionExpiresAt: When the session will expire (xx mins after creation)
 *
 * Error handling:
 * - If Redis access fails: Logs error and continues without session timing info
 * - If session key not found: Continues without session timing info
 * - Non-user-info endpoints: Passes through unmodified
 *
 * The filter maintains the original response integrity even when Redis operations fail,
 * ensuring the user-info endpoint remains functional with or without the additional
 * session timing data.
 *
 * @property redisTemplate For accessing Redis session data
 * @property sessionProperties Configuration for Redis session namespacing
 * @property serverProperties Server configuration including endpoint prefixes
 * @property modifyResponseBodyFilterFactory Factory for response body modification
 */
@Component
internal class SessionInfoResponseFilter(
    private val redisTemplate: ReactiveRedisTemplate<String, Any>,
    private val sessionProperties: SessionProperties,
    private val serverProperties: ServerProperties,
    private val modifyResponseBodyFilterFactory: ModifyResponseBodyGatewayFilterFactory
) : AbstractGatewayFilterFactory<SessionInfoResponseFilter.Config>() {

    /** Configuration class for the session info response filter */
    class Config

    private val logger = LoggerFactory.getLogger(SessionInfoResponseFilter::class.java)

    /**
     * Applies the filter to add session timing information to /me responses.
     *
     * @param config Filter configuration (unused in this implementation)
     * @return GatewayFilter that enriches /user-info responses with session data
     */
    override fun apply(config: Config): GatewayFilter = GatewayFilter { exchange, chain ->

        // only modify /user-info endpoint responses
        if (exchange.request.path.value() != "${serverProperties.resourceServerPrefix}/me") {
            logger.debug("request did not match me endpoint")
            chain.filter(exchange)
        } else {
            chain.filter(exchange).then(
                exchange.session.flatMap { session ->
                    logger.debug("request did match me endpoint")
                    val sessionId = session.id
                    val sessionKey = "${sessionProperties.redis?.sessionNamespace}:sessions:$sessionId"

                    redisTemplate.opsForHash<String, Any>()
                        .get(sessionKey, "creationTime")
                        .doOnNext { creationTime ->
                            logger.debug("3. Found creation time in Redis: {}", creationTime)
                        }
                        .flatMap { creationTime ->

                            val created = Instant.ofEpochMilli(creationTime.toString().toLong())
                            logger.debug("4. Converted to Instant: {}", created)

                            // Define the RewriteFunction separately
                            val rewriteFunction = RewriteFunction<String, String> { _, originalResponse ->
                                logger.debug("Entering RewriteFunction with body: {}", originalResponse)

                                if (originalResponse.isNullOrEmpty()) {
                                    logger.warn("Response body is empty or null")
                                    return@RewriteFunction Mono.just(originalResponse)
                                }

                                return@RewriteFunction try {
                                    val jsonNode = JSONUtilities.objectMapper.readTree(originalResponse)
                                    logger.debug("Parsed JSON node: {}", jsonNode)

                                    (jsonNode as ObjectNode).apply {
                                        put("sessionCreatedAt", created.toString())
                                        put("sessionExpiresAt", created.plusSeconds(sessionProperties.timeout.toLong()).toString())
                                    }

                                    val modifiedResponse = JSONUtilities.objectMapper.writeValueAsString(jsonNode)
                                    logger.debug("Modified response body: {}", modifiedResponse)

                                    Mono.just(modifiedResponse)
                                } catch (ex: Exception) {
                                    logger.error("Failed to process response body", ex)
                                    Mono.error(ex)
                                }
                            }

                            val modifyConfig = ModifyResponseBodyGatewayFilterFactory.Config().apply {
                                logger.debug("Setting up rewrite function")
                                setInClass(String::class.java)
                                setOutClass(String::class.java)
                                setRewriteFunction(String::class.java, String::class.java, rewriteFunction)
                            }

                            // Apply modification filter first, then the chain
                            modifyResponseBodyFilterFactory.apply(modifyConfig).filter(exchange, chain)
                            
                        }
                        .onErrorResume { error ->
                            // log the Redis error but allow the chain to continue
                            logger.error("Failed to get session timing from Redis", error)
                            Mono.empty()
                        }
                }
            )
        }
    }
}

In the logs all I can see is:


2025-01-15T21:21:43.302Z DEBUG 35283 --- [BFFApplication] [ctor-http-nio-4] c.v.b.r.s.SessionInfoResponseFilter      : request did match userinfo endpoint
2025-01-15T21:21:43.316Z DEBUG 35283 --- [BFFApplication] [xecutorLoop-3-5] c.v.b.r.s.SessionInfoResponseFilter      : 3. Found creation time in Redis: 1736971305338
2025-01-15T21:21:43.317Z DEBUG 35283 --- [BFFApplication] [xecutorLoop-3-5] c.v.b.r.s.SessionInfoResponseFilter      : 4. Converted to Instant: 2025-01-15T20:01:45.338Z
2025-01-15T21:21:43.317Z DEBUG 35283 --- [BFFApplication] [xecutorLoop-3-5] c.v.b.r.s.SessionInfoResponseFilter      : Setting up rewrite function

I've spent 3 hours going over the internet trying to find a solution, but am getting stuck.

Would appreciate if anyone could help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant