Tuesday, June 16, 2015

Enabling Cross-Origin Resource Sharing (CORS) with HAProxy


Beberapa waktu yang lalu, teman saya sempat meminta tolong kepada saya ketika menemui isu CORS pada aplikasi AngularJS yang akan dia deploy ke cloud. Aplikasi AngularJS tersebut meng-consume REST services yang berada di belakang HAProxy. Setelah saya googling ke beberapa tempat, saya menemukan cara yang sesuai dengan kasus yang saya temui. 

Pada dasarnya ketika sebuah aplikasi Javascript me-request REST service untuk pertama kalinya, aplikasi tersebut akan mengirimkan request dengan method OPTION terlebih dahulu. Request OPTION tadi bertujuan untuk meminta otoritas mengakses resource REST yang akan diakses setelahnya. OPTION request mengharapkan response yang menyatakan origin, headers, dan HTTP method apa saja yang diijinkan oleh aplikasi backend untuk diakses secara cross-origin. Setelah mendapatkan response tersebut dan diijinkan oleh backend, barulah aplikasi Javascript memanggil REST request yang asli.

Terinspirasi ide dari salah satu pertanyaan di StackOverflow [1], yang saya lakukan adalah sebagai berikut:
  1. Filter HTTP request yang masuk ke HAProxy.
  2. Jika ada HTTP request dengan OPTION method, arahkan ke CORS handler.
  3. HTTP request selain itu arahkan ke default backend.
  4. Buat sebuah dummy CORS handler yang akan mengembalikan error file yang berisi HTTP OK 200. 

Berikut adalah potongan konfigurasi HAProxy yang diperlukan:
...
frontend web-frontend
    bind app.nostratech.com:80
    mode http
    option httplog
    use_backend cors-handlers if METH_OPTIONS
    default_backend nginx-servers

backend cors-handlers
    errorfile 503 /etc/haproxy/errors/custom/cors.http
    server cors app.nostratech.com:9999 maxconn 50 disabled

backend nginx-servers
    mode http
    option httplog
    option httpclose
    option forwardfor
    option httpchk HEAD / HTTP/1.1\r\nHost:\ app.nostratech.com
    option redispatch
    retries 5
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    balance roundrobin
    server app1 app1.nostratech.com:80 cookie app1
...

Pada potongan konfigurasi di atas, HAProxy memiliki 1 frontend yang akan mengarahkan request dengan method OPTIONS ke CORS handlers. Selain request OPTIONS tersebut, request akan diarahkan secara normal ke backend Nginx servers. Perlu diperhatikan bahwa sebenarnya CORS handlers ini hanyalah sebuah dummy backend yang tidak memiliki backend servers sama sekali. Server app.nostratech.com:9999 tidak pernah ada, dan bahkan statusnya disabled sehingga akan memicu HAProxy untuk mengembalikan response code 503 error ke client. Namun, CORS handlers meng-override dokumen 503 default HAProxy menjadi seperti ini:
HTTP/1.0 200 OK
Cache-Control: no-cache
Connection: close
Content-Type: text/html
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Content-Length: 0


Alih-alih dokumen 503 standar, HAProxy akan mengembalikan response HTTP 200 OK dan menambahkan HTTP headers yang diperlukan untuk menjawab method OPTIONS yang pertama yaitu Access-Control-Allow-Origin, Access-Control-Allow-Methods, dan Access-Control-Allow-Headers. Setelah melakukan perubahan tersebut, saya restart  proses HAProxy server dan isu yang ditemui teman saya sudah berhasil diselesaikan.

Silahkan mencoba :).

[1] StackOverflow: How to Send a Response with HAProxy without Passing the Request to Web Servers?

No comments:

Post a Comment