Open
Description
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