I have recently added websocket configuration to my spring application, which is behind a Spring cloud gateway. There is also an angular app which communicates with my service through the gateway. I am trying to route request, to and from a spring-boot app with the gateway.
The routing in spring cloud gateway is this:
@Bean
public RouteLocator applicationRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p.path("/my-service/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec
.rewritePath("/my-service/(?<remaining>.*)", "/${remaining}")
.dedupeResponseHeader("Access-Control-Allow-Origin","RETAIN_UNIQUE")
.dedupeResponseHeader("Access-Control-Allow-Credentials","RETAIN_UNIQUE")
.filter(customLoggingFilter))
.uri("lb://my-service/"))
.build();
}
This is the custom filter that is used.
@RefreshScope
@Component
public class CustomLoggingFilter implements GatewayFilter {
final Logger LOGGER = LoggerFactory.getLogger(CustomLoggingFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
LOGGER.info("Forward request: " + request.getURI());
return chain.filter(exchange);
}
}
This is the websocket configuration of the my-service:
Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/notification");
config.setApplicationDestinationPrefixes("/product");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
}
And when called from the front end:
connect() {
console.log("Initialize WebSocket Connection");
const _this = this;
let socket = new SockJS(this.appComponent.connectionUrl);
let destinationUrl=this.appComponent.destinationUrl;
_this.stompClient = Stomp.over(socket);
_this.stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
_this.stompClient.subscribe(destinationUrl, function (greeting: any) {
let obj = JSON.parse(greeting.body);
console.log(obj);
_this.onMessageReceived(obj);
});
});
};
This exception is thrown in the gateway:
java.lang.ClassCastException: class org.apache.catalina.connector.ResponseFacade cannot be cast to class reactor.netty.http.server.HttpServerResponse (org.apache.catalina.connector.ResponseFacade and reactor.netty.http.server.HttpServerResponse are in unnamed module of loader 'app')
at org.springframework.web.reactive.socket.server.upgrade.ReactorNettyRequestUpgradeStrategy.upgrade(ReactorNettyRequestUpgradeStrategy.java:163)
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.web.cors.reactive.CorsWebFilter [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.authentication.AuthenticationWebFilter [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.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
*__checkpoint ? HTTP GET "/escrow-service/ws/546/patwefr1/websocket" [ExceptionHandlingWebHandler]
Original Stack Trace:
at org.springframework.web.reactive.socket.server.upgrade.ReactorNettyRequestUpgradeStrategy.upgrade(ReactorNettyRequestUpgradeStrategy.java:163)
at org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService.lambda$handleRequest$1(HandshakeWebSocketService.java:243)
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:152)
at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53)
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:240)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203)
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)
at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102)
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:83)
at reactor.core.publisher.FluxDematerialize$DematerializeSubscriber.onNext(FluxDematerialize.java:98)
at reactor.core.publisher.FluxDematerialize$DematerializeSubscriber.onNext(FluxDematerialize.java:44)