Handling web-Sockets in Apache, Nginx and Ha-proxy

Handling web-Sockets in Apache, Nginx and Ha-proxy

WebSockets were introduced to open two-way interactive communication sessions, between a client and a server. This paved the way for event-driven responses, such as notifying a user of new content without refreshing the page.

Dealing with WebSockets - Apache

Handling WebSockets in Apache Web Server 2.4 isn’t as straight forward as with other web servers. The Apache Config with a proxy pass will work most of the time

But when the web socket comes to picture, sometimes the site breaks in the browsers

In the below config we will assume you have already enabled proxy pass for a website. To enable WebSocket to reverse proxying, the Apache modules for handling such requests must be enabled.

Ubuntu

If you are running Ubuntu 16.04 or higher, run the following commands to enable the modules.

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

it will ask you to restart apache2. do it.

systemctl restart apache2

CentOS 7

The prerequisite modules are already enabled by default on a CentOS 7 install of httpd. Enabling and disabling these modules is done by editing a configuration file.

  1. Open the module configuration file for proxies.
    sudo vi /etc/httpd/conf.modules.d/00-proxy.conf

  2. All modules related to proxying are listed in this configuration file. Verify that the following lines exist and are uncommented.
    LoadModule proxy_http_module modules/mod_proxy_http.so
    LoadModule proxy_wstunnel modules/mod_proxy_wstunnel.so

  3. If you made any changes to the file, save them now.

  4. Restart Apache Web Server to apply your changes.
    sudo systemctl restart httpd

mod_proxy = It provides support for the tunnelling of web socket connections to a backend websockets server. The connection is automatically upgraded to a websocket connection:

Things to be considered

Upgrade: WebSocket

Connection: Upgrade

Proxying requests to a websockets server like echo.websocket.org can be done using the ProxyPass directive:

ProxyPass "/ws2/"  "ws://echo.websocket.org/"

ProxyPass "/wss2/" "wss://echo.websocket.org/"

Proxying both HTTP and websockets at the same time, with a specific set of URL's being websocket-only, can be done by specifying the websockets ProxyPass directive before the HTTP directive:

ProxyPassMatch  ^/(myApp/ws)$  ws://backend.example.com:9080/$1

ProxyPass / http://backend.example.com:9080/

Proxying both HTTP and websockets at the same time, where the websockets URL's are not websocket-only or not known in advance can be done by using the RewriteRule directive to configure the websockets proxying:

ProxyPass / http://example.com:9080/

RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule  ^/?(.*) "ws://example.com:9080/$1" [P,L]

The web proxy port and host should be proper . add and save and restart httpd or Apache it should work properly

Note : if using proxy pass and websocket at same time the rewrite directive should be below proxypass directive.


Dealing with Websockets - Nginx

NGINX supports WebSocket by allowing a tunnel to be set up between a client and a backend server. For NGINX to send the Upgrade request from the client to the backend server, the Upgrade and Connection headers must be set explicitly, as in this example:-

location /wsapp/ {
    proxy_pass http://wsbackend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
}

You also need to enable Connection and http upgrade in http section

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

You may also need to configure a upstream as below

upstream wsbackend {
        server 192.168.100.10:8010;
    }

8010 is port where ws is running

Reload nginx

systemctl reload nginx

Once this is done, NGINX deals with this as a WebSocket connection.

Visit the link for more info on Nginx Document . It's pretty straight forward .


HAProxy & Websockets

There are 2 protocols embedded in websockets:

  1. HTTP: for the websocket setup

  2. TCP: websocket data exchange

HAProxy must be able to support websockets on these two protocols without breaking the TCP connection at any time.
There are 2 things to take care of:

  1. being able to switch a connection from HTTP to TCP without breaking it

  2. smartly manage timeouts for both protocols at the same time

Fortunately, HAProxy embeds all you need to load-balance properly websockets and can meet the 2 requirements above.
It can even route regular HTTP traffic from websocket traffic to different backends and perform websocket aware health check (setup phase only).

During the setup phase, HAProxy can work in HTTP mode, processing layer 7 information. It detects automatically the Connection: Upgrade exchange and is ready to switch to tunnel mode if the upgrade negotiation succeeds. During this phase, there are 3 timeouts involved:

  1. timeout client: client inactivity

  2. timeout connect: allowed TCP connection establishment time

  3. timeout server: allowed time to the server to process the request

If everything goes well, the websocket is established, then HAProxy fails over to tunnel mode, no data is analyzed anymore (and anyway, websocket does not speak HTTP). There is a single timeout involved:

  1. timeout tunnel: take precedence over client and server timeout

  2. timeout connect is not used since the TCP connection is already established.

Just make sure You have a following section in ha proxy config.

  timeout client          25s
  timeout connect          5s
  timeout server          25s
  timeout tunnel        3600s
  timeout http-keep-alive  1s
  timeout http-request    15s

Happy Dealing with websockets .

If you find it usefull . Follow me on github