Haproxy 2.0 not support 1.8x lua?

this is work for 1.8.x

version:
HA-Proxy version 2.0.9 2019/11/15 - https://haproxy.org/

#config

frontend api_http
acl api_check path_beg -i “/check”
http-request lua.check_quth if api_check

#lua
---------check_req
core.register_action(“api_check_req”, { “http-req” }, function(applet)
local msg = ‘{“status”:1,“message”:“Could not authenticate you.”}’
applet.res:send(‘HTTP/1.0 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n’…msg)
applet:done()
end)

#error
[ALERT] 323/163943 (1236) : Lua function ‘api_check_req’: runtime error: 0 from [C] method ‘send’, /root/hap/api.lua:762 C function line 756.

There are 2 problems with this lua script. First, the parameter of a lua action is a transaction not an applet. It is just a naming error but it may mislead you. Second, since HAProxy 1.9, it is explicitly forbidden to call some channel’s functions from HTTP actions. Following functions are concerned: Channel.get, Channel.dup, Channel.getline, Channel.set, Channel.append, Channel.send, Channel.forward. It was a serious bugs of previous versions. These functions were totally hijacking the internal HTTP parser, leaving it in an undefined state. It was working by chance.

For now, there is no return statement in lua (Note there is no such statement either in http rules). And there is no way to deny or redirect a request from the lua itself. So I can see 2 solutions to send a custom reply from HAProxy.

  • If the response is static, you store it in an error file with the code 200. And, you may set a variable from your lua action to check it in an acl to deny the request with a deny_status set to 200. If your lua action does nothing except returning a custom response, you may consider to use the monitor-uri directive.
#config
frontend api_http
    errorfile 200 /path/to/200.http

    http-request lua.check_auth if { path_beg -i /checks }
    http-request deny deny_status 200 if { var(req.blocked) -m bool }

#lua
core.register_action(“api_check_req”, { “http-req” }, function(txn)
    ...
    txn.set-var('req.blocked', true)
end)
  • If your response must be customized, or if your script is too complex to be reduced to a variable at the end, you may use a lua service. So, here, definitely, there is a documentation problem, because it is an undocumented feature. Note that a service is an endpoint. So you MUST send a response from it. But here is your example with a lua service:
#config
frontend api_http
    http-request use-service api_check_service if { path_beg -i /checks }

#lua
core.register_service("api_check_service", "http", function(applet)
    local msg = ‘{“status”:1,“message”:“Could not authenticate you.”}’
    applet:set_status(200)
    applet:add_header("content-length", string.len(msg))
    applet:add_header("content-type", "application/json")
    applet:start_response()
    applet:send(response)
end)
1 Like

thanks for your reply and help.
have a good day