Search code examples
httpsplayframeworkwebsocketmod-proxyplayframework-2.5

How to proxy WebSocket via Apache to Play-Framework


I'm stuck with my Apache-config and appreciate any help on this.

The config is like this:

This works very well with the following Apache-config:

<VirtualHost *:80>
    ServerName domain.tld
    DocumentRoot /var/www/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    Redirect permanent / https://domain.tld/
    Redirect permanent / https://domain.tld/
</VirtualHost>

<VirtualHost _default_:443>
#ssl-config here
<Proxy http://localhost:9000/*>
      Order deny,allow
      Allow from all
</Proxy>
ProxyPass         /app1  http://domain.tld:9000/app1
ProxyPassReverse  /app1  http://domain.tld:9000/app1
ProxyPassReverse  /app1  http://domain.tld/app1
</VirtualHost>

The problem ist, that one play-application got a WebSocket added. Which isn't working with the above setup. So I read the stuff on the play-pages. Which led me to install mod_proxy_wstunnel. I also add the following lines to the config, but had no success with that:

ProxyPass         /app1/timerWs ws://domain.tld:9000/app1/timerWs
ProxyPassReverse  /app1/timerWs ws://domain.tld:9000/app1/timerWs

When I'm trying to connect to https://domain.tld/rlc/timerWs I got an 500 Internal Server Error, but there are no new, more specific errors in the apache error log.

How can I configure Apache to proxy WebSocket requests properly to my play applications?

My play-apps do not have https-adapters. All the https stuff is done by the Apache-proxy.

Play-apps are on version 2.5. Apache is on 2.4.7.

Thanks a lot for your help. Tobias


Solution

  • I have solved the issue now. What did the trick was to set-up https not only on the Apache, but also on Plays application server Jetty. To do so see this link. This leads to another ProxyPass address (notice the wss instead of ws):

    ProxyPass         /app1/timerWs wss://domain.tld:9000/app1/timerWs
    ProxyPassReverse  /app1/timerWs wss://domain.tld:9000/app1/timerWs
    

    I also had to change the WebSocket address in my Javascript, to let the Browser know, where to find the WebSocket-Backend.

    $(function() {
        var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
        var dateSocket = new WS("wss://domain.tld/rlc/timerWs")
        var receiveEvent = function(event) {
            $("#timer").html(event.data);
        }
        dateSocket.onmessage = receiveEvent
    });
    

    Before that I used a Play-route @routes.Application.timerWs().webSocketURL(request) to address the WebSocket.