HAproxy: different 503 errorfile for OPTIONS and P

2020-07-30 00:22发布

问题:

We have a Web application using ajax calls to a backend running on different domain (-> CORS needed). The backend consists of a HAproxy 1.4.22 and then multiple Wildflys (running on OpenShift PaaS). In case no Wildfly is available (e.g. during "Maintenance"), HAproxy serves 503 to every request or the configured errorfile. So far so good...

This is a problem for the Web application to properly visualise "Maintenance Mode" according to a rejected backend request (with 503), because the browser first sends an OPTIONS request (preflight) and gets already a 503 to it. This ends up that the Browser does not reflect this status code to the performed ajax call in JavaScript (we get always status code 0 as response because the browser interpret it as fatal failure of preflight and denies any access). This story is not new and there are many posts at stackoverflow.

So how to solve this problem? My idea is to deliver two different errorfiles ("errorfile" in HAproxy language) - one serving OPTIONS requests with content "HTTP/1.1 200 OK.... Access-Control-Allow-Origin: *...." to pass the preflight check in the browser, then one errorfile serving the POST requests with content "HTTP/1.1 503 ....." to let the browser really reflect the status 503 in the ajax response. However, I am not able to get this running.

global
    maxconn     256

defaults
    mode                    http
    log                     global
    option                  httplog
    ...

listen express 127.4.184.2:8080
    acl is_options method OPTIONS
    acl is_post method POST

    errorfile 503 /var/lib/openshift/564468c90c1e66c7f2000077/app-root/runtime/repo/503.http if is_post
    errorfile 503 /var/lib/openshift/564468c90c1e66c7f2000077/app-root/runtime/repo/options.http if is_options

    option httpchk GET /
    http-check expect rstatus 2..|3..|401

    balance leastconn
    server local-gear 127.4.184.1:8080 check fall 2 rise 3 inter 2000 cookie local-564468c90c1e66c7f2000077

I understand that this cannot work because errorfile does not allow the if <condition> variant.

How can I achieve my wished behaviour? And if someone has another solution to solve this "Maintanace Mode" / CORS problem, I am open for any idea...

Thanks in advance!

回答1:

I found a good solution:

  • define two "backends" (therefore split the "listen" section into "frontend" and "backend"
  • The "frontend" section checks for the request method and uses one "backend" to answer OPTIONS request and one "backend" to answer all others.
  • The first defined "backend" is used to answer all OPTIONS requests with 200: This can be done with an errorfile and if we do not list any server in this section, this "backend" is marked as Down and responses therefore the errorfile (in which we send 200 back to the OPTIONS request).
  • The second defined "backend" stays for the "real" backend and responds - in case the real servers are down - also the content from an errorfile.
  • In the errorfiles, we can add the CORS headers.


HAproxy.cfg:

global
     maxconn     256

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    maxconn                 128
    ...

frontend balancer
    bind 127.8.155.130:8080
    mode http
    acl is_options method OPTIONS
    use_backend cors_backend if is_options
    default_backend business_backend

backend cors_backend
    errorfile 503 options.http

backend business_backend
    errorfile 503 503.http
    server ...
    server ...


options.http

HTTP/1.1 200 OK
Access-Control-Allow-Headers: Origin, Accept, X-Session_id, Content-Type
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Access-Control-Allow-Methods: HEAD, DELETE, POST, GET, OPTIONS, PUT
Connection: close
[empty line]


503.http

HTTP/1.1 503 Service Unavailable
Cache-Control: no-cache
Access-Control-Allow-Origin: *
Connection: close
[empty line]

There is one additional adventage: This configuration serves all OPTIONS requests autonomeously by HAproxy - even with CORS support!