Lua tcp-request content action timeout on core.sleep()

Hi,

I’m trying to read the first bytes (below 500 bytes) of a custom protocol to extract and log session identifying information. Since the client might be extremely slow to send the initial bytes I wanted to try and implement a while loop to read the first incoming message and tried with a very simple sleep to get started.

However this ends up with the message aborting Lua processing on expired timeout.

Is this type of thing generally not possible within a tcp-request content action?

test.lua

local function my_tcp_action(txn)
    txn.Info(txn, ">>> TCP ACTION")
    txn.Info(txn, ">>> request buffer not complete, waiting a second")
    core.sleep(10)
    txn.Info(txn, ">>> done sleeping")
    local request_buffer_content_len = txn.req:get_in_len()
    core.Debug(request_buffer_content_len)
    local request_buffer_content = txn.req:dup()
    core.Debug(tostring(request_buffer_content))
end

core.register_action('my_tcp_action', {'tcp-req'}, my_tcp_action, 0)

haproxy.cfg

global
  log stdout format raw local0
  tune.ssl.default-dh-param 2048
  ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:!aNULL:!MD5:!DSS
  ssl-default-bind-options ssl-min-ver TLSv1.2
  ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:!aNULL:!MD5:!DSS
  ssl-default-server-options ssl-min-ver TLSv1.2
  server-state-file global

  # lua settings
  tune.lua.session-timeout 30s
  lua-load /srv/lua/test.lua

defaults
  log               global
  retries                   3
  backlog               10000
  maxconn               10000
  timeout connect         30s
  timeout client          30s
  timeout server          30s
  timeout tunnel        3600s
  timeout http-keep-alive  1s
  timeout http-request    15s
  timeout queue           30s
  timeout tarpit          60s

frontend minimal_frontend
  bind *:55777 ssl crt /srv/crt/some_crt.pem tfo tls-ticket-keys /srv/crt/tls-ticket-keys
  mode tcp
  timeout client 10m
  option clitcpka
  tcp-request inspect-delay 30s
  tcp-request content accept if { req_ssl_hello_type 1 }
  use_backend minimal_backend

backend minimal_backend
  balance roundrobin
  mode tcp
  timeout server 10m
  tcp-request inspect-delay 30s
  tcp-request content lua.stratus_tcp_action
  server nc 127.0.0.1:6667 id 6667 weight 0

With the long tune.lua.session-timeout 30s I am able to run a while loop to wait for the bytes to trickle in but as soon as a core.sleep/core.msleep is involved the [ALERT] 125/084517 (6) : Lua function 'my_tcp_action': aborting Lua processing on expired timeout. error stops the action.

When I put a core.yield() the following error crashes haproxy:

A bogus STREAM [0x557bc1d85cf0] is spinning at 188177 calls per second and refuses to die, aborting now! Please report this error to developers [strm=0x557bc1d85cf0 src=127.0.0.1 fe=minimal_frontend be=minimal_backend dst=unknown rqf=0 rqa=480 rpf=80000000 rpa=0 sif=EST,200008 sib=INI,10 af=(nil),0 csf=0x557bc1d82d90,8200 ab=(nil),0 csb=(nil),0 cof=0x557bc1d28f30,80201306:PASS(0x557bc1d81320)/SSL(0x557bc1d73970)/tcpv4(36) cob=(nil),0:NONE((nil))/NONE((nil))/NONE(0) ]

Have you solved this problem? I’m facing the same issue when using core.msleep or core.sleep in the lua function. Setting the lua session timeout value doesn’t help. Getting aborting Lua processing on expired timeout. alert.

Still unsolved. I guess there are not that many people that use tcp-req action because all code examples I’ve found mention the use of core.sleep but it seems impossible to use it without the action being stopped :neutral_face:

I think I’ve solved mine.Add a tcp-request inspect-delay line before your tcp-request content line.

Example:

frontend testfront
    bind: *:8080
    tcp-request inspect-delay 2m
    tcp-request content lua.your_function

    default_backend some_backend

It seems like for TCP specific tasks, you need to tell HAProxy to delay the TCP processing for a certain time until a verdict of where to pass the packet is in place. In this example, we give the your_function function 2 minutes to decide where to pass the packet, else it will simply timeout.

Check here: https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/#inspect-delay

Hope this helps.

Great you’ve found a solution for your case. Maybe I have to tinker a little bit more with it.

In my case the lua action is in the backend. Both backend and frontend have tcp-request inspect-delay 30s set prior to tcp-request content ...

So maybe I just have to put it to the frontend. I’ll try to make some time for it.

What is that you want to sleep for anyway in LUA? A certain amount of bytes available from the client?

Then just the appropriate tcp-request rules, like:

tcp-request inspect-delay 2m
tcp-request content lua.your_function if { req.len gt 100 }

This should start your lua scrip only if 100 byte of data from the client are present. And after 2 minutes it gives up.

And yes, this belongs to a frontend.

1 Like

What is that you want to sleep for anyway in LUA? A certain amount of bytes available from the client?

Exactly!

tcp-request content lua.your_function if { req.len gt 100 }

Well, thats tidy! Didn’t think of the simplest way to do it. :+1:

I guess it’s obvious I’m still quite a noob when it comes to HAProxy :sweat_smile: