3

I'm having some troubles running a simple application using Spring WebSockets 4.1.6, Tomcat 7.0.54 and Apache 2.4.16 as Web Server.

I have read a lot of posts in internet, and I don't know what's happening. The server starts without problems. I have an index.html published in my server project that starts a WebSocket connection.

If I access to this file directly from Tomcat, it works perfectly.

http://localhost:8080/myserver/messaging/index.html

But if I access to this file from Apache Server, it doesn't work.

http://localhost/messages/messaging/index.html

In my Apache Server I have configured a proxy pass:

ProxyPass           /messages http://localhost:8080/myserver
ProxyPassReverse    /messages http://localhost:8080/myserver

My server WebSocket configuration looks as follows:

@Configuration
@EnableWebSocketMessageBroker
public class PuiWebSocketConfig extends
    AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/user", "/topic");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/puimessaging").withSockJS();
    }
}

And my client does the connection as follows:

function connect() {
    var socket = new SockJS('/server/puimessaging');
    var stompClient = Stomp.over(socket);
    var headers = {
        puiSessionId : 'a1234567890z'
    };
    stompClient.connect(headers, function(frame) {
        setConnected(true);
        stompClient.subscribe('/user/a1234567890z/response', function(data) {
            ...
        });
    });
}

The server throws an error saying that I need to enable Async support, but I don't know what to do.

Async support must be enabled on a servlet and for all filters involved
in async request processing. This is done in Java code using the Servlet
API or by adding "<async-supported>true</async-supported>" to servlet and
filter declarations in web.xml. Also you must use a Servlet 3.0+ container

I tried to add the property on my web.xml file, but it doesn't work:

<servlet>
    <servlet-name>spring-mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>

<servlet-mapping>
    <servlet-name>spring-mvc</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

Any idea?

Thanks in advance.

Marc

Marc Gil Sendra
  • 819
  • 2
  • 9
  • 22
  • Did you change your tomcat connector?? That is required to use Websockets, Non-blocking IO. – We are Borg May 12 '15 at 09:53
  • Sorry to hear that, btw, where is your web.xml, did you tag your web.xml to use servlet 3.0 and use true . Something like this : http://pastebin.com/RYfEnp4m – We are Borg May 12 '15 at 11:10
  • Please, can you paste again the tip of the connector? I think the answer has been deleted... Thanks! – Marc Gil Sendra May 12 '15 at 13:30
  • I had deleted it as you said it did not help. I have undeleted now. – We are Borg May 12 '15 at 13:32
  • I made it working but not 100%... I built an independent war with all the messaging support, and another war with the main app. When I open the client, the WebSocket is initialized correctly, but Apache couldn't serve me a lot of local files... I have a lot of HTTP errors saying that the file XXX doesn't exist, when they exist. – Marc Gil Sendra May 12 '15 at 13:39
  • I require more information Marc. I cannot tell you without that what is going wrong. Plus tomcat is not very well know for its support for websocket. – We are Borg May 12 '15 at 13:43
  • And when these errors occurs, if I refresh the webpage, Tomcat says me that this page doesn't exist... :| What the hell... :| After 10/15 seconds it works, but with the errors again... – Marc Gil Sendra May 12 '15 at 13:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/77622/discussion-between-we-are-borg-and-marc-gil-sendra). – We are Borg May 12 '15 at 13:46
  • Did you click on the link for chat man? – We are Borg May 12 '15 at 13:57

4 Answers4

1

I get it working, not via WebSockets but via xhr_streaming. My configuration looks as follows:

Web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="webApp" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

    <servlet>
        <servlet-name>spring-mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring-mvc</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <async-supported>true</async-supported>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Tomcat (server.xml):

<Connector URIEncoding="UTF-8" connectionTimeout="20000"
        port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
        redirectPort="8443" />

Apache httpd:

...
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_express_module modules/mod_proxy_express.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
# LoadModule proxy_html_module modules/mod_proxy_html.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
...
ProxyPass   /pideweb-gijon  http://localhost:8080/pideweb_gijon
ProxyPassReverse    /pideweb-gijon  http://localhost:8080/pideweb_gijon
ProxyPass   /pideweb-gijon/endpointpuisocket/   ws://localhost:8080/pideweb_gijon/endpointpuisocket/
ProxyPassReverse    /pideweb-gijon/endpointpuisocket/   ws://localhost:8080/pideweb_gijon/endpointpuisocket/

Spring Configuration:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Autowired
    private WebSocketService websocketService;

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/user", "/topic");
        // config.setUserDestinationPrefix("/user");
        // config.enableSimpleBroker("/queue", "/topic");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpointpuisocket").withSockJS();
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        ChannelInterceptorAdapter interceptor = new ChannelInterceptorAdapter() {

            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
                // store or remove the Session ID in the list depending on the
                // message is for Connecting or Disconnecting
                StompHeaderAccessor accessor = StompHeaderAccessor
                        .wrap(message);
                StompCommand command = accessor.getCommand();
                List<String> headersSessionId = accessor
                        .getNativeHeader("puiSessionId");
                if (!CollectionUtils.isEmpty(headersSessionId)) {
                    String sessionId = headersSessionId.get(0);

                    switch (command) {
                    case CONNECT:
                        // add the session ID
                        websocketService.addSession(sessionId);
                        break;
                    case DISCONNECT:
                        // remove the session ID
                        websocketService.removeSession(sessionId);
                        break;
                    default:
                        break;
                    }
                }

                return message;
            }
        };
        registration.setInterceptors(interceptor);
    }

}

Javascript Client:

function(gst) {
    var url = '/pideweb-gijon/endpointpuisocket';
    var socket = new SockJS(url);
    var stompClient = Stomp.over(socket);
    var headers = {
        puiSessionId : gst
    };

    var self = this;
    stompClient.connect(headers, function(frame) {
        console.log('Connected: ' + frame);
        self.stompClient.subscribe('/user/' + gst + '/response', function(data) {
            console.log(data.body);
        });
    });
};

With this, when I enter the client, automatically tries to connect to the Server. I'm using Chrome, and this is the stack of requests:

Info request info request

Websocket request websocket request

XHR_streaming request xhr_streaming request

Anybody knows what could be happening with Websocket request?

Thanks!

Marc Gil Sendra
  • 819
  • 2
  • 9
  • 22
0

For this, you need to update your tomcat connector, go in your tomcat directory --> conf --> server.xml. Locate the below line :

<Connector connectionTimeout="20000" maxThreads="1000" port="8080" protocol="org.apache.coyote.http1.1"/> 

Something similar it should be, notice the protocol, and change it to :

<Connector connectionTimeout="20000" maxThreads="1000" port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"/>

Restart your tomcat.

We are Borg
  • 5,117
  • 17
  • 102
  • 225
0

Knowing that everything works as expected with Tomcat, there's probably a problem with your Apache configuration.

By default, Apache does not support websocket UPGRADE, you need to install+configure mod_proxy_wstunnel.

You'll probably have to make sure that mod_proxy is in HTTP mode (i.e. not AJP) and that HTTP persistent connections are properly supported if you want the various SockJS transports to work.

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
  • I have this module enabled, but it doesn't work. How can I check if mod_proxy is in HTTP mode and configure the connectors? Thanks... – Marc Gil Sendra May 12 '15 at 13:35
  • This is not reflected in your question; in fact, one has to read your question and all comments to try guessing what is your actual setup. Could you copy/paste your whole apache mod_proxy* configuration (load modules and config statements) – Brian Clozel May 12 '15 at 14:38
  • Sorry Brian, I have post an answer with all the detailed configuration. If you can help I'll thank you. – Marc Gil Sendra May 15 '15 at 06:29
0

I got the same issue and solution of @Marc Gil Sendra works for me. It need to configure Proxypass from Apache for WebSocket protocol.

ProxyPass /pideweb-gijon/endpointpuisocket/ ws://localhost:8080/pideweb_gijon/endpointpuisocket/

ProxyPassReverse /pideweb-gijon/endpointpuisocket/ ws://localhost:8080/pideweb_gijon/endpointpuisocket/

I tested in Bitnami EC2 (Apache + Tomcat), the configure as bellow File config: /opt/bitnami/apache-tomcat/conf/apache-tomcat-prefix.conf

<Location /AppContext/endpointpuisocket> ProxyPass ws://localhost:8080/AppContext/endpointpuisocket </Location>

Johnny Le
  • 1
  • 5