haproxy/src/hlua.c
Thierry FOURNIER 53e08ecc41 BUG/MEDIUM: lua: the Lua process is not waked up after sending data on requests side
If we are writing in the request buffer, we are not waked up
when the data are forwarded because it is useles. The request
analyzers are waked up only when data is incoming. So, if the
request buffer is full, we set the WAKE_ON_WRITE flag.
2015-03-09 17:47:52 +01:00

3889 lines
109 KiB
C

#include <sys/socket.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
#error "Requires Lua 5.2 or later."
#endif
#include <ebpttree.h>
#include <common/cfgparse.h>
#include <types/connection.h>
#include <types/hlua.h>
#include <types/proto_tcp.h>
#include <types/proxy.h>
#include <proto/arg.h>
#include <proto/channel.h>
#include <proto/hdr_idx.h>
#include <proto/hlua.h>
#include <proto/obj_type.h>
#include <proto/pattern.h>
#include <proto/payload.h>
#include <proto/proto_http.h>
#include <proto/proto_tcp.h>
#include <proto/raw_sock.h>
#include <proto/sample.h>
#include <proto/server.h>
#include <proto/session.h>
#include <proto/ssl_sock.h>
#include <proto/stream_interface.h>
#include <proto/task.h>
/* Lua uses longjmp to perform yield or throwing errors. This
* macro is used only for identifying the function that can
* not return because a longjmp is executed.
* __LJMP marks a prototype of hlua file that can use longjmp.
* WILL_LJMP() marks an lua function that will use longjmp.
* MAY_LJMP() marks an lua function that may use longjmp.
*/
#define __LJMP
#define WILL_LJMP(func) func
#define MAY_LJMP(func) func
/* The main Lua execution context. */
struct hlua gL;
/* This is the memory pool containing all the signal structs. These
* struct are used to store each requiered signal between two tasks.
*/
struct pool_head *pool2_hlua_com;
struct pool_head *pool2_hlua_sleep;
/* Used for Socket connection. */
static struct proxy socket_proxy;
static struct server socket_tcp;
#ifdef USE_OPENSSL
static struct server socket_ssl;
#endif
/* List head of the function called at the initialisation time. */
struct list hlua_init_functions = LIST_HEAD_INIT(hlua_init_functions);
/* Store the fast lua context for coroutines. This tree uses the
* Lua stack pointer value as indexed entry, and store the associated
* hlua context.
*/
struct eb_root hlua_ctx = EB_ROOT_UNIQUE;
/* The following variables contains the reference of the different
* Lua classes. These references are useful for identify metadata
* associated with an object.
*/
static int class_core_ref;
static int class_txn_ref;
static int class_socket_ref;
static int class_channel_ref;
/* Global Lua execution timeout. By default Lua, execution linked
* with session (actions, sample-fetches and converters) have a
* short timeout. Lua linked with tasks doesn't have a timeout
* because a task may remain alive during all the haproxy execution.
*/
static unsigned int hlua_timeout_session = 4000; /* session timeout. */
static unsigned int hlua_timeout_task = TICK_ETERNITY; /* task timeout. */
/* Interrupts the Lua processing each "hlua_nb_instruction" instructions.
* it is used for preventing infinite loops.
*
* I test the scheer with an infinite loop containing one incrementation
* and one test. I run this loop between 10 seconds, I raise a ceil of
* 710M loops from one interrupt each 9000 instructions, so I fix the value
* to one interrupt each 10 000 instructions.
*
* configured | Number of
* instructions | loops executed
* between two | in milions
* forced yields |
* ---------------+---------------
* 10 | 160
* 500 | 670
* 1000 | 680
* 5000 | 700
* 7000 | 700
* 8000 | 700
* 9000 | 710 <- ceil
* 10000 | 710
* 100000 | 710
* 1000000 | 710
*
*/
static unsigned int hlua_nb_instruction = 10000;
/* These functions converts types between HAProxy internal args or
* sample and LUA types. Another function permits to check if the
* LUA stack contains arguments according with an required ARG_T
* format.
*/
static int hlua_arg2lua(lua_State *L, const struct arg *arg);
static int hlua_lua2arg(lua_State *L, int ud, struct arg *arg);
__LJMP static int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp, unsigned int mask);
static int hlua_smp2lua(lua_State *L, const struct sample *smp);
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp);
/* Used to check an Lua function type in the stack. It creates and
* returns a reference of the function. This function throws an
* error if the rgument is not a "function".
*/
__LJMP unsigned int hlua_checkfunction(lua_State *L, int argno)
{
if (!lua_isfunction(L, argno)) {
const char *msg = lua_pushfstring(L, "function expected, got %s", luaL_typename(L, -1));
WILL_LJMP(luaL_argerror(L, argno, msg));
}
lua_pushvalue(L, argno);
return luaL_ref(L, LUA_REGISTRYINDEX);
}
/* The three following functions are useful for adding entries
* in a table. These functions takes a string and respectively an
* integer, a string or a function and add it to the table in the
* top of the stack.
*
* These functions throws an error if no more stack size is
* available.
*/
__LJMP static inline void hlua_class_const_int(lua_State *L, const char *name,
unsigned int value)
{
if (!lua_checkstack(L, 2))
WILL_LJMP(luaL_error(L, "full stack"));
lua_pushstring(L, name);
lua_pushunsigned(L, value);
lua_settable(L, -3);
}
__LJMP static inline void hlua_class_const_str(lua_State *L, const char *name,
const char *value)
{
if (!lua_checkstack(L, 2))
WILL_LJMP(luaL_error(L, "full stack"));
lua_pushstring(L, name);
lua_pushstring(L, value);
lua_settable(L, -3);
}
__LJMP static inline void hlua_class_function(lua_State *L, const char *name,
int (*function)(lua_State *L))
{
if (!lua_checkstack(L, 2))
WILL_LJMP(luaL_error(L, "full stack"));
lua_pushstring(L, name);
lua_pushcclosure(L, function, 0);
lua_settable(L, -3);
}
/* This function check the number of arguments available in the
* stack. If the number of arguments available is not the same
* then <nb> an error is throwed.
*/
__LJMP static inline void check_args(lua_State *L, int nb, char *fcn)
{
if (lua_gettop(L) == nb)
return;
WILL_LJMP(luaL_error(L, "'%s' needs %d arguments", fcn, nb));
}
/* Return true if the data in stack[<ud>] is an object of
* type <class_ref>.
*/
static int hlua_udataistype(lua_State *L, int ud, int class_ref)
{
void *p = lua_touserdata(L, ud);
if (!p)
return 0;
if (!lua_getmetatable(L, ud))
return 0;
lua_rawgeti(L, LUA_REGISTRYINDEX, class_ref);
if (!lua_rawequal(L, -1, -2)) {
lua_pop(L, 2);
return 0;
}
lua_pop(L, 2);
return 1;
}
/* Return an object of the expected type, or throws an error. */
__LJMP static void *hlua_checkudata(lua_State *L, int ud, int class_ref)
{
if (!hlua_udataistype(L, ud, class_ref))
WILL_LJMP(luaL_argerror(L, 1, NULL));
return lua_touserdata(L, ud);
}
/* This fucntion push an error string prefixed by the file name
* and the line number where the error is encountered.
*/
static int hlua_pusherror(lua_State *L, const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
luaL_where(L, 1);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
return 1;
}
/* This function register a new signal. "lua" is the current lua
* execution context. It contains a pointer to the associated task.
* "link" is a list head attached to an other task that must be wake
* the lua task if an event occurs. This is useful with external
* events like TCP I/O or sleep functions. This funcion allocate
* memory for the signal.
*/
static int hlua_com_new(struct hlua *lua, struct list *link)
{
struct hlua_com *com = pool_alloc2(pool2_hlua_com);
if (!com)
return 0;
LIST_ADDQ(&lua->com, &com->purge_me);
LIST_ADDQ(link, &com->wake_me);
com->task = lua->task;
return 1;
}
/* This function purge all the pending signals when the LUA execution
* is finished. This prevent than a coprocess try to wake a deleted
* task. This function remove the memory associated to the signal.
*/
static void hlua_com_purge(struct hlua *lua)
{
struct hlua_com *com, *back;
/* Delete all pending communication signals. */
list_for_each_entry_safe(com, back, &lua->com, purge_me) {
LIST_DEL(&com->purge_me);
LIST_DEL(&com->wake_me);
pool_free2(pool2_hlua_com, com);
}
}
/* This function sends signals. It wakes all the tasks attached
* to a list head, and remove the signal, and free the used
* memory.
*/
static void hlua_com_wake(struct list *wake)
{
struct hlua_com *com, *back;
/* Wake task and delete all pending communication signals. */
list_for_each_entry_safe(com, back, wake, wake_me) {
LIST_DEL(&com->purge_me);
LIST_DEL(&com->wake_me);
task_wakeup(com->task, TASK_WOKEN_MSG);
pool_free2(pool2_hlua_com, com);
}
}
/* This functions is used with sample fetch and converters. It
* converts the HAProxy configuration argument in a lua stack
* values.
*
* It takes an array of "arg", and each entry of the array is
* converted and pushed in the LUA stack.
*/
static int hlua_arg2lua(lua_State *L, const struct arg *arg)
{
switch (arg->type) {
case ARGT_SINT:
lua_pushinteger(L, arg->data.sint);
break;
case ARGT_UINT:
case ARGT_TIME:
case ARGT_SIZE:
lua_pushunsigned(L, arg->data.sint);
break;
case ARGT_STR:
lua_pushlstring(L, arg->data.str.str, arg->data.str.len);
break;
case ARGT_IPV4:
case ARGT_IPV6:
case ARGT_MSK4:
case ARGT_MSK6:
case ARGT_FE:
case ARGT_BE:
case ARGT_TAB:
case ARGT_SRV:
case ARGT_USR:
case ARGT_MAP:
default:
lua_pushnil(L);
break;
}
return 1;
}
/* This function take one entrie in an LUA stack at the index "ud",
* and try to convert it in an HAProxy argument entry. This is useful
* with sample fetch wrappers. The input arguments are gived to the
* lua wrapper and converted as arg list by thi function.
*/
static int hlua_lua2arg(lua_State *L, int ud, struct arg *arg)
{
switch (lua_type(L, ud)) {
case LUA_TNUMBER:
case LUA_TBOOLEAN:
arg->type = ARGT_SINT;
arg->data.sint = lua_tointeger(L, ud);
break;
case LUA_TSTRING:
arg->type = ARGT_STR;
arg->data.str.str = (char *)lua_tolstring(L, ud, (size_t *)&arg->data.str.len);
break;
case LUA_TUSERDATA:
case LUA_TNIL:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
arg->type = ARGT_SINT;
arg->data.uint = 0;
break;
}
return 1;
}
/* the following functions are used to convert a struct sample
* in Lua type. This useful to convert the return of the
* fetchs or converters.
*/
static int hlua_smp2lua(lua_State *L, const struct sample *smp)
{
switch (smp->type) {
case SMP_T_SINT:
lua_pushinteger(L, smp->data.sint);
break;
case SMP_T_BOOL:
case SMP_T_UINT:
lua_pushunsigned(L, smp->data.uint);
break;
case SMP_T_BIN:
case SMP_T_STR:
lua_pushlstring(L, smp->data.str.str, smp->data.str.len);
break;
case SMP_T_METH:
switch (smp->data.meth.meth) {
case HTTP_METH_OPTIONS: lua_pushstring(L, "OPTIONS"); break;
case HTTP_METH_GET: lua_pushstring(L, "GET"); break;
case HTTP_METH_HEAD: lua_pushstring(L, "HEAD"); break;
case HTTP_METH_POST: lua_pushstring(L, "POST"); break;
case HTTP_METH_PUT: lua_pushstring(L, "PUT"); break;
case HTTP_METH_DELETE: lua_pushstring(L, "DELETE"); break;
case HTTP_METH_TRACE: lua_pushstring(L, "TRACE"); break;
case HTTP_METH_CONNECT: lua_pushstring(L, "CONNECT"); break;
case HTTP_METH_OTHER:
lua_pushlstring(L, smp->data.meth.str.str, smp->data.meth.str.len);
break;
default:
lua_pushnil(L);
break;
}
break;
case SMP_T_IPV4:
case SMP_T_IPV6:
case SMP_T_ADDR: /* This type is never used to qualify a sample. */
default:
lua_pushnil(L);
break;
}
return 1;
}
/* the following functions are used to convert an Lua type in a
* struct sample. This is useful to provide data from a converter
* to the LUA code.
*/
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp)
{
switch (lua_type(L, ud)) {
case LUA_TNUMBER:
smp->type = SMP_T_SINT;
smp->data.sint = lua_tointeger(L, ud);
break;
case LUA_TBOOLEAN:
smp->type = SMP_T_BOOL;
smp->data.uint = lua_toboolean(L, ud);
break;
case LUA_TSTRING:
smp->type = SMP_T_STR;
smp->flags |= SMP_F_CONST;
smp->data.str.str = (char *)lua_tolstring(L, ud, (size_t *)&smp->data.str.len);
break;
case LUA_TUSERDATA:
case LUA_TNIL:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
smp->type = SMP_T_BOOL;
smp->data.uint = 0;
break;
}
return 1;
}
/* This function check the "argp" builded by another conversion function
* is in accord with the expected argp defined by the "mask". The fucntion
* returns true or false. It can be adjust the types if there compatibles.
*/
__LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp, unsigned int mask)
{
int min_arg;
int idx;
idx = 0;
min_arg = ARGM(mask);
mask >>= ARGM_BITS;
while (1) {
/* Check oversize. */
if (idx >= ARGM_NBARGS && argp[idx].type != ARGT_STOP) {
WILL_LJMP(luaL_argerror(L, first + idx, "Malformed argument mask"));
}
/* Check for mandatory arguments. */
if (argp[idx].type == ARGT_STOP) {
if (idx + 1 < min_arg)
WILL_LJMP(luaL_argerror(L, first + idx, "Mandatory argument expected"));
return 0;
}
/* Check for exceed the number of requiered argument. */
if ((mask & ARGT_MASK) == ARGT_STOP &&
argp[idx].type != ARGT_STOP) {
WILL_LJMP(luaL_argerror(L, first + idx, "Last argument expected"));
}
if ((mask & ARGT_MASK) == ARGT_STOP &&
argp[idx].type == ARGT_STOP) {
return 0;
}
/* Compatibility mask. */
switch (argp[idx].type) {
case ARGT_SINT:
switch (mask & ARGT_MASK) {
case ARGT_UINT: argp[idx].type = mask & ARGT_MASK; break;
case ARGT_TIME: argp[idx].type = mask & ARGT_MASK; break;
case ARGT_SIZE: argp[idx].type = mask & ARGT_MASK; break;
}
break;
}
/* Check for type of argument. */
if ((mask & ARGT_MASK) != argp[idx].type) {
const char *msg = lua_pushfstring(L, "'%s' expected, got '%s'",
arg_type_names[(mask & ARGT_MASK)],
arg_type_names[argp[idx].type & ARGT_MASK]);
WILL_LJMP(luaL_argerror(L, first + idx, msg));
}
/* Next argument. */
mask >>= ARGT_BITS;
idx++;
}
}
/*
* The following functions are used to make correspondance between the the
* executed lua pointer and the "struct hlua *" that contain the context.
* They run with the tree head "hlua_ctx", they just perform lookup in the
* tree.
*
* - hlua_gethlua : return the hlua context associated with an lua_State.
* - hlua_delhlua : remove the association between hlua context and lua_state.
* - hlua_sethlua : create the association between hlua context and lua_state.
*/
static inline struct hlua *hlua_gethlua(lua_State *L)
{
struct ebpt_node *node;
node = ebpt_lookup(&hlua_ctx, L);
if (!node)
return NULL;
return ebpt_entry(node, struct hlua, node);
}
static inline void hlua_delhlua(struct hlua *hlua)
{
if (hlua->node.key)
ebpt_delete(&hlua->node);
}
static inline void hlua_sethlua(struct hlua *hlua)
{
hlua->node.key = hlua->T;
ebpt_insert(&hlua_ctx, &hlua->node);
}
/* This function just ensure that the yield will be always
* returned with a timeout and permit to set some flags
*/
__LJMP void hlua_yieldk(lua_State *L, int nresults, int ctx,
lua_CFunction k, int timeout, unsigned int flags)
{
struct hlua *hlua = hlua_gethlua(L);
/* Set the wake timeout. If timeout is required, we set
* the expiration time.
*/
hlua->wake_time = tick_first(timeout, hlua->expire);
hlua->flags |= flags;
/* Process the yield. */
WILL_LJMP(lua_yieldk(L, nresults, ctx, k));
}
/* This function initialises the Lua environment stored in the session.
* It must be called at the start of the session. This function creates
* an LUA coroutine. It can not be use to crete the main LUA context.
*/
int hlua_ctx_init(struct hlua *lua, struct task *task)
{
lua->Mref = LUA_REFNIL;
lua->flags = 0;
LIST_INIT(&lua->com);
lua->T = lua_newthread(gL.T);
if (!lua->T) {
lua->Tref = LUA_REFNIL;
return 0;
}
hlua_sethlua(lua);
lua->Tref = luaL_ref(gL.T, LUA_REGISTRYINDEX);
lua->task = task;
return 1;
}
/* Used to destroy the Lua coroutine when the attached session or task
* is destroyed. The destroy also the memory context. The struct "lua"
* is not freed.
*/
void hlua_ctx_destroy(struct hlua *lua)
{
if (!lua->T)
return;
/* Remove context. */
hlua_delhlua(lua);
/* Purge all the pending signals. */
hlua_com_purge(lua);
/* The thread is garbage collected by Lua. */
luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);
luaL_unref(gL.T, LUA_REGISTRYINDEX, lua->Tref);
}
/* This function is used to restore the Lua context when a coroutine
* fails. This function copy the common memory between old coroutine
* and the new coroutine. The old coroutine is destroyed, and its
* replaced by the new coroutine.
* If the flag "keep_msg" is set, the last entry of the old is assumed
* as string error message and it is copied in the new stack.
*/
static int hlua_ctx_renew(struct hlua *lua, int keep_msg)
{
lua_State *T;
int new_ref;
/* Renew the main LUA stack doesn't have sense. */
if (lua == &gL)
return 0;
/* Remove context. */
hlua_delhlua(lua);
/* New Lua coroutine. */
T = lua_newthread(gL.T);
if (!T)
return 0;
/* Copy last error message. */
if (keep_msg)
lua_xmove(lua->T, T, 1);
/* Copy data between the coroutines. */
lua_rawgeti(lua->T, LUA_REGISTRYINDEX, lua->Mref);
lua_xmove(lua->T, T, 1);
new_ref = luaL_ref(T, LUA_REGISTRYINDEX); /* Valur poped. */
/* Destroy old data. */
luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);
/* The thread is garbage collected by Lua. */
luaL_unref(gL.T, LUA_REGISTRYINDEX, lua->Tref);
/* Fill the struct with the new coroutine values. */
lua->Mref = new_ref;
lua->T = T;
lua->Tref = luaL_ref(gL.T, LUA_REGISTRYINDEX);
/* Set context. */
hlua_sethlua(lua);
return 1;
}
void hlua_hook(lua_State *L, lua_Debug *ar)
{
hlua_yieldk(L, 0, 0, NULL, TICK_ETERNITY, HLUA_CTRLYIELD);
}
/* This function start or resumes the Lua stack execution. If the flag
* "yield_allowed" if no set and the LUA stack execution returns a yield
* The function return an error.
*
* The function can returns 4 values:
* - HLUA_E_OK : The execution is terminated without any errors.
* - HLUA_E_AGAIN : The execution must continue at the next associated
* task wakeup.
* - HLUA_E_ERRMSG : An error has occured, an error message is set in
* the top of the stack.
* - HLUA_E_ERR : An error has occured without error message.
*
* If an error occured, the stack is renewed and it is ready to run new
* LUA code.
*/
static enum hlua_exec hlua_ctx_resume(struct hlua *lua, int yield_allowed)
{
int ret;
const char *msg;
HLUA_SET_RUN(lua);
/* If we want to resume the task, then check first the execution timeout.
* if it is reached, we can interrupt the Lua processing.
*/
if (tick_is_expired(lua->expire, now_ms))
goto timeout_reached;
resume_execution:
/* This hook interrupts the Lua processing each 'hlua_nb_instruction'
* instructions. it is used for preventing infinite loops.
*/
lua_sethook(lua->T, hlua_hook, LUA_MASKCOUNT, hlua_nb_instruction);
/* Remove all flags except the running flags. */
lua->flags = HLUA_RUN;
/* Call the function. */
ret = lua_resume(lua->T, gL.T, lua->nargs);
switch (ret) {
case LUA_OK:
ret = HLUA_E_OK;
break;
case LUA_YIELD:
/* Check if the execution timeout is expired. It it is the case, we
* break the Lua execution.
*/
if (tick_is_expired(lua->expire, now_ms)) {
timeout_reached:
lua_settop(lua->T, 0); /* Empty the stack. */
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
lua_pushfstring(lua->T, "execution timeout");
ret = HLUA_E_ERRMSG;
break;
}
/* Process the forced yield. if the general yield is not allowed or
* if no task were associated this the current Lua execution
* coroutine, we resume the execution. Else we want to return in the
* scheduler and we want to be waked up again, to continue the
* current Lua execution. So we schedule our own task.
*/
if (HLUA_IS_CTRLYIELDING(lua)) {
if (!yield_allowed || !lua->task)
goto resume_execution;
task_wakeup(lua->task, TASK_WOKEN_MSG);
}
if (!yield_allowed) {
lua_settop(lua->T, 0); /* Empty the stack. */
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
lua_pushfstring(lua->T, "yield not allowed");
ret = HLUA_E_ERRMSG;
break;
}
ret = HLUA_E_AGAIN;
break;
case LUA_ERRRUN:
lua->wake_time = TICK_ETERNITY;
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
msg = lua_tostring(lua->T, -1);
lua_settop(lua->T, 0); /* Empty the stack. */
lua_pop(lua->T, 1);
if (msg)
lua_pushfstring(lua->T, "runtime error: %s", msg);
else
lua_pushfstring(lua->T, "unknown runtime error");
ret = HLUA_E_ERRMSG;
break;
case LUA_ERRMEM:
lua->wake_time = TICK_ETERNITY;
lua_settop(lua->T, 0); /* Empty the stack. */
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
lua_pushfstring(lua->T, "out of memory error");
ret = HLUA_E_ERRMSG;
break;
case LUA_ERRERR:
lua->wake_time = TICK_ETERNITY;
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
msg = lua_tostring(lua->T, -1);
lua_settop(lua->T, 0); /* Empty the stack. */
lua_pop(lua->T, 1);
if (msg)
lua_pushfstring(lua->T, "message handler error: %s", msg);
else
lua_pushfstring(lua->T, "message handler error");
ret = HLUA_E_ERRMSG;
break;
default:
lua->wake_time = TICK_ETERNITY;
lua_settop(lua->T, 0); /* Empty the stack. */
if (!lua_checkstack(lua->T, 1)) {
ret = HLUA_E_ERR;
break;
}
lua_pushfstring(lua->T, "unknonwn error");
ret = HLUA_E_ERRMSG;
break;
}
switch (ret) {
case HLUA_E_AGAIN:
break;
case HLUA_E_ERRMSG:
hlua_com_purge(lua);
hlua_ctx_renew(lua, 1);
HLUA_CLR_RUN(lua);
break;
case HLUA_E_ERR:
HLUA_CLR_RUN(lua);
hlua_com_purge(lua);
hlua_ctx_renew(lua, 0);
break;
case HLUA_E_OK:
HLUA_CLR_RUN(lua);
hlua_com_purge(lua);
break;
}
return ret;
}
/* This function is an LUA binding. It provides a function
* for deleting ACL from a referenced ACL file.
*/
__LJMP static int hlua_del_acl(lua_State *L)
{
const char *name;
const char *key;
struct pat_ref *ref;
MAY_LJMP(check_args(L, 2, "del_acl"));
name = MAY_LJMP(luaL_checkstring(L, 1));
key = MAY_LJMP(luaL_checkstring(L, 2));
ref = pat_ref_lookup(name);
if (!ref)
WILL_LJMP(luaL_error(L, "'del_acl': unkown acl file '%s'", name));
pat_ref_delete(ref, key);
return 0;
}
/* This function is an LUA binding. It provides a function
* for deleting map entry from a referenced map file.
*/
static int hlua_del_map(lua_State *L)
{
const char *name;
const char *key;
struct pat_ref *ref;
MAY_LJMP(check_args(L, 2, "del_map"));
name = MAY_LJMP(luaL_checkstring(L, 1));
key = MAY_LJMP(luaL_checkstring(L, 2));
ref = pat_ref_lookup(name);
if (!ref)
WILL_LJMP(luaL_error(L, "'del_map': unkown acl file '%s'", name));
pat_ref_delete(ref, key);
return 0;
}
/* This function is an LUA binding. It provides a function
* for adding ACL pattern from a referenced ACL file.
*/
static int hlua_add_acl(lua_State *L)
{
const char *name;
const char *key;
struct pat_ref *ref;
MAY_LJMP(check_args(L, 2, "add_acl"));
name = MAY_LJMP(luaL_checkstring(L, 1));
key = MAY_LJMP(luaL_checkstring(L, 2));
ref = pat_ref_lookup(name);
if (!ref)
WILL_LJMP(luaL_error(L, "'add_acl': unkown acl file '%s'", name));
if (pat_ref_find_elt(ref, key) == NULL)
pat_ref_add(ref, key, NULL, NULL);
return 0;
}
/* This function is an LUA binding. It provides a function
* for setting map pattern and sample from a referenced map
* file.
*/
static int hlua_set_map(lua_State *L)
{
const char *name;
const char *key;
const char *value;
struct pat_ref *ref;
MAY_LJMP(check_args(L, 3, "set_map"));
name = MAY_LJMP(luaL_checkstring(L, 1));
key = MAY_LJMP(luaL_checkstring(L, 2));
value = MAY_LJMP(luaL_checkstring(L, 3));
ref = pat_ref_lookup(name);
if (!ref)
WILL_LJMP(luaL_error(L, "'set_map': unkown map file '%s'", name));
if (pat_ref_find_elt(ref, key) != NULL)
pat_ref_set(ref, key, value, NULL);
else
pat_ref_add(ref, key, value, NULL);
return 0;
}
/* A class is a lot of memory that contain data. This data can be a table,
* an integer or user data. This data is associated with a metatable. This
* metatable have an original version registred in the global context with
* the name of the object (_G[<name>] = <metable> ).
*
* A metable is a table that modify the standard behavior of a standard
* access to the associated data. The entries of this new metatable are
* defined as is:
*
* http://lua-users.org/wiki/MetatableEvents
*
* __index
*
* we access an absent field in a table, the result is nil. This is
* true, but it is not the whole truth. Actually, such access triggers
* the interpreter to look for an __index metamethod: If there is no
* such method, as usually happens, then the access results in nil;
* otherwise, the metamethod will provide the result.
*
* Control 'prototype' inheritance. When accessing "myTable[key]" and
* the key does not appear in the table, but the metatable has an __index
* property:
*
* - if the value is a function, the function is called, passing in the
* table and the key; the return value of that function is returned as
* the result.
*
* - if the value is another table, the value of the key in that table is
* asked for and returned (and if it doesn't exist in that table, but that
* table's metatable has an __index property, then it continues on up)
*
* - Use "rawget(myTable,key)" to skip this metamethod.
*
* http://www.lua.org/pil/13.4.1.html
*
* __newindex
*
* Like __index, but control property assignment.
*
* __mode - Control weak references. A string value with one or both
* of the characters 'k' and 'v' which specifies that the the
* keys and/or values in the table are weak references.
*
* __call - Treat a table like a function. When a table is followed by
* parenthesis such as "myTable( 'foo' )" and the metatable has
* a __call key pointing to a function, that function is invoked
* (passing any specified arguments) and the return value is
* returned.
*
* __metatable - Hide the metatable. When "getmetatable( myTable )" is
* called, if the metatable for myTable has a __metatable
* key, the value of that key is returned instead of the
* actual metatable.
*
* __tostring - Control string representation. When the builtin
* "tostring( myTable )" function is called, if the metatable
* for myTable has a __tostring property set to a function,
* that function is invoked (passing myTable to it) and the
* return value is used as the string representation.
*
* __len - Control table length. When the table length is requested using
* the length operator ( '#' ), if the metatable for myTable has
* a __len key pointing to a function, that function is invoked
* (passing myTable to it) and the return value used as the value
* of "#myTable".
*
* __gc - Userdata finalizer code. When userdata is set to be garbage
* collected, if the metatable has a __gc field pointing to a
* function, that function is first invoked, passing the userdata
* to it. The __gc metamethod is not called for tables.
* (See http://lua-users.org/lists/lua-l/2006-11/msg00508.html)
*
* Special metamethods for redefining standard operators:
* http://www.lua.org/pil/13.1.html
*
* __add "+"
* __sub "-"
* __mul "*"
* __div "/"
* __unm "!"
* __pow "^"
* __concat ".."
*
* Special methods for redfining standar relations
* http://www.lua.org/pil/13.2.html
*
* __eq "=="
* __lt "<"
* __le "<="
*/
/*
*
*
* Class Socket
*
*
*/
__LJMP static struct hlua_socket *hlua_checksocket(lua_State *L, int ud)
{
return (struct hlua_socket *)MAY_LJMP(hlua_checkudata(L, ud, class_socket_ref));
}
/* This function is the handler called for each I/O on the established
* connection. It is used for notify space avalaible to send or data
* received.
*/
static void hlua_socket_handler(struct stream_interface *si)
{
struct appctx *appctx = objt_appctx(si->end);
struct connection *c = objt_conn(si->ib->cons->end);
/* Wakeup the main session if the client connection is closed. */
if (!c || channel_output_closed(si->ib) || channel_input_closed(si->ob)) {
if (appctx->ctx.hlua.socket) {
appctx->ctx.hlua.socket->s = NULL;
appctx->ctx.hlua.socket = NULL;
}
si_shutw(si);
si_shutr(si);
si->ib->flags |= CF_READ_NULL;
hlua_com_wake(&appctx->ctx.hlua.wake_on_read);
hlua_com_wake(&appctx->ctx.hlua.wake_on_write);
return;
}
if (!(c->flags & CO_FL_CONNECTED))
return;
/* This function is called after the connect. */
appctx->ctx.hlua.connected = 1;
/* Wake the tasks which wants to write if the buffer have avalaible space. */
if (channel_may_recv(si->ob))
hlua_com_wake(&appctx->ctx.hlua.wake_on_write);
/* Wake the tasks which wants to read if the buffer contains data. */
if (channel_is_empty(si->ib))
hlua_com_wake(&appctx->ctx.hlua.wake_on_read);
}
/* This function is called when the "struct session" is destroyed.
* Remove the link from the object to this session.
* Wake all the pending signals.
*/
static void hlua_socket_release(struct stream_interface *si)
{
struct appctx *appctx = objt_appctx(si->end);
/* Remove my link in the original object. */
if (appctx->ctx.hlua.socket)
appctx->ctx.hlua.socket->s = NULL;
/* Wake all the task waiting for me. */
hlua_com_wake(&appctx->ctx.hlua.wake_on_read);
hlua_com_wake(&appctx->ctx.hlua.wake_on_write);
}
/* If the garbage collectio of the object is launch, nobody
* uses this object. If the session does not exists, just quit.
* Send the shutdown signal to the session. In some cases,
* pending signal can rest in the read and write lists. destroy
* it.
*/
__LJMP static int hlua_socket_gc(lua_State *L)
{
struct hlua_socket *socket;
struct appctx *appctx;
MAY_LJMP(check_args(L, 1, "__gc"));
socket = MAY_LJMP(hlua_checksocket(L, 1));
if (!socket->s)
return 0;
/* Remove all reference between the Lua stack and the coroutine session. */
appctx = objt_appctx(socket->s->si[0].end);
session_shutdown(socket->s, SN_ERR_KILLED);
socket->s = NULL;
appctx->ctx.hlua.socket = NULL;
return 0;
}
/* The close function send shutdown signal and break the
* links between the session and the object.
*/
__LJMP static int hlua_socket_close(lua_State *L)
{
struct hlua_socket *socket;
struct appctx *appctx;
MAY_LJMP(check_args(L, 1, "close"));
socket = MAY_LJMP(hlua_checksocket(L, 1));
if (!socket->s)
return 0;
/* Close the session and remove the associated stop task. */
session_shutdown(socket->s, SN_ERR_KILLED);
appctx = objt_appctx(socket->s->si[0].end);
appctx->ctx.hlua.socket = NULL;
socket->s = NULL;
return 0;
}
/* This Lua function assumes that the stack contain three parameters.
* 1 - USERDATA containing a struct socket
* 2 - INTEGER with values of the macro defined below
* If the integer is -1, we must read at most one line.
* If the integer is -2, we ust read all the data until the
* end of the stream.
* If the integer is positive value, we must read a number of
* bytes corresponding to this value.
*/
#define HLSR_READ_LINE (-1)
#define HLSR_READ_ALL (-2)
__LJMP static int hlua_socket_receive_yield(struct lua_State *L)
{
struct hlua_socket *socket = MAY_LJMP(hlua_checksocket(L, 1));
int wanted = lua_tointeger(L, 2);
struct hlua *hlua = hlua_gethlua(L);
struct appctx *appctx;
int len;
int nblk;
char *blk1;
int len1;
char *blk2;
int len2;
/* Check if this lua stack is schedulable. */
if (!hlua || !hlua->task)
WILL_LJMP(luaL_error(L, "The 'receive' function is only allowed in "
"'frontend', 'backend' or 'task'"));
/* check for connection closed. If some data where read, return it. */
if (!socket->s)
goto connection_closed;
if (wanted == HLSR_READ_LINE) {
/* Read line. */
nblk = bo_getline_nc(socket->s->si[0].ob, &blk1, &len1, &blk2, &len2);
if (nblk < 0) /* Connection close. */
goto connection_closed;
if (nblk == 0) /* No data avalaible. */
goto connection_empty;
}
else if (wanted == HLSR_READ_ALL) {
/* Read all the available data. */
nblk = bo_getblk_nc(socket->s->si[0].ob, &blk1, &len1, &blk2, &len2);
if (nblk < 0) /* Connection close. */
goto connection_closed;
if (nblk == 0) /* No data avalaible. */
goto connection_empty;
}
else {
/* Read a block of data. */
nblk = bo_getblk_nc(socket->s->si[0].ob, &blk1, &len1, &blk2, &len2);
if (nblk < 0) /* Connection close. */
goto connection_closed;
if (nblk == 0) /* No data avalaible. */
goto connection_empty;
if (len1 > wanted) {
nblk = 1;
len1 = wanted;
} if (nblk == 2 && len1 + len2 > wanted)
len2 = wanted - len1;
}
len = len1;
luaL_addlstring(&socket->b, blk1, len1);
if (nblk == 2) {
len += len2;
luaL_addlstring(&socket->b, blk2, len2);
}
/* Consume data. */
bo_skip(socket->s->si[0].ob, len);
/* Don't wait anything. */
si_update(&socket->s->si[0]);
/* If the pattern reclaim to read all the data
* in the connection, got out.
*/
if (wanted == HLSR_READ_ALL)
goto connection_empty;
else if (wanted >= 0 && len < wanted)
goto connection_empty;
/* Return result. */
luaL_pushresult(&socket->b);
return 1;
connection_closed:
/* If the buffer containds data. */
if (socket->b.n > 0) {
luaL_pushresult(&socket->b);
return 1;
}
lua_pushnil(L);
lua_pushstring(L, "connection closed.");
return 2;
connection_empty:
appctx = objt_appctx(socket->s->si[0].end);
if (!hlua_com_new(hlua, &appctx->ctx.hlua.wake_on_read))
WILL_LJMP(luaL_error(L, "out of memory"));
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_socket_receive_yield, TICK_ETERNITY, 0));
return 0;
}
/* This Lus function gets two parameters. The first one can be string
* or a number. If the string is "*l", the user require one line. If
* the string is "*a", the user require all the content of the stream.
* If the value is a number, the user require a number of bytes equal
* to the value. The default value is "*l" (a line).
*
* This paraeter with a variable type is converted in integer. This
* integer takes this values:
* -1 : read a line
* -2 : read all the stream
* >0 : amount if bytes.
*
* The second parameter is optinal. It contains a string that must be
* concatenated with the read data.
*/
__LJMP static int hlua_socket_receive(struct lua_State *L)
{
int wanted = HLSR_READ_LINE;
const char *pattern;
int type;
char *error;
size_t len;
struct hlua_socket *socket;
if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
WILL_LJMP(luaL_error(L, "The 'receive' function requires between 1 and 3 arguments."));
socket = MAY_LJMP(hlua_checksocket(L, 1));
/* check for pattern. */
if (lua_gettop(L) >= 2) {
type = lua_type(L, 2);
if (type == LUA_TSTRING) {
pattern = lua_tostring(L, 2);
if (strcmp(pattern, "*a") == 0)
wanted = HLSR_READ_ALL;
else if (strcmp(pattern, "*l") == 0)
wanted = HLSR_READ_LINE;
else {
wanted = strtoll(pattern, &error, 10);
if (*error != '\0')
WILL_LJMP(luaL_error(L, "Unsupported pattern."));
}
}
else if (type == LUA_TNUMBER) {
wanted = lua_tointeger(L, 2);
if (wanted < 0)
WILL_LJMP(luaL_error(L, "Unsupported size."));
}
}
/* Set pattern. */
lua_pushinteger(L, wanted);
lua_replace(L, 2);
/* init bufffer, and fiil it wih prefix. */
luaL_buffinit(L, &socket->b);
/* Check prefix. */
if (lua_gettop(L) >= 3) {
if (lua_type(L, 3) != LUA_TSTRING)
WILL_LJMP(luaL_error(L, "Expect a 'string' for the prefix"));
pattern = lua_tolstring(L, 3, &len);
luaL_addlstring(&socket->b, pattern, len);
}
return __LJMP(hlua_socket_receive_yield(L));
}
/* Write the Lua input string in the output buffer.
* This fucntion returns a yield if no space are available.
*/
static int hlua_socket_write_yield(struct lua_State *L)
{
struct hlua_socket *socket;
struct hlua *hlua = hlua_gethlua(L);
struct appctx *appctx;
size_t buf_len;
const char *buf;
int len;
int send_len;
int sent;
/* Check if this lua stack is schedulable. */
if (!hlua || !hlua->task)
WILL_LJMP(luaL_error(L, "The 'write' function is only allowed in "
"'frontend', 'backend' or 'task'"));
/* Get object */
socket = MAY_LJMP(hlua_checksocket(L, 1));
buf = MAY_LJMP(luaL_checklstring(L, 2, &buf_len));
sent = MAY_LJMP(luaL_checkunsigned(L, 3));
/* Check for connection close. */
if (!socket->s || channel_output_closed(socket->s->req)) {
lua_pushinteger(L, -1);
return 1;
}
/* Update the input buffer data. */
buf += sent;
send_len = buf_len - sent;
/* All the data are sent. */
if (sent >= buf_len)
return 1; /* Implicitly return the length sent. */
/* Check for avalaible space. */
len = buffer_total_space(socket->s->si[0].ib->buf);
if (len <= 0)
goto hlua_socket_write_yield_return;
/* send data */
if (len < send_len)
send_len = len;
len = bi_putblk(socket->s->si[0].ib, buf+sent, send_len);
/* "Not enough space" (-1), "Buffer too little to contain
* the data" (-2) are not expected because the available length
* is tested.
* Other unknown error are also not expected.
*/
if (len <= 0) {
MAY_LJMP(hlua_socket_close(L));
lua_pop(L, 1);
lua_pushunsigned(L, -1);
return 1;
}
/* update buffers. */
si_update(&socket->s->si[0]);
socket->s->si[0].ib->rex = TICK_ETERNITY;
socket->s->si[0].ob->wex = TICK_ETERNITY;
/* Update length sent. */
lua_pop(L, 1);
lua_pushunsigned(L, sent + len);
/* All the data buffer is sent ? */
if (sent + len >= buf_len)
return 1;
hlua_socket_write_yield_return:
appctx = objt_appctx(socket->s->si[0].end);
if (!hlua_com_new(hlua, &appctx->ctx.hlua.wake_on_write))
WILL_LJMP(luaL_error(L, "out of memory"));
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_socket_write_yield, TICK_ETERNITY, 0));
return 0;
}
/* This function initiate the send of data. It just check the input
* parameters and push an integer in the Lua stack that contain the
* amount of data writed in the buffer. This is used by the function
* "hlua_socket_write_yield" that can yield.
*
* The Lua function gets between 3 and 4 parameters. The first one is
* the associated object. The second is a string buffer. The third is
* a facultative integer that represents where is the buffer position
* of the start of the data that can send. The first byte is the
* position "1". The default value is "1". The fourth argument is a
* facultative integer that represents where is the buffer position
* of the end of the data that can send. The default is the last byte.
*/
static int hlua_socket_send(struct lua_State *L)
{
int i;
int j;
const char *buf;
size_t buf_len;
/* Check number of arguments. */
if (lua_gettop(L) < 2 || lua_gettop(L) > 4)
WILL_LJMP(luaL_error(L, "'send' needs between 2 and 4 arguments"));
/* Get the string. */
buf = MAY_LJMP(luaL_checklstring(L, 2, &buf_len));
/* Get and check j. */
if (lua_gettop(L) == 4) {
j = MAY_LJMP(luaL_checkinteger(L, 4));
if (j < 0)
j = buf_len + j + 1;
if (j > buf_len)
j = buf_len + 1;
lua_pop(L, 1);
}
else
j = buf_len;
/* Get and check i. */
if (lua_gettop(L) == 3) {
i = MAY_LJMP(luaL_checkinteger(L, 3));
if (i < 0)
i = buf_len + i + 1;
if (i > buf_len)
i = buf_len + 1;
lua_pop(L, 1);
} else
i = 1;
/* Check bth i and j. */
if (i > j) {
lua_pushunsigned(L, 0);
return 1;
}
if (i == 0 && j == 0) {
lua_pushunsigned(L, 0);
return 1;
}
if (i == 0)
i = 1;
if (j == 0)
j = 1;
/* Pop the string. */
lua_pop(L, 1);
/* Update the buffer length. */
buf += i - 1;
buf_len = j - i + 1;
lua_pushlstring(L, buf, buf_len);
/* This unsigned is used to remember the amount of sent data. */
lua_pushunsigned(L, 0);
return MAY_LJMP(hlua_socket_write_yield(L));
}
#define SOCKET_INFO_EXPANDED_FORM "[0000:0000:0000:0000:0000:0000:0000:0000]:12345"
static char _socket_info_expanded_form[] = SOCKET_INFO_EXPANDED_FORM;
#define SOCKET_INFO_MAX_LEN (sizeof(_socket_info_expanded_form))
__LJMP static inline int hlua_socket_info(struct lua_State *L, struct sockaddr_storage *addr)
{
static char buffer[SOCKET_INFO_MAX_LEN];
int ret;
int len;
char *p;
ret = addr_to_str(addr, buffer+1, SOCKET_INFO_MAX_LEN-1);
if (ret <= 0) {
lua_pushnil(L);
return 1;
}
if (ret == AF_UNIX) {
lua_pushstring(L, buffer+1);
return 1;
}
else if (ret == AF_INET6) {
buffer[0] = '[';
len = strlen(buffer);
buffer[len] = ']';
len++;
buffer[len] = ':';
len++;
p = buffer;
}
else if (ret == AF_INET) {
p = buffer + 1;
len = strlen(p);
p[len] = ':';
len++;
}
else {
lua_pushnil(L);
return 1;
}
if (port_to_str(addr, p + len, SOCKET_INFO_MAX_LEN-1 - len) <= 0) {
lua_pushnil(L);
return 1;
}
lua_pushstring(L, p);
return 1;
}
/* Returns information about the peer of the connection. */
__LJMP static int hlua_socket_getpeername(struct lua_State *L)
{
struct hlua_socket *socket;
struct connection *conn;
MAY_LJMP(check_args(L, 1, "getpeername"));
socket = MAY_LJMP(hlua_checksocket(L, 1));
/* Check if the tcp object is avalaible. */
if (!socket->s) {
lua_pushnil(L);
return 1;
}
conn = objt_conn(socket->s->si[1].end);
if (!conn) {
lua_pushnil(L);
return 1;
}
if (!(conn->flags & CO_FL_ADDR_TO_SET)) {
unsigned int salen = sizeof(conn->addr.to);
if (getpeername(conn->t.sock.fd, (struct sockaddr *)&conn->addr.to, &salen) == -1) {
lua_pushnil(L);
return 1;
}
conn->flags |= CO_FL_ADDR_TO_SET;
}
return MAY_LJMP(hlua_socket_info(L, &conn->addr.to));
}
/* Returns information about my connection side. */
static int hlua_socket_getsockname(struct lua_State *L)
{
struct hlua_socket *socket;
struct connection *conn;
MAY_LJMP(check_args(L, 1, "getsockname"));
socket = MAY_LJMP(hlua_checksocket(L, 1));
/* Check if the tcp object is avalaible. */
if (!socket->s) {
lua_pushnil(L);
return 1;
}
conn = objt_conn(socket->s->si[1].end);
if (!conn) {
lua_pushnil(L);
return 1;
}
if (!(conn->flags & CO_FL_ADDR_FROM_SET)) {
unsigned int salen = sizeof(conn->addr.from);
if (getsockname(conn->t.sock.fd, (struct sockaddr *)&conn->addr.from, &salen) == -1) {
lua_pushnil(L);
return 1;
}
conn->flags |= CO_FL_ADDR_FROM_SET;
}
return hlua_socket_info(L, &conn->addr.from);
}
/* This struct define the applet. */
static struct si_applet update_applet = {
.obj_type = OBJ_TYPE_APPLET,
.name = "<LUA_TCP>",
.fct = hlua_socket_handler,
.release = hlua_socket_release,
};
__LJMP static int hlua_socket_connect_yield(struct lua_State *L)
{
struct hlua_socket *socket = MAY_LJMP(hlua_checksocket(L, 1));
struct hlua *hlua = hlua_gethlua(L);
struct appctx *appctx;
/* Check for connection close. */
if (!hlua || !socket->s || channel_output_closed(socket->s->req)) {
lua_pushnil(L);
lua_pushstring(L, "Can't connect");
return 2;
}
appctx = objt_appctx(socket->s->si[0].end);
/* Check for connection established. */
if (appctx->ctx.hlua.connected) {
lua_pushinteger(L, 1);
return 1;
}
if (!hlua_com_new(hlua, &appctx->ctx.hlua.wake_on_write))
WILL_LJMP(luaL_error(L, "out of memory error"));
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_socket_connect_yield, TICK_ETERNITY, 0));
return 0;
}
/* This function fail or initite the connection. */
__LJMP static int hlua_socket_connect(struct lua_State *L)
{
struct hlua_socket *socket;
unsigned int port;
const char *ip;
struct connection *conn;
MAY_LJMP(check_args(L, 3, "connect"));
/* Get args. */
socket = MAY_LJMP(hlua_checksocket(L, 1));
ip = MAY_LJMP(luaL_checkstring(L, 2));
port = MAY_LJMP(luaL_checkunsigned(L, 3));
conn = si_alloc_conn(socket->s->req->cons, 0);
if (!conn)
WILL_LJMP(luaL_error(L, "connect: internal error"));
/* Parse ip address. */
conn->addr.to.ss_family = AF_UNSPEC;
if (!str2ip2(ip, &conn->addr.to, 0))
WILL_LJMP(luaL_error(L, "connect: cannot parse ip address '%s'", ip));
/* Set port. */
if (conn->addr.to.ss_family == AF_INET)
((struct sockaddr_in *)&conn->addr.to)->sin_port = htons(port);
else if (conn->addr.to.ss_family == AF_INET6)
((struct sockaddr_in6 *)&conn->addr.to)->sin6_port = htons(port);
/* it is important not to call the wakeup function directly but to
* pass through task_wakeup(), because this one knows how to apply
* priorities to tasks.
*/
task_wakeup(socket->s->task, TASK_WOKEN_INIT);
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_socket_connect_yield, TICK_ETERNITY, 0));
return 0;
}
#ifdef USE_OPENSSL
__LJMP static int hlua_socket_connect_ssl(struct lua_State *L)
{
struct hlua_socket *socket;
MAY_LJMP(check_args(L, 3, "connect_ssl"));
socket = MAY_LJMP(hlua_checksocket(L, 1));
socket->s->target = &socket_ssl.obj_type;
return MAY_LJMP(hlua_socket_connect(L));
}
#endif
__LJMP static int hlua_socket_setoption(struct lua_State *L)
{
return 0;
}
__LJMP static int hlua_socket_settimeout(struct lua_State *L)
{
struct hlua_socket *socket;
unsigned int tmout;
MAY_LJMP(check_args(L, 2, "settimeout"));
socket = MAY_LJMP(hlua_checksocket(L, 1));
tmout = MAY_LJMP(luaL_checkunsigned(L, 2)) * 1000;
socket->s->req->rto = tmout;
socket->s->req->wto = tmout;
socket->s->rep->rto = tmout;
socket->s->rep->wto = tmout;
return 0;
}
__LJMP static int hlua_socket_new(lua_State *L)
{
struct hlua_socket *socket;
struct appctx *appctx;
/* Check stack size. */
if (!lua_checkstack(L, 2)) {
hlua_pusherror(L, "socket: full stack");
goto out_fail_conf;
}
socket = MAY_LJMP(lua_newuserdata(L, sizeof(*socket)));
memset(socket, 0, sizeof(*socket));
/* Pop a class session metatable and affect it to the userdata. */
lua_rawgeti(L, LUA_REGISTRYINDEX, class_socket_ref);
lua_setmetatable(L, -2);
/*
*
* Get memory for the request.
*
*/
socket->s = pool_alloc2(pool2_session);
if (!socket->s) {
hlua_pusherror(L, "socket: out of memory");
goto out_fail_conf;
}
socket->s->task = task_new();
if (!socket->s->task) {
hlua_pusherror(L, "socket: out of memory");
goto out_free_session;
}
socket->s->req = pool_alloc2(pool2_channel);
if (!socket->s->req) {
hlua_pusherror(L, "socket: out of memory");
goto out_fail_req;
}
socket->s->req->buf = pool_alloc2(pool2_buffer);
if (!socket->s->req->buf) {
hlua_pusherror(L, "socket: out of memory");
goto out_fail_req_buf;
}
socket->s->rep = pool_alloc2(pool2_channel);
if (!socket->s->rep) {
hlua_pusherror(L, "socket: out of memory");
goto out_fail_rep;
}
socket->s->rep->buf = pool_alloc2(pool2_buffer);
if (!socket->s->rep->buf) {
hlua_pusherror(L, "socket: out of memory");
goto out_fail_rep_buf;
}
/* Configura empty Lua for the session. */
socket->s->hlua.T = NULL;
socket->s->hlua.Tref = LUA_REFNIL;
socket->s->hlua.Mref = LUA_REFNIL;
socket->s->hlua.nargs = 0;
socket->s->hlua.flags = 0;
LIST_INIT(&socket->s->hlua.com);
/* session initialisation. */
session_init_srv_conn(socket->s);
/*
*
* Configure the associated task.
*
*/
/* This is the dedicated function to process the session. This function
* is able to establish the conection, process the timeouts, etc ...
*/
socket->s->task->process = process_session;
/* Back reference to session. This is used by process_session(). */
socket->s->task->context = socket->s;
/* The priority of the task is normal. */
socket->s->task->nice = 0;
/* Init the next run to eternity. Later in this function, this task is
* waked.
*/
socket->s->task->expire = TICK_ETERNITY;
/*
*
* Initialize the attached buffers
*
*/
socket->s->req->buf->size = global.tune.bufsize;
socket->s->rep->buf->size = global.tune.bufsize;
/*
*
* Initialize channels.
*
*/
/* This function reset the struct. It must be called
* before the configuration.
*/
channel_init(socket->s->req);
channel_init(socket->s->rep);
socket->s->req->prod = &socket->s->si[0];
socket->s->req->cons = &socket->s->si[1];
socket->s->rep->prod = &socket->s->si[1];
socket->s->rep->cons = &socket->s->si[0];
socket->s->si[0].ib = socket->s->req;
socket->s->si[0].ob = socket->s->rep;
socket->s->si[1].ib = socket->s->rep;
socket->s->si[1].ob = socket->s->req;
socket->s->req->analysers = 0;
socket->s->req->rto = socket_proxy.timeout.client;
socket->s->req->wto = socket_proxy.timeout.server;
socket->s->req->rex = TICK_ETERNITY;
socket->s->req->wex = TICK_ETERNITY;
socket->s->req->analyse_exp = TICK_ETERNITY;
socket->s->rep->analysers = 0;
socket->s->rep->rto = socket_proxy.timeout.server;
socket->s->rep->wto = socket_proxy.timeout.client;
socket->s->rep->rex = TICK_ETERNITY;
socket->s->rep->wex = TICK_ETERNITY;
socket->s->rep->analyse_exp = TICK_ETERNITY;
/*
*
* Configure the session.
*
*/
/* The session dont have listener. The listener is used with real
* proxies.
*/
socket->s->listener = NULL;
/* The flags are initialized to 0. Values are setted later. */
socket->s->flags = 0;
/* Assign the configured proxy to the new session. */
socket->s->be = &socket_proxy;
socket->s->fe = &socket_proxy;
/* XXX: Set namy variables */
socket->s->store_count = 0;
memset(socket->s->stkctr, 0, sizeof(socket->s->stkctr));
/* Configure logs. */
socket->s->logs.logwait = 0;
socket->s->logs.level = 0;
socket->s->logs.accept_date = date; /* user-visible date for logging */
socket->s->logs.tv_accept = now; /* corrected date for internal use */
socket->s->do_log = NULL;
/* Function used if an error is occured. */
socket->s->srv_error = default_srv_error;
/* Init the list of buffers. */
LIST_INIT(&socket->s->buffer_wait);
/* Dont configure the unique ID. */
socket->s->uniq_id = 0;
socket->s->unique_id = NULL;
/* XXX: ? */
socket->s->pend_pos = NULL;
/* XXX: See later. */
socket->s->txn.sessid = NULL;
socket->s->txn.srv_cookie = NULL;
socket->s->txn.cli_cookie = NULL;
socket->s->txn.uri = NULL;
socket->s->txn.req.cap = NULL;
socket->s->txn.rsp.cap = NULL;
socket->s->txn.hdr_idx.v = NULL;
socket->s->txn.hdr_idx.size = 0;
socket->s->txn.hdr_idx.used = 0;
/* Configure "left" stream interface as applet. This "si" produce
* and use the data received from the server. The applet is initialized
* and is attached to the stream interface.
*/
/* The data producer is already connected. It is the applet. */
socket->s->req->flags = CF_READ_ATTACHED;
channel_auto_connect(socket->s->req); /* don't wait to establish connection */
channel_auto_close(socket->s->req); /* let the producer forward close requests */
si_reset(&socket->s->si[0], socket->s->task);
si_set_state(&socket->s->si[0], SI_ST_EST); /* connection established (resource exists) */
appctx = stream_int_register_handler(&socket->s->si[0], &update_applet);
if (!appctx)
goto out_fail_conn1;
appctx->ctx.hlua.socket = socket;
appctx->ctx.hlua.connected = 0;
LIST_INIT(&appctx->ctx.hlua.wake_on_write);
LIST_INIT(&appctx->ctx.hlua.wake_on_read);
/* Configure "right" stream interface. this "si" is used to connect
* and retrieve data from the server. The connection is initialized
* with the "struct server".
*/
si_reset(&socket->s->si[1], socket->s->task);
si_set_state(&socket->s->si[1], SI_ST_INI);
socket->s->si[1].conn_retries = socket_proxy.conn_retries;
/* Force destination server. */
socket->s->flags |= SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET | SN_BE_ASSIGNED;
socket->s->target = &socket_tcp.obj_type;
/* This session is added to te lists of alive sessions. */
LIST_ADDQ(&sessions, &socket->s->list);
/* XXX: I think that this list is used by stats. */
LIST_INIT(&socket->s->back_refs);
/* Update statistics counters. */
socket_proxy.feconn++; /* beconn will be increased later */
jobs++;
totalconn++;
/* Return yield waiting for connection. */
return 1;
out_fail_conn1:
pool_free2(pool2_buffer, socket->s->rep->buf);
out_fail_rep_buf:
pool_free2(pool2_channel, socket->s->rep);
out_fail_rep:
pool_free2(pool2_buffer, socket->s->req->buf);
out_fail_req_buf:
pool_free2(pool2_channel, socket->s->req);
out_fail_req:
task_free(socket->s->task);
out_free_session:
pool_free2(pool2_session, socket->s);
out_fail_conf:
WILL_LJMP(lua_error(L));
return 0;
}
/*
*
*
* Class Channel
*
*
*/
/* Returns the struct hlua_channel join to the class channel in the
* stack entry "ud" or throws an argument error.
*/
__LJMP static struct hlua_channel *hlua_checkchannel(lua_State *L, int ud)
{
return (struct hlua_channel *)MAY_LJMP(hlua_checkudata(L, ud, class_channel_ref));
}
/* Creates new channel object and put it on the top of the stack.
* If the stask does not have a free slots, the function fails
* and returns 0;
*/
static int hlua_channel_new(lua_State *L, struct session *s, struct channel *channel)
{
struct hlua_channel *chn;
/* Check stack size. */
if (!lua_checkstack(L, 2))
return 0;
/* NOTE: The allocation never fails. The failure
* throw an error, and the function never returns.
*/
chn = lua_newuserdata(L, sizeof(*chn));
chn->chn = channel;
chn->s = s;
/* Pop a class sesison metatable and affect it to the userdata. */
lua_rawgeti(L, LUA_REGISTRYINDEX, class_channel_ref);
lua_setmetatable(L, -2);
return 1;
}
/* Duplicate all the data present in the input channel and put it
* in a string LUA variables. Returns -1 and push a nil value in
* the stack if the channel is closed and all the data are consumed,
* returns 0 if no data are available, otherwise it returns the length
* of the builded string.
*/
static inline int _hlua_channel_dup(struct hlua_channel *chn, lua_State *L)
{
char *blk1;
char *blk2;
int len1;
int len2;
int ret;
luaL_Buffer b;
ret = bi_getblk_nc(chn->chn, &blk1, &len1, &blk2, &len2);
if (unlikely(ret == 0))
return 0;
if (unlikely(ret < 0)) {
lua_pushnil(L);
return -1;
}
luaL_buffinit(L, &b);
luaL_addlstring(&b, blk1, len1);
if (unlikely(ret == 2))
luaL_addlstring(&b, blk2, len2);
luaL_pushresult(&b);
if (unlikely(ret == 2))
return len1 + len2;
return len1;
}
/* "_hlua_channel_dup" wrapper. If no data are available, it returns
* a yield. This function keep the data in the buffer.
*/
__LJMP static int hlua_channel_dup(lua_State *L)
{
struct hlua_channel *chn;
MAY_LJMP(check_args(L, 1, "dup"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
if (_hlua_channel_dup(chn, L) == 0)
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_dup, TICK_ETERNITY, 0));
return 1;
}
/* "_hlua_channel_dup" wrapper. If no data are available, it returns
* a yield. This function consumes the data in the buffer. It returns
* a string containing the data or a nil pointer if no data are available
* and the channel is closed.
*/
__LJMP static int hlua_channel_get(lua_State *L)
{
struct hlua_channel *chn;
int ret;
MAY_LJMP(check_args(L, 1, "get"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
ret = _hlua_channel_dup(chn, L);
if (unlikely(ret == 0))
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_get, TICK_ETERNITY, 0));
if (unlikely(ret == -1))
return 1;
chn->chn->buf->i -= ret;
return 1;
}
/* This functions consumes and returns one line. If the channel is closed,
* and the last data does not contains a final '\n', the data are returned
* without the final '\n'. When no more data are avalaible, it returns nil
* value.
*/
__LJMP static int hlua_channel_getline(lua_State *L)
{
char *blk1;
char *blk2;
int len1;
int len2;
int len;
struct hlua_channel *chn;
int ret;
luaL_Buffer b;
MAY_LJMP(check_args(L, 1, "getline"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
ret = bi_getline_nc(chn->chn, &blk1, &len1, &blk2, &len2);
if (ret == 0)
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_getline, TICK_ETERNITY, 0));
if (ret == -1) {
lua_pushnil(L);
return 1;
}
luaL_buffinit(L, &b);
luaL_addlstring(&b, blk1, len1);
len = len1;
if (unlikely(ret == 2)) {
luaL_addlstring(&b, blk2, len2);
len += len2;
}
luaL_pushresult(&b);
buffer_replace2(chn->chn->buf, chn->chn->buf->p, chn->chn->buf->p + len, NULL, 0);
return 1;
}
/* This function takes a string as input, and append it at the
* input side of channel. If the data is too big, but a space
* is probably available after sending some data, the function
* yield. If the data is bigger than the buffer, or if the
* channel is closed, it returns -1. otherwise, it returns the
* amount of data writed.
*/
__LJMP static int _hlua_channel_append(lua_State *L)
{
struct hlua_channel *chn = MAY_LJMP(hlua_checkchannel(L, 1));
size_t len;
const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
int l = MAY_LJMP(luaL_checkinteger(L, 3));
int ret;
int max;
max = channel_recv_limit(chn->chn) - buffer_len(chn->chn->buf);
if (max > len - l)
max = len - l;
ret = bi_putblk(chn->chn, str+l, max);
if (ret == -2 || ret == -3) {
lua_pushinteger(L, -1);
return 1;
}
if (ret == -1)
WILL_LJMP(hlua_yieldk(L, 0, 0, _hlua_channel_append, TICK_ETERNITY, 0));
l += ret;
lua_pop(L, 1);
lua_pushinteger(L, l);
max = channel_recv_limit(chn->chn) - buffer_len(chn->chn->buf);
if (max == 0 && chn->chn->buf->o == 0) {
/* There are no space avalaible, and the output buffer is empty.
* in this case, we cannot add more data, so we cannot yield,
* we return the amount of copyied data.
*/
return 1;
}
if (l < len)
WILL_LJMP(hlua_yieldk(L, 0, 0, _hlua_channel_append, TICK_ETERNITY, 0));
return 1;
}
/* just a wrapper of "_hlua_channel_append". It returns the length
* of the writed string, or -1 if the channel is closed or if the
* buffer size is too little for the data.
*/
__LJMP static int hlua_channel_append(lua_State *L)
{
MAY_LJMP(check_args(L, 2, "append"));
lua_pushinteger(L, 0);
return MAY_LJMP(_hlua_channel_append(L));
}
/* just a wrapper of "_hlua_channel_append". This wrapper starts
* his process by cleaning the buffer. The result is a replacement
* of the current data. It returns the length of the writed string,
* or -1 if the channel is closed or if the buffer size is too
* little for the data.
*/
__LJMP static int hlua_channel_set(lua_State *L)
{
struct hlua_channel *chn;
MAY_LJMP(check_args(L, 2, "set"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
lua_pushinteger(L, 0);
chn->chn->buf->i = 0;
return MAY_LJMP(_hlua_channel_append(L));
}
/* Append data in the output side of the buffer. This data is immediatly
* sent. The fcuntion returns the ammount of data writed. If the buffer
* cannot contains the data, the function yield. The function returns -1
* if the channel is closed.
*/
__LJMP static int _hlua_channel_send(lua_State *L)
{
struct hlua_channel *chn = MAY_LJMP(hlua_checkchannel(L, 1));
size_t len;
const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
int l = MAY_LJMP(luaL_checkinteger(L, 3));
int max;
struct hlua *hlua = hlua_gethlua(L);
if (unlikely(channel_output_closed(chn->chn))) {
lua_pushinteger(L, -1);
return 1;
}
/* Check if the buffer is avalaible because HAProxy doesn't allocate
* the request buffer if its not required.
*/
if (chn->chn->buf->size == 0) {
if (!session_alloc_recv_buffer(chn->s, &chn->chn->buf)) {
chn->chn->prod->flags |= SI_FL_WAIT_ROOM;
WILL_LJMP(hlua_yieldk(L, 0, 0, _hlua_channel_send, TICK_ETERNITY, 0));
}
}
max = channel_recv_limit(chn->chn) - buffer_len(chn->chn->buf);
if (max > len - l)
max = len - l;
max = buffer_replace2(chn->chn->buf, chn->chn->buf->p, chn->chn->buf->p, str+l, max);
/* buffer replace considers that the input part is filled.
* so, I must forward these new data in the output part.
*/
b_adv(chn->chn->buf, max);
l += max;
lua_pop(L, 1);
lua_pushinteger(L, l);
max = channel_recv_limit(chn->chn) - buffer_len(chn->chn->buf);
if (max == 0 && chn->chn->buf->o == 0) {
/* There are no space avalaible, and the output buffer is empty.
* in this case, we cannot add more data, so we cannot yield,
* we return the amount of copyied data.
*/
return 1;
}
if (l < len) {
/* If we are waiting for space in the response buffer, we
* must set the flag WAKERESWR. This flag required the task
* wake up if any activity is detected on the response buffer.
*/
if (chn->chn == chn->s->rep)
HLUA_SET_WAKERESWR(hlua);
else
HLUA_SET_WAKEREQWR(hlua);
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_send_yield, TICK_ETERNITY, 0));
}
return 1;
}
/* Just a wraper of "_hlua_channel_send". This wrapper permits
* yield the LUA process, and resume it without checking the
* input arguments.
*/
__LJMP static int hlua_channel_send(lua_State *L)
{
MAY_LJMP(check_args(L, 2, "send"));
lua_pushinteger(L, 0);
return MAY_LJMP(_hlua_channel_send(L));
}
/* This function forward and amount of butes. The data pass from
* the input side of the buffer to the output side, and can be
* forwarded. This function never fails.
*
* The Lua function takes an amount of bytes to be forwarded in
* imput. It returns the number of bytes forwarded.
*/
__LJMP static int hlua_channel_forward_yield(lua_State *L)
{
struct hlua_channel *chn;
int len;
int l;
int max;
struct hlua *hlua = hlua_gethlua(L);
chn = MAY_LJMP(hlua_checkchannel(L, 1));
len = MAY_LJMP(luaL_checkinteger(L, 2));
l = MAY_LJMP(luaL_checkinteger(L, -1));
max = len - l;
if (max > chn->chn->buf->i)
max = chn->chn->buf->i;
channel_forward(chn->chn, max);
l += max;
lua_pop(L, 1);
lua_pushinteger(L, l);
/* Check if it miss bytes to forward. */
if (l < len) {
/* The the input channel or the output channel are closed, we
* must return the amount of data forwarded.
*/
if (channel_input_closed(chn->chn) || channel_output_closed(chn->chn))
return 1;
/* If we are waiting for space data in the response buffer, we
* must set the flag WAKERESWR. This flag required the task
* wake up if any activity is detected on the response buffer.
*/
if (chn->chn == chn->s->rep)
HLUA_SET_WAKERESWR(hlua);
else
HLUA_SET_WAKEREQWR(hlua);
/* Otherwise, we can yield waiting for new data in the inpout side. */
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_forward_yield, TICK_ETERNITY, 0));
}
return 1;
}
/* Just check the input and prepare the stack for the previous
* function "hlua_channel_forward_yield"
*/
__LJMP static int hlua_channel_forward(lua_State *L)
{
MAY_LJMP(check_args(L, 2, "forward"));
MAY_LJMP(hlua_checkchannel(L, 1));
MAY_LJMP(luaL_checkinteger(L, 2));
lua_pushinteger(L, 0);
return MAY_LJMP(hlua_channel_forward_yield(L));
}
/* Just returns the number of bytes available in the input
* side of the buffer. This function never fails.
*/
__LJMP static int hlua_channel_get_in_len(lua_State *L)
{
struct hlua_channel *chn;
MAY_LJMP(check_args(L, 1, "get_in_len"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
lua_pushinteger(L, chn->chn->buf->i);
return 1;
}
/* Just returns the number of bytes available in the output
* side of the buffer. This function never fails.
*/
__LJMP static int hlua_channel_get_out_len(lua_State *L)
{
struct hlua_channel *chn;
MAY_LJMP(check_args(L, 1, "get_out_len"));
chn = MAY_LJMP(hlua_checkchannel(L, 1));
lua_pushinteger(L, chn->chn->buf->o);
return 1;
}
/*
*
*
* Class TXN
*
*
*/
/* Returns a struct hlua_session if the stack entry "ud" is
* a class session, otherwise it throws an error.
*/
__LJMP static struct hlua_txn *hlua_checktxn(lua_State *L, int ud)
{
return (struct hlua_txn *)MAY_LJMP(hlua_checkudata(L, ud, class_txn_ref));
}
__LJMP static int hlua_setpriv(lua_State *L)
{
struct hlua *hlua;
MAY_LJMP(check_args(L, 2, "set_priv"));
/* It is useles to retrieve the session, but this function
* runs only in a session context.
*/
MAY_LJMP(hlua_checktxn(L, 1));
hlua = hlua_gethlua(L);
/* Remove previous value. */
if (hlua->Mref != -1)
luaL_unref(L, hlua->Mref, LUA_REGISTRYINDEX);
/* Get and store new value. */
lua_pushvalue(L, 2); /* Copy the element 2 at the top of the stack. */
hlua->Mref = luaL_ref(L, LUA_REGISTRYINDEX); /* pop the previously pushed value. */
return 0;
}
__LJMP static int hlua_getpriv(lua_State *L)
{
struct hlua *hlua;
MAY_LJMP(check_args(L, 1, "get_priv"));
/* It is useles to retrieve the session, but this function
* runs only in a session context.
*/
MAY_LJMP(hlua_checktxn(L, 1));
hlua = hlua_gethlua(L);
/* Push configuration index in the stack. */
lua_rawgeti(L, LUA_REGISTRYINDEX, hlua->Mref);
return 1;
}
/* Create stack entry containing a class TXN. This function
* return 0 if the stack does not contains free slots,
* otherwise it returns 1.
*/
static int hlua_txn_new(lua_State *L, struct session *s, struct proxy *p, void *l7)
{
struct hlua_txn *hs;
/* Check stack size. */
if (!lua_checkstack(L, 2))
return 0;
/* NOTE: The allocation never fails. The failure
* throw an error, and the function never returns.
* if the throw is not avalaible, the process is aborted.
*/
hs = lua_newuserdata(L, sizeof(struct hlua_txn));
hs->s = s;
hs->p = p;
hs->l7 = l7;
/* Pop a class sesison metatable and affect it to the userdata. */
lua_rawgeti(L, LUA_REGISTRYINDEX, class_txn_ref);
lua_setmetatable(L, -2);
return 1;
}
/* This function returns a channel object associated
* with the request channel. This function never fails,
* however if the stack is full, it throws an error.
*/
__LJMP static int hlua_txn_req_channel(lua_State *L)
{
struct hlua_txn *s;
MAY_LJMP(check_args(L, 1, "req_channel"));
s = MAY_LJMP(hlua_checktxn(L, 1));
if (!hlua_channel_new(L, s->s, s->s->req))
WILL_LJMP(luaL_error(L, "full stack"));
return 1;
}
/* This function returns a channel object associated
* with the response channel. This function never fails,
* however if the stack is full, it throws an error.
*/
__LJMP static int hlua_txn_res_channel(lua_State *L)
{
struct hlua_txn *s;
MAY_LJMP(check_args(L, 1, "req_channel"));
s = MAY_LJMP(hlua_checktxn(L, 1));
if (!hlua_channel_new(L, s->s, s->s->rep))
WILL_LJMP(luaL_error(L, "full stack"));
return 1;
}
/* This function is an Lua binding that send pending data
* to the client, and close the stream interface.
*/
__LJMP static int hlua_txn_close(lua_State *L)
{
struct hlua_txn *s;
MAY_LJMP(check_args(L, 1, "close"));
s = MAY_LJMP(hlua_checktxn(L, 1));
channel_abort(s->s->si[0].ib);
channel_auto_close(s->s->si[0].ib);
channel_erase(s->s->si[0].ib);
channel_auto_read(s->s->si[0].ob);
channel_auto_close(s->s->si[0].ob);
channel_shutr_now(s->s->si[0].ob);
return 0;
}
/* This function is an LUA binding. It is called with each sample-fetch.
* It uses closure argument to store the associated sample-fetch. It
* returns only one argument or throws an error. An error is thrown
* only if an error is encountered during the argument parsing. If
* the "sample-fetch" function fails, nil is returned.
*/
__LJMP static int hlua_run_sample_fetch(lua_State *L)
{
struct hlua_txn *s;
struct hlua_sample_fetch *f;
struct arg args[ARGM_NBARGS + 1];
int i;
struct sample smp;
/* Get closure arguments. */
f = (struct hlua_sample_fetch *)lua_touserdata(L, lua_upvalueindex(1));
/* Get traditionnal arguments. */
s = MAY_LJMP(hlua_checktxn(L, 1));
/* Get extra arguments. */
for (i = 0; i < lua_gettop(L) - 1; i++) {
if (i >= ARGM_NBARGS)
break;
hlua_lua2arg(L, i + 2, &args[i]);
}
args[i].type = ARGT_STOP;
/* Check arguments. */
MAY_LJMP(hlua_lua2arg_check(L, 1, args, f->f->arg_mask));
/* Run the special args checker. */
if (f->f->val_args && !f->f->val_args(args, NULL)) {
lua_pushfstring(L, "error in arguments");
WILL_LJMP(lua_error(L));
}
/* Initialise the sample. */
memset(&smp, 0, sizeof(smp));
/* Run the sample fetch process. */
if (!f->f->process(s->p, s->s, s->l7, 0, args, &smp, f->f->kw, f->f->private)) {
lua_pushnil(L);
return 1;
}
/* Convert the returned sample in lua value. */
hlua_smp2lua(L, &smp);
return 1;
}
/* This function is an LUA binding. It creates ans returns
* an array of HTTP headers. This function does not fails.
*/
static int hlua_session_getheaders(lua_State *L)
{
struct hlua_txn *s = MAY_LJMP(hlua_checktxn(L, 1));
struct session *sess = s->s;
const char *cur_ptr, *cur_next, *p;
int old_idx, cur_idx;
struct hdr_idx_elem *cur_hdr;
const char *hn, *hv;
int hnl, hvl;
/* Create the table. */
lua_newtable(L);
/* Build array of headers. */
old_idx = 0;
cur_next = sess->req->buf->p + hdr_idx_first_pos(&sess->txn.hdr_idx);
while (1) {
cur_idx = sess->txn.hdr_idx.v[old_idx].next;
if (!cur_idx)
break;
old_idx = cur_idx;
cur_hdr = &sess->txn.hdr_idx.v[cur_idx];
cur_ptr = cur_next;
cur_next = cur_ptr + cur_hdr->len + cur_hdr->cr + 1;
/* Now we have one full header at cur_ptr of len cur_hdr->len,
* and the next header starts at cur_next. We'll check
* this header in the list as well as against the default
* rule.
*/
/* look for ': *'. */
hn = cur_ptr;
for (p = cur_ptr; p < cur_ptr + cur_hdr->len && *p != ':'; p++);
if (p >= cur_ptr+cur_hdr->len)
continue;
hnl = p - hn;
p++;
while (p < cur_ptr+cur_hdr->len && ( *p == ' ' || *p == '\t' ))
p++;
if (p >= cur_ptr+cur_hdr->len)
continue;
hv = p;
hvl = cur_ptr+cur_hdr->len-p;
/* Push values in the table. */
lua_pushlstring(L, hn, hnl);
lua_pushlstring(L, hv, hvl);
lua_settable(L, -3);
}
return 1;
}
__LJMP static int hlua_sleep_yield(lua_State *L)
{
int wakeup_ms = lua_tointeger(L, -1);
if (now_ms < wakeup_ms)
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_sleep_yield, wakeup_ms, 0));
return 0;
}
__LJMP static int hlua_sleep(lua_State *L)
{
unsigned int delay;
unsigned int wakeup_ms;
MAY_LJMP(check_args(L, 1, "sleep"));
delay = MAY_LJMP(luaL_checkunsigned(L, 1)) * 1000;
wakeup_ms = tick_add(now_ms, delay);
lua_pushinteger(L, wakeup_ms);
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_sleep_yield, wakeup_ms, 0));
return 0;
}
__LJMP static int hlua_msleep(lua_State *L)
{
unsigned int delay;
unsigned int wakeup_ms;
MAY_LJMP(check_args(L, 1, "msleep"));
delay = MAY_LJMP(luaL_checkunsigned(L, 1));
wakeup_ms = tick_add(now_ms, delay);
lua_pushinteger(L, wakeup_ms);
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_sleep_yield, wakeup_ms, 0));
return 0;
}
/* This functionis an LUA binding. it permits to give back
* the hand at the HAProxy scheduler. It is used when the
* LUA processing consumes a lot of time.
*/
__LJMP static int hlua_yield_yield(lua_State *L)
{
return 0;
}
__LJMP static int hlua_yield(lua_State *L)
{
WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_yield_yield, TICK_ETERNITY, HLUA_CTRLYIELD));
return 0;
}
/* This function change the nice of the currently executed
* task. It is used set low or high priority at the current
* task.
*/
__LJMP static int hlua_setnice(lua_State *L)
{
struct hlua *hlua;
int nice;
MAY_LJMP(check_args(L, 1, "set_nice"));
hlua = hlua_gethlua(L);
nice = MAY_LJMP(luaL_checkinteger(L, 1));
/* If he task is not set, I'm in a start mode. */
if (!hlua || !hlua->task)
return 0;
if (nice < -1024)
nice = -1024;
else if (nice > 1024)
nice = 1024;
hlua->task->nice = nice;
return 0;
}
/* This function is used as a calback of a task. It is called by the
* HAProxy task subsystem when the task is awaked. The LUA runtime can
* return an E_AGAIN signal, the emmiter of this signal must set a
* signal to wake the task.
*/
static struct task *hlua_process_task(struct task *task)
{
struct hlua *hlua = task->context;
enum hlua_exec status;
/* We need to remove the task from the wait queue before executing
* the Lua code because we don't know if it needs to wait for
* another timer or not in the case of E_AGAIN.
*/
task_delete(task);
/* If it is the first call to the task, we must initialize the
* execution timeouts.
*/
if (!HLUA_IS_RUNNING(hlua))
hlua->expire = tick_add(now_ms, hlua_timeout_task);
/* Execute the Lua code. */
status = hlua_ctx_resume(hlua, 1);
switch (status) {
/* finished or yield */
case HLUA_E_OK:
hlua_ctx_destroy(hlua);
task_delete(task);
task_free(task);
break;
case HLUA_E_AGAIN: /* co process or timeout wake me later. */
if (hlua->wake_time != TICK_ETERNITY)
task_schedule(task, hlua->wake_time);
break;
/* finished with error. */
case HLUA_E_ERRMSG:
send_log(NULL, LOG_ERR, "Lua task: %s.", lua_tostring(hlua->T, -1));
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua task: %s.\n", lua_tostring(hlua->T, -1));
hlua_ctx_destroy(hlua);
task_delete(task);
task_free(task);
break;
case HLUA_E_ERR:
default:
send_log(NULL, LOG_ERR, "Lua task: unknown error.");
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua task: unknown error.\n");
hlua_ctx_destroy(hlua);
task_delete(task);
task_free(task);
break;
}
return NULL;
}
/* This function is an LUA binding that register LUA function to be
* executed after the HAProxy configuration parsing and before the
* HAProxy scheduler starts. This function expect only one LUA
* argument that is a function. This function returns nothing, but
* throws if an error is encountered.
*/
__LJMP static int hlua_register_init(lua_State *L)
{
struct hlua_init_function *init;
int ref;
MAY_LJMP(check_args(L, 1, "register_init"));
ref = MAY_LJMP(hlua_checkfunction(L, 1));
init = malloc(sizeof(*init));
if (!init)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
init->function_ref = ref;
LIST_ADDQ(&hlua_init_functions, &init->l);
return 0;
}
/* This functio is an LUA binding. It permits to register a task
* executed in parallel of the main HAroxy activity. The task is
* created and it is set in the HAProxy scheduler. It can be called
* from the "init" section, "post init" or during the runtime.
*
* Lua prototype:
*
* <none> core.register_task(<function>)
*/
static int hlua_register_task(lua_State *L)
{
struct hlua *hlua;
struct task *task;
int ref;
MAY_LJMP(check_args(L, 1, "register_task"));
ref = MAY_LJMP(hlua_checkfunction(L, 1));
hlua = malloc(sizeof(*hlua));
if (!hlua)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
task = task_new();
task->context = hlua;
task->process = hlua_process_task;
if (!hlua_ctx_init(hlua, task))
WILL_LJMP(luaL_error(L, "lua out of memory error."));
/* Restore the function in the stack. */
lua_rawgeti(hlua->T, LUA_REGISTRYINDEX, ref);
hlua->nargs = 0;
/* Schedule task. */
task_schedule(task, now_ms);
return 0;
}
/* Wrapper called by HAProxy to execute an LUA converter. This wrapper
* doesn't allow "yield" functions because the HAProxy engine cannot
* resume converters.
*/
static int hlua_sample_conv_wrapper(struct session *session, const struct arg *arg_p,
struct sample *smp, void *private)
{
struct hlua_function *fcn = (struct hlua_function *)private;
/* In the execution wrappers linked with a session, the
* Lua context can be not initialized. This behavior
* permits to save performances because a systematic
* Lua initialization cause 5% performances loss.
*/
if (!session->hlua.T && !hlua_ctx_init(&session->hlua, session->task)) {
send_log(session->be, LOG_ERR, "Lua converter '%s': can't initialize Lua context.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua converter '%s': can't initialize Lua context.\n", fcn->name);
return 0;
}
/* If it is the first run, initialize the data for the call. */
if (!HLUA_IS_RUNNING(&session->hlua)) {
/* Check stack available size. */
if (!lua_checkstack(session->hlua.T, 1)) {
send_log(session->be, LOG_ERR, "Lua converter '%s': full stack.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua converter '%s': full stack.\n", fcn->name);
return 0;
}
/* Restore the function in the stack. */
lua_rawgeti(session->hlua.T, LUA_REGISTRYINDEX, fcn->function_ref);
/* convert input sample and pust-it in the stack. */
if (!lua_checkstack(session->hlua.T, 1)) {
send_log(session->be, LOG_ERR, "Lua converter '%s': full stack.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua converter '%s': full stack.\n", fcn->name);
return 0;
}
hlua_smp2lua(session->hlua.T, smp);
session->hlua.nargs = 2;
/* push keywords in the stack. */
if (arg_p) {
for (; arg_p->type != ARGT_STOP; arg_p++) {
if (!lua_checkstack(session->hlua.T, 1)) {
send_log(session->be, LOG_ERR, "Lua converter '%s': full stack.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua converter '%s': full stack.\n", fcn->name);
return 0;
}
hlua_arg2lua(session->hlua.T, arg_p);
session->hlua.nargs++;
}
}
/* We must initialize the execution timeouts. */
session->hlua.expire = tick_add(now_ms, hlua_timeout_session);
/* Set the currently running flag. */
HLUA_SET_RUN(&session->hlua);
}
/* Execute the function. */
switch (hlua_ctx_resume(&session->hlua, 0)) {
/* finished. */
case HLUA_E_OK:
/* Convert the returned value in sample. */
hlua_lua2smp(session->hlua.T, -1, smp);
lua_pop(session->hlua.T, 1);
return 1;
/* yield. */
case HLUA_E_AGAIN:
send_log(session->be, LOG_ERR, "Lua converter '%s': cannot use yielded functions.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua converter '%s': cannot use yielded functions.\n", fcn->name);
return 0;
/* finished with error. */
case HLUA_E_ERRMSG:
/* Display log. */
send_log(session->be, LOG_ERR, "Lua converter '%s': %s.", fcn->name, lua_tostring(session->hlua.T, -1));
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua converter '%s': %s.\n", fcn->name, lua_tostring(session->hlua.T, -1));
lua_pop(session->hlua.T, 1);
return 0;
case HLUA_E_ERR:
/* Display log. */
send_log(session->be, LOG_ERR, "Lua converter '%s' returns an unknown error.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua converter '%s' returns an unknown error.\n", fcn->name);
default:
return 0;
}
}
/* Wrapper called by HAProxy to execute a sample-fetch. this wrapper
* doesn't allow "yield" functions because the HAProxy engine cannot
* resume sample-fetches.
*/
static int hlua_sample_fetch_wrapper(struct proxy *px, struct session *s, void *l7,
unsigned int opt, const struct arg *arg_p,
struct sample *smp, const char *kw, void *private)
{
struct hlua_function *fcn = (struct hlua_function *)private;
/* In the execution wrappers linked with a session, the
* Lua context can be not initialized. This behavior
* permits to save performances because a systematic
* Lua initialization cause 5% performances loss.
*/
if (!s->hlua.T && !hlua_ctx_init(&s->hlua, s->task)) {
send_log(s->be, LOG_ERR, "Lua sample-fetch '%s': can't initialize Lua context.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': can't initialize Lua context.\n", fcn->name);
return 0;
}
/* If it is the first run, initialize the data for the call. */
if (!HLUA_IS_RUNNING(&s->hlua)) {
/* Check stack available size. */
if (!lua_checkstack(s->hlua.T, 2)) {
send_log(px, LOG_ERR, "Lua sample-fetch '%s': full stack.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': full stack.\n", fcn->name);
return 0;
}
/* Restore the function in the stack. */
lua_rawgeti(s->hlua.T, LUA_REGISTRYINDEX, fcn->function_ref);
/* push arguments in the stack. */
if (!hlua_txn_new(s->hlua.T, s, px, l7)) {
send_log(px, LOG_ERR, "Lua sample-fetch '%s': full stack.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': full stack.\n", fcn->name);
return 0;
}
s->hlua.nargs = 1;
/* push keywords in the stack. */
for (; arg_p && arg_p->type != ARGT_STOP; arg_p++) {
/* Check stack available size. */
if (!lua_checkstack(s->hlua.T, 1)) {
send_log(px, LOG_ERR, "Lua sample-fetch '%s': full stack.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': full stack.\n", fcn->name);
return 0;
}
if (!lua_checkstack(s->hlua.T, 1)) {
send_log(px, LOG_ERR, "Lua sample-fetch '%s': full stack.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': full stack.\n", fcn->name);
return 0;
}
hlua_arg2lua(s->hlua.T, arg_p);
s->hlua.nargs++;
}
/* We must initialize the execution timeouts. */
s->hlua.expire = tick_add(now_ms, hlua_timeout_session);
/* Set the currently running flag. */
HLUA_SET_RUN(&s->hlua);
}
/* Execute the function. */
switch (hlua_ctx_resume(&s->hlua, 0)) {
/* finished. */
case HLUA_E_OK:
/* Convert the returned value in sample. */
hlua_lua2smp(s->hlua.T, -1, smp);
lua_pop(s->hlua.T, 1);
/* Set the end of execution flag. */
smp->flags &= ~SMP_F_MAY_CHANGE;
return 1;
/* yield. */
case HLUA_E_AGAIN:
send_log(px, LOG_ERR, "Lua sample-fetch '%s': cannot use yielded functions.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': cannot use yielded functions.\n", fcn->name);
return 0;
/* finished with error. */
case HLUA_E_ERRMSG:
/* Display log. */
send_log(px, LOG_ERR, "Lua sample-fetch '%s': %s.", fcn->name, lua_tostring(s->hlua.T, -1));
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': %s.\n", fcn->name, lua_tostring(s->hlua.T, -1));
lua_pop(s->hlua.T, 1);
return 0;
case HLUA_E_ERR:
/* Display log. */
send_log(px, LOG_ERR, "Lua sample-fetch '%s' returns an unknown error.", fcn->name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua sample-fetch '%s': returns an unknown error.\n", fcn->name);
default:
return 0;
}
}
/* This function is an LUA binding used for registering
* "sample-conv" functions. It expects a converter name used
* in the haproxy configuration file, and an LUA function.
*/
__LJMP static int hlua_register_converters(lua_State *L)
{
struct sample_conv_kw_list *sck;
const char *name;
int ref;
int len;
struct hlua_function *fcn;
MAY_LJMP(check_args(L, 2, "register_converters"));
/* First argument : converter name. */
name = MAY_LJMP(luaL_checkstring(L, 1));
/* Second argument : lua function. */
ref = MAY_LJMP(hlua_checkfunction(L, 2));
/* Allocate and fill the sample fetch keyword struct. */
sck = malloc(sizeof(struct sample_conv_kw_list) +
sizeof(struct sample_conv) * 2);
if (!sck)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
fcn = malloc(sizeof(*fcn));
if (!fcn)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
/* Fill fcn. */
fcn->name = strdup(name);
if (!fcn->name)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
fcn->function_ref = ref;
/* List head */
sck->list.n = sck->list.p = NULL;
/* converter keyword. */
len = strlen("lua.") + strlen(name) + 1;
sck->kw[0].kw = malloc(len);
if (!sck->kw[0].kw)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
snprintf((char *)sck->kw[0].kw, len, "lua.%s", name);
sck->kw[0].process = hlua_sample_conv_wrapper;
sck->kw[0].arg_mask = ARG5(0,STR,STR,STR,STR,STR);
sck->kw[0].val_args = NULL;
sck->kw[0].in_type = SMP_T_STR;
sck->kw[0].out_type = SMP_T_STR;
sck->kw[0].private = fcn;
/* End of array. */
memset(&sck->kw[1], 0, sizeof(struct sample_conv));
/* Register this new converter */
sample_register_convs(sck);
return 0;
}
/* This fucntion is an LUA binding used for registering
* "sample-fetch" functions. It expects a converter name used
* in the haproxy configuration file, and an LUA function.
*/
__LJMP static int hlua_register_fetches(lua_State *L)
{
const char *name;
int ref;
int len;
struct sample_fetch_kw_list *sfk;
struct hlua_function *fcn;
MAY_LJMP(check_args(L, 2, "register_fetches"));
/* First argument : sample-fetch name. */
name = MAY_LJMP(luaL_checkstring(L, 1));
/* Second argument : lua function. */
ref = MAY_LJMP(hlua_checkfunction(L, 2));
/* Allocate and fill the sample fetch keyword struct. */
sfk = malloc(sizeof(struct sample_fetch_kw_list) +
sizeof(struct sample_fetch) * 2);
if (!sfk)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
fcn = malloc(sizeof(*fcn));
if (!fcn)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
/* Fill fcn. */
fcn->name = strdup(name);
if (!fcn->name)
WILL_LJMP(luaL_error(L, "lua out of memory error."));
fcn->function_ref = ref;
/* List head */
sfk->list.n = sfk->list.p = NULL;
/* sample-fetch keyword. */
len = strlen("lua.") + strlen(name) + 1;
sfk->kw[0].kw = malloc(len);
if (!sfk->kw[0].kw)
return luaL_error(L, "lua out of memory error.");
snprintf((char *)sfk->kw[0].kw, len, "lua.%s", name);
sfk->kw[0].process = hlua_sample_fetch_wrapper;
sfk->kw[0].arg_mask = ARG5(0,STR,STR,STR,STR,STR);
sfk->kw[0].val_args = NULL;
sfk->kw[0].out_type = SMP_T_STR;
sfk->kw[0].use = SMP_USE_HTTP_ANY;
sfk->kw[0].val = 0;
sfk->kw[0].private = fcn;
/* End of array. */
memset(&sfk->kw[1], 0, sizeof(struct sample_fetch));
/* Register this new fetch. */
sample_register_fetches(sfk);
return 0;
}
/* global {tcp|http}-request parser. Return 1 in succes case, else return 0. */
static int hlua_parse_rule(const char **args, int *cur_arg, struct proxy *px,
struct hlua_rule **rule_p, char **err)
{
struct hlua_rule *rule;
/* Memory for the rule. */
rule = malloc(sizeof(*rule));
if (!rule) {
memprintf(err, "out of memory error");
return 0;
}
*rule_p = rule;
/* The requiered arg is a function name. */
if (!args[*cur_arg]) {
memprintf(err, "expect Lua function name");
return 0;
}
/* Lookup for the symbol, and check if it is a function. */
lua_getglobal(gL.T, args[*cur_arg]);
if (lua_isnil(gL.T, -1)) {
lua_pop(gL.T, 1);
memprintf(err, "Lua function '%s' not found", args[*cur_arg]);
return 0;
}
if (!lua_isfunction(gL.T, -1)) {
lua_pop(gL.T, 1);
memprintf(err, "'%s' is not a function", args[*cur_arg]);
return 0;
}
/* Reference the Lua function and store the reference. */
rule->fcn.function_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX);
rule->fcn.name = strdup(args[*cur_arg]);
if (!rule->fcn.name) {
memprintf(err, "out of memory error.");
return 0;
}
(*cur_arg)++;
/* TODO: later accept arguments. */
rule->args = NULL;
return 1;
}
/* This function is a wrapper to execute each LUA function declared
* as an action wrapper during the initialisation period. This function
* return 1 if the processing is finished (with oe without error) and
* return 0 if the function must be called again because the LUA
* returns a yield.
*/
static int hlua_request_act_wrapper(struct hlua_rule *rule, struct proxy *px,
struct session *s, struct http_txn *http_txn,
unsigned int analyzer)
{
char **arg;
/* In the execution wrappers linked with a session, the
* Lua context can be not initialized. This behavior
* permits to save performances because a systematic
* Lua initialization cause 5% performances loss.
*/
if (!s->hlua.T && !hlua_ctx_init(&s->hlua, s->task)) {
send_log(px, LOG_ERR, "Lua action '%s': can't initialize Lua context.", rule->fcn.name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua action '%s': can't initialize Lua context.\n", rule->fcn.name);
return 0;
}
/* If it is the first run, initialize the data for the call. */
if (!HLUA_IS_RUNNING(&s->hlua)) {
/* Check stack available size. */
if (!lua_checkstack(s->hlua.T, 1)) {
send_log(px, LOG_ERR, "Lua function '%s': full stack.", rule->fcn.name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua function '%s': full stack.\n", rule->fcn.name);
return 0;
}
/* Restore the function in the stack. */
lua_rawgeti(s->hlua.T, LUA_REGISTRYINDEX, rule->fcn.function_ref);
/* Create and and push object session in the stack. */
if (!hlua_txn_new(s->hlua.T, s, px, http_txn)) {
send_log(px, LOG_ERR, "Lua function '%s': full stack.", rule->fcn.name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua function '%s': full stack.\n", rule->fcn.name);
return 0;
}
s->hlua.nargs = 1;
/* push keywords in the stack. */
for (arg = rule->args; arg && *arg; arg++) {
if (!lua_checkstack(s->hlua.T, 1)) {
send_log(px, LOG_ERR, "Lua function '%s': full stack.", rule->fcn.name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua function '%s': full stack.\n", rule->fcn.name);
return 0;
}
lua_pushstring(s->hlua.T, *arg);
s->hlua.nargs++;
}
/* We must initialize the execution timeouts. */
s->hlua.expire = tick_add(now_ms, hlua_timeout_session);
/* Set the currently running flag. */
HLUA_SET_RUN(&s->hlua);
}
/* Execute the function. */
switch (hlua_ctx_resume(&s->hlua, 1)) {
/* finished. */
case HLUA_E_OK:
return 1;
/* yield. */
case HLUA_E_AGAIN:
/* Set timeout in the required channel. */
if (s->hlua.wake_time != TICK_ETERNITY) {
if (analyzer & (AN_REQ_INSPECT_FE|AN_REQ_HTTP_PROCESS_FE))
s->req->analyse_exp = s->hlua.wake_time;
else if (analyzer & (AN_RES_INSPECT|AN_RES_HTTP_PROCESS_BE))
s->rep->analyse_exp = s->hlua.wake_time;
}
/* Some actions can be wake up when a "write" event
* is detected on a response channel. This is useful
* only for actions targetted on the requests.
*/
if (HLUA_IS_WAKERESWR(&s->hlua)) {
s->rep->flags |= CF_WAKE_WRITE;
if ((analyzer & (AN_REQ_INSPECT_FE|AN_REQ_HTTP_PROCESS_FE)))
s->rep->analysers |= analyzer;
}
if (HLUA_IS_WAKEREQWR(&s->hlua))
s->req->flags |= CF_WAKE_WRITE;
return 0;
/* finished with error. */
case HLUA_E_ERRMSG:
/* Display log. */
send_log(px, LOG_ERR, "Lua function '%s': %s.", rule->fcn.name, lua_tostring(s->hlua.T, -1));
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua function '%s': %s.\n", rule->fcn.name, lua_tostring(s->hlua.T, -1));
lua_pop(s->hlua.T, 1);
return 1;
case HLUA_E_ERR:
/* Display log. */
send_log(px, LOG_ERR, "Lua function '%s' return an unknown error.", rule->fcn.name);
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Alert("Lua function '%s' return an unknown error.\n", rule->fcn.name);
default:
return 1;
}
}
/* Lua execution wrapper for "tcp-request". This function uses
* "hlua_request_act_wrapper" for executing the LUA code.
*/
int hlua_tcp_req_act_wrapper(struct tcp_rule *tcp_rule, struct proxy *px,
struct session *s)
{
return hlua_request_act_wrapper((struct hlua_rule *)tcp_rule->act_prm.data,
px, s, NULL, AN_REQ_INSPECT_FE);
}
/* Lua execution wrapper for "tcp-response". This function uses
* "hlua_request_act_wrapper" for executing the LUA code.
*/
int hlua_tcp_res_act_wrapper(struct tcp_rule *tcp_rule, struct proxy *px,
struct session *s)
{
return hlua_request_act_wrapper((struct hlua_rule *)tcp_rule->act_prm.data,
px, s, NULL, AN_RES_INSPECT);
}
/* Lua execution wrapper for http-request.
* This function uses "hlua_request_act_wrapper" for executing
* the LUA code.
*/
int hlua_http_req_act_wrapper(struct http_req_rule *rule, struct proxy *px,
struct session *s, struct http_txn *http_txn)
{
return hlua_request_act_wrapper((struct hlua_rule *)rule->arg.data, px,
s, http_txn, AN_REQ_HTTP_PROCESS_FE);
}
/* Lua execution wrapper for http-response.
* This function uses "hlua_request_act_wrapper" for executing
* the LUA code.
*/
int hlua_http_res_act_wrapper(struct http_res_rule *rule, struct proxy *px,
struct session *s, struct http_txn *http_txn)
{
return hlua_request_act_wrapper((struct hlua_rule *)rule->arg.data, px,
s, http_txn, AN_RES_HTTP_PROCESS_BE);
}
/* tcp-request <*> configuration wrapper. */
static int tcp_req_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
struct tcp_rule *rule, char **err)
{
if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->act_prm.data, err))
return 0;
rule->action = TCP_ACT_CUSTOM;
rule->action_ptr = hlua_tcp_req_act_wrapper;
return 1;
}
/* tcp-response <*> configuration wrapper. */
static int tcp_res_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
struct tcp_rule *rule, char **err)
{
if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->act_prm.data, err))
return 0;
rule->action = TCP_ACT_CUSTOM;
rule->action_ptr = hlua_tcp_res_act_wrapper;
return 1;
}
/* http-request <*> configuration wrapper. */
static int http_req_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
struct http_req_rule *rule, char **err)
{
if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->arg.data, err))
return -1;
rule->action = HTTP_REQ_ACT_CUSTOM_CONT;
rule->action_ptr = hlua_http_req_act_wrapper;
return 1;
}
/* http-response <*> configuration wrapper. */
static int http_res_action_register_lua(const char **args, int *cur_arg, struct proxy *px,
struct http_res_rule *rule, char **err)
{
if (!hlua_parse_rule(args, cur_arg, px, (struct hlua_rule **)&rule->arg.data, err))
return -1;
rule->action = HTTP_RES_ACT_CUSTOM_CONT;
rule->action_ptr = hlua_http_res_act_wrapper;
return 1;
}
static int hlua_read_timeout(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err, unsigned int *timeout)
{
const char *error;
error = parse_time_err(args[1], timeout, TIME_UNIT_MS);
if (error && *error != '\0') {
memprintf(err, "%s: invalid timeout", args[0]);
return -1;
}
return 0;
}
static int hlua_session_timeout(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
return hlua_read_timeout(args, section_type, curpx, defpx,
file, line, err, &hlua_timeout_session);
}
static int hlua_task_timeout(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
return hlua_read_timeout(args, section_type, curpx, defpx,
file, line, err, &hlua_timeout_task);
}
static int hlua_forced_yield(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
char *error;
hlua_nb_instruction = strtoll(args[1], &error, 10);
if (*error != '\0') {
memprintf(err, "%s: invalid number", args[0]);
return -1;
}
return 0;
}
/* This function is called by the main configuration key "lua-load". It loads and
* execute an lua file during the parsing of the HAProxy configuration file. It is
* the main lua entry point.
*
* This funtion runs with the HAProxy keywords API. It returns -1 if an error is
* occured, otherwise it returns 0.
*
* In some error case, LUA set an error message in top of the stack. This function
* returns this error message in the HAProxy logs and pop it from the stack.
*/
static int hlua_load(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, const char *file, int line,
char **err)
{
int error;
/* Just load and compile the file. */
error = luaL_loadfile(gL.T, args[1]);
if (error) {
memprintf(err, "error in lua file '%s': %s", args[1], lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
}
/* If no syntax error where detected, execute the code. */
error = lua_pcall(gL.T, 0, LUA_MULTRET, 0);
switch (error) {
case LUA_OK:
break;
case LUA_ERRRUN:
memprintf(err, "lua runtime error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
case LUA_ERRMEM:
memprintf(err, "lua out of memory error\n");
return -1;
case LUA_ERRERR:
memprintf(err, "lua message handler error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
case LUA_ERRGCMM:
memprintf(err, "lua garbage collector error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
default:
memprintf(err, "lua unknonwn error: %s\n", lua_tostring(gL.T, -1));
lua_pop(gL.T, 1);
return -1;
}
return 0;
}
/* configuration keywords declaration */
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "lua-load", hlua_load },
{ CFG_GLOBAL, "tune.lua.session-timeout", hlua_session_timeout },
{ CFG_GLOBAL, "tune.lua.task-timeout", hlua_task_timeout },
{ CFG_GLOBAL, "tune.lua.forced-yield", hlua_forced_yield },
{ 0, NULL, NULL },
}};
static struct http_req_action_kw_list http_req_kws = {"lua", { }, {
{ "lua", http_req_action_register_lua },
{ NULL, NULL }
}};
static struct http_res_action_kw_list http_res_kws = {"lua", { }, {
{ "lua", http_res_action_register_lua },
{ NULL, NULL }
}};
static struct tcp_action_kw_list tcp_req_cont_kws = {"lua", { }, {
{ "lua", tcp_req_action_register_lua },
{ NULL, NULL }
}};
static struct tcp_action_kw_list tcp_res_cont_kws = {"lua", { }, {
{ "lua", tcp_res_action_register_lua },
{ NULL, NULL }
}};
int hlua_post_init()
{
struct hlua_init_function *init;
const char *msg;
enum hlua_exec ret;
list_for_each_entry(init, &hlua_init_functions, l) {
lua_rawgeti(gL.T, LUA_REGISTRYINDEX, init->function_ref);
ret = hlua_ctx_resume(&gL, 0);
switch (ret) {
case HLUA_E_OK:
lua_pop(gL.T, -1);
return 1;
case HLUA_E_AGAIN:
Alert("lua init: yield not allowed.\n");
return 0;
case HLUA_E_ERRMSG:
msg = lua_tostring(gL.T, -1);
Alert("lua init: %s.\n", msg);
return 0;
case HLUA_E_ERR:
default:
Alert("lua init: unknown runtime error.\n");
return 0;
}
}
return 1;
}
void hlua_init(void)
{
int i;
int idx;
struct sample_fetch *sf;
struct hlua_sample_fetch *hsf;
char *p;
#ifdef USE_OPENSSL
char *args[4];
struct srv_kw *kw;
int tmp_error;
char *error;
#endif
/* Initialise com signals pool session. */
pool2_hlua_com = create_pool("hlua_com", sizeof(struct hlua_com), MEM_F_SHARED);
/* Initialise sleep pool. */
pool2_hlua_sleep = create_pool("hlua_sleep", sizeof(struct hlua_sleep), MEM_F_SHARED);
/* Register configuration keywords. */
cfg_register_keywords(&cfg_kws);
/* Register custom HTTP rules. */
http_req_keywords_register(&http_req_kws);
http_res_keywords_register(&http_res_kws);
tcp_req_cont_keywords_register(&tcp_req_cont_kws);
tcp_res_cont_keywords_register(&tcp_res_cont_kws);
/* Init main lua stack. */
gL.Mref = LUA_REFNIL;
gL.flags = 0;
LIST_INIT(&gL.com);
gL.T = luaL_newstate();
hlua_sethlua(&gL);
gL.Tref = LUA_REFNIL;
gL.task = NULL;
/* Initialise lua. */
luaL_openlibs(gL.T);
/*
*
* Create "core" object.
*
*/
/* This integer entry is just used as base value for the object "core". */
lua_pushinteger(gL.T, 0);
/* Create and fill the metatable. */
lua_newtable(gL.T);
/* Create and fill the __index entry. */
lua_pushstring(gL.T, "__index");
lua_newtable(gL.T);
/* Push the loglevel constants. */
for (i = 0; i < NB_LOG_LEVELS; i++)
hlua_class_const_int(gL.T, log_levels[i], i);
/* Register special functions. */
hlua_class_function(gL.T, "register_init", hlua_register_init);
hlua_class_function(gL.T, "register_task", hlua_register_task);
hlua_class_function(gL.T, "register_fetches", hlua_register_fetches);
hlua_class_function(gL.T, "register_converters", hlua_register_converters);
hlua_class_function(gL.T, "yield", hlua_yield);
hlua_class_function(gL.T, "set_nice", hlua_setnice);
hlua_class_function(gL.T, "sleep", hlua_sleep);
hlua_class_function(gL.T, "msleep", hlua_msleep);
hlua_class_function(gL.T, "add_acl", hlua_add_acl);
hlua_class_function(gL.T, "del_acl", hlua_del_acl);
hlua_class_function(gL.T, "set_map", hlua_set_map);
hlua_class_function(gL.T, "del_map", hlua_del_map);
hlua_class_function(gL.T, "tcp", hlua_socket_new);
/* Store the table __index in the metable. */
lua_settable(gL.T, -3);
/* Register previous table in the registry with named entry. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_CORE); /* register class session. */
/* Register previous table in the registry with reference. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
class_core_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
/* Create new object with class Core. */
lua_setmetatable(gL.T, -2);
lua_setglobal(gL.T, "core");
/*
*
* Register class Channel
*
*/
/* Create and fill the metatable. */
lua_newtable(gL.T);
/* Create and fille the __index entry. */
lua_pushstring(gL.T, "__index");
lua_newtable(gL.T);
/* Register . */
hlua_class_function(gL.T, "get", hlua_channel_get);
hlua_class_function(gL.T, "dup", hlua_channel_dup);
hlua_class_function(gL.T, "getline", hlua_channel_getline);
hlua_class_function(gL.T, "set", hlua_channel_set);
hlua_class_function(gL.T, "append", hlua_channel_append);
hlua_class_function(gL.T, "send", hlua_channel_send);
hlua_class_function(gL.T, "forward", hlua_channel_forward);
hlua_class_function(gL.T, "get_in_len", hlua_channel_get_in_len);
hlua_class_function(gL.T, "get_out_len", hlua_channel_get_out_len);
lua_settable(gL.T, -3);
/* Register previous table in the registry with reference and named entry. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_CHANNEL); /* register class session. */
class_channel_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
/*
*
* Register class TXN
*
*/
/* Create and fill the metatable. */
lua_newtable(gL.T);
/* Create and fille the __index entry. */
lua_pushstring(gL.T, "__index");
lua_newtable(gL.T);
/* Browse existing fetches and create the associated
* object method.
*/
sf = NULL;
while ((sf = sample_fetch_getnext(sf, &idx)) != NULL) {
/* Dont register the keywork if the arguments check function are
* not safe during the runtime.
*/
if ((sf->val_args != NULL) &&
(sf->val_args != val_payload_lv) &&
(sf->val_args != val_hdr))
continue;
/* gL.Tua doesn't support '.' and '-' in the function names, replace it
* by an underscore.
*/
strncpy(trash.str, sf->kw, trash.size);
trash.str[trash.size - 1] = '\0';
for (p = trash.str; *p; p++)
if (*p == '.' || *p == '-' || *p == '+')
*p = '_';
/* Register the function. */
lua_pushstring(gL.T, trash.str);
hsf = lua_newuserdata(gL.T, sizeof(struct hlua_sample_fetch));
hsf->f = sf;
lua_pushcclosure(gL.T, hlua_run_sample_fetch, 1);
lua_settable(gL.T, -3);
}
/* Register Lua functions. */
hlua_class_function(gL.T, "get_headers", hlua_session_getheaders);
hlua_class_function(gL.T, "set_priv", hlua_setpriv);
hlua_class_function(gL.T, "get_priv", hlua_getpriv);
hlua_class_function(gL.T, "req_channel", hlua_txn_req_channel);
hlua_class_function(gL.T, "res_channel", hlua_txn_res_channel);
hlua_class_function(gL.T, "close", hlua_txn_close);
lua_settable(gL.T, -3);
/* Register previous table in the registry with reference and named entry. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_TXN); /* register class session. */
class_txn_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
/*
*
* Register class Socket
*
*/
/* Create and fill the metatable. */
lua_newtable(gL.T);
/* Create and fille the __index entry. */
lua_pushstring(gL.T, "__index");
lua_newtable(gL.T);
#ifdef USE_OPENSSL
hlua_class_function(gL.T, "connect_ssl", hlua_socket_connect_ssl);
#endif
hlua_class_function(gL.T, "connect", hlua_socket_connect);
hlua_class_function(gL.T, "send", hlua_socket_send);
hlua_class_function(gL.T, "receive", hlua_socket_receive);
hlua_class_function(gL.T, "close", hlua_socket_close);
hlua_class_function(gL.T, "getpeername", hlua_socket_getpeername);
hlua_class_function(gL.T, "getsockname", hlua_socket_getsockname);
hlua_class_function(gL.T, "setoption", hlua_socket_setoption);
hlua_class_function(gL.T, "settimeout", hlua_socket_settimeout);
lua_settable(gL.T, -3); /* Push the last 2 entries in the table at index -3 */
/* Register the garbage collector entry. */
lua_pushstring(gL.T, "__gc");
lua_pushcclosure(gL.T, hlua_socket_gc, 0);
lua_settable(gL.T, -3); /* Push the last 2 entries in the table at index -3 */
/* Register previous table in the registry with reference and named entry. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_SOCKET); /* register class socket. */
class_socket_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class socket. */
/* Proxy and server configuration initialisation. */
memset(&socket_proxy, 0, sizeof(socket_proxy));
init_new_proxy(&socket_proxy);
socket_proxy.parent = NULL;
socket_proxy.last_change = now.tv_sec;
socket_proxy.id = "LUA-SOCKET";
socket_proxy.cap = PR_CAP_FE | PR_CAP_BE;
socket_proxy.maxconn = 0;
socket_proxy.accept = NULL;
socket_proxy.options2 |= PR_O2_INDEPSTR;
socket_proxy.srv = NULL;
socket_proxy.conn_retries = 0;
socket_proxy.timeout.connect = 5000; /* By default the timeout connection is 5s. */
/* Init TCP server: unchanged parameters */
memset(&socket_tcp, 0, sizeof(socket_tcp));
socket_tcp.next = NULL;
socket_tcp.proxy = &socket_proxy;
socket_tcp.obj_type = OBJ_TYPE_SERVER;
LIST_INIT(&socket_tcp.actconns);
LIST_INIT(&socket_tcp.pendconns);
socket_tcp.state = SRV_ST_RUNNING; /* early server setup */
socket_tcp.last_change = 0;
socket_tcp.id = "LUA-TCP-CONN";
socket_tcp.check.state &= ~CHK_ST_ENABLED; /* Disable health checks. */
socket_tcp.agent.state &= ~CHK_ST_ENABLED; /* Disable health checks. */
socket_tcp.pp_opts = 0; /* Remove proxy protocol. */
/* XXX: Copy default parameter from default server,
* but the default server is not initialized.
*/
socket_tcp.maxqueue = socket_proxy.defsrv.maxqueue;
socket_tcp.minconn = socket_proxy.defsrv.minconn;
socket_tcp.maxconn = socket_proxy.defsrv.maxconn;
socket_tcp.slowstart = socket_proxy.defsrv.slowstart;
socket_tcp.onerror = socket_proxy.defsrv.onerror;
socket_tcp.onmarkeddown = socket_proxy.defsrv.onmarkeddown;
socket_tcp.onmarkedup = socket_proxy.defsrv.onmarkedup;
socket_tcp.consecutive_errors_limit = socket_proxy.defsrv.consecutive_errors_limit;
socket_tcp.uweight = socket_proxy.defsrv.iweight;
socket_tcp.iweight = socket_proxy.defsrv.iweight;
socket_tcp.check.status = HCHK_STATUS_INI;
socket_tcp.check.rise = socket_proxy.defsrv.check.rise;
socket_tcp.check.fall = socket_proxy.defsrv.check.fall;
socket_tcp.check.health = socket_tcp.check.rise; /* socket, but will fall down at first failure */
socket_tcp.check.server = &socket_tcp;
socket_tcp.agent.status = HCHK_STATUS_INI;
socket_tcp.agent.rise = socket_proxy.defsrv.agent.rise;
socket_tcp.agent.fall = socket_proxy.defsrv.agent.fall;
socket_tcp.agent.health = socket_tcp.agent.rise; /* socket, but will fall down at first failure */
socket_tcp.agent.server = &socket_tcp;
socket_tcp.xprt = &raw_sock;
#ifdef USE_OPENSSL
/* Init TCP server: unchanged parameters */
memset(&socket_ssl, 0, sizeof(socket_ssl));
socket_ssl.next = NULL;
socket_ssl.proxy = &socket_proxy;
socket_ssl.obj_type = OBJ_TYPE_SERVER;
LIST_INIT(&socket_ssl.actconns);
LIST_INIT(&socket_ssl.pendconns);
socket_ssl.state = SRV_ST_RUNNING; /* early server setup */
socket_ssl.last_change = 0;
socket_ssl.id = "LUA-SSL-CONN";
socket_ssl.check.state &= ~CHK_ST_ENABLED; /* Disable health checks. */
socket_ssl.agent.state &= ~CHK_ST_ENABLED; /* Disable health checks. */
socket_ssl.pp_opts = 0; /* Remove proxy protocol. */
/* XXX: Copy default parameter from default server,
* but the default server is not initialized.
*/
socket_ssl.maxqueue = socket_proxy.defsrv.maxqueue;
socket_ssl.minconn = socket_proxy.defsrv.minconn;
socket_ssl.maxconn = socket_proxy.defsrv.maxconn;
socket_ssl.slowstart = socket_proxy.defsrv.slowstart;
socket_ssl.onerror = socket_proxy.defsrv.onerror;
socket_ssl.onmarkeddown = socket_proxy.defsrv.onmarkeddown;
socket_ssl.onmarkedup = socket_proxy.defsrv.onmarkedup;
socket_ssl.consecutive_errors_limit = socket_proxy.defsrv.consecutive_errors_limit;
socket_ssl.uweight = socket_proxy.defsrv.iweight;
socket_ssl.iweight = socket_proxy.defsrv.iweight;
socket_ssl.check.status = HCHK_STATUS_INI;
socket_ssl.check.rise = socket_proxy.defsrv.check.rise;
socket_ssl.check.fall = socket_proxy.defsrv.check.fall;
socket_ssl.check.health = socket_ssl.check.rise; /* socket, but will fall down at first failure */
socket_ssl.check.server = &socket_ssl;
socket_ssl.agent.status = HCHK_STATUS_INI;
socket_ssl.agent.rise = socket_proxy.defsrv.agent.rise;
socket_ssl.agent.fall = socket_proxy.defsrv.agent.fall;
socket_ssl.agent.health = socket_ssl.agent.rise; /* socket, but will fall down at first failure */
socket_ssl.agent.server = &socket_ssl;
socket_ssl.xprt = &raw_sock;
args[0] = "ssl";
args[1] = "verify";
args[2] = "none";
args[3] = NULL;
for (idx = 0; idx < 3; idx++) {
if ((kw = srv_find_kw(args[idx])) != NULL) { /* Maybe it's registered server keyword */
/*
*
* If the keyword is not known, we can search in the registered
* server keywords. This is usefull to configure special SSL
* features like client certificates and ssl_verify.
*
*/
tmp_error = kw->parse(args, &idx, &socket_proxy, &socket_ssl, &error);
if (tmp_error != 0) {
fprintf(stderr, "INTERNAL ERROR: %s\n", error);
abort(); /* This must be never arrives because the command line
not editable by the user. */
}
idx += kw->skip;
}
}
/* Initialize SSL server. */
if (socket_ssl.xprt == &ssl_sock) {
socket_ssl.use_ssl = 1;
ssl_sock_prepare_srv_ctx(&socket_ssl, &socket_proxy);
}
#endif
}