Backport Websockets to Apache 2.2 (x86!)

Hello all – today’s post is on backporting Websockets to an ancient PE2650 running CentOS 6.6. Yup…i686 in all its glory. Read on for our solution!

The problem? We have a Shiny app that uses Websockets. But we are proxying access to the Shiny app (via the R Server) from a front-end Apache. And – as we all know – on CentOS standard yum repositories only go up to Apache 2.2 (2.2.15 currently). And Websockets is supported – via mod_proxy_wstunnel – only in Apache 2.4.

Hmmm. So what to do? Backport the fix in, of course!

Here are our steps:

  1. See following resources; we modify them heavily to meet our CentOS 6.6 i686 environment:
    • http://serverfault.com/questions/290121/configuring-apache2-to-proxy-websocket
    • https://issues.apache.org/bugzilla/show_bug.cgi?id=49053
  2. Continue to the next step.

  3. Install Apache for CentOS:

    sudo yum install httpd
    chkconfig httpd24-httpd on
    service httpd24-httpd start
    

    Continue to the next step.

  4. Install necessary packages to base CentOS and setup firewall to permit HTTPS. Note that we are *not* running as root:

    sudo yum install -y patch autoconf libtool
    sudo yum install -y httpd mod_ssl mod_proxy_html
    sudo iptables -I INPUT 2 -p tcp -m state --state NEW -m tcp \
      -m multiport --dports 80,443 -m comment --comment "HTTP/HTTPS" -j ACCEPT
    sudo service iptables reload
    sudo service iptables save

    Note that libtool is version 2.0 while Apache source (specifically, apr-utils) was built for libtool version 1. We will address that problem below.

    On the firewall: Be sure to do the same for the VM Security Group if running virtually.

    Continue to the next step.

  5. Create the dev tree to do your work. We’re running our builds locally:

    mkdir -p ~/proj/websockets
    cd ~/proj/websockets

    Continue to the next step.

  6. Download packages and patches to build Apache 2.2.15 (which matches the CentOS Apache distro):

    wget https://archive.apache.org/dist/httpd/httpd-2.2.15.tar.gz
    wget https://archive.apache.org/dist/apr/apr-util-1.5.4.tar.gz
    wget https://archive.apache.org/dist/apr/apr-1.5.1.tar.gz
    wget http://people.apache.org/~rjung/patches/expat-libtool2.patch
    wget https://gist.github.com/vitkin/6661683/raw/873dd8b4de4ad1ff69757ffe48fc574374aedc57/apache-2.2-wstunnel.patch

    Continue to the next step.

  7. Extract files and apply patches:

    tar zxf httpd-2.2.15.tar.gz
    cd httpd-2.2.15
    patch -p1 -i ../apache-2.2-wstunnel.patch
    cd srclib/apr
    tar xzf ../../../apr-1.5.1.tar.gz
    cd ../apr-util/
    tar xzf ../../../apr-util-1.5.4.tar.gz
    patch -p1 -i ../../../expat-libtool2.patch
    cd ../../..

    Continue to the next step.

  8. Configure and build system. Note we build all the dependent modules as our production environment requires them.

    ./buildconf
    ./configure --enable-proxy=shared --enable-proxy_ajp=shared \
      --enable-proxy_balancer=shared --enable-proxy_connect=shared \
      --enable-proxy_ftp=shared --enable-proxy_http=shared \
      --enable-proxy_wstunnel=shared
    make

    Continue to the next step.

  9. Install the compiled mod_proxy over the base (saving the original for the first run). You can run these commands as-is any number of times.

    [ ! -d /usr/lib/httpd/modules/bak ] && sudo mkdir /usr/lib/httpd/modules/bak
    l_lib=/usr/lib/httpd/modules; l_lib_loc=modules/proxy/.libs
    l_sos=''; for i in $(ls $l_lib_loc/*.so); do l_sos="$l_sos $(basename $i)"; done
    for i in $l_sos; do [ ! -f $l_lib/bak/$i ] && sudo cp $l_lib/$i $l_lib/bak/; done
    for i in $l_sos; do yes | sudo cp $l_lib_loc/$i $l_lib/; sudo chmod 755 $l_lib/$i; done

    service httpd restart
    Continue to the next step.

  10. Modify your Apache config file; here we create a new file to hold our Websockets process:

    # /etc/httpd/conf.d/z-websockets-demo.conf
    LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
    
    <VirtualHost *:80>
      ServerName websockets-demo.hlsdev.com
      ProxyRequests Off
      ProxyPreserveHost Off
      <Proxy *>
        Order allow,deny
        Allow from all
      </Proxy>
    
      # websockets?
      ProxyPassMatch ^/(.*/__sockjs__/[0-9]+/.*) ws://backend.example.local:3838/$1
      ProxyPass /  http://backend.example.local:3838/ max=1 disablereuse=on keepalive=on
      ProxyPassReverse / http://backend.example.local:3838/
      CustomLog /var/log/httpd/demo.log combined
      ErrorLog  /var/log/httpd/demo_error.log
    </VirtualHost>

    In the above: the Websockets support is provided by two additional lines. The first line loads the Websockets Tunneling code (which is an add-on to standard ws-proxy module):

    LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so

    The second line performs the check for the “websocketized” application using the ProxyPassMatch command. The regular expression checks for __socksjs__ followed by an all-numeric identifier. If found, then the proxy will send the request to the “ws://” destination, which automagically invokes the ws_tunnel code.

That is all. The proxy now works.

Team-oriented systems mentor with deep knowledge of numerous software methodologies, technologies, languages, and operating systems. Excited about turning emerging technology into working production-ready systems. Focused on moving software teams to a higher level of world-class application development. Specialties:Software analysis and development...Product management through the entire lifecycle...Discrete product integration specialist!

Leave a Reply

Your email address will not be published. Required fields are marked *

*