Support webflux.base-path in StripFilter and possibly others
See original GitHub issueDescribe the bug
StripPrefix filter (and maybe RewritePath too) isn’t able to remove a contextPath if there is any set. It started with upgrading to Spring Boot 2.3. I think it is connected with introducing spring.webflux.base-path
but I don’t use it. I use ForwardedHeaderTransformer
for adding na prefix from a header X-Forwarded-Prefix
(SCG runs behind NGINX proxy). This transformer sets contextPath to prefix builder.contextPath(prefix)
. StripPrefix filter strips only in path and not in contextPath request.mutate().path(newPath)
and then it throws an IllegalArgumentException if build()
method is called.
See a stacktrace:
java.lang.IllegalArgumentException: Invalid contextPath '/admin': must match the start of requestPath: '/sba/'
at org.springframework.http.server.DefaultRequestPath.validateContextPath(DefaultRequestPath.java:82)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.ui.LogoutPageGeneratingWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.ui.LoginPageGeneratingWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.cloud.sleuth.instrument.web.TraceWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/admin/sba/" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.http.server.DefaultRequestPath.validateContextPath(DefaultRequestPath.java:82)
at org.springframework.http.server.DefaultRequestPath.initContextPath(DefaultRequestPath.java:57)
at org.springframework.http.server.DefaultRequestPath.<init>(DefaultRequestPath.java:42)
at org.springframework.http.server.RequestPath.parse(RequestPath.java:60)
at org.springframework.http.server.reactive.AbstractServerHttpRequest.<init>(AbstractServerHttpRequest.java:81)
at org.springframework.http.server.reactive.DefaultServerHttpRequestBuilder$MutatedServerHttpRequest.<init>(DefaultServerHttpRequestBuilder.java:197)
at org.springframework.http.server.reactive.DefaultServerHttpRequestBuilder.build(DefaultServerHttpRequestBuilder.java:136)
at org.springframework.cloud.gateway.filter.factory.StripPrefixGatewayFilterFactory$1.filter(StripPrefixGatewayFilterFactory.java:71)
at org.springframework.cloud.gateway.filter.OrderedGatewayFilter.filter(OrderedGatewayFilter.java:44)
at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.lambda$filter$0(FilteringWebHandler.java:118)
Sample Configuration
spring:
cloud:
gateway:
routes:
- id: sba
uri: http://localhost:20010
predicates:
- Path=/admin/sba/**
filters:
- StripPrefix=1
Enable ForwardedHeaderTransformer
@Configuration
public class ForwardedHeaderConfiguration {
@Bean
public ForwardedHeaderTransformer forwardedHeaderTransformer() {
return new ForwardedHeaderTransformerImpl();
}
}
Call http://localhost:{applicationPort}/sba
with X-Forwarded-Prefix: /admin
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:12 (3 by maintainers)
PRs welcome.
This is a huge quagmire. I would happily provide a PR, but for a solid fix a view conceptual clarifications are necessary.
Look at this route configuration:
Now if a context-path (base-path) is defined, all given path patterns should continue to work, so they must be considered as app-local paths without context-path. The fix for the Path-predicate is trivial. The context-path must also be preserved for the local route.
But how shall the context-path be handled for the upstream service? Is it included by default, or is it stripped by default?
One could argue that all paths in the configuration are app-local paths without context-path, so the context-path should be stripped by default (in other words, the scope of the context-path is only the local gateway itself, without affecting outgoing routes).
On the other hand, this might cause confusion when looking at the full incoming and outgoing paths (request URIs), so it might be a valid strategy that the context-path has to be explicitly stripped with an additional “StripContextPath” filter.
Additionally, a lot of clarifications are necessary in the docs, e.g. for StripPrefix and all other components manipulating the path.
Any ideas?