2

I'm having trouble setting up a websocket configuration in an existing web application.

@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfig extends AbstractWebSocketMessageBrokerConfigurer{

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/mobile");
        config.setApplicationDestinationPrefixes("/mobile-server");
        config.setUserDestinationPrefix("/mobile-user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/mobile-socket")
        .withSockJS()
        .setInterceptors(new HttpSessionHandshakeInterceptor());
    }
}

Controller

@Controller
public class WebSocketInboxController{

    @MessageMapping("/inbox")
    @SendToUser("/mobile")
    public Map<String,Object> inbox(
    ){

        Map<String,Object> res = new HashMap<>();

        res.put("hello", "hello");

        return res;
    }

client

const webstomp = require('webstomp-client');

const socket = webstomp.client('ws://www.dev.server.com/mobile-socket',{
  debug:true
});

socket.connect('marc@gmail.com', '123456', (client) => {
  console.log('connected');

  socket.send("/mobile-server/inbox",)
  socket.subscribe("/mobile/inbox");
}, (client, err) => {
  console.log(err);
});

What I see when the client tries to connect is spring trying to match the /mobile-socket against the RequestMappings of the existing web application, finally hitting one that matches it by way of a @RequestMapping("/{somevar}").

I'm new to WebSockets, but I would expect the endpoint registration be a catchall for these kinds of connects?

Even after removing the erronous RequestMapping being hit, I can't seem to get the MessageMapping to be hit. I see this in my log

AntPathRequestMatcher.matches(150) | Request '/mobile-socket' matched by universal pattern '/**'
[MSA] DEBUG [2016-06-03T11:16:21,025] FilterSecurityInterceptor.beforeInvocation(219) | Secure object: FilterInvocation: URL: /mobile-socket; Attributes: [permitAll]
[MSA] DEBUG [2016-06-03T11:16:21,025] FilterSecurityInterceptor.authenticateIfRequired(348) | Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
[MSA] DEBUG [2016-06-03T11:16:21,025] AffirmativeBased.decide(66) | Voter: org.springframework.security.web.access.expression.WebExpressionVoter@444af45, returned: 1
[MSA] DEBUG [2016-06-03T11:16:21,025] FilterSecurityInterceptor.beforeInvocation(243) | Authorization successful
[MSA] DEBUG [2016-06-03T11:16:21,026] FilterSecurityInterceptor.beforeInvocation(256) | RunAsManager did not change Authentication object
[MSA] DEBUG [2016-06-03T11:16:21,026] FilterChainProxy.doFilter(325) | /mobile-socket at position 16 of 16 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
[MSA] DEBUG [2016-06-03T11:16:21,026] FilterChainProxy.doFilter(310) | /mobile-socket reached end of additional filter chain; proceeding with original chain
[MSA] DEBUG [2016-06-03T11:16:21,027] ExceptionTranslationFilter.doFilter(117) | Chain processed normally
[MSA] DEBUG [2016-06-03T11:16:21,027] HstsHeaderWriter.writeHeaders(130) | Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@53cc2afb
[MSA] DEBUG [2016-06-03T11:16:21,027] HttpSessionSecurityContextRepository.saveContext(352) | SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
[MSA] DEBUG [2016-06-03T11:16:21,028] SecurityContextPersistenceFilter.doFilter(120) | SecurityContextHolder now cleared, as request processing completed
Marc
  • 6,773
  • 10
  • 44
  • 68
  • I finally at least managed to remove the catchall RequestMapping from the by specifying RequestMapping(value="/{loc}", consumes="text/plain"), but now it is: Resolving exception from handler [null]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'null' not supported :-( – Marc Jun 02 '16 at 18:08
  • After 2 days of going down the rabbit hole, I finally got a successful CONNECT after hacking nginx to send an Upgrade "WebSocket" Header against the endpoint/websocket url. Jeez this is ridiculously hard – Marc Jun 03 '16 at 19:23

1 Answers1

1

The Spring tries to match the "/mobile-socket" against the RequestMappings because all the requests go to HandlerMapping beans in the web application context to map incoming web requests to appropriate handlers. With the introduction of annotated controllers, RequestMappingHandlerMapping automatically looks for @RequestMapping annotations on all @Controller beans including the controller which has @MessageMapping.

Since the @MessageMapping can only be defined under @Controller annotation, Spring would try to match other RequestMappings as well.

One possible solution could be to introduce interceptor to handle the websocket request url to specifically map to a particular controller. You can give it a try!

Mike
  • 706
  • 7
  • 16
notionquest
  • 37,595
  • 6
  • 111
  • 105
  • Yes, I understand this, but as I understand it a more specific match would prevail over a non specific match. In this case a non specific RequestMapping match /{pathVar} is prevailing over a more specific MessageMapping one /inbox. I also cannot find the relevant mapping in the list of requestmappings that is being processed for a match. I would expect to see it there, which leads me to believe ComponentScanning is not hitting the MessageMapping annotation – Marc Jun 02 '16 at 17:16
  • I tried adding an additional ComponentScan for the Controller methods that contain the websocket Controllers. But Websocket maintains its own SimpAnnotationMethodMessageHandler but the AbstractHandlerMethodMapping that processes RequestHandlers runs first. I guess I have to find a way to either make the SimpAnnotationMethodMessageHandler run first or make sure the MessageMappings are registered with AbstractHandlerMethodMapping – Marc Jun 02 '16 at 17:34