Phate
Phate

Reputation: 6602

Intercept websocket initial handshake in rsockets

I want to be able to intercept the first request a client makes to my endpoint so to be able to answer with 101 (websocket estabilished) or deny it.

Is there any interceptor I can use? I tried registering a filter bean but it seems it is just skipped, I think because I'm in a webflux application...

 @Bean
public FilterRegistrationBean<ContextFilter> initialFilter() {
    FilterRegistrationBean<ContextFilter> registrationBean
            = new FilterRegistrationBean<>();

    registrationBean.setFilter(new MyFilter());
    registrationBean.addUrlPatterns("/*");
    registrationBean.setOrder(1);
    return registrationBean;
}

and

public class MyFilter extends OncePerRequestFilter{

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    log.info("You know, for filtering");
    filterChain.doFilter(request, response);

}

Upvotes: 4

Views: 1114

Answers (2)

Hari Darshan
Hari Darshan

Reputation: 1920

You can intercept the first request by implementing WebSocketHandler and using ServerHttpRequest you can access headers and query parameters from the request and can modify the response using ServerHttpResponse to return a 101 status code.

import io.rsocket.RSocket;
import io.rsocket.core.RSocketServer;
import io.rsocket.transport.netty.server.CloseableChannel;
import io.rsocket.transport.netty.server.WebsocketServerTransport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class WebSocketConfig {

    @Bean
    public RSocket rSocket() {
        // Your RSocket bean configuration
        return RSocketServer.create()
                .bind(WebsocketServerTransport.create("localhost", 8080))
                .start()
                .block();
    }

    @Bean
    public HandlerMapping webSocketHandlerMapping() {
        Map<String, WebSocketHandler> map = new HashMap<>();
        map.put("/rsocket", new CustomWebSocketHandler());

        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setOrder(1);
        mapping.setUrlMap(map);
        return mapping;
    }

    @Bean
    public WebSocketHandlerAdapter handlerAdapter() {
        return new WebSocketHandlerAdapter();
    }

    private static class CustomWebSocketHandler implements WebSocketHandler {

        @Override
        public CloseableChannel handle(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
            // Access headers and query parameters here
            String headerValue = serverHttpRequest.getHeaders().getFirst("your-header");
            String queryParamValue = serverHttpRequest.getQueryParams().getFirst("your-query-param");

            // Modify the response to return 101 status code
            serverHttpResponse.setStatusCode(HttpStatus.SWITCHING_PROTOCOLS);

            // Implement your logic here

            return null; // Return the CloseableChannel or handle as needed
        }
    }
}

Upvotes: 0

kojot
kojot

Reputation: 1776

To establish the RSocket connection client must send a first frame called SETUP frame. You can catch it with the Spring @ConnectMapping annotation.

    @ConnectMapping
    fun onConnect(rSocketRequester: RSocketRequester, clientId: String) {
        if (clientId == "wrong_client") rSocketRequester.rsocket().dispose()   //to reject connection
    }

To reject the connection use the rSocketRequester.rsocket().dispose() as shown above. Note that with the SETUP frame you can pass the payload and metadata from the client as usual.

Upvotes: 1

Related Questions