February 27, 2015

Configure WildFly, Apache and websocket connections on Ubuntu 14.04

This tutorial assumes that you have already installed WildFly and you want to configure Apache as a proxy in front of WildFly, and you want to allow websocket connections. For a detailed guide on installing and configuring WildFly, see this post.

In this guide are going to install and configure Apache, mod_proxy and proxy_wstunnel. proxy_wstunnel is required if you want successfully connect to WildFly via websockets (thru Apache). Any attempt to connect via websockets without proxy_wstunnel will fail as the HTTP Upgrade headers will be removed from your request.

Apache install

First install Apache and the required mods:
sudo apt-get install apache2
sudo apt-get install libapache2-mod-proxy-html
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
sudo /etc/init.d/apache2 restart

Apache configuration

Now configure your VirtualHost. For simplicity we are going to update the 000-default.conf file. In this example the context root of the application is app. Update yours as needed.
vim /etc/apache2/sites-available/000-default.conf

These two lines are required to proxy your main http connections
ProxyPass         /app  http://localhost:8080/pss
ProxyPassReverse  /app  http://localhost:8080/pss

These two lines will proxy your websocket connections
ProxyPass         /app/ws/  ws://localhost:8080/app/ws/
ProxyPassReverse  /app/ws/  ws://localhost:8080/app/ws/

Here is the full VirtualHost file, with the mod_proxy and proxy_wstunnel configuration highlighted:
<VirtualHost *:80>
    ServerName www.example.com
    ServerAlias example.com

    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/example.com

    ProxyPass         /app  http://localhost:8080/pss
    ProxyPassReverse  /app  http://localhost:8080/pss
    ProxyPass         /app/ws/  ws://localhost:8080/app/ws/
    ProxyPassReverse  /app/ws/  ws://localhost:8080/app/ws/

    ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
    CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
Reload Apache for the changes to take effect
sudo /etc/init.d/apache2 reload

Java configuration

Make sure that the value you pass into your @ServerEndPoint annotation matches that in your Apache VirtualHost configuration.
@ServerEndpoint("/ws")
public class WebSocketEndpoint {
 
    private final Logger logger = Logger.getLogger(this.getClass().getName());
 
    @OnOpen
    public void onConnectionOpen(Session session) {
        logger.info("Connection opened");
    }
 
    @OnMessage
    public String onMessage(String message) {
        return "Message received";
    }
 
    @OnClose
    public void onConnectionClose(Session session) {
        logger.info("Connection closed");
    }
}

WildFly configuration

Make sure that you allow connections to your public interface from localhost (127.0.0.1) only. While this is not mandatory, leaving it open will allow connections to circumvent Apache.
<interfaces>
    <interface name="management">
        <inet-address value="0.0.0.0"/>
    </interface>
    <interface name="public">
        <inet-address value="${jboss.bind.address:127.0.0.1}"/>
    </interface>
    <interface name="unsecure">
        <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/>
    </interface>
</interfaces>

In WildFly websockets work out of the box, so no other configuration required. Here is the default undertow configuration, with the relevant parts highlighted.
<subsystem xmlns="urn:jboss:domain:undertow:1.2">
    <buffer-cache name="default"/>
    <server name="default-server">
        <http-listener name="default" socket-binding="http"/>
        <host name="default-host" alias="localhost">
            <location name="/" handler="welcome-content"/>
            <filter-ref name="server-header"/>
            <filter-ref name="x-powered-by-header"/>
        </host>
    </server>
    <servlet-container name="default">
        <jsp-config/>
        <websockets/>
    </servlet-container>
</subsystem>
You should now be able to deploy your websocket applications to WildFly and access them via Apache.

5 comments :

  1. Hi Chris,

    I wonder what your take is on a couple of issues I faced regarding this topic:

    - Would you consider using AJP instead of HTTP between Apache and Wildfly? My understanding is that AJP offers better performance.
    - When securing the outside traffic (using SSL/TLS, always a good idea IMHO), would you have Apache offload the encryption (i.e. end encryption at Apache and connect unencrypted to Wildfly) or carry the encryption up to Wildfly?

    Thanks,
    Maurice

    ReplyDelete
    Replies
    1. Hi Maurice, thanks for the message.

      I find HTTP easier to set up and configure. AJP is designed to communicate between the web server and the application server, so in theory, should be better performing. Also, if you use mod_proxy_http the host headers will be stripped out where as the headers will be preserved with AJP. (For example you will loose your HTTP Upgrade)

      To keep the headers with HTTP you need to use 'ProxyPreserveHost On' in you Apache config.

      With regards to your SSL question. I have always installed my SSL certs in Apache and let Apache deal with the encryption. I then forward the traffic to WildFly using HTTP. I have favoured this approach as I can quickly configure Apache SSL. Also when I upgrade my JBoss/WildFly server I have less to configure. I suppose it comes down to personal preference.

      Delete
  2. Hi Chris...
    My environment is Wildfly 8.2 and Apache 2.2.21

    I did the following things and I couldnt get this work...
    1)I enabled proxy_wstunnel_module (shared)
    2)I disabled mod_proxy_ajp.so due to AJP is not compatible with websocket according to documentation
    3)I am using Atmosphere Websocket with wildfly...

    My web.xml config is:

    Push Servlet
    org.primefaces.push.PushServlet

    org.atmosphere.cpr.broadcasterCacheClass
    org.atmosphere.cache.UUIDBroadcasterCache

    0
    true




    Push Servlet
    /primepush/*


    About my EndPoint is @PushEndpoint("/{primepush}/{user}")

    4) I changed my subsystem wildfly config from AJP to HTTP
    5)I created my reverse proxies:

    ProxyPass /company http://app.tiedocs.com/company
    ProxyPassReverse /company http://asubdomain.website.com/company
    ProxyPass /company/primepush/ ws://subdomain.website.com/company/primepush/
    ProxyPassReverse /company/primepush/ ws://subdomain.website.com/company/primepush/

    ProxyPass /company http://localhost:8080/company
    ProxyPassReverse /company http://localhost:8080/company
    ProxyPass /company/primepush/ ws://localhost:8080/company/primepush/
    ProxyPassReverse /company/primepush/ ws://localhost:8080/company/primepush/

    But ever, I'm still getting the same error UT000077: The underlying transport does not support HTTP upgrade..

    I dont know exactly what I'm doing wrong.

    ReplyDelete
  3. At the time of this answer AJP [which is the default one] does not support HTTP upgrade and hence not websocket.

    If you switch to HTTP websocket will work.

    Following changes you need to do to switch to AJP

    Change

    LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
    #LoadModule proxy_http_module modules/mod_proxy_http.so

    To

    #LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
    LoadModule proxy_http_module modules/mod_proxy_http.so

    [comment out proxy_ajp_module and added proxy_http_module]

    In modcluster sub-system

    Change









    To









    [“default” is the name of the http listener]

    Also you need to load mod_proxy_wstunnel in your httpd

    ReplyDelete
  4. Thx for this scripts, it's very useful, I thinking abot using it in my dissertation work, if y don't mind.
    Best regards
    Toby, data room solutions

    ReplyDelete