Using HAproxy as a reverse proxy

HAproxy has a great feature set when used in conjunction with Wt:
  • Uses async I/O and thus handles thousands of connections without any problem. Just like Wt!
  • Supports reverse proxying of WebSocket connections (as per draft-76).
  • Can be configured to use session affinity without needing cookies.

You need a fairly recent of haproxy for the options 'http-server-close' and 'http-pretend-keepalive' to work, which is needed for reliable load-balancing.

Basic setup

global
        log 127.0.0.1 local0
        log 127.0.0.1 local1 notice
        maxconn 4096
        user haproxy
        group haproxy
        daemon

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        option  http-server-close
        option  http-pretend-keepalive
        option  forwardfor
        option  originalto
        retries 3
        option redispatch
        maxconn 2000
        contimeout      5000
        clitimeout      50000
        srvtimeout      50000

listen 0.0.0.0:8181
        server srv1 0.0.0.0:9090 check

Using session affinity

All of the built-in mechanisms in HAproxy for session affinity using the appsession option rely on cookies, but cookies are not our preferred method since this does not give an intuitive user experience (e.g. a user cannot open multiple sessions), are not entirely reliable (a user can disable cookies) and a source of security risks (CSRF).

Luckily there is a work-around: using Wt's ability to generate session-id's that have a prefix which identifies the back-end, we can have HAproxy match on this prefix in the request URL and send the requests to the correct server.

Below is an example configuration for two back-end servers.

global
    log 127.0.0.1 local0 
    log 127.0.0.1 local1 notice
    maxconn 4096
    user haproxy
    group haproxy
    daemon

defaults
    log    global
    mode    http
    option    httplog
    option    dontlognull
        option  http-server-close
        option  http-pretend-keepalive
        option  forwardfor
        option  originalto
    retries    3
    option redispatch
    maxconn    2000
    contimeout    5000
    clitimeout    50000
    srvtimeout    50000

frontend wt
        bind 0.0.0.0:80
        acl srv1 url_sub wtd=wt1
        acl srv2 url_sub wtd=wt2
        acl srv1_up nbsrv(bck1) gt 0
        acl srv2_up nbsrv(bck2) gt 0
        use_backend bck1 if srv1_up srv1
        use_backend bck2 if srv2_up srv2
        default_backend bck_lb

backend bck_lb
        balance roundrobin
        server srv1 0.0.0.0:9090 track bck1/srv1
        server srv2 0.0.0.0:9091 track bck2/srv2

backend bck1
        balance roundrobin
        server srv1 0.0.0.0:9090 check

backend bck2
        balance roundrobin
        server srv2 0.0.0.0:9091 check

And start the two Wt httpd servers using:

$ app.wt --session-id-prefix=wt1 --http-port 9090 ...
$ app.wt --session-id-prefix=wt2 --http-port 9091 ...