HAProxy community

Remote rest call

I have to write and check with remote API for each request coming into my haproxy server. Is there a way to send a request with parameters to an API endpoint and based on the response of the endpoint to select a backend.

So I’ve found that this is possible using LUA so I’ll post some sample code if anyone is interested.

1 Like

Yes, please do.

ok, so I’ve got it pretty much working but I’m having a problem getting the value of the web service. I’ve tried a few different libraries and rebuilding haproxy / lua / luarocks from scratch and still issues.

Pretty straightforward code and the url is valid.

local stream = assert(http_request.new_from_uri(api_call):go())
local body = assert(stream:get_body_as_string())

[ALERT] 164/081414 (14527) : Lua sample-fetch ‘forensiq’: runtime error: /root/haproxy_build/forensiq.lua:93: attempt to call a nil value (method ‘get_body_as_string’) from /root/haproxy_build/forensiq.lua:93 C function line 2.

and using the http.get method, I get this message.

[ALERT] 164/082838 (14788) : Lua sample-fetch ‘forensiq’: runtime error: /root/haproxy_build/forensiq.lua:92: attempt to index a nil value (global ‘http’) from /root/haproxy_build/forensiq.lua:92 C function line 2.

http.get(api_call, nil, function(code, data)
    if (code ~= 200) then
            print("HTTP request failed")
    else
            print(code, data)
    end

end)

I found it’s pretty easy. Here’s the basics on how I did it. I’m always open to improvements.

In the global section you load the lua script

  lua-load check_fraud.lua

and then in the front end section I had this.

     frontend frontend-http-https
         bind :80
         bind :443 ssl crt /etc/haproxy/ssl
         default_backend backend-default

     backend backend-default
         option forwardfor
         http-request deny if { lua.check_fraud 0 }
         http-response set-header X-Detection passed
         server web-1 www.website.com:443 check ssl verify none

and in the check_fraud.lua file

  core.register_fetches("check_fraud", function(txn)

      -- load up the libraries 
      local http_util = require "http.util" -- for dict_to_query
      local io = require("io")
      local https = require("ssl.https")
      local ltn12 = require("ltn12")
      local json = requires("json")

      -- local variables
      local api_root = "https://website.com/services/check"
      local now = os.date("*t")
      local parameters = {}
      local clientip = txn.f:src() 
      local risk_tolerance = 85
      
      parameters["client_key"] = "asdf123asdf"
      parameters["request_type"] = "click"
      parameters["ip_address"] = clientip
      parameters["click_time"] = string.format("%d-%02d-%02d %02d:%02d:%02d",now["year"],now["month"],now["day"],now["hour"],now["min"],now["sec"])
      parameters["path"] = txn.f:path()
      parameters["referrer"] = txn.sf:req_fhdr("host")
      parameters["ua"] = txn.sf:req_fhdr("User-Agent")
      parameters["output"] = "JSON"

      local api_call = api_root .. "?" .. http_util.dict_to_query(parameters)
      
      result, statuscode, content = https.request(api_call)

      local fraud_data = json:decode(result)
      fraud = fraud_data.items[1]
      
      if fraud.riskScore < risk_tolerance then
        return 1
      else
        return 0
      end
    
    end)