blob: 25db8a30470f4b60a13c37eb20f3fc54331bb309 [file] [log] [blame]
Thierry FOURNIER79c10512015-11-08 22:02:21 +01001 Lua: Architecture and first steps
2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Willy Tarreau3b50e5c2023-04-23 10:21:37 +02003 version 2.8
Thierry FOURNIER79c10512015-11-08 22:02:21 +01004
5 author: Thierry FOURNIER
6 contact: tfournier at arpalert dot org
7
8
9
10HAProxy is a powerful load balancer. It embeds many options and many
11configuration styles in order to give a solution to many load balancing
12problems. However, HAProxy is not universal and some special or specific
Godbach06c80992016-02-16 11:59:17 +080013problems do not have solution with the native software.
Thierry FOURNIER79c10512015-11-08 22:02:21 +010014
15This text is not a full explanation of the Lua syntax.
16
17This text is not a replacement of the HAProxy Lua API documentation. The API
18documentation can be found at the project root, in the documentation directory.
19The goal of this text is to discover how Lua is implemented in HAProxy and using
20it efficiently.
21
22However, this can be read by Lua beginners. Some examples are detailed.
23
24Why a scripting language in HAProxy
25===================================
26
27HAProxy 1.5 makes at possible to do many things using samples, but some people
Godbach06c80992016-02-16 11:59:17 +080028want to more combining results of samples fetches, programming conditions and
Ilya Shipitsin2075ca82020-03-06 23:22:22 +050029loops which is not possible. Sometimes people implement these functionalities
Thierry FOURNIER79c10512015-11-08 22:02:21 +010030in patches which have no meaning outside their network. These people must
31maintain these patches, or worse we must integrate them in the HAProxy
32mainstream.
33
34Their need is to have an embedded programming language in order to no longer
35modify the HAProxy source code, but to write their own control code. Lua is
36encountered very often in the software industry, and in some open source
37projects. It is easy to understand, efficient, light without external
Joseph Herlant71b4b152018-11-13 16:55:16 -080038dependencies, and leaves the resource control to the implementation. Its design
Thierry FOURNIER79c10512015-11-08 22:02:21 +010039is close to the HAProxy philosophy which uses components for what they do
40perfectly.
41
42The HAProxy control block allows one to take a decision based on the comparison
43between samples and patterns. The samples are extracted using fetch functions
44easily extensible, and are used by actions which are also extensible. It seems
45natural to allow Lua to give samples, modify them, and to be an action target.
46So, Lua uses the same entities as the configuration language. This is the most
Godbach06c80992016-02-16 11:59:17 +080047natural and reliable way for the Lua integration. So, the Lua engine allows one
Thierry FOURNIER79c10512015-11-08 22:02:21 +010048to add new sample fetch functions, new converter functions and new actions.
49These new entities can access the existing samples fetches and converters
50allowing to extend them without rewriting them.
51
52The writing of the first Lua functions shows that implementing complex concepts
53like protocol analysers is easy and can be extended to full services. It appears
54that these services are not easy to implement with the HAProxy configuration
Godbach06c80992016-02-16 11:59:17 +080055model which is based on four steps: fetch, convert, compare and action. HAProxy
Thierry FOURNIER79c10512015-11-08 22:02:21 +010056is extended with a notion of services which are a formalisation of the existing
57services like stats, cli and peers. The service is an autonomous entity with a
58behaviour pattern close to that of an external client or server. The Lua engine
59inherits from this new service and offers new possibilities for writing
60services.
61
62This scripting language is useful for testing new features as proof of concept.
63Later, if there is general interest, the proof of concept could be integrated
64with C language in the HAProxy core.
65
Godbach06c80992016-02-16 11:59:17 +080066The HAProxy Lua integration also provides a simple way for distributing Lua
Thierry FOURNIER79c10512015-11-08 22:02:21 +010067packages. The final user needs only to install the Lua file, load it in HAProxy
68and follow the attached documentation.
69
70Design and technical things
71===========================
72
73Lua is integrated into the HAProxy event driven core. We want to preserve the
74fast processing of HAProxy. To ensure this, we implement some technical concepts
75between HAProxy and the Lua library.
76
77The following paragraph also describes the interactions between Lua and HAProxy
78from a technical point of view.
79
80Prerequisite
81-----------
82
83Reading the following documentation links is required to understand the
84current paragraph:
85
Willy Tarreau77ec4622022-04-16 07:58:19 +020086 HAProxy doc: http://docs.haproxy.org/
Thierry FOURNIER79c10512015-11-08 22:02:21 +010087 Lua API: http://www.lua.org/manual/5.3/
Willy Tarreau77ec4622022-04-16 07:58:19 +020088 HAProxy API: http://www.arpalert.org/src/haproxy-lua-api/2.6/index.html
Thierry FOURNIER79c10512015-11-08 22:02:21 +010089 Lua guide: http://www.lua.org/pil/
90
91more about Lua choice
92---------------------
93
94Lua language is very simple to extend. It is easy to add new functions written
Godbach06c80992016-02-16 11:59:17 +080095in C in the core language. It is not required to embed very intrusive libraries,
96and we do not change compilation processes.
Thierry FOURNIER79c10512015-11-08 22:02:21 +010097
98The amount of memory consumed can be controlled, and the issues due to lack of
99memory are perfectly caught. The maximum amount of memory allowed for the Lua
100processes is configurable. If some memory is missing, the current Lua action
101fails, and the HAProxy processing flow continues.
102
103Lua provides a way for implementing event driven design. When the Lua code
104wants to do a blocking action, the action is started, it executes non blocking
105operations, and returns control to the HAProxy scheduler when it needs to wait
106for some external event.
107
108The Lua process can be interrupted after a number of instructions executed. The
109Lua execution will resume later. This is a useful way for controlling the
110execution time. This system also keeps HAProxy responsive. When the Lua
111execution is interrupted, HAProxy accepts some connections or transfers pending
112data. The Lua execution does not block the main HAProxy processing, except in
113some cases which we will see later.
114
115Lua function integration
116------------------------
117
118The Lua actions, sample fetches, converters and services are integrated in
119HAProxy with "register_*" functions. The register system is a choice for
120providing HAProxy Lua packages easily. The register system adds new sample
121fetches, converters, actions or services usable in the HAProxy configuration
122file.
123
124The register system is defined in the "core" functions collection. This
125collection is provided by HAProxy and is always available. Below, the list of
126these functions:
127
128 - core.register_action()
129 - core.register_converters()
130 - core.register_fetches()
131 - core.register_init()
132 - core.register_service()
133 - core.register_task()
134
135These functions are the execution entry points.
136
137HTTP action must be used for manipulating HTTP request headers. This action
138can not manipulates HTTP content. It is dangerous to use the channel
139manipulation object with an HTTP request in an HTTP action. The channel
140manipulation can transform a valid request in an invalid request. In this case,
141the action will never resume and the processing will be frozen. HAProxy
142discards the request after the reception timeout.
143
144Non blocking design
145-------------------
146
147HAProxy is an event driven software, so blocking system calls are absolutely
148forbidden. However, the Lua allows to do blocking actions. When an action
149blocks, HAProxy is waiting and do nothing, so the basic functionalities like
150accepting connections or forwarding data are blocked while the end of the system
151call. In this case HAProxy will be less responsive.
152
153This is very insidious because when the developer tries to execute its Lua code
154with only one stream, HAProxy seems to run fine. When the code is used with
155production stream, HAProxy encounters some slow processing, and it cannot
156hold the load.
157
158However, during the initialisation state, you can obviously using blocking
159functions. There are typically used for loading files.
160
161The list of prohibited standard Lua functions during the runtime contains all
162that do filesystem access:
163
164 - os.remove()
165 - os.rename()
166 - os.tmpname()
167 - package.*()
168 - io.*()
169 - file.*()
170
171Some other functions are prohibited:
172
173 - os.execute(), waits for the end of the required execution blocking HAProxy.
174
Godbach06c80992016-02-16 11:59:17 +0800175 - os.exit(), is not really dangerous for the process, but it's not the good way
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100176 for exiting the HAProxy process.
177
178 - print(), writes data on stdout. In some cases these writes are blocking, the
179 best practice is reserving this call for debugging. We must prefer
180 to use core.log() or TXN.log() for sending messages.
181
182Some HAProxy functions have a blocking behaviour pattern in the Lua code, but
183there are compatible with the non blocking design. These functions are:
184
185 - All the socket class
186 - core.sleep()
187
188Responsive design
189-----------------
190
Godbach06c80992016-02-16 11:59:17 +0800191HAProxy must process connections accept, forwarding data and processing timeouts
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100192as soon as possible. The first thing is to believe that a Lua script with a long
193execution time should impact the expected responsive behaviour.
194
195It is not the case, the Lua script execution are regularly interrupted, and
196HAProxy can process other things. These interruptions are exprimed in number of
197Lua instructions. The number of interruptions between two interrupts is
198configured with the following "tune" option:
199
200 tune.lua.forced-yield <nb>
201
202The default value is 10 000. For determining it, I ran benchmark on my laptop.
Godbach06c80992016-02-16 11:59:17 +0800203I executed a Lua loop between 10 seconds with different values for the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100204"tune.lua.forced-yield" option, and I noted the results:
205
206 configured | Number of
207 instructions | loops executed
Godbach06c80992016-02-16 11:59:17 +0800208 between two | in millions
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100209 forced yields |
210 ---------------+---------------
211 10 | 160
212 500 | 670
213 1000 | 680
214 5000 | 700
215 7000 | 700
216 8000 | 700
217 9000 | 710 <- ceil
218 10000 | 710
219 100000 | 710
220 1000000 | 710
221
222The result showed that from 9000 instructions between two interrupt, we reached
223a ceil, so the default parameter is 10 000.
224
225When HAProxy interrupts the Lua processing, we have two states possible:
226
227 - Lua is resumable, and it returns control to the HAProxy scheduler,
228 - Lua is not resumable, and we just check the execution timeout.
229
230The second case occurs if it is required by the HAProxy core. This state is
231forced if the Lua is processed in a non resumable HAProxy part, like sample
232fetches or converters.
233
234It occurs also if the Lua is non resumable. For example, if some code is
235executed through the Lua pcall() function, the execution is not resumable. This
236is explained later.
237
238So, the Lua code must be fast and simple when is executed as sample fetches and
239converters, it could be slow and complex when is executed as actions and
240services.
241
242Execution time
243--------------
244
Godbach06c80992016-02-16 11:59:17 +0800245The Lua execution time is measured and limited. Each group of functions has its
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100246own timeout configured. The time measured is the real Lua execution time, and
247not the difference between the end time and the start time. The groups are:
248
249 - main code and init are not submitted to the timeout,
250 - fetches, converters and action have a default timeout of 4s,
251 - task, by default does not have timeout,
252 - service have a default timeout of 4s.
253
Godbach06c80992016-02-16 11:59:17 +0800254The corresponding tune options are:
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100255
Aurelien DARRAGON58e36e52023-04-06 22:51:56 +0200256 - tune.lua.session-timeout (action, filter, cli)
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100257 - tune.lua.task-timeout (task)
258 - tune.lua.service-timeout (services)
Aurelien DARRAGON58e36e52023-04-06 22:51:56 +0200259 - tune.lua.burst-timeout (max time between two lua yields)
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100260
Godbach06c80992016-02-16 11:59:17 +0800261The task does not have a timeout because it runs in background along the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100262HAProxy process life.
263
Godbach06c80992016-02-16 11:59:17 +0800264For example, if an Lua script is executed during 1.1s and the script executes a
265sleep of 1 second, the effective measured running time is 0.1s.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100266
267This timeout is useful for preventing infinite loops. During the runtime, it
Godbach06c80992016-02-16 11:59:17 +0800268should be never triggered.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100269
270The stack and the coprocess
271---------------------------
272
273The Lua execution is organized around a stack. Each Lua action, even out of the
274effective execution, affects the stack. HAProxy integration uses one main stack,
275which is common for all the process, and a secondary one used as coprocess.
276After the initialization, the main stack is no longer used by HAProxy, except
277for global storage. The second type of stack is used by all the Lua functions
278called from different Lua actions declared in HAProxy. The main stack permits
279to store coroutines pointers, and some global variables.
280
281Do you want to see an example of how seems Lua C development around a stack ?
282Some examples follows. This first one, is a simple addition:
283
284 lua_pushnumber(L, 1)
285 lua_pushnumber(L, 2)
286 lua_arith(L, LUA_OPADD)
287
Godbach06c80992016-02-16 11:59:17 +0800288It's easy, we push 1 on the stack, after, we push 2, and finally, we perform an
Ilya Shipitsin2075ca82020-03-06 23:22:22 +0500289addition. The two top entries of the stack are added, popped, and the result is
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100290pushed. It is a classic way with a stack.
291
Godbach06c80992016-02-16 11:59:17 +0800292Now an example for constructing array and objects. It's a little bit more
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100293complicated. The difficult consist to keep in mind the state of the stack while
294we write the code. The goal is to create the entity described below. Note that
295the notation "*1" is a metatable reference. The metatable will be explained
296later.
297
298 name*1 = {
299 [0] = <userdata>,
300 }
301
302 *1 = {
303 "__index" = {
304 "method1" = <function>,
305 "method2" = <function>
306 }
307 "__gc" = <function>
308 }
309
310Let's go:
311
312 lua_newtable() // The "name" table
313 lua_newtable() // The metatable *1
314 lua_pushstring("__index")
315 lua_newtable() // The "__index" table
316 lua_pushstring("method1")
317 lua_pushfunction(function)
318 lua_settable(-3) // -3 is an index in the stack. insert method1
319 lua_pushstring("method2")
320 lua_pushfunction(function)
321 lua_settable(-3) // insert method2
322 lua_settable(-3) // insert "__index"
323 lua_pushstring("__gc")
324 lua_pushfunction(function)
325 lua_settable() // insert "__gc"
326 lua_setmetatable(-1) // attach metatable to "name"
327 lua_pushnumber(0)
328 lua_pushuserdata(userdata)
329 lua_settable(-3)
330 lua_setglobal("name")
331
332So, coding for Lua in C, is not complex, but it needs some mental gymnastic.
333
334The object concept and the HAProxy format
335-----------------------------------------
336
Godbach06c80992016-02-16 11:59:17 +0800337The object seems to be not a native concept. An Lua object is a table. We can
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100338note that the table notation accept three forms:
339
340 1. mytable["entry"](mytable, "param")
341 2. mytable.entry(mytable, "param")
342 3. mytable:entry("param")
343
344These three notation have the same behaviour pattern: a function is executed
Godbach06c80992016-02-16 11:59:17 +0800345with the table itself as first parameter and string "param" as second parameter
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100346The notation with [] is commonly used for storing data in a hash table, and the
347dotted notation is used for objects. The notation with ":" indicates that the
348first parameter is the element at the left of the symbol ":".
349
350So, an object is a table and each entry of the table is a variable. A variable
351can be a function. These are the first concepts of the object notation in the
352Lua, but it is not the end.
353
354With the objects, we usually expect classes and inheritance. This is the role of
355the metable. A metable is a table with predefined entries. These entries modify
356the default behaviour of the table. The simplest example is the "__index" entry.
357If this entry exists, it is called when a value is requested in the table. The
358behaviour is the following:
359
360 1 - looks in the table if the entry exists, and if it the case, return it
361
362 2 - looks if a metatable exists, and if the "__index" entry exists
363
364 3 - if "__index" is a function, execute it with the key as parameter, and
365 returns the result of the function.
366
367 4 - if "__index" is a table, looks if the requested entry exists, and if
368 exists, return it.
369
370 5 - if not exists, return to step 2
371
372The behaviour of the point 5 represents the inheritance.
373
374In HAProxy all the provided objects are tables, the entry "[0]" contains private
Ilya Shipitsin11057a32020-06-21 21:18:27 +0500375data, there are often userdata or lightuserdata. The metatable is registered in
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100376the global part of the main Lua stack, and it is called with the case sensitive
377class name. A great part of these class must not be used directly because it
378requires an initialisation using the HAProxy internal structs.
379
Godbach06c80992016-02-16 11:59:17 +0800380The HAProxy objects use unified conventions. An Lua object is always a table.
381In most cases, an HAProxy Lua object needs some private data. These are always
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100382set in the index [0] of the array. The metatable entry "__tostring" returns the
383object name.
384
Jackie Tapia749f74c2020-07-22 18:59:40 -0500385The Lua developer can add entries to the HAProxy object. They just work carefully
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100386and prevent to modify the index [0].
387
Willy Tarreau714f3452021-05-09 06:47:26 +0200388Common HAProxy objects are:
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100389
390 - TXN : manipulates the transaction between the client and the server
391 - Channel : manipulates proxified data between the client and the server
392 - HTTP : manipulates HTTP between the client and the server
393 - Map : manipulates HAProxy maps.
394 - Fetches : access to all HAProxy sample fetches
395 - Converters : access to all HAProxy sample converters
396 - AppletTCP : process client request like a TCP server
397 - AppletHTTP : process client request like an HTTP server
398 - Socket : establish tcp connection to a server (ipv4/ipv6/socket/ssl/...)
399
400The garbage collector and the memory allocation
401-----------------------------------------------
402
403Lua doesn't really have a global memory limit, but HAProxy implements it. This
404permits to control the amount of memory dedicated to the Lua processes. It is
405specially useful with embedded environments.
406
407When the memory limit is reached, HAProxy refuses to give more memory to the Lua
408scripts. The current Lua execution is terminated with an error and HAProxy
Godbach06c80992016-02-16 11:59:17 +0800409continues its processing.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100410
411The max amount of memory is configured with the option:
412
413 tune.lua.maxmem
414
415As many other script languages, Lua uses a garbage collector for reusing its
Michael Prokop4438c602019-05-24 10:25:45 +0200416memory. The Lua developer can work without memory preoccupation. Usually, the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100417garbage collector is controlled by the Lua core, but sometimes it will be useful
418to run when the user/developer requires. So the garbage collector can be called
419from C part or Lua part.
420
421Sometimes, objects using lightuserdata or userdata requires to free some memory
422block or close filedescriptor not controlled by the Lua. A dedicated garbage
Godbach06c80992016-02-16 11:59:17 +0800423collection function is provided through the metatable. It is referenced with the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100424special entry "__gc".
425
426Generally, in HAProxy, the garbage collector does this job without any
Godbach06c80992016-02-16 11:59:17 +0800427intervention. However some objects use a great amount of memory, and we want to
428release as quickly as possible. The problem is that only the GC knows if the
429object is in use or not. The reason is simple variable containing objects can be
430shared between coroutines and the main thread, so an object can be used
431everywhere in HAProxy.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100432
433The only one example is the HAProxy sockets. These are explained later, just for
434understanding the GC issues, a quick overview of the socket follows. The HAProxy
Godbach06c80992016-02-16 11:59:17 +0800435socket uses an internal session and stream, the session uses resources like
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100436memory and file descriptor and in some cases keeps a socket open while it is no
Godbach06c80992016-02-16 11:59:17 +0800437longer used by Lua.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100438
439If the HAProxy socket is used, we forcing a garbage collector cycle after the
440end of each function using HAProxy socket. The reason is simple: if the socket
441is no longer used, we want to close the connection quickly.
442
443A special flag is used in HAProxy indicating that a HAProxy socket is created.
444If this flag is set, a full GC cycle is started after each Lua action. This is
445not free, we loose about 10% of performances, but it is the only way for closing
446sockets quickly.
447
448The yield concept / longjmp issues
449----------------------------------
450
451The "yield" is an action which does some Lua processing in pause and give back
452the hand to the HAProxy core. This action is do when the Lua needs to wait about
Godbach06c80992016-02-16 11:59:17 +0800453data or other things. The most basically example is the sleep() function. In an
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100454event driven software the code must not process blocking systems call, so the
455sleep blocks the software between a lot of time. In HAProxy, an Lua sleep does a
Godbach06c80992016-02-16 11:59:17 +0800456yield, and ask to the scheduler to be woken up in a required sleep time.
457Meanwhile, the HAProxy scheduler does other things, like accepting new
458connection or forwarding data.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100459
Godbach06c80992016-02-16 11:59:17 +0800460A yield is also executed regularly, after a lot of Lua instructions processed.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100461This yield permits to control the effective execution time, and also give back
Godbach06c80992016-02-16 11:59:17 +0800462the hand to the HAProxy core. When HAProxy finishes to process the pending jobs,
463the Lua execution continues.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100464
465This special "yield" uses the Lua "debug" functions. Lua provides a debug method
466called "lua_sethook()" which permits to interrupt the execution after some
467configured condition and call a function. This condition used in HAProxy is
Godbach06c80992016-02-16 11:59:17 +0800468a number of instructions processed and when a function returns. The function
469called controls the effective execution time, and if it is possible to send a
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100470"yield".
471
472The yield system is based on a couple setjmp/longjmp. In brief, the setjmp()
473stores a stack state, and the longjmp restores the stack in its state which had
474before the last Lua execution.
475
Godbach06c80992016-02-16 11:59:17 +0800476Lua can immediately stop its execution if an error occurs. This system uses also
Joseph Herlant71b4b152018-11-13 16:55:16 -0800477the longjmp system. In HAProxy, we try to use this system only for unrecoverable
Godbach06c80992016-02-16 11:59:17 +0800478errors. Maybe some trivial errors target an exception, but we try to remove it.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100479
480It seems that Lua uses the longjmp system for having a behaviour like the java
Godbach06c80992016-02-16 11:59:17 +0800481try / catch. We can use the function pcall() to execute some code. The function
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100482pcall() run a setjmp(). So, if any error occurs while the Lua code execution,
Godbach06c80992016-02-16 11:59:17 +0800483the flow immediately returns from the pcall() with an error.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100484
485The big issue of this behaviour is that we cannot do a yield. So if some Lua code
486executes a library using pcall for catching errors, HAProxy must be wait for the
487end of execution without processing any accept or any stream. The cause is the
Godbach06c80992016-02-16 11:59:17 +0800488yield must be jump to the root of execution. The intermediate setjmp() avoids
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100489this behaviour.
490
491
Willy Tarreau714f3452021-05-09 06:47:26 +0200492 HAProxy start Lua execution
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100493 + Lua puts a setjmp()
494 + Lua executes code
495 + Some code is executed in a pcall()
496 + pcall() puts a setjmp()
497 + Lua executes code
498 + A yield is require for a sleep function
499 it cannot be jumps to the Lua root execution.
500
501
502Another issue with the processing of strong errors is the manipulation of the
503Lua stack outside of an Lua processing. If one of the functions called occurs a
504strong error, the default behaviour is an abort(). It is not acceptable when
505HAProxy is in runtime mode. The Lua documentation propose to use another
Godbach06c80992016-02-16 11:59:17 +0800506setjmp/longjmp to avoid the abort(). The goal is to put a setjmp between
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100507manipulating the Lua stack and using an alternative "panic" function which jumps
508to the setjmp() in error case.
509
510All of these behaviours are very dangerous for the stability, and the internal
511HAProxy code must be modified with many precautions.
512
513For preserving a good behaviour of HAProxy, the yield is mandatory.
Godbach06c80992016-02-16 11:59:17 +0800514Unfortunately, some HAProxy parts are not adapted for resuming an execution
515after a yield. These parts are the sample fetches and the sample converters. So,
516the Lua code written in these parts of HAProxy must be quickly executed, and can
517not do actions which require yield like TCP connection or simple sleep.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100518
Willy Tarreau714f3452021-05-09 06:47:26 +0200519HAProxy socket object
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100520---------------------
521
522The HAProxy design is optimized for the data transfers between a client and a
523server, and processing the many errors which can occurs during these exchanges.
524HAProxy is not designed for having a third connection established to a third
525party server.
526
Godbach06c80992016-02-16 11:59:17 +0800527The solution consist to put the main stream in pause waiting for the end of the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100528exchanges with the third connection. This is completed by a signal between
529internal tasks. The following graph shows the HAProxy Lua socket:
530
531
532 +--------------------+
533 | Lua processing |
534 ------------------\ | creates socket | ------------------\
535 incoming request > | and puts the | Outgoing request >
536 ------------------/ | current processing | ------------------/
537    | in pause waiting |
538 | for TCP applet |
539 +-----------------+--+
540 ^ |
541 | |
542 | signal | read / write
543 | | data
544 | |
545 +-------------+---------+ v
546 | HAProxy internal +----------------+
547 | applet send signals | |
548 | when data is received | | -------------------\
549 | or some room is | Attached I/O | Client TCP stream >
550 | available | Buffers | -------------------/
551 +--------------------+--+ |
552 | |
553 +-------------------+
554
555
556A more detailed graph is available in the "doc/internals" directory.
557
558The HAProxy Lua socket uses a full HAProxy session / stream for establishing the
559connection. This mechanism provides all the facilities and HAProxy features,
560like the SSL stack, many socket type, and support for namespaces.
Godbach06c80992016-02-16 11:59:17 +0800561Technically it supports the proxy protocol, but there are no way to enable it.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100562
563How compiling HAProxy with Lua
564==============================
565
Godbach06c80992016-02-16 11:59:17 +0800566HAProxy 1.6 requires Lua 5.3. Lua 5.3 offers some features which make easy the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100567integration. Lua 5.3 is young, and some distros do not distribute it. Luckily,
568Lua is a great product because it does not require exotic dependencies, and its
569build process is really easy.
570
571The compilation process for linux is easy:
572
573 - download the source tarball
574 wget http://www.lua.org/ftp/lua-5.3.1.tar.gz
575
576 - untar it
577 tar xf lua-5.3.1.tar.gz
578
579 - enter the directory
580 cd lua-5.3.1
581
582 - build the library for linux
583 make linux
584
585 - install it:
Godbach06c80992016-02-16 11:59:17 +0800586 sudo make INSTALL_TOP=/opt/lua-5.3.1 install
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100587
588HAProxy builds with your favourite options, plus the following options for
589embedding the Lua script language:
590
591 - download the source tarball
592 wget http://www.haproxy.org/download/1.6/src/haproxy-1.6.2.tar.gz
593
594 - untar it
595 tar xf haproxy-1.6.2.tar.gz
596
597 - enter the directory
598 cd haproxy-1.6.2
599
600 - build HAProxy:
Willy Tarreaud254aa82019-06-14 18:40:48 +0200601 make TARGET=linux-glibc \
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100602 USE_LUA=1 \
603 LUA_LIB=/opt/lua-5.3.1/lib \
604 LUA_INC=/opt/lua-5.3.1/include
605
606 - install it:
607 sudo make PREFIX=/opt/haproxy-1.6.2 install
608
609First steps with Lua
610====================
611
Godbach06c80992016-02-16 11:59:17 +0800612Now, it's time to use Lua in HAProxy.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100613
614Start point
615-----------
616
Godbach06c80992016-02-16 11:59:17 +0800617The HAProxy global directive "lua-load <file>" allows to load an Lua file. This
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100618is the entry point. This load become during the configuration parsing, and the
619Lua file is immediately executed.
620
Godbach06c80992016-02-16 11:59:17 +0800621All the register_*() functions must be called at this time because they are used
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100622just after the processing of the global section, in the frontend/backend/listen
623sections.
624
625The most simple "Hello world !" is the following line a loaded Lua file:
626
627 core.Alert("Hello World !");
628
Godbach06c80992016-02-16 11:59:17 +0800629It displays a log during the HAProxy startup:
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100630
631 [alert] 285/083533 (14465) : Hello World !
632
Tristan2632d042023-10-23 13:07:39 +0100633Note: By default, logs originating from a LUA script are sent to the loggers
634applicable to the current context, if any, and additionally to stderr. See
635tune.lua.log.loggers and tune.lua.log.stderr for more information.
636
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100637Default path and libraries
638--------------------------
639
640Lua can embed some libraries. These libraries can be included from different
Godbach06c80992016-02-16 11:59:17 +0800641paths. It seems that Lua doesn't like subdirectories. In the following example,
642I try to load a compiled library, so the first line is Lua code, the second line
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100643is an 'strace' extract proving that the library was opened. The next lines are
644the associated error.
645
646 require("luac/concat")
647
648 open("./luac/concat.so", O_RDONLY|O_CLOEXEC) = 4
649
Willy Tarreau9f903af2021-05-07 08:42:39 +0200650 [ALERT] (22806) : parsing [commonstats.conf:15] : lua runtime
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100651 error: error loading module 'luac/concat' from file './luac/concat.so':
652 ./luac/concat.so: undefined symbol: luaopen_luac/concat
653
654Lua tries to load the C symbol 'luaopen_luac/concat'. When Lua tries to open a
655library, it tries to execute the function associated to the symbol
656"luaopen_<libname>".
657
658The variable "<libname>" is defined using the content of the variable
659"package.cpath" and/or "package.path". The default definition of the
660"package.cpath" (on my computer is ) variable is:
661
662 /usr/local/lib/lua/5.3/?.so;/usr/local/lib/lua/5.3/loadall.so;./?.so
663
Godbach06c80992016-02-16 11:59:17 +0800664The "<libname>" is the content which replaces the symbol "<?>". In the previous
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100665example, its "luac/concat", and obviously the Lua core try to load the function
666associated with the symbol "luaopen_luac/concat".
667
668My conclusion is that Lua doesn't support subdirectories. So, for loading
669libraries in subdirectory, it must fill the variable with the name of this
670subdirectory. The extension .so must disappear, otherwise Lua try to execute the
671function associated with the symbol "luaopen_concat.so". The following syntax is
672correct:
673
674 package.cpath = package.cpath .. ";./luac/?.so"
675 require("concat")
676
677First useful example
678--------------------
679
680 core.register_fetches("my-hash", function(txn, salt)
681 return txn.sc:sdbm(salt .. txn.sf:req_fhdr("host") .. txn.sf:path() .. txn.sf:src(), 1)
682 end)
683
Godbach06c80992016-02-16 11:59:17 +0800684You will see that these 3 lines can generate a lot of explanations :)
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100685
686Core.register_fetches() is executed during the processing of the global section
687by the HAProxy configuration parser. A new sample fetch is declared with name
688"my-hash", this name is always prefixed by "lua.". So this new declared
689sample fetch will be used calling "lua.my-hash" in the HAProxy configuration
690file.
691
692The second parameter is an inline declared anonymous function. Note the closed
Godbach06c80992016-02-16 11:59:17 +0800693parenthesis after the keyword "end" which ends the function. The first parameter
694of this anonymous function is "txn". It is an object of class TXN. It provides
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100695access functions. The second parameter is an arbitrary value provided by the
696HAProxy configuration file. This parameter is optional, the developer must
Godbach06c80992016-02-16 11:59:17 +0800697check if it is present.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100698
699The anonymous function registration is executed when the HAProxy backend or
700frontend configuration references the sample fetch "lua.my-hash".
701
Godbach06c80992016-02-16 11:59:17 +0800702This example can be written with another style, like below:
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100703
704 function my_hash(txn, salt)
705 return txn.sc:sdbm(salt .. txn.sf:req_fhdr("host") .. txn.sf:path() .. txn.sf:src(), 1)
706 end
707
708 core.register_fetches("my-hash", my_hash)
709
710This second form is clearer, but the first one is compact.
711
Godbach06c80992016-02-16 11:59:17 +0800712The operator ".." is a string concatenation. If one of the two operands is not a
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100713string, an error occurs and the execution is immediately stopped. This is
714important to keep in mind for the following things.
715
716Now I write the example on more than one line. Its an easiest way for commenting
717the code:
718
719 1. function my_hash(txn, salt)
720 2. local str = ""
721 3. str = str .. salt
722 4. str = str .. txn.sf:req_fhdr("host")
723 5. str = str .. txn.sf:path()
724 6. str = str .. txn.sf:src()
725 7. local result = txn.sc:sdbm(str, 1)
726 8. return result
727 9. end
728 10.
729 11. core.register_fetches("my-hash", my_hash)
730
731local
732~~~~~
733
734The first keyword is "local". This is a really important keyword. You must
735understand that the function "my_hash" will be called for each HAProxy request
736using the declared sample fetch. So, this function can be executed many times in
737parallel.
738
Godbach06c80992016-02-16 11:59:17 +0800739By default, Lua uses global variables. So in this example, if the variable "str"
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100740is declared without the keyword "local", it will be shared by all the parallel
741executions of the function and obviously, the content of the requests will be
742shared.
743
744This warning is very important. I tried to write useful Lua code like a rewrite
Godbach06c80992016-02-16 11:59:17 +0800745of the statistics page, and it is very hard thing to declare each variable as
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100746"local".
747
Godbach06c80992016-02-16 11:59:17 +0800748I guess that this behaviour will be the cause of many troubles on the mailing
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100749list.
750
751str = str ..
752~~~~~~~~~~~~
753
Godbach06c80992016-02-16 11:59:17 +0800754Now a parenthesis about the form "str = str ..". This form allows to do string
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100755concatenations. Remember that Lua uses a garbage collector, so what happens when
756we do "str = str .. 'another string'" ?
757
758 str = str .. "another string"
759 ^ ^ ^ ^
760 1 2 3 4
761
Godbach06c80992016-02-16 11:59:17 +0800762Lua executes first the concatenation operator (3), it allocates memory for the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100763resulting string and fill this memory with the concatenation of the operands 2
Godbach06c80992016-02-16 11:59:17 +0800764and 4. Next, it frees the variable 1, now the old content of 1 can be garbage
765collected. And finally, the new content of 1 is the concatenation.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100766
767what the matter ? when we do this operation many times, we consume a lot of
768memory, and the string data is duplicated and move many times. So, this practice
769is expensive in execution time and memory consumption.
770
771There are easy ways to prevent this behaviour. I guess that a C binding for
772concatenation with chunks will be available ASAP (it is already written). I do
773some benchmarks. I compare the execution time of 1 000 times, 1 000
774concatenation of 10 bytes written in pure Lua and with a C library. The result is
77510 times faster in C (1s in Lua, and 0.1s in C).
776
777txn
778~~~
779
780txn is an HAProxy object of class TXN. The documentation is available in the
781HAProxy Lua API reference. This class allow the access to the native HAProxy
782sample fetches and converters. The object txn contains 2 members dedicated to
783the sample fetches and 2 members dedicated to the converters.
784
785The sample fetches members are "f" (as sample-Fetch) and "sf" (as String
Godbach06c80992016-02-16 11:59:17 +0800786sample-Fetch). These two members contain exactly the same functions. All the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100787HAProxy native sample fetches are available, obviously, the Lua registered sample
788fetches are not available. Unfortunately, HAProxy sample fetches names are not
Godbach06c80992016-02-16 11:59:17 +0800789compatible with the Lua function names, and they are renamed. The rename
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100790convention is simple, we replace all the '.', '+' and '-' by '_'. The '.' is the
791object member separator, and the "-" and "+" is math operator.
792
Godbach06c80992016-02-16 11:59:17 +0800793Now, that I'm writing this article, I know the Lua better than I wrote the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100794sample-fetches wrapper. The original HAProxy sample-fetches name should be used
795using alternative manner to call an object member, so the sample-fetch
Godbach06c80992016-02-16 11:59:17 +0800796"req.fhdr" (actually renamed req_fhdr") should be used like this:
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100797
798 txn.f["req.fhdr"](txn.f, ...)
799
800However, I think that this form is not elegant.
801
802The "s" collection return a data with a type near to the original returned type.
Godbach06c80992016-02-16 11:59:17 +0800803A string returns an Lua string, an integer returns an Lua integer and an IP
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100804address returns an Lua string. Sometime the data is not or not yet available, in
805this case it returns the Lua nil value.
806
Godbach06c80992016-02-16 11:59:17 +0800807The "sf" collection guarantees that a string will be always returned. If the data
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100808is not available, an empty string is returned. The main usage of these collection
809is to concatenate the returned sample-fetches without testing each function.
810
Godbach06c80992016-02-16 11:59:17 +0800811The parameters of the sample-fetches are according with the HAProxy
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100812documentation.
813
Godbach06c80992016-02-16 11:59:17 +0800814The converters run exactly with the same manner as the sample fetches. The
815only one difference is that the first parameter is the converter entry element.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100816The "c" collection returns a precise result, and the "sc" collection returns
817always a string.
818
819The sample-fetches used in the example function are "txn.sf:req_fhdr()",
Godbach06c80992016-02-16 11:59:17 +0800820"txn.sf:path()" and "txn.sf:src()". The converter is "txn.sc:sdbm()". The same
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100821function with the "s" collection of sample-fetches and the "c" collection of
822converter should be written like this:
823
824 1. function my_hash(txn, salt)
825 2. local str = ""
826 3. str = str .. salt
827 4. str = str .. tostring(txn.f:req_fhdr("host"))
828 5. str = str .. tostring(txn.f:path())
829 6. str = str .. tostring(txn.f:src())
830 7. local result = tostring(txn.c:sdbm(str, 1))
831 8. return result
832 9. end
833 10.
834 11. core.register_fetches("my-hash", my_hash)
835
836tostring
837~~~~~~~~
838
Godbach06c80992016-02-16 11:59:17 +0800839The function tostring ensures that its parameter is returned as a string. If the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100840parameter is a table or a thread or anything that will not have any sense as a
841string, a form like the typename followed by a pointer is returned. For example:
842
843 t = {}
844 print(tostring(t))
845
846returns:
847
848 table: 0x15facc0
849
850For objects, if the special function __tostring() is registered in the attached
851metatable, it will be called with the table itself as first argument. The
Godbach06c80992016-02-16 11:59:17 +0800852HAProxy object returns its own type.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100853
854About the converters entry point
855--------------------------------
856
857In HAProxy, a converter is a stateless function that takes a data as entry and
858returns a transformation of this data as output. In Lua it is exactly the same
859behaviour.
860
Godbach06c80992016-02-16 11:59:17 +0800861So, the registered Lua function doesn't have any special parameters, just a
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100862variable as input which contains the value to convert, and it must return data.
863
864The data required as input by the Lua converter is a string. So HAProxy will
865always provide a string as input. If the native sample fetch is not a string it
Godbach06c80992016-02-16 11:59:17 +0800866will be converted in best effort.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100867
868The returned value will have anything type, it will be converted as sample of
869the near HAProxy type. The conversion rules from Lua variables to HAProxy
870samples are:
871
872 Lua | HAProxy sample types
873 -----------+---------------------
874 "number" | "sint"
875 "boolean" | "bool"
876 "string" | "str"
877 "userdata" | "bool" (false)
878 "nil" | "bool" (false)
879 "table" | "bool" (false)
880 "function" | "bool" (false)
881 "thread" | "bool" (false)
882
883The function used for registering a converter is:
884
885 core.register_converters()
886
887The task entry point
888--------------------
889
890The function "core.register_task(fcn)" executes once the function "fcn" when the
891scheduler starts. This way is used for executing background task. For example,
Michael Prokop4438c602019-05-24 10:25:45 +0200892you can use this functionality for periodically checking the health of another
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100893service, and giving the result to each proxy needing it.
894
895The task is started once, if you want periodic actions, you can use the
896"core.sleep()" or "core.msleep()" for waiting the next runtime.
897
898Storing Lua variable between function in the same session
899---------------------------------------------------------
900
901All the functions registered as action or sample fetch can share an Lua context.
Godbach06c80992016-02-16 11:59:17 +0800902This context is a memory zone in the stack. sample fetch and action use the
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100903same stack, so both can access to the context.
904
905The context is accessible via the function get_priv and set_priv provided by an
906object of class TXN. The value given to set_priv replaces the current stored
907value. This value can be a table, it is useful if a lot of data can be shared.
908
909If the value stored is a table, you can add or remove entries from the table
910without storing again the new table. Maybe an example will be clearer:
911
912 local t = {}
913 txn:set_priv(t)
914
915 t["entry1"] = "foo"
916 t["entry2"] = "bar"
917
918 -- this will display "foo"
919 print(txn:get_priv()["entry1"])
920
921HTTP actions
922============
923
Joseph Herlant71b4b152018-11-13 16:55:16 -0800924 ... coming soon ...
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100925
926Lua is fast, but my service require more execution speed
927========================================================
928
Godbach06c80992016-02-16 11:59:17 +0800929We can write C modules for Lua. These modules must run with HAProxy while they
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100930are compliant with the HAProxy Lua version. A simple example is the "concat"
931module.
932
933It is very easy to write and compile a C Lua library, however, I don't see
934documentation about this process. So the current chapter is a quick howto.
935
936The entry point
937---------------
938
939The entry point is called "luaopen_<name>", where <name> is the name of the ".so"
940file. An hello world is like this:
941
942 #include <stdio.h>
943 #include <lua.h>
944 #include <lauxlib.h>
945
946 int luaopen_mymod(lua_State *L)
947 {
948 printf("Hello world\n");
949 return 0;
950 }
951
952The build
953---------
954
955The compilation of the source file requires the Lua "include" directory. The
Godbach06c80992016-02-16 11:59:17 +0800956compilation and the link of the object file requires the -fPIC option. That's
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100957all.
958
959 cc -I/opt/lua/include -fPIC -shared -o mymod.so mymod.c
960
961Usage
962-----
963
964You can load this module with the following Lua syntax:
965
966 require("mymod")
967
Godbach06c80992016-02-16 11:59:17 +0800968When you start HAProxy, this module just print "Hello world" when it is loaded.
Thierry FOURNIER79c10512015-11-08 22:02:21 +0100969Please, remember that HAProxy doesn't allow blocking method, so if you write a
970function doing filesystem access or synchronous network access, all the HAProxy
971process will fail.