/*
 * Lua unsafe core engine
 *
 * Copyright 2015-2016 Thierry Fournier <tfournier@arpalert.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 */

#define _GNU_SOURCE

#include <ctype.h>
#include <setjmp.h>

#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>

#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 503
#error "Requires Lua 5.3 or later."
#endif

/* LUA_GNAME was introduced in Lua 5.4 */
#ifndef LUA_GNAME
#define LUA_GNAME "_G"
#endif

#include <import/ebpttree.h>

#include <haproxy/api.h>
#include <haproxy/applet.h>
#include <haproxy/arg.h>
#include <haproxy/auth.h>
#include <haproxy/cfgparse.h>
#include <haproxy/channel.h>
#include <haproxy/cli.h>
#include <haproxy/clock.h>
#include <haproxy/connection.h>
#include <haproxy/filters.h>
#include <haproxy/h1.h>
#include <haproxy/hlua.h>
#include <haproxy/hlua_fcn.h>
#include <haproxy/http_ana.h>
#include <haproxy/http_client.h>
#include <haproxy/http_fetch.h>
#include <haproxy/http_htx.h>
#include <haproxy/http_rules.h>
#include <haproxy/log.h>
#include <haproxy/map.h>
#include <haproxy/obj_type.h>
#include <haproxy/pattern.h>
#include <haproxy/payload.h>
#include <haproxy/protocol.h>
#include <haproxy/proxy.h>
#include <haproxy/regex.h>
#include <haproxy/sample.h>
#include <haproxy/sc_strm.h>
#include <haproxy/server.h>
#include <haproxy/session.h>
#include <haproxy/ssl_ckch.h>
#include <haproxy/ssl_sock.h>
#include <haproxy/stconn.h>
#include <haproxy/stream.h>
#include <haproxy/task.h>
#include <haproxy/tcp_rules.h>
#include <haproxy/thread.h>
#include <haproxy/tools.h>
#include <haproxy/vars.h>
#include <haproxy/xref.h>
#include <haproxy/event_hdl.h>
#include <haproxy/check.h>
#include <haproxy/mailers.h>

/* Global LUA flags */

enum hlua_log_opt {
	/* tune.lua.log.loggers */
	HLUA_LOG_LOGGERS_ON      = 0x00000001, /* forward logs to current loggers */

	/* tune.lua.log.stderr */
	HLUA_LOG_STDERR_ON       = 0x00000010, /* forward logs to stderr */
	HLUA_LOG_STDERR_AUTO     = 0x00000020, /* forward logs to stderr if no loggers */
	HLUA_LOG_STDERR_MASK     = 0x00000030,
};
/* default log options, made of flags in hlua_log_opt */
static uint hlua_log_opts = HLUA_LOG_LOGGERS_ON | HLUA_LOG_STDERR_AUTO;

/* set to 1 once some lua was loaded already */
static uint8_t hlua_loaded = 0;
/* set to 1 during body evaluation (very early lua file loading), then 0 */
static uint8_t hlua_body = 1;

#define HLUA_BOOL_SAMPLE_CONVERSION_UNK     0x0
#define HLUA_BOOL_SAMPLE_CONVERSION_NORMAL  0x1
#define HLUA_BOOL_SAMPLE_CONVERSION_BUG     0X2

/* used to know how to deal with smp2lua bool handling, option implicitly
 * defaults to historical behavior (BUG), but it has to be set explicitly
 * to avoid ambiguity, else a warning will be emitted
 *
 * FIXME: make it default to NORMAL in 3.3??
 */
static uint8_t hlua_bool_sample_conversion = HLUA_BOOL_SAMPLE_CONVERSION_UNK;

/* 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) do { func; my_unreachable(); } while(0)
#define MAY_LJMP(func) func

/* This couple of function executes securely some Lua calls outside of
 * the lua runtime environment. Each Lua call can return a longjmp
 * if it encounter a memory error.
 *
 * Lua documentation extract:
 *
 *   If an error happens outside any protected environment, Lua calls
 *   a panic function (see lua_atpanic) and then calls abort, thus
 *   exiting the host application. Your panic function can avoid this
 *   exit by never returning (e.g., doing a long jump to your own
 *   recovery point outside Lua).
 *
 *   The panic function runs as if it were a message handler (see
 *   #2.3); in particular, the error message is at the top of the
 *   stack. However, there is no guarantee about stack space. To push
 *   anything on the stack, the panic function must first check the
 *   available space (see #4.2).
 *
 * We must check all the Lua entry point. This includes:
 *  - The include/proto/hlua.h exported functions
 *  - the task wrapper function
 *  - The action wrapper function
 *  - The converters wrapper function
 *  - The sample-fetch wrapper functions
 *
 * It is tolerated that the initialisation function returns an abort.
 * Before each Lua abort, an error message is written on stderr.
 *
 * The macro SET_SAFE_LJMP initialise the longjmp. The Macro
 * RESET_SAFE_LJMP reset the longjmp. These function must be macro
 * because they must be exists in the program stack when the longjmp
 * is called.
 *
 * Note that the Lua processing is not really thread safe. It provides
 * heavy system which consists to add our own lock function in the Lua
 * code and recompile the library. This system will probably not accepted
 * by maintainers of various distribs.
 *
 * Our main execution point of the Lua is the function lua_resume(). A
 * quick looking on the Lua sources displays a lua_lock() a the start
 * of function and a lua_unlock() at the end of the function. So I
 * conclude that the Lua thread safe mode just perform a mutex around
 * all execution. So I prefer to do this in the HAProxy code, it will be
 * easier for distro maintainers.
 *
 * Note that the HAProxy lua functions rounded by the macro SET_SAFE_LJMP
 * and RESET_SAFE_LJMP manipulates the Lua stack, so it will be careful
 * to set mutex around these functions.
 */
__decl_spinlock(hlua_global_lock);
THREAD_LOCAL jmp_buf safe_ljmp_env;
static int hlua_panic_safe(lua_State *L) { return 0; }
static int hlua_panic_ljmp(lua_State *L) { WILL_LJMP(longjmp(safe_ljmp_env, 1)); return 0; }

/* This is the chained list of struct hlua_function referenced
 * for haproxy action, sample-fetches, converters, cli and
 * applet bindings. It is used for a post-initialisation control.
 */
static struct list referenced_functions = LIST_HEAD_INIT(referenced_functions);

/* This variable is used only during initialization to identify the Lua state
 * currently being initialized. 0 is the common lua state, 1 to n are the Lua
 * states dedicated to each thread (in this case hlua_state_id==tid+1).
 */
static int hlua_state_id;

/* This is a NULL-terminated list of lua file which are referenced to load per thread */
static char ***per_thread_load = NULL;

lua_State *hlua_init_state(int thread_id);

/* This function takes the Lua global lock. Keep this function's visibility
 * global so that it can appear in stack dumps and performance profiles!
 */
static inline void lua_take_global_lock()
{
	HA_SPIN_LOCK(LUA_LOCK, &hlua_global_lock);
}

static inline void lua_drop_global_lock()
{
	HA_SPIN_UNLOCK(LUA_LOCK, &hlua_global_lock);
}

/* lua lock helpers: only lock when required
 *
 * state_id == 0: we're operating on the main lua stack (shared between
 * os threads), so we need to acquire the main lock
 *
 * If the thread already owns the lock (_hlua_locked != 0), skip the lock
 * attempt. This could happen if we run under protected lua environment.
 * Not doing this could result in deadlocks because of nested locking
 * attempts from the same thread
 */
static THREAD_LOCAL int _hlua_locked = 0;
static inline void hlua_lock(struct hlua *hlua)
{
	if (hlua->state_id != 0)
		return;
	if (!_hlua_locked)
		lua_take_global_lock();
	_hlua_locked += 1;
}
static inline void hlua_unlock(struct hlua *hlua)
{
	if (hlua->state_id != 0)
		return;
	BUG_ON(_hlua_locked <= 0);
	_hlua_locked--;
	/* drop the lock once the lock count reaches 0 */
	if (!_hlua_locked)
		lua_drop_global_lock();
}

/* below is an helper function to retrieve string on on Lua stack at <index>
 * in a safe way (function may not LJMP). It can be useful to retrieve errors
 * at the top of the stack from an unprotected environment.
 *
 * The returned string will is only valid as long as the value at <index> is
 * not removed from the stack.
 *
 * It is assumed that the calling function is allowed to manipulate <L>
 */
__LJMP static int _hlua_tostring_safe(lua_State *L)
{
	const char **str = lua_touserdata(L, 1);
	const char *cur_str = MAY_LJMP(lua_tostring(L, 2));

	if (cur_str)
		*str = cur_str;
	return 0;
}
static const char *hlua_tostring_safe(lua_State *L, int index)
{
	const char *str = NULL;

	if (!lua_checkstack(L, 4))
		return NULL;

	/* before any stack modification, save the targeted value on the top of
	 * the stack: this will allow us to use relative index to target it.
	 */
	lua_pushvalue(L, index);

	/* push our custom _hlua_tostring_safe() function on the stack, then push
	 * our own string pointer and targeted value (at <index>) as argument
	 */
	lua_pushcfunction(L, _hlua_tostring_safe);
	lua_pushlightuserdata(L, &str); // 1st func argument = string pointer
	lua_pushvalue(L, -3);           // 2nd func argument = targeted value

	lua_remove(L, -4); // remove <index> copy as we're done using it

	/* call our custom function with proper arguments using pcall() to catch
	 * exceptions (if any)
	 */
	switch (lua_pcall(L, 2, 0, 0)) {
		case LUA_OK:
			break;
		default:
			/* error was caught */
			lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
			return NULL;
	}
	return str;
}

/* below is an helper function similar to lua_pushvfstring() to push a
 * formatted string on Lua stack but in a safe way (function may not LJMP).
 * It can be useful to push allocated strings (ie: error messages) on the
 * stack and ensure proper cleanup.
 *
 * Returns a pointer to the internal copy of the string on success and NULL
 * on error.
 *
 * It is assumed that the calling function is allowed to manipulate <L>
 */
__LJMP static int _hlua_pushvfstring_safe(lua_State *L)
{
	const char **dst = lua_touserdata(L, 1);
	const char *fmt = lua_touserdata(L, 2);
	va_list *argp = lua_touserdata(L, 3);

	*dst = lua_pushvfstring(L, fmt, *argp);
	return 1;
}
static const char *hlua_pushvfstring_safe(lua_State *L, const char *fmt, va_list argp)
{
	const char *dst = NULL;
	va_list cpy_argp; /* required if argp is implemented as array type */

	if (!lua_checkstack(L, 4))
		return NULL;

	va_copy(cpy_argp, argp);

	/* push our custom _hlua_pushvfstring_safe() function on the stack, then
	 * push our destination string pointer, fmt and arg list
	 */
	lua_pushcfunction(L, _hlua_pushvfstring_safe);
	lua_pushlightuserdata(L, &dst);        // 1st func argument = dst string pointer
	lua_pushlightuserdata(L, (void *)fmt); // 2nd func argument = fmt
	lua_pushlightuserdata(L, &cpy_argp);   // 3rd func argument = arg list

	/* call our custom function with proper arguments using pcall() to catch
	 * exceptions (if any)
	 */
	switch (lua_pcall(L, 3, 1, 0)) {
		case LUA_OK:
			break;
		default:
			/* error was caught */
			lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
			dst = NULL;
	}
	va_end(cpy_argp);

	return dst;
}

static const char *hlua_pushfstring_safe(lua_State *L, const char *fmt, ...)
{
	va_list argp;
	const char *dst;

	va_start(argp, fmt);
	dst = hlua_pushvfstring_safe(L, fmt, argp);
	va_end(argp);

	return dst;
}

#define SET_SAFE_LJMP_L(__L, __HLUA) \
	({ \
		int ret; \
		hlua_lock(__HLUA); \
		if (setjmp(safe_ljmp_env) != 0) { \
			lua_atpanic(__L, hlua_panic_safe); \
			ret = 0; \
			hlua_unlock(__HLUA); \
		} else { \
			lua_atpanic(__L, hlua_panic_ljmp); \
			ret = 1; \
		} \
		ret; \
	})

/* If we are the last function catching Lua errors, we
 * must reset the panic function.
 */
#define RESET_SAFE_LJMP_L(__L, __HLUA) \
	do { \
		lua_atpanic(__L, hlua_panic_safe); \
		hlua_unlock(__HLUA); \
	} while(0)

#define SET_SAFE_LJMP(__HLUA) \
	SET_SAFE_LJMP_L((__HLUA)->T, __HLUA)

#define RESET_SAFE_LJMP(__HLUA) \
	RESET_SAFE_LJMP_L((__HLUA)->T, __HLUA)

#define SET_SAFE_LJMP_PARENT(__HLUA) \
	SET_SAFE_LJMP_L(hlua_states[(__HLUA)->state_id], __HLUA)

#define RESET_SAFE_LJMP_PARENT(__HLUA) \
	RESET_SAFE_LJMP_L(hlua_states[(__HLUA)->state_id], __HLUA)

/* Applet status flags */
#define APPLET_DONE     0x01 /* applet processing is done. */
#define APPLET_REQ_RECV 0x02 /* The request was fully received */
/* unused: 0x02 */
#define APPLET_HDR_SENT 0x04 /* Response header sent. */
/* unused: 0x08, 0x10 */
#define APPLET_HTTP11   0x20 /* Last chunk sent. */
#define APPLET_RSP_SENT 0x40 /* The response was fully sent */

/* The main Lua execution context. The 0 index is the
 * common state shared by all threads.
 */
static lua_State *hlua_states[MAX_THREADS + 1];

#define HLUA_FLT_CB_FINAL         0x00000001
#define HLUA_FLT_CB_RETVAL        0x00000002
#define HLUA_FLT_CB_ARG_CHN       0x00000004
#define HLUA_FLT_CB_ARG_HTTP_MSG  0x00000008

#define HLUA_FLT_CTX_FL_PAYLOAD  0x00000001

struct hlua_reg_filter  {
	char *name;
	int flt_ref[MAX_THREADS + 1];
	int fun_ref[MAX_THREADS + 1];
	struct list l;
};

struct hlua_flt_config {
	struct hlua_reg_filter *reg;
	int ref[MAX_THREADS + 1];
	char **args;
};

struct hlua_flt_ctx {
	struct hlua *_hlua;      /* main hlua context */
	int ref;                 /* ref to the filter lua object (in main hlua context) */
	struct hlua *hlua[2];    /* lua runtime context (0: request, 1: response) */
	unsigned int cur_off[2]; /* current offset (0: request, 1: response) */
	unsigned int cur_len[2]; /* current forwardable length (0: request, 1: response) */
	unsigned int flags;      /* HLUA_FLT_CTX_FL_* */
};

/* appctx context used by the cosockets */
struct hlua_csk_ctx {
	int connected;
	struct xref xref; /* cross reference with the Lua object owner. */
	struct mt_list wake_on_read;
	struct mt_list wake_on_write;
	struct appctx *appctx;
	struct server *srv;
	int timeout;
	int die;
};

/* appctx context used by TCP services */
struct hlua_tcp_ctx {
	struct hlua *hlua;
	int flags;
	struct task *task;
};

/* appctx context used by HTTP services */
struct hlua_http_ctx {
	struct hlua *hlua;
	int left_bytes;         /* The max amount of bytes that we can read. */
	int flags;
	int status;
	const char *reason;
	struct task *task;
};

/* used by registered CLI keywords */
struct hlua_cli_ctx {
	struct hlua *hlua;
	struct task *task;
	struct hlua_function *fcn;
};

DECLARE_STATIC_TYPED_POOL(pool_head_hlua_flt_ctx, "hlua_flt_ctx", struct hlua_flt_ctx);

static int hlua_filter_from_payload(struct filter *filter);

/* This is the chained list of struct hlua_flt referenced
 * for haproxy filters. It is used for a post-initialisation control.
 */
static struct list referenced_filters = LIST_HEAD_INIT(referenced_filters);


/* This is the memory pool containing struct lua for applets
 * (including cli).
 */
DECLARE_STATIC_TYPED_POOL(pool_head_hlua, "hlua", struct hlua);

/* 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[MAX_THREADS + 1];

/* 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_txn_ref;
static int class_socket_ref;
static int class_channel_ref;
static int class_fetches_ref;
static int class_converters_ref;
static int class_http_ref;
static int class_http_msg_ref;
static int class_httpclient_ref;
static int class_map_ref;
static int class_applet_tcp_ref;
static int class_applet_http_ref;
static int class_txn_reply_ref;

/* Lua max execution timeouts. By default, stream-related
 * lua coroutines (e.g.: actions) have a short timeout.
 * On the other hand tasks coroutines don't have a timeout because
 * a task may remain alive during all the haproxy execution.
 *
 * Timeouts are expressed in milliseconds, they are meant to be used
 * with hlua timer's API exclusively.
 * 0 means no timeout
 */
static uint32_t hlua_timeout_burst = 1000; /* burst timeout. */
static uint32_t hlua_timeout_session = 4000; /* session timeout. */
static uint32_t hlua_timeout_task = 0; /* task timeout. */
static uint32_t hlua_timeout_applet = 4000; /* applet timeout. */

/* tune.lua.openlibs: bitmask of optional Lua standard libraries to open.
 * The base and coroutine libraries are always loaded regardless of this
 * setting (base provides core functions required by HAProxy; coroutine is
 * always overridden by HAProxy's safe wrapper).
 */
#define HLUA_OPENLIBS_ALL  0xFF

static const struct {
	const char    *name;
	lua_CFunction  open;
	uint           flag;
} hlua_openlibs_tbl[] = {
	{ LUA_TABLIBNAME,  luaopen_table,   0x01 },
	{ LUA_IOLIBNAME,   luaopen_io,      0x02 },
	{ LUA_OSLIBNAME,   luaopen_os,      0x04 },
	{ LUA_STRLIBNAME,  luaopen_string,  0x08 },
	{ LUA_MATHLIBNAME, luaopen_math,    0x10 },
	{ LUA_UTF8LIBNAME, luaopen_utf8,    0x20 },
	{ LUA_LOADLIBNAME, luaopen_package, 0x40 },
	{ LUA_DBLIBNAME,   luaopen_debug,   0x80 },
	{ NULL, NULL, 0 }
};

static uint hlua_openlibs_flags = HLUA_OPENLIBS_ALL;

/* hlua multipurpose timer:
 *  used to compute burst lua time (within a single hlua_ctx_resume())
 *  and cumulative lua time for a given coroutine, and to check
 *  the lua coroutine against the configured timeouts
 */

/* fetch per-thread cpu_time with ms precision (may wrap) */
static inline uint32_t _hlua_time_ms()
{
	/* We're interested in the current cpu time in ms, which will be returned
	 * as a uint32_t to save some space.
	 * We must take the following into account:
	 *
	 * - now_cpu_time_fast() which returns the time in nanoseconds as a uint64_t
	 *   will wrap every 585 years.
	 * - uint32_t may only contain 4294967295ms (~=49.7 days), so _hlua_time_ms()
	 *   itself will also wrap every 49.7 days.
	 *
	 * While we can safely ignore the now_cpu_time_fast() wrap, we must
	 * take care of the uint32_t wrap by making sure to exclusively
	 * manipulate the time using uint32_t everywhere _hlua_time_ms()
	 * is involved.
	 */
	return (uint32_t)(now_cpu_time_fast() / 1000000ULL);
}

/* computes time spent in a single lua execution (in ms) */
static inline uint32_t _hlua_time_burst(const struct hlua_timer *timer)
{
	uint32_t burst_ms;

	/* wrapping is expected and properly
	 * handled thanks to _hlua_time_ms() and burst_ms
	 * being of the same type
	 */
	burst_ms = _hlua_time_ms() - timer->start;
	return burst_ms;
}

static inline void hlua_timer_init(struct hlua_timer *timer, unsigned int max)
{
	timer->cumulative = 0;
	timer->burst = 0;
	timer->max = max;
}

/* reset the timer ctx between 2 yields */
static inline void hlua_timer_reset(struct hlua_timer *timer)
{
	timer->cumulative += timer->burst;
	timer->burst = 0;
}

/* start the timer right before a new execution */
static inline void hlua_timer_start(struct hlua_timer *timer)
{
	timer->start = _hlua_time_ms();
}

/* update hlua timer when finishing an execution */
static inline void hlua_timer_stop(struct hlua_timer *timer)
{
	timer->burst += _hlua_time_burst(timer);
}

/* check the timers for current hlua context:
 * - first check for burst timeout (max execution time for the current
     hlua resume, ie: time between effective yields)
 * - then check for yield cumulative timeout
 *
 * Returns 1 if the check succeeded, 0 if it failed because cumulative
 * timeout is exceeded, and -1 if it failed because burst timeout is
 * exceeded.
 */
static inline int hlua_timer_check(const struct hlua_timer *timer)
{
	uint32_t pburst = _hlua_time_burst(timer); /* pending burst time in ms */

	if (hlua_timeout_burst && (timer->burst + pburst) > hlua_timeout_burst)
		return -1; /* burst timeout exceeded */
	if (timer->max && (timer->cumulative + timer->burst + pburst) > timer->max)
		return 0; /* cumulative timeout exceeded */
	return 1; /* ok */
}

/* Interrupts the Lua processing each "hlua_nb_instruction" instructions.
 * it is used for preventing infinite loops.
 */
static unsigned int hlua_nb_instruction = 0;

/* Wrapper to retrieve the number of instructions between two interrupts
 * depending on user settings and current hlua context. If not already
 * explicitly set, we compute the ideal value using hard limits revealed
 * by Thierry Fournier's work, whose original notes may be found below:
 *
 * --
 * 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 millions
 *  forced yields |
 * ---------------+---------------
 *  10            | 160
 *  500           | 670
 *  1000          | 680
 *  5000          | 700
 *  7000          | 700
 *  8000          | 700
 *  9000          | 710 <- ceil
 *  10000         | 710
 *  100000        | 710
 *  1000000       | 710
 * --
 *
 * Thanks to his work, we know we can safely use values between 500 and 10000
 * without a significant impact on performance.
 */
static inline unsigned int hlua_get_nb_instruction(struct hlua *hlua)
{
	int ceil = 10000; /* above 10k, no significant performance gain */
	int floor = 500;  /* below 500, significant performance loss */

	if (hlua_nb_instruction) {
		/* value enforced by user */
		return hlua_nb_instruction;
	}

	/* not set, assign automatic value */
	if (hlua->state_id == 0) {
		/* this function is expected to be called during runtime (after config
		 * parsing), thus global.nb_thread is expected to be set.
		 */
		BUG_ON(global.nbthread == 0);

		/* main lua stack (shared global lock), take number of threads into
		 * account in an attempt to reduce thread contention
		 */
		return MAX(floor, ceil / global.nbthread);
	}
	else {
		/* per-thread lua stack, less contention is expected (no global lock),
		 * allow up to the maximum number of instructions and hope that the
		 * user manually yields after heavy (lock dependent) work from lua
		 * script (e.g.: map manipulation).
		 */
		return ceil;
	}
}

/* Descriptor for the memory allocation state. The limit is pre-initialised to
 * 0 until it is replaced by "tune.lua.maxmem" during the config parsing, or it
 * is replaced with ~0 during post_init after everything was loaded. This way
 * it is guaranteed that if limit is ~0 the boot is complete and that if it's
 * zero it's not yet limited and proper accounting is required.
 */
struct hlua_mem_allocator {
	size_t allocated;
	size_t limit;
};

static struct hlua_mem_allocator hlua_global_allocator THREAD_ALIGNED();

/* hlua event subscription */
struct hlua_event_sub {
	int fcn_ref;
	int state_id;
	struct hlua *hlua;
	struct task *task;
	event_hdl_async_equeue equeue;
	struct event_hdl_sub *sub;
	uint8_t paused;
};

/* This is the memory pool containing struct hlua_event_sub
 * for event subscriptions from lua
 */
DECLARE_STATIC_TYPED_POOL(pool_head_hlua_event_sub, "hlua_esub", struct hlua_event_sub);

/* 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.
 */
__LJMP 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,
                                     uint64_t mask, struct proxy *p);
__LJMP static int hlua_smp2lua(lua_State *L, struct sample *smp);
__LJMP static int hlua_smp2lua_str(lua_State *L, struct sample *smp);
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp);

__LJMP static int hlua_http_get_headers(lua_State *L, struct buffer *buf);

struct prepend_path {
	struct list l;
	char *type;
	char *path;
};

static struct list prepend_path_list = LIST_HEAD_INIT(prepend_path_list);

#define SEND_ERR(__be, __fmt, __args...) \
	do { \
		send_log(__be, LOG_ERR, __fmt, ## __args); \
		if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) \
			ha_alert(__fmt, ## __args); \
	} while (0)

static inline struct hlua_function *new_hlua_function()
{
	struct hlua_function *fcn;
	int i;

	fcn = calloc(1, sizeof(*fcn));
	if (!fcn)
		return NULL;
	LIST_APPEND(&referenced_functions, &fcn->l);
	for (i = 0; i < MAX_THREADS + 1; i++)
		fcn->function_ref[i] = -1;
	return fcn;
}

static inline void release_hlua_function(struct hlua_function *fcn)
{
	if (!fcn)
		return;
	if (fcn->name)
		ha_free(&fcn->name);
	LIST_DELETE(&fcn->l);
	ha_free(&fcn);
}

/* If the common state is set, the stack id is 0, otherwise it is the tid + 1 */
static inline int fcn_ref_to_stack_id(struct hlua_function *fcn)
{
	if (fcn->function_ref[0] == -1)
		return tid + 1;
	return 0;
}

/* Create a new registered filter. Only its name is filled */
static inline struct hlua_reg_filter *new_hlua_reg_filter(const char *name)
{
	struct hlua_reg_filter *reg_flt;
	int i;

	reg_flt = calloc(1, sizeof(*reg_flt));
	if (!reg_flt)
		return NULL;
	reg_flt->name = strdup(name);
	if (!reg_flt->name) {
		free(reg_flt);
		return NULL;
	}
	LIST_APPEND(&referenced_filters, &reg_flt->l);
	for (i = 0; i < MAX_THREADS + 1; i++) {
		reg_flt->flt_ref[i] = -1;
		reg_flt->fun_ref[i] = -1;
	}
	return reg_flt;
}

/* Release a registered filter */
static inline void release_hlua_reg_filter(struct hlua_reg_filter *reg_flt)
{
	if (!reg_flt)
		return;
	if (reg_flt->name)
		ha_free(&reg_flt->name);
	LIST_DELETE(&reg_flt->l);
	ha_free(&reg_flt);
}

/* If the common state is set, the stack id is 0, otherwise it is the tid + 1 */
static inline int reg_flt_to_stack_id(struct hlua_reg_filter *reg_flt)
{
	if (reg_flt->fun_ref[0] == -1)
		return tid + 1;
	return 0;
}

/* 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 argument is not a "function".
 * When no longer used, the ref must be released with hlua_unref()
 */
__LJMP 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, argno));
		WILL_LJMP(luaL_argerror(L, argno, msg));
	}
	lua_pushvalue(L, argno);
	return luaL_ref(L, LUA_REGISTRYINDEX);
}

/* Used to check an Lua table type in the stack. It creates and
 * returns a reference of the table. This function throws an
 * error if the argument is not a "table".
 * When no longer used, the ref must be released with hlua_unref()
 */
__LJMP int hlua_checktable(lua_State *L, int argno)
{
	if (!lua_istable(L, argno)) {
		const char *msg = lua_pushfstring(L, "table expected, got %s", luaL_typename(L, argno));
		WILL_LJMP(luaL_argerror(L, argno, msg));
	}
	lua_pushvalue(L, argno);
	return luaL_ref(L, LUA_REGISTRYINDEX);
}

/* Get a reference to the object that is at the top of the stack
 * The referenced object will be popped from the stack
 *
 * The function returns the reference to the object which must
 * be cleared using hlua_unref() when no longer used
 */
__LJMP int hlua_ref(lua_State *L)
{
	return MAY_LJMP(luaL_ref(L, LUA_REGISTRYINDEX));
}

/* Pushes a reference previously created using luaL_ref(L, LUA_REGISTRYINDEX)
 * on <L> stack
 * (ie: hlua_checkfunction(), hlua_checktable() or hlua_ref())
 *
 * When the reference is no longer used, it should be released by calling
 * hlua_unref()
 *
 * <L> can be from any co-routine as long as it belongs to the same lua
 * parent state that the one used to get the reference.
 */
void hlua_pushref(lua_State *L, int ref)
{
	lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
}

/* Releases a reference previously created using luaL_ref(L, LUA_REGISTRYINDEX)
 * (ie: hlua_checkfunction(), hlua_checktable() or hlua_ref())
 *
 * This will allow the reference to be reused and the referred object
 * to be garbage collected.
 *
 * <L> can be from any co-routine as long as it belongs to the same lua
 * parent state that the one used to get the reference.
 */
void hlua_unref(lua_State *L, int ref)
{
	luaL_unref(L, LUA_REGISTRYINDEX, ref);
}

__LJMP static int _hlua_traceback(lua_State *L)
{
	lua_Debug *ar = lua_touserdata(L, 1);
	int ret;

	/* Fill fields:
	 * 'S': fills in the fields source, short_src, linedefined, lastlinedefined, and what;
	 * 'l': fills in the field currentline;
	 * 'n': fills in the field name and namewhat;
	 * 't': fills in the field istailcall;
	 */
	ret = lua_getinfo(L, "Slnt", ar);
	if (!ret)
		WILL_LJMP(luaL_error(L, "unexpected"));
	return 0;
}


/* This function cannot fail (output will simply be truncated upon errors) */
const char *hlua_traceback(lua_State *L, const char* sep)
{
	lua_Debug ar;
	int level = 0;
	struct buffer *msg = get_trash_chunk();

	while (lua_getstack(L, level++, &ar)) {
		if (!lua_checkstack(L, 2))
			goto end; // abort

		lua_pushcfunction(L, _hlua_traceback);
		lua_pushlightuserdata(L, &ar);

		/* safe getinfo */
		switch (lua_pcall(L, 1, 0, 0)) {
			case LUA_OK:
				break;
			default:
				lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
				goto end; // abort
		}

		/* skip these empty entries, usually they come from deep C functions */
		if (ar.currentline < 0 && *ar.what == 'C' && !*ar.namewhat && !ar.name)
			continue;

		/* Add separator */
		if (b_data(msg))
			chunk_appendf(msg, "%s", sep);

		/* Append code localisation */
		if (ar.currentline > 0)
			chunk_appendf(msg, "%s:%d: ", ar.short_src, ar.currentline);
		else
			chunk_appendf(msg, "%s: ", ar.short_src);

		/*
		 * Get function name
		 *
		 * if namewhat is no empty, name is defined.
		 * what contains "Lua" for Lua function, "C" for C function,
		 * or "main" for main code.
		 */
		if (*ar.namewhat != '\0' && ar.name != NULL)  /* is there a name from code? */
			chunk_appendf(msg, "in %s '%s'", ar.namewhat, ar.name);  /* use it */

		else if (*ar.what == 'm')  /* "main", the code is not executed in a function */
			chunk_appendf(msg, "in main chunk");

		else if (*ar.what != 'C')  /* for Lua functions, use <file:line> */
			chunk_appendf(msg, "in function line %d", ar.linedefined);

		else  /* nothing left... */
			chunk_appendf(msg, "?");


		/* Display tailed call */
		if (ar.istailcall)
			chunk_appendf(msg, " ...");
	}

 end:
	return msg->area;
}


/* 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 thrown.
 */
__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));
}

/* This function pushes an error string prefixed by the file name
 * and the line number where the error is encountered.
 *
 * It returns 1 on success and 0 on failure (function won't LJMP)
 */
__LJMP static int _hlua_pusherror(lua_State *L)
{
	const char *fmt = lua_touserdata(L, 1);
	va_list *argp = lua_touserdata(L, 2);

	luaL_where(L, 2);
	lua_pushvfstring(L, fmt, *argp);
	lua_concat(L, 2);

	return 1;
}
static int hlua_pusherror(lua_State *L, const char *fmt, ...)
{
	va_list argp;
	int ret = 1;

	if (!lua_checkstack(L, 3))
		return 0;

	va_start(argp, fmt);

	/* push our custom _hlua_pusherror() function on the stack, then
	 * push fmt and arg list
	 */
	lua_pushcfunction(L, _hlua_pusherror);
	lua_pushlightuserdata(L, (void *)fmt); // 1st func argument = fmt
	lua_pushlightuserdata(L, &argp);       // 2nd func argument = arg list

	/* call our custom function with proper arguments using pcall() to catch
	 * exceptions (if any)
	 */
	switch (lua_pcall(L, 2, 1, 0)) {
		case LUA_OK:
			break;
		default:
			lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
			ret = 0;
	}

	va_end(argp);

	return ret;
}

/* 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.
 */
__LJMP static int hlua_arg2lua(lua_State *L, const struct arg *arg)
{
	switch (arg->type) {
	case ARGT_SINT:
	case ARGT_TIME:
	case ARGT_SIZE:
		lua_pushinteger(L, arg->data.sint);
		break;

	case ARGT_STR:
		lua_pushlstring(L, arg->data.str.area, arg->data.str.data);
		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 entry 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 given to the
 * lua wrapper and converted as arg list by the function.
 *
 * Note: although lua_tolstring() may raise a memory error according to
 * lua documentation, in practise this could only happen when using to
 * use lua_tolstring() on a number (lua will try to push the number as a
 * string on the stack, and this may result in memory failure), so here we
 * assume that hlua_lua2arg() will never raise an exception since it is
 * exclusively used with lua string inputs.
 *
 * Note2: You should be extra careful when using <arg> argument, since
 * string arguments rely on lua_tolstring() which returns a pointer to lua
 * object that may be garbage collected at any time when removed from lua
 * stack, thus you should make sure that <arg> is only used from a local
 * scope within lua context (and not exported or stored in a lua-independent
 * ctx) and that related lua object still exists when accessing arg data.
 * See: https://www.lua.org/manual/5.4/manual.html#4.1.3
 */
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.area = (char *)lua_tolstring(L, ud, &arg->data.str.data);
		/* We don't know the actual size of the underlying allocation, so be conservative. */
		arg->data.str.size = arg->data.str.data+1; /* count the terminating null byte */
		arg->data.str.head = 0;
		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.sint = 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
 * fetches or converters.
 */
__LJMP static int hlua_smp2lua(lua_State *L, struct sample *smp)
{
	switch (smp->data.type) {
	case SMP_T_SINT:
		lua_pushinteger(L, smp->data.u.sint);
		break;
	case SMP_T_BOOL:
		if (hlua_bool_sample_conversion == HLUA_BOOL_SAMPLE_CONVERSION_NORMAL)
			lua_pushboolean(L, !!smp->data.u.sint);
		else
			lua_pushinteger(L, smp->data.u.sint);
		break;

	case SMP_T_BIN:
	case SMP_T_STR:
		lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
		break;

	case SMP_T_METH:
		switch (smp->data.u.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.u.meth.str.area, smp->data.u.meth.str.data);
			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. */
		if (sample_casts[smp->data.type][SMP_T_STR] &&
		    sample_casts[smp->data.type][SMP_T_STR](smp))
			lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
		else
			lua_pushnil(L);
		break;
	default:
		lua_pushnil(L);
		break;
	}
	return 1;
}

/* the following functions are used to convert a struct sample
 * in Lua strings. This is useful to convert the return of the
 * fetches or converters.
 */
__LJMP static int hlua_smp2lua_str(lua_State *L, struct sample *smp)
{
	switch (smp->data.type) {

	case SMP_T_BIN:
	case SMP_T_STR:
		lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
		break;

	case SMP_T_METH:
		switch (smp->data.u.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.u.meth.str.area, smp->data.u.meth.str.data);
			break;
		default:
			lua_pushstring(L, "");
			break;
		}
		break;

	case SMP_T_SINT:
	case SMP_T_BOOL:
	case SMP_T_IPV4:
	case SMP_T_IPV6:
	case SMP_T_ADDR: /* This type is never used to qualify a sample. */
		if (sample_casts[smp->data.type][SMP_T_STR] &&
		    sample_casts[smp->data.type][SMP_T_STR](smp))
			lua_pushlstring(L, smp->data.u.str.area, smp->data.u.str.data);
		else
			lua_pushstring(L, "");
		break;
	default:
		lua_pushstring(L, "");
		break;
	}
	return 1;
}

/* The following function is used to convert a Lua type to a
 * struct sample. This is useful to provide data from LUA code to
 * a converter.
 *
 * Note: although lua_tolstring() may raise a memory error according to
 * lua documentation, in practise this could only happen when using to
 * use lua_tolstring() on a number (lua will try to push the number as a
 * string on the stack, and this may result in memory failure), so here we
 * assume that hlua_lua2arg() will never raise an exception since it is
 * exclusively used with lua string inputs.
 *
 * Note2: You should be extra careful when using <smp> argument, since
 * string arguments rely on lua_tolstring() which returns a pointer to lua
 * object that may be garbage collected at any time when removed from lua
 * stack, thus you should make sure that <smp> is only used from a local
 * scope within lua context (not exported or stored in a lua-independent
 * ctx) and that related lua object still exists when accessing arg data.
 * See: https://www.lua.org/manual/5.4/manual.html#4.1.3
 *
 * If you don't comply with this usage restriction, then you should consider
 * duplicating the smp using smp_dup() to make it portable (little overhead),
 * as this will ensure that the smp always points to valid memory block.
 */
static int hlua_lua2smp(lua_State *L, int ud, struct sample *smp)
{
	switch (lua_type(L, ud)) {

	case LUA_TNUMBER:
		smp->data.type = SMP_T_SINT;
		smp->data.u.sint = lua_tointeger(L, ud);
		break;


	case LUA_TBOOLEAN:
		smp->data.type = SMP_T_BOOL;
		smp->data.u.sint = lua_toboolean(L, ud);
		break;

	case LUA_TSTRING:
		smp->data.type = SMP_T_STR;
		smp->flags |= SMP_F_CONST;
		smp->data.u.str.area = (char *)lua_tolstring(L, ud, &smp->data.u.str.data);
		/* We don't know the actual size of the underlying allocation, so be conservative. */
		smp->data.u.str.size = smp->data.u.str.data+1; /* count the terminating null byte */
		smp->data.u.str.head = 0;
		break;

	case LUA_TUSERDATA:
	case LUA_TNIL:
	case LUA_TTABLE:
	case LUA_TFUNCTION:
	case LUA_TTHREAD:
	case LUA_TLIGHTUSERDATA:
	case LUA_TNONE:
	default:
		smp->data.type = SMP_T_BOOL;
		smp->data.u.sint = 0;
		break;
	}
	return 1;
}

/* This function check the "argp" built by another conversion function
 * is in accord with the expected argp defined by the "mask". The function
 * returns true or false. It can be adjust the types if there compatibles.
 *
 * This function assumes that the argp argument contains ARGM_NBARGS + 1
 * entries and that there is at least one stop at the last position.
 */
__LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp,
                              uint64_t mask, struct proxy *p)
{
	int min_arg;
	int idx;
	struct proxy *px;
	struct userlist *ul;
	struct my_regex *reg;
	const char *msg = NULL;
	char *sname, *pname, *err = NULL;

	idx = 0;
	min_arg = ARGM(mask);
	mask >>= ARGM_BITS;

	while (1) {
		struct buffer tmp = BUF_NULL;

		/* Check for mandatory arguments. */
		if (argp[idx].type == ARGT_STOP) {
			if (idx < min_arg) {

				/* If miss other argument than the first one, we return an error. */
				if (idx > 0) {
					msg = "Mandatory argument expected";
					goto error;
				}

				/* If first argument have a certain type, some default values
				 * may be used. See the function smp_resolve_args().
				 */
				switch (mask & ARGT_MASK) {

				case ARGT_FE:
					if (!(p->cap & PR_CAP_FE)) {
						msg = "Mandatory argument expected";
						goto error;
					}
					argp[idx].data.prx = p;
					argp[idx].type = ARGT_FE;
					argp[idx+1].type = ARGT_STOP;
					break;

				case ARGT_BE:
					if (!(p->cap & PR_CAP_BE)) {
						msg = "Mandatory argument expected";
						goto error;
					}
					argp[idx].data.prx = p;
					argp[idx].type = ARGT_BE;
					argp[idx+1].type = ARGT_STOP;
					break;

				case ARGT_TAB:
					if (!p->table) {
						msg = "Mandatory argument expected";
						goto error;
					}
					argp[idx].data.t = p->table;
					argp[idx].type = ARGT_TAB;
					argp[idx+1].type = ARGT_STOP;
					break;

				default:
					msg = "Mandatory argument expected";
					goto error;
					break;
				}
			}
			break;
		}

		/* Check for exceed the number of required argument. */
		if ((mask & ARGT_MASK) == ARGT_STOP &&
		    argp[idx].type != ARGT_STOP) {
			msg = "Last argument expected";
			goto error;
		}

		if ((mask & ARGT_MASK) == ARGT_STOP &&
		    argp[idx].type == ARGT_STOP) {
			break;
		}

		/* Convert some argument types. All string in argp[] are for not
		 * duplicated yet.
		 */
		switch (mask & ARGT_MASK) {
		case ARGT_SINT:
			if (argp[idx].type != ARGT_SINT) {
				msg = "integer expected";
				goto error;
			}
			argp[idx].type = ARGT_SINT;
			break;

		case ARGT_TIME:
			if (argp[idx].type != ARGT_SINT) {
				msg = "integer expected";
				goto error;
			}
			argp[idx].type = ARGT_TIME;
			break;

		case ARGT_SIZE:
			if (argp[idx].type != ARGT_SINT) {
				msg = "integer expected";
				goto error;
			}
			argp[idx].type = ARGT_SIZE;
			break;

		case ARGT_FE:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			argp[idx].data.prx = proxy_fe_by_name(argp[idx].data.str.area);
			if (!argp[idx].data.prx) {
				msg = "frontend doesn't exist";
				goto error;
			}
			argp[idx].type = ARGT_FE;
			break;

		case ARGT_BE:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			argp[idx].data.prx = proxy_be_by_name(argp[idx].data.str.area);
			if (!argp[idx].data.prx) {
				msg = "backend doesn't exist";
				goto error;
			}
			argp[idx].type = ARGT_BE;
			break;

		case ARGT_TAB:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			argp[idx].data.t = stktable_find_by_name(argp[idx].data.str.area);
			if (!argp[idx].data.t) {
				msg = "table doesn't exist";
				goto error;
			}
			argp[idx].type = ARGT_TAB;
			break;

		case ARGT_SRV:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			sname = strrchr(argp[idx].data.str.area, '/');
			if (sname) {
				*sname++ = '\0';
				pname = argp[idx].data.str.area;
				px = proxy_be_by_name(pname);
				if (!px) {
					msg = "backend doesn't exist";
					goto error;
				}
			}
			else {
				sname = argp[idx].data.str.area;
				px = p;
			}
			argp[idx].data.srv = server_find_by_name(px, sname);
			if (!argp[idx].data.srv) {
				msg = "server doesn't exist";
				goto error;
			}
			argp[idx].type = ARGT_SRV;
			break;

		case ARGT_IPV4:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			if (inet_pton(AF_INET, argp[idx].data.str.area, &argp[idx].data.ipv4)) {
				msg = "invalid IPv4 address";
				goto error;
			}
			argp[idx].type = ARGT_IPV4;
			break;

		case ARGT_MSK4:
			if (argp[idx].type == ARGT_SINT)
				len2mask4(argp[idx].data.sint, &argp[idx].data.ipv4);
			else if (argp[idx].type == ARGT_STR) {
				if (!str2mask(argp[idx].data.str.area, &argp[idx].data.ipv4)) {
					msg = "invalid IPv4 mask";
					goto error;
				}
			}
			else  {
				msg = "integer or string expected";
				goto error;
			}
			argp[idx].type = ARGT_MSK4;
			break;

		case ARGT_IPV6:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			if (inet_pton(AF_INET6, argp[idx].data.str.area, &argp[idx].data.ipv6)) {
				msg = "invalid IPv6 address";
				goto error;
			}
			argp[idx].type = ARGT_IPV6;
			break;

		case ARGT_MSK6:
			if (argp[idx].type == ARGT_SINT)
				len2mask6(argp[idx].data.sint, &argp[idx].data.ipv6);
			else if (argp[idx].type == ARGT_STR) {
				if (!str2mask6(argp[idx].data.str.area, &argp[idx].data.ipv6)) {
					msg = "invalid IPv6 mask";
					goto error;
				}
			}
			else {
				msg = "integer or string expected";
				goto error;
			}
			argp[idx].type = ARGT_MSK6;
			break;

		case ARGT_REG:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			reg = regex_comp(argp[idx].data.str.area, !(argp[idx].type_flags & ARGF_REG_ICASE), 1, &err);
			if (!reg) {
				msg = hlua_pushfstring_safe(L, "error compiling regex '%s' : '%s'",
						            argp[idx].data.str.area, err);
				free(err);
				goto error;
			}
			argp[idx].type = ARGT_REG;
			argp[idx].data.reg = reg;
			break;

		case ARGT_USR:
			if (argp[idx].type != ARGT_STR) {
				msg = "string expected";
				goto error;
			}
			if (p->uri_auth && p->uri_auth->userlist &&
			    strcmp(p->uri_auth->userlist->name, argp[idx].data.str.area) == 0)
				ul = p->uri_auth->userlist;
			else
				ul = auth_find_userlist(argp[idx].data.str.area);

			if (!ul) {
				msg = hlua_pushfstring_safe(L, "unable to find userlist '%s'",
				                            argp[idx].data.str.area);
				goto error;
			}
			argp[idx].type = ARGT_USR;
			argp[idx].data.usr = ul;
			break;

		case ARGT_STR:
			if (!chunk_dup(&tmp, &argp[idx].data.str)) {
				msg = "unable to duplicate string arg";
				goto error;
			}
			argp[idx].data.str = tmp;
			break;

		case ARGT_MAP:
			msg = "type not yet supported";
			goto error;
			break;

		}

		/* Check for type of argument. */
		if ((mask & ARGT_MASK) != argp[idx].type) {
			msg = hlua_pushfstring_safe(L, "'%s' expected, got '%s'",
					            arg_type_names[(mask & ARGT_MASK)],
					            arg_type_names[argp[idx].type & ARGT_MASK]);
			goto error;
		}

		/* Next argument. */
		mask >>= ARGT_BITS;
		idx++;
	}
	return 0;

  error:
	argp[idx].type = ARGT_STOP;
	free_args(argp);
	WILL_LJMP(luaL_argerror(L, first + idx, msg));
	return 0; /* Never reached */
}

/*
 * The following functions are used to make correspondence between the the
 * executed lua pointer and the "struct hlua *" that contain the context.
 *
 *  - hlua_gethlua : return the hlua context associated with an lua_State.
 *  - hlua_sethlua : create the association between hlua context and lua_state.
 */
inline struct hlua *hlua_gethlua(lua_State *L)
{
	struct hlua **hlua = lua_getextraspace(L);
	return *hlua;
}
static inline void hlua_sethlua(struct hlua *hlua)
{
	struct hlua **hlua_store = lua_getextraspace(hlua->T);
	*hlua_store = hlua;
}

/* Will return a non-NULL string indicating the Lua call trace if the caller
 * currently is executing from within a Lua function. One line per entry will
 * be emitted, and each extra line will be prefixed with <pfx>. If a current
 * Lua function is not detected, NULL is returned.
 */
const char *hlua_show_current_location(const char *pfx)
{
	lua_State *L;
	lua_Debug ar;

	/* global or per-thread stack initializing ? */
	if (hlua_state_id != -1 && (L = hlua_states[hlua_state_id]) && lua_getstack(L, 0, &ar))
		return hlua_traceback(L, pfx);

	/* per-thread stack running ? */
	if (hlua_states[tid + 1] && (L = hlua_states[tid + 1]) && lua_getstack(L, 0, &ar))
		return hlua_traceback(L, pfx);

	/* global stack running ? */
	if (hlua_states[0] && (L = hlua_states[0]) && lua_getstack(L, 0, &ar))
		return hlua_traceback(L, pfx);

	return NULL;
}

/* This function is used to send logs. It tries to send them to:
 * - the log target applicable in the current context, OR
 * - stderr when no logger is in use for the current context
 */
static inline void hlua_sendlog(struct proxy *px, int level, const char *msg)
{
	struct tm tm;
	char *p;

	/* Cleanup the log message. */
	p = trash.area;
	for (; *msg != '\0'; msg++, p++) {
		if (p >= trash.area + trash.size - 1) {
			/* Break the message if exceed the buffer size. */
			*(p-4) = ' ';
			*(p-3) = '.';
			*(p-2) = '.';
			*(p-1) = '.';
			break;
		}
		if (isprint((unsigned char)*msg))
			*p = *msg;
		else
			*p = '.';
	}
	*p = '\0';

	if (hlua_log_opts & HLUA_LOG_LOGGERS_ON)
		send_log(px, level, "%s\n", trash.area);

	if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
		if (!(hlua_log_opts & HLUA_LOG_STDERR_MASK))
			return;

		/* when logging via stderr is set to 'auto', it behaves like 'off' unless one of:
		 * - logging via loggers is disabled
		 * - this is a non-proxy context and there is no global logger configured
		 * - this is a proxy context and the proxy has no logger configured
		 */
		if ((hlua_log_opts & (HLUA_LOG_STDERR_MASK | HLUA_LOG_LOGGERS_ON)) == (HLUA_LOG_STDERR_AUTO | HLUA_LOG_LOGGERS_ON)) {
			/* AUTO=OFF in non-proxy context only if at least one global logger is defined */
			if ((px == NULL) && (!LIST_ISEMPTY(&global.loggers)))
				return;

			/* AUTO=OFF in proxy context only if at least one logger is configured for the proxy */
			if ((px != NULL) && (!LIST_ISEMPTY(&px->loggers)))
				return;
		}

		if (level == LOG_DEBUG && !(global.mode & MODE_DEBUG))
			return;

		get_localtime(date.tv_sec, &tm);
		fprintf(stderr, "[%s] %03d/%02d%02d%02d (%d) : %s\n",
		        log_levels[level], tm.tm_yday, tm.tm_hour, tm.tm_min, tm.tm_sec,
		        (int)getpid(), trash.area);
		fflush(stderr);
	}
}

/* This function just ensure that the yield will be always
 * returned with a timeout and permit to set some flags
 * <timeout> is a tick value
 */
__LJMP void hlua_yieldk(lua_State *L, int nresults, lua_KContext ctx,
                        lua_KFunction k, int timeout, unsigned int flags)
{
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua) {
		return;
	}

	/* Set the wake timeout. If timeout is required, we set
	 * the expiration time.
	 */
	hlua->wake_time = timeout;

	hlua->flags |= flags;

	/* Process the yield. */
	MAY_LJMP(lua_yieldk(L, nresults, ctx, k));
}

/* This function initialises the Lua environment stored in the stream.
 * It must be called at the start of the stream. This function creates
 * an LUA coroutine. It can not be use to crete the main LUA context.
 *
 * This function is particular. it initialises a new Lua thread. If the
 * initialisation fails (example: out of memory error), the lua function
 * throws an error (longjmp).
 *
 * This function manipulates two Lua stacks: the main and the thread. Only
 * the main stack can fail. The thread is not manipulated. This function
 * MUST NOT manipulate the created thread stack state, because it is not
 * protected against errors thrown by the thread stack.
 */
int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task)
{
	lua->Mref = LUA_REFNIL;
	lua->flags = 0;
	lua->gc_count = 0;
	lua->wake_time = TICK_ETERNITY;
	lua->state_id = state_id;
	hlua_timer_init(&lua->timer, 0); /* default value, no timeout */
	LIST_INIT(&lua->com);
	MT_LIST_INIT(&lua->hc_list);
	if (!SET_SAFE_LJMP_PARENT(lua)) {
		lua->Tref = LUA_REFNIL;
		return 0;
	}
	lua->T = lua_newthread(hlua_states[state_id]);
	if (!lua->T) {
		lua->Tref = LUA_REFNIL;
		RESET_SAFE_LJMP_PARENT(lua);
		return 0;
	}
	hlua_sethlua(lua);
	lua->Tref = luaL_ref(hlua_states[state_id], LUA_REGISTRYINDEX);
	lua->task = task;
	RESET_SAFE_LJMP_PARENT(lua);
	return 1;
}

/* kill all associated httpclient to this hlua task
 * We must take extra precautions as we're manipulating lua-exposed
 * objects without the main lua lock.
 */
static void hlua_httpclient_destroy_all(struct hlua *hlua)
{
	struct hlua_httpclient *hlua_hc;

	/* use thread-safe accessors for hc_list since GC cycle initiated by
	 * another thread sharing the same main lua stack (lua coroutine)
	 * could execute hlua_httpclient_gc() on the hlua->hc_list items
	 * in parallel: Lua GC applies on the main stack, it is not limited to
	 * a single coroutine stack, see Github issue #2037 for reference.
	 * Remember, coroutines created using lua_newthread() are not meant to
	 * be thread safe in Lua. (From lua co-author:
	 * http://lua-users.org/lists/lua-l/2011-07/msg00072.html)
	 *
	 * This security measure is superfluous when 'lua-load-per-thread' is used
	 * since in this case coroutines exclusively run on the same thread
	 * (main stack is not shared between OS threads).
	 */
	while ((hlua_hc = MT_LIST_POP(&hlua->hc_list, typeof(hlua_hc), by_hlua))) {
		httpclient_stop_and_destroy(hlua_hc->hc);
		hlua_hc->hc = NULL;
	}
}


/* Used to destroy the Lua coroutine when the attached stream or task
 * is destroyed. The destroy also the memory context. The struct "lua"
 * will be freed.
 */
void hlua_ctx_destroy(struct hlua *lua)
{
	if (!lua)
		return;

	if (!lua->T)
		goto end;

	/* clean all running httpclient */
	hlua_httpclient_destroy_all(lua);

	/* Purge all the pending signals. */
	notification_purge(&lua->com);

	if (!SET_SAFE_LJMP(lua))
		return;
	luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);
	RESET_SAFE_LJMP(lua);

	if (!SET_SAFE_LJMP_PARENT(lua))
		return;
	luaL_unref(hlua_states[lua->state_id], LUA_REGISTRYINDEX, lua->Tref);
	RESET_SAFE_LJMP_PARENT(lua);
	/* Forces a garbage collecting process. If the Lua program is finished
	 * without error, we run the GC on the thread pointer. Its freed all
	 * the unused memory.
	 * If the thread is finnish with an error or is currently yielded,
	 * it seems that the GC applied on the thread doesn't clean anything,
	 * so e run the GC on the main thread.
	 * NOTE: maybe this action locks all the Lua threads untiml the en of
	 * the garbage collection.
	 */
	if (lua->gc_count) {
		if (!SET_SAFE_LJMP_PARENT(lua))
			return;
		lua_gc(hlua_states[lua->state_id], LUA_GCCOLLECT, 0);
		RESET_SAFE_LJMP_PARENT(lua);
	}

	lua->T = NULL;

end:
	pool_free(pool_head_hlua, lua);
}

/* 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;

	if (!SET_SAFE_LJMP_PARENT(lua))
		return 0;

	/* New Lua coroutine. */
	T = lua_newthread(hlua_states[lua->state_id]);
	if (!T) {
		RESET_SAFE_LJMP_PARENT(lua);
		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); /* Value popped. */

	/* Destroy old data. */
	luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);

	/* The thread is garbage collected by Lua. */
	luaL_unref(hlua_states[lua->state_id], LUA_REGISTRYINDEX, lua->Tref);

	/* Fill the struct with the new coroutine values. */
	lua->Mref = new_ref;
	lua->T = T;
	lua->Tref = luaL_ref(hlua_states[lua->state_id], LUA_REGISTRYINDEX);

	RESET_SAFE_LJMP_PARENT(lua);

	/* Set context. */
	hlua_sethlua(lua);

	return 1;
}

/* Helper function to get the lua ctx for a given stream and state_id */
static inline struct hlua *hlua_stream_ctx_get(struct stream *s, int state_id)
{
	/* state_id == 0 -> global runtime ctx
	 * state_id != 0 -> per-thread runtime ctx
	 */
	return s->hlua[!!state_id];
}

/* Helper function to prepare the lua ctx for a given stream and state id
 *
 * It uses the global or per-thread ctx depending on the expected
 * <state_id>.
 *
 * Returns hlua ctx on success and NULL on failure
 */
static struct hlua *hlua_stream_ctx_prepare(struct stream *s, int state_id)
{
	/* In the execution wrappers linked with a stream, 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[!!state_id]) {
		struct hlua *hlua;

		hlua = pool_alloc(pool_head_hlua);
		if (!hlua)
			return NULL;
		HLUA_INIT(hlua);
		if (!hlua_ctx_init(hlua, state_id, s->task)) {
			pool_free(pool_head_hlua, hlua);
			return NULL;
		}
		s->hlua[!!state_id] = hlua;
	}
	return s->hlua[!!state_id];
}

void hlua_hook(lua_State *L, lua_Debug *ar)
{
	struct hlua *hlua;
	int timer_check;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return;

	if (hlua->T != L) {
		/* We don't want to enforce a yield on a sub coroutine, since
		 * we have no guarantees that the yield will be handled properly.
		 * Indeed, only the hlua->T coroutine is being handled through
		 * hlua_ctx_resume() function.
		 *
		 * Instead, we simply check for timeouts and wait for the sub
		 * coroutine to finish..
		 */
		goto check_timeout;
	}

	/* Lua cannot yield when its returning from a function,
	 * so, we can fix the interrupt hook to 1 instruction,
	 * expecting that the function is finished.
	 */
	if (lua_gethookmask(L) & LUA_MASKRET) {
		lua_sethook(hlua->T, hlua_hook, LUA_MASKCOUNT, 1);
		return;
	}

	/* If we interrupt the Lua processing in yieldable state, we yield.
	 * If the state is not yieldable, trying yield causes an error.
	 */
	if (lua_isyieldable(L)) {
		/* note: for converters/fetches.. where yielding is not allowed
		 * hlua_ctx_resume() will simply perform a goto resume_execution
		 * instead of rescheduling hlua->task.
		 * also: hlua_ctx_resume() will take care of checking execution
		 * timeout and re-applying the hook as needed.
		 */
		MAY_LJMP(hlua_yieldk(L, 0, 0, NULL, TICK_ETERNITY, HLUA_CTRLYIELD));
		/* lua docs says that the hook should return immediately after lua_yieldk
		 *
		 * From: https://www.lua.org/manual/5.3/manual.html#lua_yieldk
		 *
		 * Moreover, it seems that we don't want to continue after the yield
		 * because the end of the function is about handling unyieldable function,
		 * which is not the case here.
		 *
		 *  ->if we don't return lua_sethook gets incorrectly set with MASKRET later
		 *  in the function.
		 */
		return;
	}

 check_timeout:
	/* If we cannot yield, check the timeout. */
	timer_check = hlua_timer_check(&hlua->timer);
	if (timer_check <= 0) {
		if (!timer_check)
			lua_pushfstring(L, "execution timeout");
		else
			lua_pushfstring(L, "burst timeout");
		WILL_LJMP(lua_error(L));
	}

	/* Try to interrupt the process at the end of the current
	 * unyieldable function.
	 */
	lua_sethook(hlua->T, hlua_hook, LUA_MASKRET|LUA_MASKCOUNT, hlua_get_nb_instruction(hlua));
}

/* tries to yield as soon as possible if the context permits it */
void hlua_yield_asap(lua_State *L)
{
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return;

	/* enforce hook for current hlua ctx on the next Lua instruction */
	lua_sethook(hlua->T, hlua_hook, LUA_MASKCOUNT, 1);
}

/* 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 occurred, an error message is set in
 *                    the top of the stack.
 *  - HLUA_E_ERR    : An error has occurred without error message.
 *
 * If an error occurred, 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)
{
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
	int nres;
#endif
	int ret;
	const char *msg;
	const char *trace;

	/* Lock the whole Lua execution. This lock must be before the
	 * label "resume_execution".
	 */
	hlua_lock(lua);

	/* reset the timer as we might be re-entering the function to
	 * resume the coroutine after a successful yield
	 * (cumulative time will be updated)
	 */
	hlua_timer_reset(&lua->timer);

resume_execution:

	/* This hook interrupts the Lua processing each 'hlua_get_nb_instruction()
	 * instructions. it is used for preventing infinite loops.
	 */
	lua_sethook(lua->T, hlua_hook, LUA_MASKCOUNT, hlua_get_nb_instruction(lua));

	/* Remove all flags except the running flags. */
	HLUA_SET_RUN(lua);
	HLUA_CLR_CTRLYIELD(lua);
	HLUA_CLR_WAKERESWR(lua);
	HLUA_CLR_WAKEREQWR(lua);
	HLUA_CLR_NOYIELD(lua);
	if (!yield_allowed)
		HLUA_SET_NOYIELD(lua);

	/* reset wake_time. */
	lua->wake_time = TICK_ETERNITY;

	/* start the timer as we're about to start lua processing */
	hlua_timer_start(&lua->timer);

	HLUA_SET_BUSY(lua);

	/* Call the function. */
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
	ret = lua_resume(lua->T, hlua_states[lua->state_id], lua->nargs, &nres);
#else
	ret = lua_resume(lua->T, hlua_states[lua->state_id], lua->nargs);
#endif

	HLUA_CLR_BUSY(lua);

	/* out of lua processing, stop the timer */
	hlua_timer_stop(&lua->timer);

	/* reset nargs because those possibly passed to the lua_resume() call
	 * were already consumed, and since we may call lua_resume() again
	 * after a successful yield, we don't want to pass stale nargs hint
	 * to the Lua API. As such, nargs should be set explicitly before each
	 * lua_resume() (or hlua_ctx_resume()) invocation if needed.
	 */
	lua->nargs = 0;

	switch (ret) {

	case LUA_OK:
		ret = HLUA_E_OK;
		break;

	case LUA_YIELD:
		/* Check if the execution timeout is expired. If it is the case, we
		 * break the Lua execution.
		 */
		{
			int timer_check;

			timer_check = hlua_timer_check(&lua->timer);
			if (timer_check <= 0) {
				if (!timer_check)
					ret = HLUA_E_ETMOUT;
				else
					ret = HLUA_E_BTMOUT;
				lua_settop(lua->T, 0); /* Empty the stack. */
				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. */
			ret = HLUA_E_YIELD;
			break;
		}
		ret = HLUA_E_AGAIN;
		break;

	case LUA_ERRRUN:

		/* Special exit case. The traditional exit is returned as an error
		 * because the errors ares the only one mean to return immediately
		 * from and lua execution.
		 */
		if (lua->flags & HLUA_EXIT) {
			ret = HLUA_E_OK;
			hlua_ctx_renew(lua, 1);
			break;
		}

		lua->wake_time = TICK_ETERNITY;
		if (!lua_checkstack(lua->T, 1)) {
			ret = HLUA_E_ERR;
			break;
		}
		msg = hlua_tostring_safe(lua->T, -1);
		trace = hlua_traceback(lua->T, ", ");
		if (msg)
			hlua_pushfstring_safe(lua->T, "[state-id %d] runtime error: %s from %s",
			                      lua->state_id, msg, trace);
		else
			hlua_pushfstring_safe(lua->T, "[state-id %d] unknown runtime error from %s",
			                      lua->state_id, trace);

		/* Move the error msg at the bottom and then empty the stack except last msg */
		lua_insert(lua->T, 1);
		lua_settop(lua->T, 1);
		ret = HLUA_E_ERRMSG;
		break;

	case LUA_ERRMEM:
		lua->wake_time = TICK_ETERNITY;
		lua_settop(lua->T, 0); /* Empty the stack. */
		ret = HLUA_E_NOMEM;
		break;

	case LUA_ERRERR:
		lua->wake_time = TICK_ETERNITY;
		if (!lua_checkstack(lua->T, 1)) {
			ret = HLUA_E_ERR;
			break;
		}
		msg = hlua_tostring_safe(lua->T, -1);
		if (msg)
			hlua_pushfstring_safe(lua->T, "[state-id %d] message handler error: %s",
			                      lua->state_id, msg);
		else
			hlua_pushfstring_safe(lua->T, "[state-id %d] message handler error",
			                      lua->state_id);

		/* Move the error msg at the bottom and then empty the stack except last msg */
		lua_insert(lua->T, 1);
		lua_settop(lua->T, 1);
		ret = HLUA_E_ERRMSG;
		break;

	default:
		lua->wake_time = TICK_ETERNITY;
		lua_settop(lua->T, 0); /* Empty the stack. */
		ret = HLUA_E_ERR;
		break;
	}

	switch (ret) {
	case HLUA_E_AGAIN:
		break;

	case HLUA_E_ERRMSG:
		notification_purge(&lua->com);
		hlua_ctx_renew(lua, 1);
		HLUA_CLR_RUN(lua);
		break;

	case HLUA_E_ETMOUT:
	case HLUA_E_BTMOUT:
	case HLUA_E_NOMEM:
	case HLUA_E_YIELD:
	case HLUA_E_ERR:
		HLUA_CLR_RUN(lua);
		notification_purge(&lua->com);
		hlua_ctx_renew(lua, 0);
		break;

	case HLUA_E_OK:
		HLUA_CLR_RUN(lua);
		notification_purge(&lua->com);
		break;
	}

	/* This is the main exit point, remove the Lua lock. */
	hlua_unlock(lua);

	return ret;
}

/* This function exit the current code. */
__LJMP static int hlua_done(lua_State *L)
{
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return 0;

	hlua->flags |= HLUA_EXIT;
	WILL_LJMP(lua_error(L));

	return 0;
}

/* 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': unknown acl file '%s'", name));

	HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->lock);
	pat_ref_delete(ref, key);
	HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
	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': unknown acl file '%s'", name));

	HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->lock);
	pat_ref_delete(ref, key);
	HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
	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': unknown acl file '%s'", name));

	HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->lock);
	if (pat_ref_find_elt(ref, key) == NULL)
		pat_ref_add(ref, key, NULL, NULL);
	HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
	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;
	struct pat_ref_elt *elt;

	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': unknown map file '%s'", name));

	HA_RWLOCK_WRLOCK(PATREF_LOCK, &ref->lock);
	elt = pat_ref_find_elt(ref, key);
	if (elt)
		pat_ref_set_elt_duplicate(ref, elt, value, NULL);
	else
		pat_ref_add(ref, key, value, NULL);
	HA_RWLOCK_WRUNLOCK(PATREF_LOCK, &ref->lock);
	return 0;
}

/* This function is an LUA binding. It provides a function
 * for retrieving a patref object from a reference name
 */
static int hlua_get_patref(lua_State *L)
{
	const char *name;
	struct pat_ref *ref;

	MAY_LJMP(check_args(L, 1, "get_patref"));

	name = MAY_LJMP(luaL_checkstring(L, 1));

	ref = pat_ref_lookup(name);
	if (!ref)
		WILL_LJMP(luaL_error(L, "'get_patref': unknown pattern reference '%s'", name));

	/* push the existing patref object on the stack */
	MAY_LJMP(hlua_fcn_new_patref(L, ref));

	return 1;
}

/* This function is an LUA binding. It provides a function
 * for retrieving a var from the proc scope in core.
 */
__LJMP static int hlua_core_get_var(lua_State *L)
{
	const char *name;
	size_t len;
	struct sample smp;

	MAY_LJMP(check_args(L, 1, "get_var"));

	name = MAY_LJMP(luaL_checklstring(L, 1, &len));

	/* We can only retrieve information from the proc. scope */
	/* FIXME: I didn't want to expose vars_hash_name from vars.c */
	if (len < 5 || strncmp(name, "proc.", 5) != 0)
		WILL_LJMP(luaL_error(L, "'get_var': Only 'proc.' scope allowed to be retrieved in 'core.get_var()'."));

	memset(&smp, 0, sizeof(smp));
	if (!vars_get_by_name(name, len, &smp, NULL)) {
		lua_pushnil(L);
		return 1;
	}

	return MAY_LJMP(hlua_smp2lua(L, &smp));
}

/* This function informs haproxy that native mailers config section is
 * actually being used from Lua.
 *
 * It may not be used outside of body context.
 */
__LJMP static int hlua_use_native_mailers_config(lua_State *L)
{
	if (!hlua_body)
		WILL_LJMP(luaL_error(L, "use_native_mailers_config: "
	        "not available outside of body context"));
	mailers_used_from_lua = 1;
	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 registered 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 redefining standard relations
 * http://www.lua.org/pil/13.2.html
 *
 *    __eq "=="
 *    __lt "<"
 *    __le "<="
 */

/*
 *
 *
 * Class Map
 *
 *
 */

/* Returns a struct hlua_map if the stack entry "ud" is
 * a class session, otherwise it throws an error.
 */
__LJMP static struct map_descriptor *hlua_checkmap(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_map_ref));
}

/* This function is the map constructor. It don't need
 * the class Map object. It creates and return a new Map
 * object. It must be called only during "body" or "init"
 * context because it process some filesystem accesses.
 */
__LJMP static int hlua_map_new(struct lua_State *L)
{
	const char *fn;
	int match = PAT_MATCH_STR;
	struct sample_conv conv;
	const char *file = "";
	int line = 0;
	lua_Debug ar;
	char *err = NULL;
	struct arg args[2];

	if (lua_gettop(L) < 1 || lua_gettop(L) > 2)
		WILL_LJMP(luaL_error(L, "'new' needs at least 1 argument."));

	fn = MAY_LJMP(luaL_checkstring(L, 1));

	if (lua_gettop(L) >= 2) {
		match = MAY_LJMP(luaL_checkinteger(L, 2));
		if (match < 0 || match >= PAT_MATCH_NUM)
			WILL_LJMP(luaL_error(L, "'new' needs a valid match method."));
	}

	/* Get Lua filename and line number. */
	if (lua_getstack(L, 1, &ar)) {  /* check function at level */
		lua_getinfo(L, "Sl", &ar);  /* get info about it */
		if (ar.currentline > 0) {  /* is there info? */
			file = ar.short_src;
			line = ar.currentline;
		}
	}

	/* fill fake sample_conv struct. */
	conv.kw = ""; /* unused. */
	conv.process = NULL; /* unused. */
	conv.arg_mask = 0; /* unused. */
	conv.val_args = NULL; /* unused. */
	conv.out_type = SMP_T_STR;
	conv.private = (void *)(long)match;
	switch (match) {
	case PAT_MATCH_STR: conv.in_type = SMP_T_STR;  break;
	case PAT_MATCH_BEG: conv.in_type = SMP_T_STR;  break;
	case PAT_MATCH_SUB: conv.in_type = SMP_T_STR;  break;
	case PAT_MATCH_DIR: conv.in_type = SMP_T_STR;  break;
	case PAT_MATCH_DOM: conv.in_type = SMP_T_STR;  break;
	case PAT_MATCH_END: conv.in_type = SMP_T_STR;  break;
	case PAT_MATCH_REG: conv.in_type = SMP_T_STR;  break;
	case PAT_MATCH_INT: conv.in_type = SMP_T_SINT; break;
	case PAT_MATCH_IP:  conv.in_type = SMP_T_ADDR; break;
	default:
		WILL_LJMP(luaL_error(L, "'new' doesn't support this match mode."));
	}

	/* fill fake args. */
	args[0].type = ARGT_STR;
	args[0].data.str.area = strdup(fn);
	args[0].data.str.data = strlen(fn);
	args[0].data.str.size = args[0].data.str.data+1;
	args[1].type = ARGT_STOP;

	/* load the map. */
	if (!sample_load_map(args, &conv, file, line, &err)) {
		/* error case: we can't use luaL_error because we must
		 * free the err variable.
		 */
		hlua_pusherror(L, "'new': %s.", err);
		free(err);
		chunk_destroy(&args[0].data.str);
		WILL_LJMP(lua_error(L));
	}

	/* create the lua object. */
	lua_newtable(L);
	lua_pushlightuserdata(L, args[0].data.map);
	lua_rawseti(L, -2, 0);

	/* Pop a class Map metatable and affect it to the userdata. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_map_ref);
	lua_setmetatable(L, -2);


	return 1;
}

__LJMP static inline int _hlua_map_lookup(struct lua_State *L, int str)
{
	struct map_descriptor *desc;
	struct pattern *pat;
	struct sample smp;

	MAY_LJMP(check_args(L, 2, "lookup"));
	desc = MAY_LJMP(hlua_checkmap(L, 1));
	if (desc->pat.expect_type == SMP_T_SINT) {
		smp.data.type = SMP_T_SINT;
		smp.data.u.sint = MAY_LJMP(luaL_checkinteger(L, 2));
	}
	else {
		smp.data.type = SMP_T_STR;
		smp.flags = SMP_F_CONST;
		smp.data.u.str.area = (char *)MAY_LJMP(luaL_checklstring(L, 2, (size_t *)&smp.data.u.str.data));
		smp.data.u.str.size = smp.data.u.str.data + 1;
	}

	pat = pattern_exec_match(&desc->pat, &smp, 1);
	if (!pat || !pat->data) {
		if (str)
			lua_pushstring(L, "");
		else
			lua_pushnil(L);
		return 1;
	}

	/* The Lua pattern must return a string, so we can't check the returned type */
	lua_pushlstring(L, pat->data->u.str.area, pat->data->u.str.data);
	return 1;
}

__LJMP static int hlua_map_lookup(struct lua_State *L)
{
	return _hlua_map_lookup(L, 0);
}

__LJMP static int hlua_map_slookup(struct lua_State *L)
{
	return _hlua_map_lookup(L, 1);
}

/*
 *
 *
 * Class Socket
 *
 *
 */

__LJMP static struct hlua_socket *hlua_checksocket(lua_State *L, int ud)
{
	return 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 available to send or data
 * received.
 */
static void hlua_socket_handler(struct appctx *appctx)
{
	struct hlua_csk_ctx *ctx = appctx->svcctx;

	if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR))) {
		applet_reset_input(appctx);
		notification_wake(&ctx->wake_on_read);
		notification_wake(&ctx->wake_on_write);
		return;
	}

	if (ctx->die) {
		applet_set_eos(appctx);
		notification_wake(&ctx->wake_on_read);
		notification_wake(&ctx->wake_on_write);
		return;
	}

	/* If applet is shutdown, wakeup the pending read and write signals. */
	if (se_fl_test(appctx->sedesc, SE_FL_SHW)) {
		notification_wake(&ctx->wake_on_write);
		notification_wake(&ctx->wake_on_read);
	}

	/* if the connection is not established, inform the stream that we want
	 * to be notified whenever the connection completes.
	 */
	if (se_fl_test(appctx->sedesc, SE_FL_APPLET_NEED_CONN)) {
		applet_need_more_data(appctx);
		applet_have_more_data(appctx);
		return;
	}

	/* This function is called after the connect. */
	ctx->connected = 1;

	/* Wake the tasks which wants to write if the buffer have available space. */
	if (applet_get_outbuf(appctx) != NULL && applet_output_room(appctx))
		notification_wake(&ctx->wake_on_write);

	/* Wake the tasks which wants to read if the buffer contains data. */
	if (applet_get_inbuf(appctx) != NULL && applet_input_data(appctx)) {
		notification_wake(&ctx->wake_on_read);
		applet_wont_consume(appctx);
	}

	/* If write notifications are registered, we considers we want
	 * to write, so we clear the blocking flag.
	 */
	if (notification_registered(&ctx->wake_on_write))
		applet_have_more_data(appctx);
}

static int hlua_socket_init(struct appctx *appctx)
{
	struct hlua_csk_ctx *csk_ctx = appctx->svcctx;
	struct stream *s;

	if (appctx_finalize_startup(appctx, socket_proxy, &BUF_NULL) == -1)
		goto error;

	s = appctx_strm(appctx);

	/* Configure "right" stream connector. This stconn is used to connect
	 * and retrieve data from the server. The connection is initialized
	 * with the "struct server".
	 */
	sc_set_state(s->scb, SC_ST_ASS);
	se_need_remote_conn(appctx->sedesc);

	/* Force destination server. */
	s->flags |= SF_DIRECT | SF_ASSIGNED | SF_BE_ASSIGNED;
	stream_set_srv_target(s, csk_ctx->srv);

	if (csk_ctx->timeout) {
		s->sess->fe->timeout.connect = csk_ctx->timeout;
		s->scf->ioto = csk_ctx->timeout;
		s->scb->ioto = csk_ctx->timeout;
	}

	return 0;

  error:
	return -1;
}

/* This function is called when the "struct stream" is destroyed.
 * Remove the link from the object to this stream.
 * Wake all the pending signals.
 */
static void hlua_socket_release(struct appctx *appctx)
{
	struct hlua_csk_ctx *ctx = appctx->svcctx;
	struct xref *peer;

	/* Remove my link in the original objects. */
	peer = xref_get_peer_and_lock(&ctx->xref);
	if (peer)
		xref_disconnect(&ctx->xref, peer);

	/* Wake all the task waiting for me. */
	notification_wake(&ctx->wake_on_read);
	notification_wake(&ctx->wake_on_write);
}

/* If the garbage collectio of the object is launch, nobody
 * uses this object. If the stream does not exists, just quit.
 * Send the shutdown signal to the stream. 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 hlua_csk_ctx *ctx;
	struct xref *peer;

	MAY_LJMP(check_args(L, 1, "__gc"));

	socket = MAY_LJMP(hlua_checksocket(L, 1));
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer)
		return 0;

	ctx = container_of(peer, struct hlua_csk_ctx, xref);

	/* Remove all reference between the Lua stack and the coroutine stream. */
	xref_disconnect(&socket->xref, peer);

	if (se_fl_test(ctx->appctx->sedesc, SE_FL_ORPHAN)) {
		/* The applet was never initialized, just release it */
		appctx_free(ctx->appctx);
	}
	else {
		/* Otherwise, notify it that is must die and wake it up */
		ctx->die = 1;
		appctx_wakeup(ctx->appctx);
	}

	return 0;
}

/* The close function send shutdown signal and break the
 * links between the stream and the object.
 */
__LJMP static int hlua_socket_close_helper(lua_State *L)
{
	struct hlua_socket *socket;
	struct hlua_csk_ctx *ctx;
	struct xref *peer;
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return 0;

	socket = MAY_LJMP(hlua_checksocket(L, 1));

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer)
		return 0;

	hlua->gc_count--;
	ctx = container_of(peer, struct hlua_csk_ctx, xref);

	/* Set the flag which destroy the session. */
	ctx->die = 1;
	appctx_wakeup(ctx->appctx);

	/* Remove all reference between the Lua stack and the coroutine stream. */
	xref_disconnect(&socket->xref, peer);
	return 0;
}

/* The close function calls close_helper.
 */
__LJMP static int hlua_socket_close(lua_State *L)
{
	MAY_LJMP(check_args(L, 1, "close"));
	return hlua_socket_close_helper(L);
}

/* 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, int status, lua_KContext ctx)
{
	struct hlua_socket *socket = MAY_LJMP(hlua_checksocket(L, 1));
	int wanted = lua_tointeger(L, 2);
	struct hlua *hlua;
	struct hlua_csk_ctx *csk_ctx;
	struct appctx *appctx;
	struct buffer *inbuf;
	size_t len;
	int nblk;
	const char *blk1;
	size_t len1;
	const char *blk2;
	size_t len2;
	int skip_at_end = 0;
	struct xref *peer;
	int missing_bytes;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);

	/* 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 if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	/* check for connection break. If some data where read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer)
		goto no_peer;

	csk_ctx = container_of(peer, struct hlua_csk_ctx, xref);
	if (!csk_ctx->connected)
		goto connection_closed;

	appctx = csk_ctx->appctx;
	inbuf = applet_get_inbuf(appctx);
	if (!inbuf)
		goto connection_empty;

	if (wanted == HLSR_READ_LINE) {
		/* Read line. */
		nblk = applet_getline_nc(appctx, &blk1, &len1, &blk2, &len2);
		if (nblk < 0) /* Connection close. */
			goto connection_closed;
		if (nblk == 0) /* No data available. */
			goto connection_empty;

		/* remove final \r\n. */
		if (nblk == 1) {
			if (blk1[len1-1] == '\n') {
				len1--;
				skip_at_end++;
				if (blk1[len1-1] == '\r') {
					len1--;
					skip_at_end++;
				}
			}
		}
		else {
			if (blk2[len2-1] == '\n') {
				len2--;
				skip_at_end++;
				if (blk2[len2-1] == '\r') {
					len2--;
					skip_at_end++;
				}
			}
		}
	}

	else if (wanted == HLSR_READ_ALL) {
		/* Read all the available data. */
		nblk = applet_getblk_nc(appctx, &blk1, &len1, &blk2, &len2);
		if (nblk < 0) /* Connection close. */
			goto connection_closed;
		if (nblk == 0) /* No data available. */
			goto connection_empty;
	}

	else {
		/* Read a block of data. */
		nblk = applet_getblk_nc(appctx, &blk1, &len1, &blk2, &len2);
		if (nblk < 0) /* Connection close. */
			goto connection_closed;
		if (nblk == 0) /* No data available. */
			goto connection_empty;

		missing_bytes = wanted - socket->b.n;
		if (len1 > missing_bytes) {
			nblk = 1;
			len1 = missing_bytes;
		} if (nblk == 2 && len1 + len2 > missing_bytes)
			len2 = missing_bytes - len1;
	}

	len = len1;

	luaL_addlstring(&socket->b, blk1, len1);
	if (nblk == 2) {
		len += len2;
		luaL_addlstring(&socket->b, blk2, len2);
	}

	/* Consume data. */
	applet_skip_input(appctx, len + skip_at_end);


	/* Don't wait anything. */
	applet_will_consume(appctx);
	appctx_wakeup(appctx);

	/* 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 && socket->b.n < wanted)
		goto connection_empty;

	/* Return result. */
	luaL_pushresult(&socket->b);
	xref_unlock(&socket->xref, peer);
	return 1;

connection_closed:

	xref_unlock(&socket->xref, peer);

no_peer:

	/* 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:

	if (!notification_new(&hlua->com, &csk_ctx->wake_on_read, hlua->task)) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "out of memory"));
	}
	xref_unlock(&socket->xref, peer);
	applet_need_more_data(appctx);
	MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_socket_receive_yield, TICK_ETERNITY, 0));
	return 0;
}

/* This Lua function gets two parameters. The first one can be string
 * or a number. If the string is "*l", the user requires one line. If
 * the string is "*a", the user requires all the contents 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 parameter with a variable type is converted in integer. This
 * integer takes this values:
 *  -1 : read a line
 *  -2 : read all the stream
 *  >0 : amount of bytes.
 *
 * The second parameter is optional. 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 lastarg, 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 if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	/* 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);

	/* Check if we would replace the top by itself. */
	if (lua_gettop(L) != 2)
		lua_replace(L, 2);

	/* Save index of the top of the stack because since buffers are used, it
	 * may change
	 */
	lastarg = lua_gettop(L);

	/* init buffer, and fill it with prefix. */
	luaL_buffinit(L, &socket->b);

	/* Check prefix. */
	if (lastarg >= 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, 0, 0));
}

/* Write the Lua input string in the output buffer.
 * This function returns a yield if no space is available.
 */
static int hlua_socket_write_yield(struct lua_State *L,int status, lua_KContext ctx)
{
	struct hlua_socket *socket;
	struct hlua *hlua;
	struct hlua_csk_ctx *csk_ctx;
	struct appctx *appctx;
	struct buffer *outbuf;
	size_t buf_len;
	const char *buf;
	int len;
	int send_len;
	int sent;
	struct xref *peer;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);

	/* 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_checkinteger(L, 3));

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	/* check for connection break. If some data where read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer) {
		lua_pushinteger(L, -1);
		return 1;
	}

	csk_ctx = container_of(peer, struct hlua_csk_ctx, xref);
	if (!csk_ctx->connected) {
		xref_unlock(&socket->xref, peer);
		lua_pushinteger(L, -1);
		return 1;
	}

	appctx = csk_ctx->appctx;
	outbuf = applet_get_outbuf(appctx);
	if (!outbuf)
		goto hlua_socket_write_yield_return;

	/* Update the input buffer data. */
	buf += sent;
	send_len = buf_len - sent;

	/* All the data are sent. */
	if (sent >= buf_len) {
		xref_unlock(&socket->xref, peer);
		return 1; /* Implicitly return the length sent. */
	}

	/* Check for available space. */
	len = applet_output_room(appctx);
	if (!len)
		goto hlua_socket_write_yield_return;

	/* send data */
	if (len < send_len)
		send_len = len;
	len = applet_putblk(appctx, buf, 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_helper(L));
		lua_pop(L, 1);
		lua_pushinteger(L, -1);
		xref_unlock(&socket->xref, peer);
		return 1;
	}

	/* update buffers. */
	appctx_wakeup(appctx);

	/* Update length sent. */
	lua_pop(L, 1);
	lua_pushinteger(L, sent + len);

	/* All the data buffer is sent ? */
	if (sent + len >= buf_len) {
		xref_unlock(&socket->xref, peer);
		return 1;
	}

hlua_socket_write_yield_return:
	if (!notification_new(&hlua->com, &csk_ctx->wake_on_write, hlua->task)) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "out of memory"));
	}
	xref_unlock(&socket->xref, peer);
	applet_have_more_data(appctx);
	MAY_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 written to 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_pushinteger(L, 0);
		return 1;
	}
	if (i == 0 && j == 0) {
		lua_pushinteger(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_pushinteger(L, 0);

	return MAY_LJMP(hlua_socket_write_yield(L, 0, 0));
}

#define SOCKET_INFO_MAX_LEN sizeof("[0000:0000:0000:0000:0000:0000:0000:0000]:12345")
__LJMP static inline int hlua_socket_info(struct lua_State *L, const 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;
	}

	ret = real_family(ret);

	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 xref *peer;
	struct hlua_csk_ctx *csk_ctx;
	struct appctx *appctx;
	struct stconn *sc;
	const struct sockaddr_storage *dst;
	int ret;

	MAY_LJMP(check_args(L, 1, "getpeername"));

	socket = MAY_LJMP(hlua_checksocket(L, 1));

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	/* check for connection break. If some data where read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer) {
		lua_pushnil(L);
		return 1;
	}

	csk_ctx = container_of(peer, struct hlua_csk_ctx, xref);
	if (!csk_ctx->connected) {
		xref_unlock(&socket->xref, peer);
		lua_pushnil(L);
		return 1;
	}

	appctx = csk_ctx->appctx;
	sc = appctx_sc(appctx);
	dst = sc_dst(sc_opposite(sc));
	if (!dst) {
		xref_unlock(&socket->xref, peer);
		lua_pushnil(L);
		return 1;
	}

	ret = MAY_LJMP(hlua_socket_info(L, dst));
	xref_unlock(&socket->xref, peer);
	return ret;
}

/* Returns information about my connection side. */
static int hlua_socket_getsockname(struct lua_State *L)
{
	struct hlua_socket *socket;
	struct connection *conn;
	struct appctx *appctx;
	struct xref *peer;
	struct hlua_csk_ctx *csk_ctx;
	struct stream *s;
	int ret;

	MAY_LJMP(check_args(L, 1, "getsockname"));

	socket = MAY_LJMP(hlua_checksocket(L, 1));

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	/* check for connection break. If some data where read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer) {
		lua_pushnil(L);
		return 1;
	}

	csk_ctx = container_of(peer, struct hlua_csk_ctx, xref);
	if (!csk_ctx->connected) {
		xref_unlock(&socket->xref, peer);
		lua_pushnil(L);
		return 1;
	}

	appctx = csk_ctx->appctx;
	s = appctx_strm(appctx);

	conn = sc_conn(s->scb);
	if (!conn || !conn_get_src(conn)) {
		xref_unlock(&socket->xref, peer);
		lua_pushnil(L);
		return 1;
	}

	ret = hlua_socket_info(L, conn->src);
	xref_unlock(&socket->xref, peer);
	return ret;
}

/* This struct define the applet. */
static struct applet update_applet = {
	.obj_type = OBJ_TYPE_APPLET,
	.flags = APPLET_FL_NEW_API,
	.name = "<LUA_TCP>",
	.fct = hlua_socket_handler,
	.rcv_buf = appctx_raw_rcv_buf,
	.snd_buf = appctx_raw_snd_buf,
	.init = hlua_socket_init,
	.release = hlua_socket_release,
};

__LJMP static int hlua_socket_connect_yield(struct lua_State *L, int status, lua_KContext ctx)
{
	struct hlua_socket *socket = MAY_LJMP(hlua_checksocket(L, 1));
	struct hlua *hlua;
	struct xref *peer;
	struct hlua_csk_ctx *csk_ctx;
	struct appctx *appctx;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return 0;

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	/* check for connection break. If some data where read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer) {
		lua_pushnil(L);
		lua_pushstring(L, "Can't connect");
		return 2;
	}

	csk_ctx = container_of(peer, struct hlua_csk_ctx, xref);
	appctx = csk_ctx->appctx;

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));
	}

	/* Check for connection close. */
	if (!hlua || se_fl_test(appctx->sedesc, SE_FL_SHW)) {
		xref_unlock(&socket->xref, peer);
		lua_pushnil(L);
		lua_pushstring(L, "Can't connect");
		return 2;
	}

	/* Check for connection established. */
	if (csk_ctx->connected) {
		xref_unlock(&socket->xref, peer);
		lua_pushinteger(L, 1);
		return 1;
	}

	if (!notification_new(&hlua->com, &csk_ctx->wake_on_write, hlua->task)) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "out of memory error"));
	}
	xref_unlock(&socket->xref, peer);
	MAY_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;
	int port = -1;
	const char *ip;
	struct hlua *hlua;
	struct hlua_csk_ctx *csk_ctx;
	struct appctx *appctx;
	int low, high;
	struct sockaddr_storage *addr;
	struct xref *peer;
	struct stconn *sc;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return 0;

	if (lua_gettop(L) < 2)
		WILL_LJMP(luaL_error(L, "connect: need at least 2 arguments"));

	/* Get args. */
	socket  = MAY_LJMP(hlua_checksocket(L, 1));

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	ip      = MAY_LJMP(luaL_checkstring(L, 2));
	if (lua_gettop(L) >= 3) {
		luaL_Buffer b;
		port = MAY_LJMP(luaL_checkinteger(L, 3));

		/* Force the ip to end with a colon, to support IPv6 addresses
		 * that are not enclosed within square brackets.
		 */
		if (port > 0) {
			luaL_buffinit(L, &b);
			luaL_addstring(&b, ip);
			luaL_addchar(&b, ':');
			luaL_pushresult(&b);
			ip = lua_tolstring(L, lua_gettop(L), NULL);
		}
	}

	/* check for connection break. If some data where read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer) {
		lua_pushnil(L);
		return 1;
	}

	csk_ctx = container_of(peer, struct hlua_csk_ctx, xref);
	if (!csk_ctx->srv)
		csk_ctx->srv = socket_tcp;

	/* Parse ip address. */
	addr = str2sa_range(ip, NULL, &low, &high, NULL, NULL, NULL, NULL,
	                    NULL, NULL, NULL, PA_O_PORT_OK | PA_O_STREAM);
	if (!addr) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "connect: cannot parse destination address '%s'", ip));
	}

	/* Set port. */
	if (low == 0) {
		if (addr->ss_family == AF_INET) {
			if (port == -1) {
				xref_unlock(&socket->xref, peer);
				WILL_LJMP(luaL_error(L, "connect: port missing"));
			}
			((struct sockaddr_in *)addr)->sin_port = htons(port);
		} else if (addr->ss_family == AF_INET6) {
			if (port == -1) {
				xref_unlock(&socket->xref, peer);
				WILL_LJMP(luaL_error(L, "connect: port missing"));
			}
			((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
		}
	}

	appctx = csk_ctx->appctx;
	if (appctx_sc(appctx)) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "connect: connect already performed\n"));
	}

	if (appctx_init(appctx) == -1) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "connect: fail to init applet."));
	}

	sc = appctx_sc(appctx);

	if (!sockaddr_alloc(&sc_opposite(sc)->dst, addr, sizeof(*addr))) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "connect: internal error"));
	}

	/* inform the stream that we want to be notified whenever the
	 * connection completes.
	 */
	applet_need_more_data(appctx);
	applet_have_more_data(appctx);
	appctx_wakeup(appctx);

	if (!notification_new(&hlua->com, &csk_ctx->wake_on_write, hlua->task)) {
		xref_unlock(&socket->xref, peer);
		WILL_LJMP(luaL_error(L, "out of memory"));
	}
	xref_unlock(&socket->xref, peer);

	/* Return yield waiting for connection. */
	MAY_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;
	struct xref *peer;

	MAY_LJMP(check_args(L, 3, "connect_ssl"));
	socket  = MAY_LJMP(hlua_checksocket(L, 1));

	/* check for connection break. If some data where read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer) {
		lua_pushnil(L);
		return 1;
	}

	container_of(peer, struct hlua_csk_ctx, xref)->srv = socket_ssl;

	xref_unlock(&socket->xref, peer);
	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;
	int tmout;
	double dtmout;
	struct xref *peer;
	struct hlua_csk_ctx *csk_ctx;
	struct appctx *appctx;
	struct stream *s;

	MAY_LJMP(check_args(L, 2, "settimeout"));

	socket = MAY_LJMP(hlua_checksocket(L, 1));

	/* convert the timeout to millis */
	dtmout = MAY_LJMP(luaL_checknumber(L, 2)) * 1000;

	/* Check for negative values */
	if (dtmout < 0)
		WILL_LJMP(luaL_error(L, "settimeout: cannot set negatives values"));

	if (dtmout > INT_MAX) /* overflow check */
		WILL_LJMP(luaL_error(L, "settimeout: cannot set values larger than %d ms", INT_MAX));

	tmout = MS_TO_TICKS((int)dtmout);
	if (tmout == 0)
		tmout++; /* very small timeouts are adjusted to a minimum of 1ms */

	/* Check if we run on the same thread than the xreator thread.
	 * We cannot access to the socket if the thread is different.
	 */
	if (socket->tid != tid)
		WILL_LJMP(luaL_error(L, "connect: cannot use socket on other thread"));

	/* check for connection break. If some data were read, return it. */
	peer = xref_get_peer_and_lock(&socket->xref);
	if (!peer) {
		hlua_pusherror(L, "socket: not yet initialised, you can't set timeouts.");
		WILL_LJMP(lua_error(L));
		return 0;
	}

	csk_ctx = container_of(peer, struct hlua_csk_ctx, xref);
	csk_ctx->timeout = tmout;

	appctx = csk_ctx->appctx;
	if (!appctx_sc(appctx))
		goto end;

	s = appctx_strm(csk_ctx->appctx);

	s->sess->fe->timeout.connect = tmout;
	s->scf->ioto = tmout;
	s->scb->ioto = tmout;

	s->task->expire = (tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire);
	s->task->expire = tick_first(s->task->expire, tick_add_ifset(now_ms, tmout));
	task_queue(s->task);

  end:
	xref_unlock(&socket->xref, peer);
	lua_pushinteger(L, 1);
	return 1;
}

__LJMP static int hlua_socket_new(lua_State *L)
{
	struct hlua_socket *socket;
	struct hlua_csk_ctx *ctx;
	struct appctx *appctx;
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return 0;

	/* Check stack size. */
	if (!lua_checkstack(L, 3)) {
		hlua_pusherror(L, "socket: full stack");
		goto out_fail_conf;
	}

	/* Create the object: obj[0] = userdata. */
	lua_newtable(L);
	socket = MAY_LJMP(lua_newuserdata(L, sizeof(*socket)));
	lua_rawseti(L, -2, 0);
	memset(socket, 0, sizeof(*socket));
	socket->tid = tid;

	/* Check if the various memory pools are initialized. */
	if (!pool_head_stream || !pool_head_buffer) {
		hlua_pusherror(L, "socket: uninitialized pools.");
		goto out_fail_conf;
	}

	/* Pop a class stream metatable and affect it to the userdata. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_socket_ref);
	lua_setmetatable(L, -2);

	/* Create the applet context */
	appctx = appctx_new_here(&update_applet, NULL);
	if (!appctx) {
		hlua_pusherror(L, "socket: out of memory");
		goto out_fail_conf;
	}
	ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
	ctx->connected = 0;
	ctx->die = 0;
	ctx->srv = NULL;
	ctx->timeout = 0;
	ctx->appctx = appctx;
	MT_LIST_INIT(&ctx->wake_on_write);
	MT_LIST_INIT(&ctx->wake_on_read);

	hlua->gc_count++;

	/* Initialise cross reference between stream and Lua socket object. */
	xref_create(&socket->xref, &ctx->xref);
	return 1;

 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 channel *hlua_checkchannel(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_channel_ref));
}

/* Pushes the channel onto the top of the stack. If the stask does not have a
 * free slots, the function fails and returns 0;
 */
__LJMP static int hlua_channel_new(lua_State *L, struct channel *channel)
{
	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	lua_newtable(L);
	lua_pushlightuserdata(L, channel);
	lua_rawseti(L, -2, 0);

	/* 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;
}

/* Helper function returning a filter attached to a channel at the position <ud>
 * in the stack, filling the current offset and length of the filter. If no
 * filter is attached, NULL is returned and <offset> and <len> are not
 * initialized.
 */
static struct filter *hlua_channel_filter(lua_State *L, int ud, struct channel *chn, size_t *offset, size_t *len)
{
	struct filter *filter = NULL;

	if (lua_getfield(L, ud, "__filter") == LUA_TLIGHTUSERDATA) {
		struct hlua_flt_ctx *flt_ctx;

		filter  = lua_touserdata (L, -1);
		flt_ctx = filter->ctx;
		if (hlua_filter_from_payload(filter)) {
			*offset  = flt_ctx->cur_off[CHN_IDX(chn)];
			*len     = flt_ctx->cur_len[CHN_IDX(chn)];
		}
	}

	lua_pop(L, 1);
	return filter;
}

/* Copies <len> bytes of data present in the channel's buffer, starting at the
* offset <offset>, and put it in a LUA string variable. It is the caller
* responsibility to ensure <len> and <offset> are valid. It always return the
* length of the built string. <len> may be 0, in this case, an empty string is
* created and 0 is returned.
*/
static inline int _hlua_channel_dup(struct channel *chn, lua_State *L, size_t offset, size_t len)
{
	size_t block1, block2;
	luaL_Buffer b;

	block1 = len;
	if (block1 > b_contig_data(&chn->buf, b_peek_ofs(&chn->buf, offset)))
		block1 = b_contig_data(&chn->buf, b_peek_ofs(&chn->buf, offset));
	block2 = len - block1;

	luaL_buffinit(L, &b);
	luaL_addlstring(&b, b_peek(&chn->buf, offset), block1);
	if (block2)
		luaL_addlstring(&b, b_orig(&chn->buf), block2);
	luaL_pushresult(&b);
	return len;
}

/* Inserts the string <str> to the channel's buffer at the offset <offset>. This
 * function returns -1 if data cannot be copied. Otherwise, it returns the
 * number of bytes copied.
 */
static int _hlua_channel_insert(struct channel *chn, lua_State *L, struct ist str, size_t offset)
{
	int ret = 0;

	/* Nothing to do, just return */
	if (unlikely(istlen(str) == 0))
		goto end;

	if (istlen(str) > c_room(chn)) {
		ret = -1;
		goto end;
	}
	ret = b_insert_blk(&chn->buf, offset, istptr(str), istlen(str));

  end:
	return ret;
}

/* Removes <len> bytes of data at the absolute position <offset>.
 */
static void _hlua_channel_delete(struct channel *chn, size_t offset, size_t len)
{
	size_t end = offset + len;

	if (b_peek(&chn->buf, end) != b_tail(&chn->buf))
		b_move(&chn->buf, b_peek_ofs(&chn->buf, end),
		       b_data(&chn->buf) - end, -len);
	b_sub(&chn->buf, len);
}

/* Copies input data in the channel's buffer. It is possible to set a specific
 * offset (0 by default) and a length (all remaining input data starting for the
 * offset by default). If there is not enough input data and more data can be
 * received, this function yields.
 *
 * From an action, All input data are considered. For a filter, the offset and
 * the length of input data to consider are retrieved from the filter context.
 */
__LJMP static int hlua_channel_get_data_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct channel *chn;
	struct filter *filter;
	size_t input, output;
	int offset, len = 0;

	chn = MAY_LJMP(hlua_checkchannel(L, 1));

	output = co_data(chn);
	input = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &output, &input);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 1) {
		offset = MAY_LJMP(luaL_checkinteger(L, 2));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset < output) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}
	if (offset >= output+input)
		goto wait_more_data;

	len = output + input - offset;
	if (lua_gettop(L) == 3) {
		len = MAY_LJMP(luaL_checkinteger(L, 3));
		if (!len)
			goto dup;
		if (len == -1)
			len = c_size(chn);
		if (len < 0) {
			lua_pushfstring(L, "length out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	/* Wait for more data if possible if no length was specified and there
	 * is no data or not enough data was received.
	 */
	if (!len || offset + len > output + input) {
	  wait_more_data:
		if (!HLUA_CANT_YIELD(hlua_gethlua(L)) && !channel_input_closed(chn) && channel_may_recv(chn)) {
			/* Yield waiting for more data, as requested */
			MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_get_data_yield, TICK_ETERNITY, 0));
		}

		if (!input) {
			/* Return 'nil' if there is no data and the channel can't receive more data */
			lua_pushnil(L);
			return -1;
		}

		if (!len) {
			lua_pushstring(L, "");
			return 1;
		}

		/* Otherwise, return all data */
		len = output + input - offset;
	}

  dup:
	_hlua_channel_dup(chn, L, offset, len);
	return 1;
}

/* Copies the first line (including the trailing LF) of input data in the
 * channel's buffer. It is possible to set a specific offset (0 by default) and
 * a length (all remaining input data starting for the offset by default). If
 * there is not enough input data and more data can be received, the function
 * yields. If a length is explicitly specified, no more data are
 * copied. Otherwise, if no LF is found and more data can be received, this
 * function yields.
 *
 * From an action, All input data are considered. For a filter, the offset and
 * the length of input data to consider are retrieved from the filter context.
 */
__LJMP static int hlua_channel_get_line_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct channel *chn;
	struct filter *filter;
	size_t l, input, output;
	int offset, len = 0;

	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	output = co_data(chn);
	input = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &output, &input);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 1) {
		offset = MAY_LJMP(luaL_checkinteger(L, 2));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset < output) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}
	if (offset > output + input)
		goto wait_more_data;

	len = output + input - offset;
	if (lua_gettop(L) == 3) {
		len = MAY_LJMP(luaL_checkinteger(L, 3));
		if (!len)
			goto dup;
		if (len == -1)
			len = c_size(chn);
		if (len < 0) {
			lua_pushfstring(L, "length out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	for (l = 0; l < len; l++) {
		if (l + offset >= output + input)
			break;
		if (*(b_peek(&chn->buf, offset + l)) == '\n') {
			len = l+1;
			goto dup;
		}
	}

	/* Wait for more data if possible if no line is found and no length was
	 * specified or not enough data was received.
	 */
	if (lua_gettop(L) != 3 ||  offset + len > output + input) {
	  wait_more_data:
		if (!HLUA_CANT_YIELD(hlua_gethlua(L)) && !channel_input_closed(chn) && channel_may_recv(chn)) {
			/* Yield waiting for more data */
			MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_get_line_yield, TICK_ETERNITY, 0));
		}

		if (!input) {
			/* Return 'nil' if there is no data and the channel can't receive more data */
			lua_pushnil(L);
			return -1;
		}

		if (!len) {
			lua_pushstring(L, "");
			return 1;
		}

		/* Otherwise, return all data */
		len = output + input - offset;
	}

  dup:
	_hlua_channel_dup(chn, L, offset, len);
	return 1;
}

/* [ DEPRECATED ]
 *
 * Duplicate all input data foud in the channel's buffer. The data are not
 * removed from the buffer. This function relies on _hlua_channel_dup().
 *
 * From an action, All input data are considered. For a filter, the offset and
 * the length of input data to consider are retrieved from the filter context.
 */
__LJMP static int hlua_channel_dup(lua_State *L)
{
	struct channel *chn;
	struct filter *filter;
	size_t offset, len;

	MAY_LJMP(check_args(L, 1, "dup"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}

	offset = co_data(chn);
	len = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	if (!ci_data(chn) && channel_input_closed(chn)) {
		lua_pushnil(L);
		return 1;
	}

	_hlua_channel_dup(chn, L, offset, len);
	return 1;
}

/* [ DEPRECATED ]
 *
 * Get all input data foud in the channel's buffer. The data are removed from
 * the buffer after the copy. This function relies on _hlua_channel_dup() and
 * _hlua_channel_delete().
 *
 * From an action, All input data are considered. For a filter, the offset and
 * the length of input data to consider are retrieved from the filter context.
 */
__LJMP static int hlua_channel_get(lua_State *L)
{
	struct channel *chn;
	struct filter *filter;
	size_t offset, len;
	int ret;

	MAY_LJMP(check_args(L, 1, "get"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}

	offset = co_data(chn);
	len = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	if (!ci_data(chn) && channel_input_closed(chn)) {
		lua_pushnil(L);
		return 1;
	}

	ret = _hlua_channel_dup(chn, L, offset, len);
	_hlua_channel_delete(chn, offset, 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 available, it returns nil
 * value.
 *
 * From an action, All input data are considered. For a filter, the offset and
 * the length of input data to consider are retrieved from the filter context.
 */
__LJMP static int hlua_channel_getline_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct channel *chn;
	struct filter *filter;
	size_t l, offset, len;
	int ret;

	chn = MAY_LJMP(hlua_checkchannel(L, 1));

	offset = co_data(chn);
	len = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	if (!ci_data(chn) && channel_input_closed(chn)) {
		lua_pushnil(L);
		return 1;
	}

	for (l = 0; l < len; l++) {
		if (*(b_peek(&chn->buf, offset+l)) == '\n') {
			len = l+1;
			goto dup;
		}
	}

	if (!HLUA_CANT_YIELD(hlua_gethlua(L)) && !channel_input_closed(chn) && channel_may_recv(chn)) {
		/* Yield waiting for more data */
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_getline_yield, TICK_ETERNITY, 0));
	}

  dup:
	ret = _hlua_channel_dup(chn, L, offset, len);
	_hlua_channel_delete(chn, offset, ret);
	return 1;
}

/* [ DEPRECATED ]
 *
 * Check arguments for the function "hlua_channel_getline_yield".
 */
__LJMP static int hlua_channel_getline(lua_State *L)
{
	struct channel *chn;

	MAY_LJMP(check_args(L, 1, "getline"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}
	return MAY_LJMP(hlua_channel_getline_yield(L, 0, 0));
}

/* Retrieves a given amount of input data at the given offset. By default all
 * available input data are returned. The offset may be negactive to start from
 * the end of input data. The length may be -1 to set it to the maximum buffer
 * size.
 */
__LJMP static int hlua_channel_get_data(lua_State *L)
{
	struct channel *chn;

	if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "'data' expects at most 2 arguments"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}
	return MAY_LJMP(hlua_channel_get_data_yield(L, 0, 0));
}

/* Retrieves a given amount of input data at the given offset. By default all
 * available input data are returned. The offset may be negactive to start from
 * the end of input data. The length may be -1 to set it to the maximum buffer
 * size.
 */
__LJMP static int hlua_channel_get_line(lua_State *L)
{
	struct channel *chn;

	if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "'line' expects at most 2 arguments"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}
	return MAY_LJMP(hlua_channel_get_line_yield(L, 0, 0));
}

/* Appends a string into the input side of channel. It returns the length of the
 * written string, or -1 if the channel is closed or if the buffer size is too
 * little for the data. 0 may be returned if nothing is copied. This function
 * does not yield.
 *
 * For a filter, the context is updated on success.
 */
__LJMP static int hlua_channel_append(lua_State *L)
{
	struct channel *chn;
	struct filter *filter;
	const char *str;
	size_t sz, offset, len;
	int ret;

	MAY_LJMP(check_args(L, 2, "append"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}

	offset = co_data(chn);
	len = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
	if (ret > 0 && filter) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;

		flt_update_offsets(filter, chn, ret);
		flt_ctx->cur_len[CHN_IDX(chn)] += ret;
	}
	lua_pushinteger(L, ret);
	return 1;
}

/* Prepends a string into the input side of channel. It returns the length of the
 * written string, or -1 if the channel is closed or if the buffer size is too
 * little for the data. 0 may be returned if nothing is copied. This function
 * does not yield.
 *
 * For a filter, the context is updated on success.
 */
__LJMP static int hlua_channel_prepend(lua_State *L)
{
	struct channel *chn;
	struct filter *filter;
	const char *str;
	size_t sz, offset, len;
	int ret;

	MAY_LJMP(check_args(L, 2, "prepend"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}

	offset = co_data(chn);
	len = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
	if (ret > 0 && filter) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;

		flt_update_offsets(filter, chn, ret);
		flt_ctx->cur_len[CHN_IDX(chn)] += ret;
	}

	lua_pushinteger(L, ret);
	return 1;
}

/* Inserts a given amount of input data at the given offset by a string
 * content. By default the string is appended in front of input data. It
 * returns the length of the written string, or -1 if the channel is closed or
 * if the buffer size is too little for the data.
 *
 * For a filter, the context is updated on success.
 */
__LJMP static int hlua_channel_insert_data(lua_State *L)
{
	struct channel *chn;
	struct filter *filter;
	const char *str;
	size_t sz, input, output;
	int ret, offset;

	if (lua_gettop(L) < 2 || lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "'insert' expects at least 1 argument and at most 2 arguments"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));

	output = co_data(chn);
	input = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &output, &input);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 2) {
		offset = MAY_LJMP(luaL_checkinteger(L, 3));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset > output + input) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}

	ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
	if (ret > 0 && filter) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;

		flt_update_offsets(filter, chn, ret);
		flt_ctx->cur_len[CHN_IDX(chn)] += ret;
	}

	lua_pushinteger(L, ret);
	return 1;
}
/* Replaces a given amount of input data at the given offset by a string
 * content. By default all remaining data are removed (offset = 0 and len =
 * -1). It returns the length of the written string, or -1 if the channel is
 * closed or if the buffer size is too little for the data.
 *
 * For a filter, the context is updated on success.
 */
__LJMP static int hlua_channel_set_data(lua_State *L)
{
	struct channel *chn;
	struct filter *filter;
	const char *str;
	size_t sz, input, output;
	int ret, offset, len;

	if (lua_gettop(L) < 2 || lua_gettop(L) > 4)
		WILL_LJMP(luaL_error(L, "'set' expects at least 1 argument and at most 3 arguments"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));

	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}

	output = co_data(chn);
	input = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &output, &input);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 2) {
		offset = MAY_LJMP(luaL_checkinteger(L, 3));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset < output || offset > input + output) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	len = output + input - offset;
	if (lua_gettop(L) == 4) {
		len = MAY_LJMP(luaL_checkinteger(L, 4));
		if (!len)
			goto set;
		if (len == -1)
			len = output + input - offset;
		if (len < 0 || offset + len > output + input) {
			lua_pushfstring(L, "length out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

  set:
	/* Be sure we can copied the string once input data will be removed. */
	if (sz > c_room(chn) + len)
		lua_pushinteger(L, -1);
	else {
		_hlua_channel_delete(chn, offset, len);
		ret = _hlua_channel_insert(chn, L, ist2(str, sz), offset);
		if (filter) {
			struct hlua_flt_ctx *flt_ctx = filter->ctx;

			len -= (ret > 0 ? ret : 0);
			flt_update_offsets(filter, chn, -len);
			flt_ctx->cur_len[CHN_IDX(chn)] -= len;
		}

		lua_pushinteger(L, ret);
	}
	return 1;
}

/* Removes a given amount of input data at the given offset. By default all
 * input data are removed (offset = 0 and len = -1). It returns the amount of
 * the removed data.
 *
 * For a filter, the context is updated on success.
 */
__LJMP static int hlua_channel_del_data(lua_State *L)
{
	struct channel *chn;
	struct filter *filter;
	size_t input, output;
	int offset, len;

	if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "'remove' expects at most 2 arguments"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));

	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}

	output = co_data(chn);
	input = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &output, &input);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 1) {
		offset = MAY_LJMP(luaL_checkinteger(L, 2));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset < output || offset > input + output) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	len = output + input - offset;
	if (lua_gettop(L) == 3) {
		len = MAY_LJMP(luaL_checkinteger(L, 3));
		if (!len)
			goto end;
		if (len == -1)
			len = output + input - offset;
		if (len < 0 || offset + len > output + input) {
			lua_pushfstring(L, "length out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	_hlua_channel_delete(chn, offset, len);
	if (filter) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;

		flt_update_offsets(filter, chn, -len);
		flt_ctx->cur_len[CHN_IDX(chn)] -= len;
	}

  end:
	lua_pushinteger(L, len);
	return 1;
}

/* Append data in the output side of the buffer. This data is immediately
 * sent. The function returns the amount of data written. If the buffer
 * cannot contain the data, the function yields. The function returns -1
 * if the channel is closed.
 */
__LJMP static int hlua_channel_send_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct channel *chn;
	struct filter *filter;
	const char *str;
	size_t offset, len, sz;
	int l, ret;
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua) {
		lua_pushnil(L);
		return 1;
	}

	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	l = MAY_LJMP(luaL_checkinteger(L, 3));

	offset = co_data(chn);
	len = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));


	if (unlikely(channel_output_closed(chn))) {
		lua_pushinteger(L, -1);
		return 1;
	}

	len = c_room(chn);
	if (len > sz -l) {
		if (filter) {
			lua_pushinteger(L, -1);
			return 1;
		}
		len = sz - l;
	}

	ret = _hlua_channel_insert(chn, L, ist2(str, len), offset);
	if (ret == -1) {
		lua_pop(L, 1);
		lua_pushinteger(L, -1);
		return 1;
	}
	if (ret) {
		if (filter) {
			struct hlua_flt_ctx *flt_ctx = filter->ctx;


			flt_update_offsets(filter, chn, ret);
			FLT_OFF(filter, chn) += ret;
			flt_ctx->cur_off[CHN_IDX(chn)] += ret;
		}
		else
			c_adv(chn, ret);

		l += ret;
		lua_pop(L, 1);
		lua_pushinteger(L, l);
	}

	if (l < sz) {
		/* Yield only if the channel's output is not empty.
		 * Otherwise it means we cannot add more data. */
		if (co_data(chn) == 0 || HLUA_CANT_YIELD(hlua_gethlua(L)))
			return 1;

		/* 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->flags & CF_ISRESP)
			HLUA_SET_WAKERESWR(hlua);
		else
			HLUA_SET_WAKEREQWR(hlua);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_channel_send_yield, TICK_ETERNITY, 0));
	}

	return 1;
}

/* Just a wrapper of "_hlua_channel_send". This wrapper permits
 * yield the LUA process, and resume it without checking the
 * input arguments.
 *
 * This function cannot be called from a filter.
 */
__LJMP static int hlua_channel_send(lua_State *L)
{
	struct channel *chn;

	MAY_LJMP(check_args(L, 2, "send"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}
	lua_pushinteger(L, 0);
	return MAY_LJMP(hlua_channel_send_yield(L, 0, 0));
}

/* 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
 * input. It returns the number of bytes forwarded.
 */
__LJMP static int hlua_channel_forward_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct channel *chn;
	struct filter *filter;
	size_t offset, len, fwd;
	int l, max;
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua) {
		lua_pushnil(L);
		return 1;
	}

	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	fwd = MAY_LJMP(luaL_checkinteger(L, 2));
	l = MAY_LJMP(luaL_checkinteger(L, -1));

	offset = co_data(chn);
	len = ci_data(chn);

	filter = hlua_channel_filter(L, 1, chn, &offset, &len);
	if (filter && !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	max = fwd - l;
	if (max > len)
		max = len;

	if (filter) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;

		FLT_OFF(filter, chn) += max;
		flt_ctx->cur_off[CHN_IDX(chn)] += max;
		flt_ctx->cur_len[CHN_IDX(chn)] -= max;
	}
	else
		channel_forward(chn, max);

	l += max;
	lua_pop(L, 1);
	lua_pushinteger(L, l);

	/* Check if it miss bytes to forward. */
	if (l < fwd) {
		/* The the input channel or the output channel are closed, we
		 * must return the amount of data forwarded.
		 */
		if (channel_input_closed(chn) || channel_output_closed(chn) ||  HLUA_CANT_YIELD(hlua_gethlua(L)))
			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->flags & CF_ISRESP)
			HLUA_SET_WAKERESWR(hlua);
		else
			HLUA_SET_WAKEREQWR(hlua);

		/* Otherwise, we can yield waiting for new data in the input side. */
		MAY_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"
 *
 * This function cannot be called from a filter.
 */
__LJMP static int hlua_channel_forward(lua_State *L)
{
	struct channel *chn;

	MAY_LJMP(check_args(L, 2, "forward"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	if (IS_HTX_STRM(chn_strm(chn))) {
		lua_pushfstring(L, "Cannot manipulate HAProxy channels in HTTP mode.");
		WILL_LJMP(lua_error(L));
	}
	lua_pushinteger(L, 0);
	return MAY_LJMP(hlua_channel_forward_yield(L, 0, 0));
}

/* 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 channel *chn;
	struct filter *filter;
	size_t output, input;

	MAY_LJMP(check_args(L, 1, "input"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));

	output = co_data(chn);
	input = ci_data(chn);
	filter = hlua_channel_filter(L, 1, chn, &output, &input);
	if (filter || !IS_HTX_STRM(chn_strm(chn)))
		lua_pushinteger(L, input);
	else {
		struct htx *htx = htxbuf(&chn->buf);

		lua_pushinteger(L, htx->data - co_data(chn));
	}
	return 1;
}

/* Returns true if the channel is full. */
__LJMP static int hlua_channel_is_full(lua_State *L)
{
	struct channel *chn;

	MAY_LJMP(check_args(L, 1, "is_full"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	/* ignore the reserve, we are not on a producer side (ie in an
	 * applet).
	 */
	lua_pushboolean(L, channel_full(chn, 0));
	return 1;
}

/* Returns true if the channel may still receive data. */
__LJMP static int hlua_channel_may_recv(lua_State *L)
{
	struct channel *chn;

	MAY_LJMP(check_args(L, 1, "may_recv"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));
	lua_pushboolean(L, (!channel_input_closed(chn) && channel_may_recv(chn)));
	return 1;
}

/* Returns true if the channel is the response channel. */
__LJMP static int hlua_channel_is_resp(lua_State *L)
{
	struct channel *chn;

	MAY_LJMP(check_args(L, 1, "is_resp"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));

	lua_pushboolean(L, !!(chn->flags & CF_ISRESP));
	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 channel *chn;
	size_t output, input;

	MAY_LJMP(check_args(L, 1, "output"));
	chn = MAY_LJMP(hlua_checkchannel(L, 1));

	output = co_data(chn);
	input = ci_data(chn);
	hlua_channel_filter(L, 1, chn, &output, &input);

	lua_pushinteger(L, output);
	return 1;
}

/*
 *
 *
 * Class Fetches
 *
 *
 */

/* Returns a struct hlua_session if the stack entry "ud" is
 * a class stream, otherwise it throws an error.
 */
__LJMP static struct hlua_smp *hlua_checkfetches(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_fetches_ref));
}

/* This function creates and push in the stack a fetch object according
 * with a current TXN.
 */
__LJMP static int hlua_fetches_new(lua_State *L, struct hlua_txn *txn, unsigned int flags)
{
	struct hlua_smp *hsmp;

	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	/* Create the object: obj[0] = userdata.
	 * Note that the base of the Fetches object is the
	 * transaction object.
	 */
	lua_newtable(L);
	hsmp = lua_newuserdata(L, sizeof(*hsmp));
	lua_rawseti(L, -2, 0);

	hsmp->s = txn->s;
	hsmp->p = txn->p;
	hsmp->dir = txn->dir;
	hsmp->flags = flags;

	/* Pop a class sesison metatable and affect it to the userdata. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_fetches_ref);
	lua_setmetatable(L, -2);

	return 1;
}

/* 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_smp *hsmp;
	struct sample_fetch *f;
	char *errmsg = NULL;
	struct arg args[ARGM_NBARGS + 1] = {{0}};
	int i;
	struct sample smp;

	/* Get closure arguments. */
	f = lua_touserdata(L, lua_upvalueindex(1));

	/* Get traditional arguments. */
	hsmp = MAY_LJMP(hlua_checkfetches(L, 1));

	/* Check execution authorization. */
	if ((f->use & (SMP_USE_L6REQ|SMP_USE_L6RES|SMP_USE_HTTP_ANY)) &&
	    !(hsmp->flags & HLUA_F_MAY_USE_CHANNELS_DATA)) {
		lua_pushfstring(L, "the sample-fetch '%s' needs to access data from channel's buffer which"
				" is not available in Lua services", f->kw);
		WILL_LJMP(lua_error(L));
	}

	/* 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;
	args[i].data.str.area = NULL;

	/* Check arguments. */
	MAY_LJMP(hlua_lua2arg_check(L, 2, args, f->arg_mask, hsmp->p));

	/* Run the special args checker. */
	if (f->val_args && !f->val_args(args, &errmsg)) {
		hlua_pushfstring_safe(L, "error in arguments: %s", errmsg);
		ha_free(&errmsg);
		goto error;
	}

	/* Initialise the sample. */
	memset(&smp, 0, sizeof(smp));

	/* Run the sample fetch process. */
	smp_set_owner(&smp, hsmp->p, hsmp->s->sess, hsmp->s, hsmp->dir & SMP_OPT_DIR);
	if (!EXEC_CTX_WITH_RET(f->exec_ctx, f->process(args, &smp, f->kw, f->private))) {
		if (hsmp->flags & HLUA_F_AS_STRING)
			lua_pushstring(L, "");
		else
			lua_pushnil(L);
		goto end;
	}

	/* Convert the returned sample in lua value. */
	if (hsmp->flags & HLUA_F_AS_STRING)
		MAY_LJMP(hlua_smp2lua_str(L, &smp));
	else
		MAY_LJMP(hlua_smp2lua(L, &smp));

  end:
	free_args(args);
	return 1;

  error:
	free_args(args);
	WILL_LJMP(lua_error(L));
	return 0; /* Never reached */
}

/*
 *
 *
 * Class Converters
 *
 *
 */

/* Returns a struct hlua_session if the stack entry "ud" is
 * a class stream, otherwise it throws an error.
 */
__LJMP static struct hlua_smp *hlua_checkconverters(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_converters_ref));
}

/* This function creates and push in the stack a Converters object
 * according with a current TXN.
 */
__LJMP static int hlua_converters_new(lua_State *L, struct hlua_txn *txn, unsigned int flags)
{
	struct hlua_smp *hsmp;

	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	/* Create the object: obj[0] = userdata.
	 * Note that the base of the Converters object is the
	 * same than the TXN object.
	 */
	lua_newtable(L);
	hsmp = lua_newuserdata(L, sizeof(*hsmp));
	lua_rawseti(L, -2, 0);

	hsmp->s = txn->s;
	hsmp->p = txn->p;
	hsmp->dir = txn->dir;
	hsmp->flags = flags;

	/* Pop a class stream metatable and affect it to the table. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_converters_ref);
	lua_setmetatable(L, -2);

	return 1;
}

/* This function is an LUA binding. It is called with each converter.
 * It uses closure argument to store the associated converter. 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 converter function function fails, nil is returned.
 */
__LJMP static int hlua_run_sample_conv(lua_State *L)
{
	struct hlua_smp *hsmp;
	struct sample_conv *conv;
	char *errmsg = NULL;
	struct arg args[ARGM_NBARGS + 1] = {{0}};
	int i;
	struct sample smp;

	/* Get closure arguments. */
	conv = lua_touserdata(L, lua_upvalueindex(1));

	/* Get traditional arguments. */
	hsmp = MAY_LJMP(hlua_checkconverters(L, 1));

	/* Get extra arguments. */
	for (i = 0; i < lua_gettop(L) - 2; i++) {
		if (i >= ARGM_NBARGS)
			break;
		hlua_lua2arg(L, i + 3, &args[i]);
	}
	args[i].type = ARGT_STOP;
	args[i].data.str.area = NULL;

	/* Check arguments. */
	MAY_LJMP(hlua_lua2arg_check(L, 3, args, conv->arg_mask, hsmp->p));

	/* Run the special args checker. */
	if (conv->val_args && !conv->val_args(args, conv, "", 0, &errmsg)) {
		hlua_pushfstring_safe(L, "error in arguments: %s", errmsg);
		ha_free(&errmsg);
		goto error;
	}

	/* Initialise the sample. */
	memset(&smp, 0, sizeof(smp));
	if (!hlua_lua2smp(L, 2, &smp)) {
		hlua_pusherror(L, "error in the input argument");
		goto error;
	}

	smp_set_owner(&smp, hsmp->p, hsmp->s->sess, hsmp->s, hsmp->dir & SMP_OPT_DIR);

	/* Apply expected cast. */
	if (!sample_casts[smp.data.type][conv->in_type]) {
		hlua_pusherror(L, "invalid input argument: cannot cast '%s' to '%s'",
		               smp_to_type[smp.data.type], smp_to_type[conv->in_type]);
		goto error;
	}
	if (sample_casts[smp.data.type][conv->in_type] != c_none &&
	    !sample_casts[smp.data.type][conv->in_type](&smp)) {
		hlua_pusherror(L, "error during the input argument casting");
		goto error;
	}

	/* Run the sample conversion process. */
	if (!EXEC_CTX_WITH_RET(conv->exec_ctx, conv->process(args, &smp, conv->private))) {
		if (hsmp->flags & HLUA_F_AS_STRING)
			lua_pushstring(L, "");
		else
			lua_pushnil(L);
		goto end;
	}

	/* Convert the returned sample in lua value. */
	if (hsmp->flags & HLUA_F_AS_STRING)
		MAY_LJMP(hlua_smp2lua_str(L, &smp));
	else
		MAY_LJMP(hlua_smp2lua(L, &smp));
  end:
	free_args(args);
	return 1;

  error:
	free_args(args);
	WILL_LJMP(lua_error(L));
	return 0; /* Never reached */
}

/*
 *
 *
 * Class AppletTCP
 *
 *
 */

/* Returns a struct hlua_txn if the stack entry "ud" is
 * a class stream, otherwise it throws an error.
 */
__LJMP static struct hlua_appctx *hlua_checkapplet_tcp(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_applet_tcp_ref));
}

/* This function creates and push in the stack an Applet object
 * according with a current TXN.
 */
static int hlua_applet_tcp_new(lua_State *L, struct appctx *ctx)
{
	struct hlua_appctx *luactx;
	struct stream *s = ASSUME_NONNULL(appctx_strm(ctx));
	struct proxy *p;

	p = s->be;

	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	/* Create the object: obj[0] = userdata.
	 * Note that the base of the Converters object is the
	 * same than the TXN object.
	 */
	lua_newtable(L);
	luactx = lua_newuserdata(L, sizeof(*luactx));
	lua_rawseti(L, -2, 0);
	luactx->appctx = ctx;
	luactx->htxn.s = s;
	luactx->htxn.p = p;

	/* Create the "f" field that contains a list of fetches. */
	lua_pushstring(L, "f");
	if (!hlua_fetches_new(L, &luactx->htxn, 0))
		return 0;
	lua_settable(L, -3);

	/* Create the "sf" field that contains a list of stringsafe fetches. */
	lua_pushstring(L, "sf");
	if (!hlua_fetches_new(L, &luactx->htxn, HLUA_F_AS_STRING))
		return 0;
	lua_settable(L, -3);

	/* Create the "c" field that contains a list of converters. */
	lua_pushstring(L, "c");
	if (!hlua_converters_new(L, &luactx->htxn, 0))
		return 0;
	lua_settable(L, -3);

	/* Create the "sc" field that contains a list of stringsafe converters. */
	lua_pushstring(L, "sc");
	if (!hlua_converters_new(L, &luactx->htxn, HLUA_F_AS_STRING))
		return 0;
	lua_settable(L, -3);

	/* Pop a class stream metatable and affect it to the table. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_applet_tcp_ref);
	lua_setmetatable(L, -2);

	return 1;
}

__LJMP static int hlua_applet_tcp_set_var(lua_State *L)
{
	struct hlua_appctx *luactx;
	struct stream *s;
	const char *name;
	size_t len;
	struct sample smp;

	if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
		WILL_LJMP(luaL_error(L, "'set_var' needs between 3 and 4 arguments"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));
	s = luactx->htxn.s;

	/* Converts the third argument in a sample. */
	memset(&smp, 0, sizeof(smp));
	hlua_lua2smp(L, 3, &smp);

	/* Store the sample in a variable. We don't need to dup the smp, vars API
	 * already takes care of duplicating dynamic var data.
	 */
	smp_set_owner(&smp, s->be, s->sess, s, 0);

	if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
		lua_pushboolean(L, vars_set_by_name_ifexist(name, len, &smp) != 0);
	else
		lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);

	return 1;
}

__LJMP static int hlua_applet_tcp_unset_var(lua_State *L)
{
	struct hlua_appctx *luactx;
	struct stream *s;
	const char *name;
	size_t len;
	struct sample smp;

	MAY_LJMP(check_args(L, 2, "unset_var"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));
	s = luactx->htxn.s;

	/* Unset the variable. */
	smp_set_owner(&smp, s->be, s->sess, s, 0);
	lua_pushboolean(L, vars_unset_by_name_ifexist(name, len, &smp) != 0);
	return 1;
}

__LJMP static int hlua_applet_tcp_get_var(lua_State *L)
{
	struct hlua_appctx *luactx;
	struct stream *s;
	const char *name;
	size_t len;
	struct sample smp;

	MAY_LJMP(check_args(L, 2, "get_var"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));
	s = luactx->htxn.s;

	smp_set_owner(&smp, s->be, s->sess, s, 0);
	if (!vars_get_by_name(name, len, &smp, NULL)) {
		lua_pushnil(L);
		return 1;
	}

	return MAY_LJMP(hlua_smp2lua(L, &smp));
}

__LJMP static int hlua_applet_tcp_set_priv(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	struct hlua_cli_ctx *cli_ctx = luactx->appctx->svcctx;
	struct stream *s = luactx->htxn.s;
	struct hlua *hlua = hlua_stream_ctx_get(s, cli_ctx->hlua->state_id);

	/* Note that this hlua struct is from the session and not from the applet. */
	if (!hlua)
		return 0;

	MAY_LJMP(check_args(L, 2, "set_priv"));

	/* Remove previous value. */
	luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);

	/* 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_applet_tcp_get_priv(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	struct hlua_cli_ctx *cli_ctx = luactx->appctx->svcctx;
	struct stream *s = luactx->htxn.s;
	struct hlua *hlua = hlua_stream_ctx_get(s, cli_ctx->hlua->state_id);

	/* Note that this hlua struct is from the session and not from the applet. */
	if (!hlua) {
		lua_pushnil(L);
		return 1;
	}

	/* Push configuration index in the stack. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, hlua->Mref);

	return 1;
}

/* If expected data not yet available, it returns a yield. This function
 * consumes the data in the buffer. It returns a string containing the
 * data. This string can be empty.
 */
__LJMP static int hlua_applet_tcp_getline_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	int ret;
	const char *blk1 = NULL;
	size_t len1 = 0;
	const char *blk2 = NULL;
	size_t len2 = 0;

	/* Read the maximum amount of data available. */
	ret = applet_getline_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);

	/* Data not yet available. return yield. */
	if (ret == 0) {
		applet_need_more_data(luactx->appctx);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_getline_yield, TICK_ETERNITY, 0));
		return 0;
	}

	/* End of data: commit the total strings and return. */
	if (ret < 0) {
		/* Stop to consume */
		applet_wont_consume(luactx->appctx);

		luaL_pushresult(&luactx->b);
		return 1;
	}

	/* Ensure that the block 2 length is usable. */
	if (ret == 1)
		len2 = 0;

	/* don't check the max length read and don't check. */
	luaL_addlstring(&luactx->b, blk1, len1);
	if (len2)
		luaL_addlstring(&luactx->b, blk2, len2);

	applet_skip_input(luactx->appctx, len1+len2);

	/* Stop to consume until the next receive */
	applet_wont_consume(luactx->appctx);

	luaL_pushresult(&luactx->b);
	return 1;
}

/* Check arguments for the function "hlua_channel_get_yield". */
__LJMP static int hlua_applet_tcp_getline(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));

	/* Restart to consume - could have been disabled by a previous receive */
	applet_will_consume(luactx->appctx);

	/* Initialise the string catenation. */
	luaL_buffinit(L, &luactx->b);

	return MAY_LJMP(hlua_applet_tcp_getline_yield(L, 0, 0));
}

/* returns 1 on success (number of bytes received is pushed on the stack)
 * or 0 if yielding if required (not enough data available)
 */
__LJMP static int hlua_applet_tcp_recv_try(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	size_t len = MAY_LJMP(luaL_checkinteger(L, 2));
	int exp_date = MAY_LJMP(luaL_checkinteger(L, 3));
	int ret;
	const char *blk1 = NULL;
	size_t len1 = 0;
	const char *blk2 =  NULL;
	size_t len2 = 0;

	/* Read the maximum amount of data available. */
	ret = applet_getblk_nc(luactx->appctx, &blk1, &len1, &blk2, &len2);

	/* Data not yet available. return yield. */
	if (ret == 0) {
		if (tick_is_expired(exp_date, now_ms)) {
			/* Stop to consume until the next receive */
			applet_wont_consume(luactx->appctx);

			/* return the result. */
			lua_pushnil(L);
			return 1;
		}

		applet_need_more_data(luactx->appctx);
		return 0;
	}

	/* End of data: commit the total strings and return. */
	if (ret < 0) {
		/* Stop to consume */
		applet_wont_consume(luactx->appctx);

		luaL_pushresult(&luactx->b);
		return 1;
	}

	/* Ensure that the block 2 length is usable. */
	if (ret == 1)
		len2 = 0;

	if (len == -1) {

		/* If len == -1, catenate all the data avalaile and
		 * yield because we want to get all the data until
		 * the end of data stream.
		 */
		luaL_addlstring(&luactx->b, blk1, len1);
		if (len2)
			luaL_addlstring(&luactx->b, blk2, len2);
		applet_skip_input(luactx->appctx, len1+len2);

		if (tick_is_expired(exp_date, now_ms)) {
			/* Stop to consume until the next receive */
			applet_wont_consume(luactx->appctx);

			/* return the result. */
			luaL_pushresult(&luactx->b);
			return 1;
		}

		applet_need_more_data(luactx->appctx);
		return 0;

	} else {

		/* Copy the first block caping to the length required. */
		if (len1 > len)
			len1 = len;
		luaL_addlstring(&luactx->b, blk1, len1);
		len -= len1;

		/* Copy the second block. */
		if (len2) {
			if (len2 > len)
				len2 = len;
			luaL_addlstring(&luactx->b, blk2, len2);
			len -= len2;
		}

		applet_skip_input(luactx->appctx, len1+len2);

		/* If there is no other data available, yield waiting for new data. */
		if (len > 0 && !tick_is_expired(exp_date, now_ms)) {
			lua_pushinteger(L, len);
			lua_replace(L, 2);
			applet_need_more_data(luactx->appctx);
			return 0;
		}

		/* Stop to consume until the next receive */
		applet_wont_consume(luactx->appctx);

		/* return the result. */
		luaL_pushresult(&luactx->b);
		return 1;
	}

	/* we never execute this */
	hlua_pusherror(L, "Lua: internal error");
	WILL_LJMP(lua_error(L));
	return 0;
}

/* If expected data not yet available, it returns a yield. This function
 * consumes the data in the buffer. It returns a string containing the
 * data. This string can be empty.
 */
__LJMP static int hlua_applet_tcp_recv_yield(lua_State *L, int status, lua_KContext ctx)
{
	int exp_date = MAY_LJMP(luaL_checkinteger(L, 3));
	int ret;

	ret = hlua_applet_tcp_recv_try(L);

	if (ret == 0)
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_recv_yield, exp_date, 0));

	return ret;
}

/* Check arguments for the function "hlua_channel_get_yield". */
__LJMP static int hlua_applet_tcp_recv(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	int exp_date;
	int delay = 0;
	int len = -1;

	if (lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "The 'recv' function requires between 1 and 3 arguments."));
	if (lua_gettop(L) >= 2) {
		if (lua_gettop(L) >= 3) {
			delay = MAY_LJMP(luaL_checkinteger(L, 3));
			lua_pop(L, 1);
		}
		len = MAY_LJMP(luaL_checkinteger(L, 2));
		lua_pop(L, 1);
	}

	/* Confirm or set the required length */
	lua_pushinteger(L, len);

	/* set the expiration date */
	exp_date = delay ? tick_add(now_ms, delay) : TICK_ETERNITY;
	lua_pushinteger(L, exp_date);

	/* Restart to consume - could have been disabled by a previous receive */
	applet_will_consume(luactx->appctx);

	/* Initialise the string catenation. */
	luaL_buffinit(L, &luactx->b);

	return MAY_LJMP(hlua_applet_tcp_recv_yield(L, 0, 0));
}

/* Check arguments for the function "hlua_channel_get_yield". */
__LJMP static int hlua_applet_tcp_try_recv(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	int ret;

	if (lua_gettop(L) > 1)
		WILL_LJMP(luaL_error(L, "The 'try_recv' function only expects applet argument."));

	/* Confirm or set the required length */
	lua_pushinteger(L, -1);

	/* set the expiration date (mandatory arg but not relevant here) */
	lua_pushinteger(L, now_ms);

	/* Restart to consume - could have been disabled by a previous receive */
	applet_will_consume(luactx->appctx);

	/* Initialise the string catenation. */
	luaL_buffinit(L, &luactx->b);

	ret = hlua_applet_tcp_recv_try(L);
	if (!ret) {
		/* return the result (may be empty). */
		luaL_pushresult(&luactx->b);
		return 1;
	}

	return ret;
}

/* Append data in the output side of the buffer. This data is immediately
 * sent. The function returns the amount of data written. If the buffer
 * cannot contain the data, the function yields. The function returns -1
 * if the channel is closed.
 */
__LJMP static int hlua_applet_tcp_send_yield(lua_State *L, int status, lua_KContext ctx)
{
	size_t len;
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_tcp(L, 1));
	const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
	int l = MAY_LJMP(luaL_checkinteger(L, 3));
	int max;

	if (!applet_get_outbuf(luactx->appctx))
		goto wait;

	/* Get the max amount of data which can be written */
	max = applet_output_room(luactx->appctx);

	if (max > (len - l))
		max = len - l;

	/* Copy data. */
	applet_putblk(luactx->appctx, str + l, max);

	/* update counters. */
	l += max;
	lua_pop(L, 1);
	lua_pushinteger(L, l);

	/* If some data is not send, declares the situation to the
	 * applet, and returns a yield.
	 */
	if (l < len) {
	  wait:
		applet_have_more_data(luactx->appctx);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_tcp_send_yield, TICK_ETERNITY, 0));
	}

	return 1;
}

/* Just a wrapper of "hlua_applet_tcp_send_yield". This wrapper permits
 * yield the LUA process, and resume it without checking the
 * input arguments.
 */
__LJMP static int hlua_applet_tcp_send(lua_State *L)
{
	MAY_LJMP(check_args(L, 2, "send"));
	lua_pushinteger(L, 0);

	return MAY_LJMP(hlua_applet_tcp_send_yield(L, 0, 0));
}

/*
 *
 *
 * Class AppletHTTP
 *
 *
 */

/* Returns a struct hlua_txn if the stack entry "ud" is
 * a class stream, otherwise it throws an error.
 */
__LJMP static struct hlua_appctx *hlua_checkapplet_http(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_applet_http_ref));
}

/* This function creates and push in the stack an Applet object
 * according with a current TXN.
 * It relies on the caller to have already reserved the room in ctx->svcctx
 * for the local storage of hlua_http_ctx.
 */
static int hlua_applet_http_new(lua_State *L, struct appctx *ctx)
{
	struct hlua_http_ctx *http_ctx = ctx->svcctx;
	struct hlua_appctx *luactx;
	struct stream *s = appctx_strm(ctx);
	struct proxy *px = s->be;

	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	/* Create the object: obj[0] = userdata.
	 * Note that the base of the Converters object is the
	 * same than the TXN object.
	 */
	lua_newtable(L);
	luactx = lua_newuserdata(L, sizeof(*luactx));
	lua_rawseti(L, -2, 0);
	luactx->appctx = ctx;
	http_ctx->status = 200; /* Default status code returned. */
	http_ctx->reason = NULL; /* Use default reason based on status */
	luactx->htxn.s = s;
	luactx->htxn.p = px;

	/* Create the "f" field that contains a list of fetches. */
	lua_pushstring(L, "f");
	if (!hlua_fetches_new(L, &luactx->htxn, 0))
		return 0;
	lua_settable(L, -3);

	/* Create the "sf" field that contains a list of stringsafe fetches. */
	lua_pushstring(L, "sf");
	if (!hlua_fetches_new(L, &luactx->htxn, HLUA_F_AS_STRING))
		return 0;
	lua_settable(L, -3);

	/* Create the "c" field that contains a list of converters. */
	lua_pushstring(L, "c");
	if (!hlua_converters_new(L, &luactx->htxn, 0))
		return 0;
	lua_settable(L, -3);

	/* Create the "sc" field that contains a list of stringsafe converters. */
	lua_pushstring(L, "sc");
	if (!hlua_converters_new(L, &luactx->htxn, HLUA_F_AS_STRING))
		return 0;
	lua_settable(L, -3);

	/* Pop a class stream metatable and affect it to the table. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_applet_http_ref);
	lua_setmetatable(L, -2);

	return 1;
}

__LJMP static int hlua_applet_http_set_var(lua_State *L)
{
	struct hlua_appctx *luactx;
	struct stream *s;
	const char *name;
	size_t len;
	struct sample smp;

	if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
		WILL_LJMP(luaL_error(L, "'set_var' needs between 3 and 4 arguments"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));
	s = luactx->htxn.s;

	/* Converts the third argument in a sample. */
	memset(&smp, 0, sizeof(smp));
	hlua_lua2smp(L, 3, &smp);

	/* Store the sample in a variable. We don't need to dup the smp, vars API
	 * already takes care of duplicating dynamic var data.
	 */
	smp_set_owner(&smp, s->be, s->sess, s, 0);

	if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
		lua_pushboolean(L, vars_set_by_name_ifexist(name, len, &smp) != 0);
	else
		lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);

	return 1;
}

__LJMP static int hlua_applet_http_unset_var(lua_State *L)
{
	struct hlua_appctx *luactx;
	struct stream *s;
	const char *name;
	size_t len;
	struct sample smp;

	MAY_LJMP(check_args(L, 2, "unset_var"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));
	s = luactx->htxn.s;

	/* Unset the variable. */
	smp_set_owner(&smp, s->be, s->sess, s, 0);
	lua_pushboolean(L, vars_unset_by_name_ifexist(name, len, &smp) != 0);
	return 1;
}

__LJMP static int hlua_applet_http_get_var(lua_State *L)
{
	struct hlua_appctx *luactx;
	struct stream *s;
	const char *name;
	size_t len;
	struct sample smp;

	MAY_LJMP(check_args(L, 2, "get_var"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));
	s = luactx->htxn.s;

	smp_set_owner(&smp, s->be, s->sess, s, 0);
	if (!vars_get_by_name(name, len, &smp, NULL)) {
		lua_pushnil(L);
		return 1;
	}

	return MAY_LJMP(hlua_smp2lua(L, &smp));
}

__LJMP static int hlua_applet_http_set_priv(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	struct hlua_http_ctx *http_ctx = luactx->appctx->svcctx;
	struct stream *s = luactx->htxn.s;
	struct hlua *hlua = hlua_stream_ctx_get(s, http_ctx->hlua->state_id);

	/* Note that this hlua struct is from the session and not from the applet. */
	if (!hlua)
		return 0;

	MAY_LJMP(check_args(L, 2, "set_priv"));

	/* Remove previous value. */
	luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);

	/* 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_applet_http_get_priv(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	struct hlua_http_ctx *http_ctx = luactx->appctx->svcctx;
	struct stream *s = luactx->htxn.s;
	struct hlua *hlua = hlua_stream_ctx_get(s, http_ctx->hlua->state_id);

	/* Note that this hlua struct is from the session and not from the applet. */
	if (!hlua) {
		lua_pushnil(L);
		return 1;
	}

	/* Push configuration index in the stack. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, hlua->Mref);

	return 1;
}

/* If expected data not yet available, it returns a yield. This function
 * consumes the data in the buffer. It returns a string containing the
 * data. This string can be empty.
 */
__LJMP static int hlua_applet_http_getline_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	struct hlua_http_ctx *http_ctx = luactx->appctx->svcctx;
	struct buffer *inbuf = applet_get_inbuf(luactx->appctx);
	struct htx *htx;
	struct htx_blk *blk;
	int stop = 0;

	if (http_ctx->flags & APPLET_REQ_RECV)
		goto end;

	if (!inbuf)
		goto wait;

	htx = htx_from_buf(inbuf);
	blk = htx_get_first_blk(htx);
	while (!stop && blk) {
		enum htx_blk_type type = htx_get_blk_type(blk);
		uint32_t sz = htx_get_blksz(blk);
		struct ist v;
		uint32_t vlen;
		char *nl;

		vlen = sz;
		switch (type) {
			case HTX_BLK_UNUSED:
				break;

			case HTX_BLK_DATA:
				v = htx_get_blk_value(htx, blk);
				vlen = v.len;
				nl = istchr(v, '\n');
				if (nl != NULL) {
					stop = 1;
					vlen = nl - v.ptr + 1;
				}
				luaL_addlstring(&luactx->b, v.ptr, vlen);
				break;

			case HTX_BLK_TLR:
			case HTX_BLK_EOT:
				stop = 1;
				break;

			default:
				break;
		}

		if (sz == vlen)
			blk = htx_remove_blk(htx, blk);
		else {
			htx_cut_data_blk(htx, blk, vlen);
			break;
		}
	}

	/* The message was fully consumed and no more data are expected
	 * (EOM flag set).
	 */
	if (htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)) {
		http_ctx->flags |= APPLET_REQ_RECV;
		stop = 1;
	}

	htx_to_buf(htx, inbuf);
	if (!stop) {
	  wait:
		applet_need_more_data(luactx->appctx);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_getline_yield, TICK_ETERNITY, 0));
	}

  end:
	/* Stop to consume until the next receive or the end of the response */
	applet_wont_consume(luactx->appctx);

	/* return the result. */
	luaL_pushresult(&luactx->b);
	return 1;
}


/* Check arguments for the function "hlua_channel_get_yield". */
__LJMP static int hlua_applet_http_getline(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));

	/* Restart to consume - could have been disabled by a previous receive */
	applet_will_consume(luactx->appctx);

	/* Initialise the string catenation. */
	luaL_buffinit(L, &luactx->b);

	return MAY_LJMP(hlua_applet_http_getline_yield(L, 0, 0));
}

/* If expected data not yet available, it returns a yield. This function
 * consumes the data in the buffer. It returns a string containing the
 * data. This string can be empty.
 */
__LJMP static int hlua_applet_http_recv_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	struct hlua_http_ctx *http_ctx = luactx->appctx->svcctx;
	struct buffer *inbuf = applet_get_inbuf(luactx->appctx);
	struct htx *htx;
	struct htx_blk *blk;
	int len;

	if (http_ctx->flags & APPLET_REQ_RECV)
		goto end;

	if (!inbuf)
		goto wait;

	htx = htx_from_buf(inbuf);
	len = MAY_LJMP(luaL_checkinteger(L, 2));
	blk = htx_get_head_blk(htx);
	while (len && blk) {
		enum htx_blk_type type = htx_get_blk_type(blk);
		uint32_t sz = htx_get_blksz(blk);
		struct ist v;
		uint32_t vlen;

		vlen = sz;
		switch (type) {
			case HTX_BLK_UNUSED:
				break;

			case HTX_BLK_DATA:
				v = htx_get_blk_value(htx, blk);
				vlen = v.len;
				if (len > 0 && vlen > len)
					vlen = len;
				luaL_addlstring(&luactx->b, v.ptr, vlen);
				if (len > 0)
					len -= vlen;
				break;

			case HTX_BLK_TLR:
			case HTX_BLK_EOT:
				len = 0;
				break;

			default:
				break;
		}

		if (sz == vlen)
			blk = htx_remove_blk(htx, blk);
		else {
			htx_cut_data_blk(htx, blk, vlen);
			break;
		}
	}

	/* The message was fully consumed and no more data are expected
	 * (EOM flag set).
	 */
	if (htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)) {
		http_ctx->flags |= APPLET_REQ_RECV;
		len = 0;
	}

	htx_to_buf(htx, inbuf);
	applet_fl_clr(luactx->appctx, APPCTX_FL_INBLK_FULL);

	/* If we are no other data available, yield waiting for new data. */
	if (len) {
		if (len > 0) {
			lua_pushinteger(L, len);
			lua_replace(L, 2);
		}
	  wait:
		applet_need_more_data(luactx->appctx);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_recv_yield, TICK_ETERNITY, 0));
	}

  end:
	/* Stop to consume until the next receive or the end of the response */
	applet_wont_consume(luactx->appctx);

	/* return the result. */
	luaL_pushresult(&luactx->b);
	return 1;
}

/* Check arguments for the function "hlua_channel_get_yield". */
__LJMP static int hlua_applet_http_recv(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	int len = -1;

	/* Check arguments. */
	if (lua_gettop(L) > 2)
		WILL_LJMP(luaL_error(L, "The 'recv' function requires between 1 and 2 arguments."));
	if (lua_gettop(L) >= 2) {
		len = MAY_LJMP(luaL_checkinteger(L, 2));
		lua_pop(L, 1);
	}

	lua_pushinteger(L, len);

	/* Restart to consume - could have been disabled by a previous receive */
	applet_will_consume(luactx->appctx);

	/* Initialise the string catenation. */
	luaL_buffinit(L, &luactx->b);
	return MAY_LJMP(hlua_applet_http_recv_yield(L, 0, 0));
}

/* Append data in the output side of the buffer. This data is immediately
 * sent. The function returns the amount of data written. If the buffer
 * cannot contain the data, the function yields. The function returns -1
 * if the channel is closed.
 */
__LJMP static int hlua_applet_http_send_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	struct buffer *outbuf;
	struct htx *htx;
	const char *data;
	size_t len;
	int l = MAY_LJMP(luaL_checkinteger(L, 3));
	int max;

	outbuf = applet_get_outbuf(luactx->appctx);
	if (!outbuf)
		goto snd_yield;

	/* Get the max amount of data which can be written */
	max = applet_htx_output_room(luactx->appctx);
	if (!max)
		goto snd_yield;

	htx = htx_from_buf(outbuf);
	data = MAY_LJMP(luaL_checklstring(L, 2, &len));

	/* Get the max amount of data which can write as input in the channel. */
	if (max > (len - l))
		max = len - l;

	/* Copy data. */
	max = htx_add_data(htx, ist2(data + l, max));

	/* update counters. */
	l += max;
	lua_pop(L, 1);
	lua_pushinteger(L, l);

	/* If some data is not send, declares the situation to the
	 * applet, and returns a yield.
	 */
	if (l < len) {
		applet_fl_set(luactx->appctx, APPCTX_FL_OUTBLK_FULL);
	  snd_yield:
		applet_have_more_data(luactx->appctx);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_send_yield, TICK_ETERNITY, 0));
		return 0;
	}

	htx_to_buf(htx, outbuf);
	return 1;
}

/* Just a wrapper of "hlua_applet_send_yield". This wrapper permits
 * yield the LUA process, and resume it without checking the
 * input arguments.
 */
__LJMP static int hlua_applet_http_send(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	struct hlua_http_ctx *http_ctx = luactx->appctx->svcctx;

	/* We want to send some data. Headers must be sent. */
	if (!(http_ctx->flags & APPLET_HDR_SENT)) {
		hlua_pusherror(L, "Lua: 'send' you must call start_response() before sending data.");
		WILL_LJMP(lua_error(L));
	}

	/* This integer is used for followinf the amount of data sent. */
	lua_pushinteger(L, 0);

	return MAY_LJMP(hlua_applet_http_send_yield(L, 0, 0));
}

__LJMP static int hlua_applet_http_addheader(lua_State *L)
{
	const char *name;
	int ret;

	MAY_LJMP(hlua_checkapplet_http(L, 1));
	name = MAY_LJMP(luaL_checkstring(L, 2));
	MAY_LJMP(luaL_checkstring(L, 3));

	/* Push in the stack the "response" entry. */
	ret = lua_getfield(L, 1, "response");
	if (ret != LUA_TTABLE) {
		hlua_pusherror(L, "Lua: 'add_header' internal error: AppletHTTP['response'] "
		                  "is expected as an array. %s found", lua_typename(L, ret));
		WILL_LJMP(lua_error(L));
	}

	/* check if the header is already registered if it is not
	 * the case, register it.
	 */
	ret = lua_getfield(L, -1, name);
	if (ret == LUA_TNIL) {

		/* Entry not found. */
		lua_pop(L, 1); /* remove the nil. The "response" table is the top of the stack. */

		/* Insert the new header name in the array in the top of the stack.
		 * It left the new array in the top of the stack.
		 */
		lua_newtable(L);
		lua_pushvalue(L, 2);
		lua_pushvalue(L, -2);
		lua_settable(L, -4);

	} else if (ret != LUA_TTABLE) {

		/* corruption error. */
		hlua_pusherror(L, "Lua: 'add_header' internal error: AppletHTTP['response']['%s'] "
		                  "is expected as an array. %s found", name, lua_typename(L, ret));
		WILL_LJMP(lua_error(L));
	}

	/* Now the top of thestack is an array of values. We push
	 * the header value as new entry.
	 */
	lua_pushvalue(L, 3);
	ret = lua_rawlen(L, -2);
	lua_rawseti(L, -2, ret + 1);
	lua_pushboolean(L, 1);
	return 1;
}

__LJMP static int hlua_applet_http_status(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	int status = MAY_LJMP(luaL_checkinteger(L, 2));
	const char *reason = MAY_LJMP(luaL_optlstring(L, 3, NULL, NULL));
	struct hlua_http_ctx *http_ctx = luactx->appctx->svcctx;

	if (status < 100 || status > 599) {
		lua_pushboolean(L, 0);
		return 1;
	}

	http_ctx->status = status;
	/* Anchor the reason string in the registry so the Lua GC can't
	 * collect it before start_response() reads it back. The previous
	 * direct pointer assignment was a use-after-free if a GC ran
	 * between set_status() and start_response().
	 */
	lua_pushlightuserdata(L, &http_ctx->reason);
	if (reason)
		lua_pushvalue(L, 3);
	else
		lua_pushnil(L);
	lua_settable(L, LUA_REGISTRYINDEX);
	http_ctx->reason = reason;
	lua_pushboolean(L, 1);
	return 1;
}


__LJMP static int hlua_applet_http_send_response(lua_State *L)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));
	struct hlua_http_ctx *http_ctx = luactx->appctx->svcctx;
	struct buffer *outbuf;
	struct htx *htx;
	struct htx_sl *sl;
	struct h1m h1m;
	const char *status, *reason;
	const char *name, *value;
	size_t nlen, vlen;
        unsigned int flags;

	/* outbuf is already allocated in hlua_applet_http_start_response() */
	outbuf = DISGUISE(applet_get_outbuf(luactx->appctx));

	/* Send the message at once. */
	htx = htx_from_buf(outbuf);
	h1m_init_res(&h1m);

	/* Use the same http version than the request. */
	status = ultoa_r(http_ctx->status, trash.area, trash.size);
	reason = http_ctx->reason;
	if (reason == NULL)
		reason = http_get_reason(http_ctx->status);
	if (http_ctx->flags & APPLET_HTTP11) {
		flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11);
		sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist(status), ist(reason));
	}
	else {
		flags = HTX_SL_F_IS_RESP;
		sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.0"), ist(status), ist(reason));
	}
	if (!sl) {
		hlua_pusherror(L, "Lua applet http '%s': Failed to create response.\n",
		               luactx->appctx->applet->name);
		WILL_LJMP(lua_error(L));
	}
	sl->info.res.status = http_ctx->status;

	/* Get the array associated to the field "response" in the object AppletHTTP. */
	if (lua_getfield(L, 1, "response") != LUA_TTABLE) {
		hlua_pusherror(L, "Lua applet http '%s': AppletHTTP['response'] missing.\n",
		               luactx->appctx->applet->name);
		WILL_LJMP(lua_error(L));
	}

	/* Browse the list of headers. */
	lua_pushnil(L);
	while(lua_next(L, -2) != 0) {
		/* We expect a string as -2. */
		if (lua_type(L, -2) != LUA_TSTRING) {
			hlua_pusherror(L, "Lua applet http '%s': AppletHTTP['response'][] element must be a string. got %s.\n",
				       luactx->appctx->applet->name,
			               lua_typename(L, lua_type(L, -2)));
			WILL_LJMP(lua_error(L));
		}
		name = lua_tolstring(L, -2, &nlen);

		/* We expect an array as -1. */
		if (lua_type(L, -1) != LUA_TTABLE) {
			hlua_pusherror(L, "Lua applet http '%s': AppletHTTP['response']['%s'] element must be an table. got %s.\n",
				       luactx->appctx->applet->name,
				       name,
			               lua_typename(L, lua_type(L, -1)));
			WILL_LJMP(lua_error(L));
		}

		/* Browse the table who is on the top of the stack. */
		lua_pushnil(L);
		while(lua_next(L, -2) != 0) {
			int id;

			/* We expect a number as -2. */
			if (lua_type(L, -2) != LUA_TNUMBER) {
				hlua_pusherror(L, "Lua applet http '%s': AppletHTTP['response']['%s'][] element must be a number. got %s.\n",
					       luactx->appctx->applet->name,
					       name,
				               lua_typename(L, lua_type(L, -2)));
				WILL_LJMP(lua_error(L));
			}
			id = lua_tointeger(L, -2);

			/* We expect a string as -2. */
			if (lua_type(L, -1) != LUA_TSTRING) {
				hlua_pusherror(L, "Lua applet http '%s': AppletHTTP['response']['%s'][%d] element must be a string. got %s.\n",
					       luactx->appctx->applet->name,
					       name, id,
				               lua_typename(L, lua_type(L, -1)));
				WILL_LJMP(lua_error(L));
			}
			value = lua_tolstring(L, -1, &vlen);

			/* Simple Protocol checks. */
			if (isteqi(ist2(name, nlen), ist("transfer-encoding"))) {
				int ret;

				ret = h1_parse_xfer_enc_header(&h1m, ist2(value, vlen));
				if (ret < 0) {
					hlua_pusherror(L, "Lua applet http '%s': Invalid '%s' header.\n",
						       luactx->appctx->applet->name,
						       name);
					WILL_LJMP(lua_error(L));
				}
				else if (ret == 0)
					goto next; /* Skip it */
			}
			else if (isteqi(ist2(name, nlen), ist("content-length"))) {
				unsigned long long body_len = h1m.body_len;
				struct ist v = ist2(value, vlen);
				int ret;

				ret = http_parse_cont_len_header(&v, &body_len, (h1m.flags & H1_MF_CLEN));
				if (ret < 0) {
					hlua_pusherror(L, "Lua applet http '%s': Invalid '%s' header.\n",
						       luactx->appctx->applet->name,
						       name);
					WILL_LJMP(lua_error(L));
				}
				else if(ret == 0)
					goto next; /* Skip it */
				h1m.flags |= H1_MF_CLEN;
				h1m.curr_len = h1m.body_len = body_len;
			}

			/* Add a new header */
			if (!htx_add_header(htx, ist2(name, nlen), ist2(value, vlen))) {
				hlua_pusherror(L, "Lua applet http '%s': Failed to add header '%s' in the response.\n",
					       luactx->appctx->applet->name,
					       name);
				WILL_LJMP(lua_error(L));
			}
		  next:
			/* Remove the array from the stack, and get next element with a remaining string. */
			lua_pop(L, 1);
		}

		/* Remove the array from the stack, and get next element with a remaining string. */
		lua_pop(L, 1);
	}

	if (h1m.flags & H1_MF_CHNK)
		h1m.flags &= ~H1_MF_CLEN;
	if (h1m.flags & (H1_MF_CLEN|H1_MF_CHNK))
		h1m.flags |= H1_MF_XFER_LEN;

	/* Uset HTX start-line flags */
	if (h1m.flags & H1_MF_XFER_ENC)
		flags |= HTX_SL_F_XFER_ENC;
	if (h1m.flags & H1_MF_XFER_LEN) {
		flags |= HTX_SL_F_XFER_LEN;
		if (h1m.flags & H1_MF_CHNK)
			flags |= HTX_SL_F_CHNK;
		else if (h1m.flags & H1_MF_CLEN)
			flags |= HTX_SL_F_CLEN;
		if (h1m.body_len == 0)
			flags |= HTX_SL_F_BODYLESS;
	}
	sl->flags |= flags;

	/* If we don't have a content-length set, and the HTTP version is 1.1
	 * and the status code implies the presence of a message body, we must
	 * announce a transfer encoding chunked. This is required by haproxy
	 * for the keepalive compliance. If the applet announces a transfer-encoding
	 * chunked itself, don't do anything.
	 */
	if ((flags & (HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN)) == HTX_SL_F_VER_11 &&
	    http_ctx->status >= 200 && http_ctx->status != 204 && http_ctx->status != 304) {
		/* Add a new header */
		sl->flags |= (HTX_SL_F_XFER_ENC|H1_MF_CHNK|H1_MF_XFER_LEN);
		if (!htx_add_header(htx, ist("transfer-encoding"), ist("chunked"))) {
			hlua_pusherror(L, "Lua applet http '%s': Failed to add header 'transfer-encoding' in the response.\n",
				       luactx->appctx->applet->name);
			WILL_LJMP(lua_error(L));
		}
	}

	/* Finalize headers. */
	if (!htx_add_endof(htx, HTX_BLK_EOH)) {
		hlua_pusherror(L, "Lua applet http '%s': Failed create the response.\n",
			       luactx->appctx->applet->name);
		WILL_LJMP(lua_error(L));
	}

	if (htx_used_space(htx) > b_size(outbuf) - global.tune.maxrewrite) {
		b_reset(outbuf);
		hlua_pusherror(L, "Lua: 'start_response': response header block too big");
		WILL_LJMP(lua_error(L));
	}

	htx_to_buf(htx, outbuf);

	/* Headers sent, set the flag. */
	http_ctx->flags |= APPLET_HDR_SENT;
	return 0;
}

/* We will build the status line and the headers of the HTTP response.
 * We will try send at once if its not possible, we give back the hand
 * waiting for more room.
 */
__LJMP static int hlua_applet_http_start_response_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct hlua_appctx *luactx = MAY_LJMP(hlua_checkapplet_http(L, 1));

	if (!applet_get_outbuf(luactx->appctx)) {
		applet_have_more_data(luactx->appctx);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_start_response_yield, TICK_ETERNITY, 0));
	}
	return MAY_LJMP(hlua_applet_http_send_response(L));
}


__LJMP static int hlua_applet_http_start_response(lua_State *L)
{
	return MAY_LJMP(hlua_applet_http_start_response_yield(L, 0, 0));
}

/*
 *
 *
 * Class HTTP
 *
 *
 */

/* Returns a struct hlua_txn if the stack entry "ud" is
 * a class stream, otherwise it throws an error.
 */
__LJMP static struct hlua_txn *hlua_checkhttp(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_http_ref));
}

/* This function creates and push in the stack a HTTP object
 * according with a current TXN.
 */
__LJMP static int hlua_http_new(lua_State *L, struct hlua_txn *txn)
{
	struct hlua_txn *htxn;

	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	/* Create the object: obj[0] = userdata.
	 * Note that the base of the Converters object is the
	 * same than the TXN object.
	 */
	lua_newtable(L);
	htxn = lua_newuserdata(L, sizeof(*htxn));
	lua_rawseti(L, -2, 0);

	htxn->s = txn->s;
	htxn->p = txn->p;
	htxn->dir = txn->dir;
	htxn->flags = txn->flags;

	/* Pop a class stream metatable and affect it to the table. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_http_ref);
	lua_setmetatable(L, -2);

	return 1;
}

/* This function creates and returns an array containing the status-line
 * elements. This function does not fails.
 */
__LJMP static int hlua_http_get_stline(lua_State *L, struct htx_sl *sl)
{
	/* Create the table. */
	lua_newtable(L);

	if (sl->flags & HTX_SL_F_IS_RESP) {
		lua_pushstring(L, "version");
		lua_pushlstring(L, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl));
		lua_settable(L, -3);
		lua_pushstring(L, "code");
		lua_pushlstring(L, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl));
		lua_settable(L, -3);
		lua_pushstring(L, "reason");
		lua_pushlstring(L, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl));
		lua_settable(L, -3);
	}
	else {
		lua_pushstring(L, "method");
		lua_pushlstring(L, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl));
		lua_settable(L, -3);
		lua_pushstring(L, "uri");
		lua_pushlstring(L, HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl));
		lua_settable(L, -3);
		lua_pushstring(L, "version");
		lua_pushlstring(L, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl));
		lua_settable(L, -3);
	}
	return 1;
}

/* This function creates ans returns an array of HTTP headers.
 * This function does not fails. It is used as wrapper with the
 * 2 following functions.
 */
__LJMP static int hlua_http_get_headers(lua_State *L, struct buffer *buf)
{
	struct htx *htx;
	int32_t pos;

	/* Create the table. */
	lua_newtable(L);


	htx = htxbuf(buf);
	for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
		struct htx_blk *blk = htx_get_blk(htx, pos);
		enum htx_blk_type type = htx_get_blk_type(blk);
		struct ist n, v;
		int len;

		if (type == HTX_BLK_HDR) {
			n = htx_get_blk_name(htx,blk);
			v = htx_get_blk_value(htx, blk);
		}
		else if (type == HTX_BLK_EOH)
			break;
		else
			continue;

		/* Check for existing entry:
		 * assume that the table is on the top of the stack, and
		 * push the key in the stack, the function lua_gettable()
		 * perform the lookup.
		 */
		lua_pushlstring(L, n.ptr, n.len);
		lua_gettable(L, -2);

		switch (lua_type(L, -1)) {
			case LUA_TNIL:
				/* Table not found, create it. */
				lua_pop(L, 1); /* remove the nil value. */
				lua_pushlstring(L, n.ptr, n.len);  /* push the header name as key. */
				lua_newtable(L); /* create and push empty table. */
				lua_pushlstring(L, v.ptr, v.len); /* push header value. */
				lua_rawseti(L, -2, 0); /* index header value (pop it). */
				lua_rawset(L, -3); /* index new table with header name (pop the values). */
				break;

			case LUA_TTABLE:
				/* Entry found: push the value in the table. */
				len = lua_rawlen(L, -1);
				lua_pushlstring(L, v.ptr, v.len); /* push header value. */
				lua_rawseti(L, -2, len+1); /* index header value (pop it). */
				lua_pop(L, 1); /* remove the table (it is stored in the main table). */
				break;

			default:
				/* Other cases are errors. */
				hlua_pusherror(L, "internal error during the parsing of headers.");
				WILL_LJMP(lua_error(L));
		}
	}
	return 1;
}

__LJMP static int hlua_http_req_get_headers(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 1, "req_get_headers"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return hlua_http_get_headers(L, &htxn->s->req.buf);
}

__LJMP static int hlua_http_res_get_headers(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 1, "res_get_headers"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_RES || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return hlua_http_get_headers(L, &htxn->s->res.buf);
}

/* This function replace full header, or just a value in
 * the request or in the response. It is a wrapper fir the
 * 4 following functions.
 */
__LJMP static inline int hlua_http_rep_hdr(lua_State *L, struct http_msg *msg, int full)
{
	size_t name_len;
	const char *name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));
	const char *reg = MAY_LJMP(luaL_checkstring(L, 3));
	const char *value = MAY_LJMP(luaL_checkstring(L, 4));
	struct htx *htx;
	struct my_regex *re;

	if (!(re = regex_comp(reg, 1, 1, NULL)))
		WILL_LJMP(luaL_argerror(L, 3, "invalid regex"));

	htx = htxbuf(&msg->chn->buf);
	http_replace_hdrs(chn_strm(msg->chn), htx, ist2(name, name_len), value, re, full);
	regex_free(re);
	return 0;
}

__LJMP static int hlua_http_req_rep_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 4, "req_rep_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return MAY_LJMP(hlua_http_rep_hdr(L, &htxn->s->txn.http->req, 1));
}

__LJMP static int hlua_http_res_rep_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 4, "res_rep_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_RES || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return MAY_LJMP(hlua_http_rep_hdr(L, &htxn->s->txn.http->rsp, 1));
}

__LJMP static int hlua_http_req_rep_val(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 4, "req_rep_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return MAY_LJMP(hlua_http_rep_hdr(L, &htxn->s->txn.http->req, 0));
}

__LJMP static int hlua_http_res_rep_val(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 4, "res_rep_val"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_RES || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return MAY_LJMP(hlua_http_rep_hdr(L, &htxn->s->txn.http->rsp, 0));
}

/* This function deletes all the occurrences of an header.
 * It is a wrapper for the 2 following functions.
 */
__LJMP static inline int hlua_http_del_hdr(lua_State *L, struct http_msg *msg)
{
	size_t len;
	const char *name = MAY_LJMP(luaL_checklstring(L, 2, &len));
	struct htx *htx = htxbuf(&msg->chn->buf);
	struct http_hdr_ctx ctx;

	ctx.blk = NULL;
	while (http_find_header(htx, ist2(name, len), &ctx, 1))
		http_remove_header(htx, &ctx);
	return 0;
}

__LJMP static int hlua_http_req_del_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "req_del_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return hlua_http_del_hdr(L, &htxn->s->txn.http->req);
}

__LJMP static int hlua_http_res_del_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "res_del_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_RES || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return hlua_http_del_hdr(L, &htxn->s->txn.http->rsp);
}

/* This function adds an header. It is a wrapper used by
 * the 2 following functions.
 */
__LJMP static inline int hlua_http_add_hdr(lua_State *L, struct http_msg *msg)
{
	size_t name_len;
	const char *name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));
	size_t value_len;
	const char *value = MAY_LJMP(luaL_checklstring(L, 3, &value_len));
	struct htx *htx = htxbuf(&msg->chn->buf);

	lua_pushboolean(L, http_add_header(htx, ist2(name, name_len),
					   ist2(value, value_len), 1));
	return 0;
}

__LJMP static int hlua_http_req_add_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 3, "req_add_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return hlua_http_add_hdr(L, &htxn->s->txn.http->req);
}

__LJMP static int hlua_http_res_add_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 3, "res_add_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_RES || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	return hlua_http_add_hdr(L, &htxn->s->txn.http->rsp);
}

static int hlua_http_req_set_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 3, "req_set_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	hlua_http_del_hdr(L, &htxn->s->txn.http->req);
	return hlua_http_add_hdr(L, &htxn->s->txn.http->req);
}

static int hlua_http_res_set_hdr(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 3, "res_set_hdr"));
	htxn = MAY_LJMP(hlua_checkhttp(L, 1));

	if (htxn->dir != SMP_OPT_DIR_RES || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	hlua_http_del_hdr(L, &htxn->s->txn.http->rsp);
	return hlua_http_add_hdr(L, &htxn->s->txn.http->rsp);
}

/* This function set the method. */
static int hlua_http_req_set_meth(lua_State *L)
{
	struct hlua_txn *htxn = MAY_LJMP(hlua_checkhttp(L, 1));
	size_t name_len;
	const char *name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	lua_pushboolean(L, http_req_replace_stline(0, name, name_len, htxn->p, htxn->s) != -1);
	return 1;
}

/* This function set the method. */
static int hlua_http_req_set_path(lua_State *L)
{
	struct hlua_txn *htxn = MAY_LJMP(hlua_checkhttp(L, 1));
	size_t name_len;
	const char *name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	lua_pushboolean(L, http_req_replace_stline(1, name, name_len, htxn->p, htxn->s) != -1);
	return 1;
}

/* This function set the query-string. */
static int hlua_http_req_set_query(lua_State *L)
{
	struct hlua_txn *htxn = MAY_LJMP(hlua_checkhttp(L, 1));
	size_t name_len;
	const char *name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	/* Check length. */
	if (name_len > trash.size - 1) {
		lua_pushboolean(L, 0);
		return 1;
	}

	/* Add the mark question as prefix. */
	chunk_reset(&trash);
	trash.area[trash.data++] = '?';
	memcpy(trash.area + trash.data, name, name_len);
	trash.data += name_len;

	lua_pushboolean(L,
			http_req_replace_stline(2, trash.area, trash.data, htxn->p, htxn->s) != -1);
	return 1;
}

/* This function set the uri. */
static int hlua_http_req_set_uri(lua_State *L)
{
	struct hlua_txn *htxn = MAY_LJMP(hlua_checkhttp(L, 1));
	size_t name_len;
	const char *name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if (htxn->dir != SMP_OPT_DIR_REQ || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	lua_pushboolean(L, http_req_replace_stline(3, name, name_len, htxn->p, htxn->s) != -1);
	return 1;
}

/* This function set the response code & optionally reason. */
static int hlua_http_res_set_status(lua_State *L)
{
	struct hlua_txn *htxn = MAY_LJMP(hlua_checkhttp(L, 1));
	unsigned int code = MAY_LJMP(luaL_checkinteger(L, 2));
	const char *str = MAY_LJMP(luaL_optlstring(L, 3, NULL, NULL));
	const struct ist reason = ist2(str, (str ? strlen(str) : 0));

	if (htxn->dir != SMP_OPT_DIR_RES || !IS_HTX_STRM(htxn->s))
		WILL_LJMP(lua_error(L));

	http_res_set_status(code, reason, htxn->s);
	return 0;
}

/*
 *
 *
 * Class HTTPMessage
 *
 *
 */

/* Returns a struct http_msg if the stack entry "ud" is a class HTTPMessage,
 * otherwise it throws an error.
 */
__LJMP static struct http_msg *hlua_checkhttpmsg(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_http_msg_ref));
}

/* Creates and pushes on the stack a HTTP object according with a current TXN.
 */
static int hlua_http_msg_new(lua_State *L, struct http_msg *msg)
{
	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	lua_newtable(L);
	lua_pushlightuserdata(L, msg);
	lua_rawseti(L, -2, 0);

	/* Create the "channel" field that contains the request channel object. */
	lua_pushstring(L, "channel");
	if (!hlua_channel_new(L, msg->chn))
		return 0;
	lua_rawset(L, -3);

	/* Pop a class stream metatable and affect it to the table. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_http_msg_ref);
	lua_setmetatable(L, -2);

	return 1;
}

/* Helper function returning a filter attached to the HTTP message at the
 * position <ud> in the stack, filling the current offset and length of the
 * filter. If no filter is attached, NULL is returned and <offset> and <len> are
 * filled with output and input length respectively.
 */
static struct filter *hlua_http_msg_filter(lua_State *L, int ud, struct http_msg *msg, size_t *offset, size_t *len)
{
	struct channel *chn = msg->chn;
	struct htx *htx = htxbuf(&chn->buf);
	struct filter *filter = NULL;

	*offset = co_data(msg->chn);
	*len    = htx->data - co_data(msg->chn);

	if (lua_getfield(L, ud, "__filter") == LUA_TLIGHTUSERDATA) {
		filter  = lua_touserdata (L, -1);
		if (msg->msg_state >= HTTP_MSG_DATA) {
			struct hlua_flt_ctx *flt_ctx = filter->ctx;

			*offset  = flt_ctx->cur_off[CHN_IDX(chn)];
			*len     = flt_ctx->cur_len[CHN_IDX(chn)];
		}
	}

	lua_pop(L, 1);
	return filter;
}

/* Returns true if the channel attached to the HTTP message is the response
 * channel.
 */
__LJMP static int hlua_http_msg_is_resp(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 1, "is_resp"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	lua_pushboolean(L, !!(msg->chn->flags & CF_ISRESP));
	return 1;
}

/* Returns an array containing the elements status-line of the HTTP message. It relies
 * on hlua_http_get_stline().
 */
__LJMP static int hlua_http_msg_get_stline(lua_State *L)
{
	struct http_msg *msg;
	struct htx *htx;
	struct htx_sl *sl;

	MAY_LJMP(check_args(L, 1, "get_stline"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	htx = htxbuf(&msg->chn->buf);
	sl = http_get_stline(htx);
	if (!sl)
		return 0;
	return hlua_http_get_stline(L, sl);
}

/* Returns an array containing all headers of the HTTP message. it relies on
 * hlua_http_get_headers().
 */
__LJMP static int hlua_http_msg_get_headers(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 1, "get_headers"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	return hlua_http_get_headers(L, &msg->chn->buf);
}

/* Deletes all occurrences of an header in the HTTP message matching on its
 * name. It relies on hlua_http_del_hdr().
 */
__LJMP static int hlua_http_msg_del_hdr(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 2, "del_header"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	return hlua_http_del_hdr(L, msg);
}

/* Matches the full value line of all occurrences of an header in the HTTP
 * message given its name against a regex and replaces it if it matches. It
 * relies on hlua_http_rep_hdr().
 */
__LJMP static int hlua_http_msg_rep_hdr(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 4, "rep_header"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	return hlua_http_rep_hdr(L, msg, 1);
}

/* Matches all comma-separated values of all occurrences of an header in the HTTP
 * message given its name against a regex and replaces it if it matches. It
 * relies on hlua_http_rep_hdr().
 */
__LJMP static int hlua_http_msg_rep_val(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 4, "rep_value"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	return hlua_http_rep_hdr(L, msg, 0);
}

/* Add an header in the HTTP message. It relies on hlua_http_add_hdr() */
__LJMP static int hlua_http_msg_add_hdr(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 3, "add_header"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	return hlua_http_add_hdr(L, msg);
}

/* Add an header in the HTTP message removing existing headers with the same
 * name. It relies on hlua_http_del_hdr() and hlua_http_add_hdr().
 */
__LJMP static int hlua_http_msg_set_hdr(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 3, "set_header"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	hlua_http_del_hdr(L, msg);
	return hlua_http_add_hdr(L, msg);
}

/* Rewrites the request method. It relies on http_req_replace_stline(). */
__LJMP static int hlua_http_msg_set_meth(lua_State *L)
{
	struct stream *s;
	struct http_msg *msg;
	const char *name;
	size_t name_len;

	MAY_LJMP(check_args(L, 2, "set_method"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if ((msg->chn->flags & CF_ISRESP) || msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	s = chn_strm(msg->chn);
	lua_pushboolean(L, http_req_replace_stline(0, name, name_len, s->be, s) != -1);
	return 1;
}

/* Rewrites the request path. It relies on http_req_replace_stline(). */
__LJMP static int hlua_http_msg_set_path(lua_State *L)
{
	struct stream *s;
	struct http_msg *msg;
	const char *name;
	size_t name_len;

	MAY_LJMP(check_args(L, 2, "set_path"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if ((msg->chn->flags & CF_ISRESP) || msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	s = chn_strm(msg->chn);
	lua_pushboolean(L, http_req_replace_stline(1, name, name_len, s->be, s) != -1);
	return 1;
}

/* Rewrites the request query-string. It relies on http_req_replace_stline(). */
__LJMP static int hlua_http_msg_set_query(lua_State *L)
{
	struct stream *s;
	struct http_msg *msg;
	const char *name;
	size_t name_len;

	MAY_LJMP(check_args(L, 2, "set_query"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if ((msg->chn->flags & CF_ISRESP) || msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	/* Check length. */
	if (name_len > trash.size - 1) {
		lua_pushboolean(L, 0);
		return 1;
	}

	/* Add the mark question as prefix. */
	chunk_reset(&trash);
	trash.area[trash.data++] = '?';
	memcpy(trash.area + trash.data, name, name_len);
	trash.data += name_len;

	s = chn_strm(msg->chn);
	lua_pushboolean(L, http_req_replace_stline(2, trash.area, trash.data, s->be, s) != -1);
	return 1;
}

/* Rewrites the request URI. It relies on http_req_replace_stline(). */
__LJMP static int hlua_http_msg_set_uri(lua_State *L)
{
	struct stream *s;
	struct http_msg *msg;
	const char *name;
	size_t name_len;

	MAY_LJMP(check_args(L, 2, "set_uri"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &name_len));

	if ((msg->chn->flags & CF_ISRESP) || msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	s = chn_strm(msg->chn);
	lua_pushboolean(L, http_req_replace_stline(3, name, name_len, s->be, s) != -1);
	return 1;
}

/* Rewrites the response status code. It relies on http_res_set_status(). */
__LJMP static int hlua_http_msg_set_status(lua_State *L)
{
	struct http_msg *msg;
	unsigned int code;
	const char *reason;
	size_t reason_len;

	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	code = MAY_LJMP(luaL_checkinteger(L, 2));
	reason = MAY_LJMP(luaL_optlstring(L, 3, NULL, &reason_len));

	if (!(msg->chn->flags & CF_ISRESP) || msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(lua_error(L));

	lua_pushboolean(L, http_res_set_status(code, ist2(reason, reason_len), chn_strm(msg->chn)) != -1);
	return 1;
}

/* Change the body length. Accepts a positive integer or the string "chunked". */
__LJMP static int hlua_http_msg_set_body_len(lua_State *L)
{
	struct http_msg *msg;
	struct htx *htx;
	struct htx_sl *sl;
	int type;

	MAY_LJMP(check_args(L, 2, "set_body_len"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	if (msg->msg_state > HTTP_MSG_BODY)
		WILL_LJMP(luaL_error(L, "The 'set_body_len' function cannot be called during the message forwarding"));
	htx = htxbuf(&msg->chn->buf);
	sl = ASSUME_NONNULL(http_get_stline(htx));
	type = lua_type(L, 2);
	if (type == LUA_TSTRING) {
		const char *str = lua_tostring(L, 2);
		struct http_hdr_ctx ctx;

		if (strcmp(str, "chunked") != 0)
			goto error;

		/* Already chunked, nothing to do */
		if (msg->flags & HTTP_MSGF_TE_CHNK)
			goto success;

		/* add "Transfer-Encoding: chunked" header */
		if (!http_add_header(htx, ist("Transfer-Encoding"), ist("chunked"), 0))
			goto failure;
		msg->flags |= (HTTP_MSGF_VER_11|HTTP_MSGF_XFER_LEN|HTTP_MSGF_TE_CHNK);
		sl->flags |= (HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_XFER_ENC|HTX_SL_F_CHNK);

		/* remove any Content-Length header */
		if (msg->flags & HTTP_MSGF_CNT_LEN) {
			ctx.blk = NULL;
			while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
				http_remove_header(htx, &ctx);
			msg->flags &= ~HTTP_MSGF_CNT_LEN;
			sl->flags &= ~HTX_SL_F_CLEN;
		}
	}
	else if (type == LUA_TNUMBER) {
		const char *clen;
		ssize_t len;
		struct http_hdr_ctx ctx;

		len = lua_tointeger(L, 2);
		if (len < 0)
			goto error;
		clen = ultoa(len);

		/* remove any Content-Length header */
		if (msg->flags & HTTP_MSGF_CNT_LEN) {
			ctx.blk = NULL;
			while (http_find_header(htx, ist("Content-Length"), &ctx, 1))
				http_remove_header(htx, &ctx);
		}

		/* Now add Content-Length header */
		if (!http_add_header(htx, ist("Content-Length"), ist(clen), 0))
			goto failure;
		msg->flags |= (HTTP_MSGF_VER_11|HTTP_MSGF_XFER_LEN|HTTP_MSGF_CNT_LEN);
		sl->flags |= (HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN);

		/* remove any Transfer-Encoding header */
		if (msg->flags & HTTP_MSGF_TE_CHNK) {
			ctx.blk = NULL;
			while (http_find_header(htx, ist("Transfer-Encoding"), &ctx, 1))
				http_remove_header(htx, &ctx);
			msg->flags &= ~HTTP_MSGF_TE_CHNK;
			sl->flags &= ~(HTX_SL_F_XFER_ENC|HTX_SL_F_CHNK);
		}
	}
	else {
	  error:
		WILL_LJMP(luaL_error(L, "The 'set_body_len' function expects a positive integer or the string 'chunked' as argument."));
	}

  success:
	chn_prod(msg->chn)->flags |= SC_FL_NO_FASTFWD;
	lua_pushboolean(L, 1);
	return 1;

  failure:
	lua_pushboolean(L, 0);
	return 1;
}

/* Returns true if the HTTP message is full. */
__LJMP static int hlua_http_msg_is_full(lua_State *L)
{
	struct http_msg *msg;

	MAY_LJMP(check_args(L, 1, "is_full"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	lua_pushboolean(L, channel_full(msg->chn, 0));
	return 1;
}

/* Returns true if the HTTP message may still receive data. */
__LJMP static int hlua_http_msg_may_recv(lua_State *L)
{
	struct http_msg *msg;
	struct htx *htx;

	MAY_LJMP(check_args(L, 1, "may_recv"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	htx = htxbuf(&msg->chn->buf);
	lua_pushboolean(L, (htx_expect_more(htx) && !channel_input_closed(msg->chn) && channel_may_recv(msg->chn)));
	return 1;
}

/* Returns true if the HTTP message EOM was received */
__LJMP static int hlua_http_msg_is_eom(lua_State *L)
{
	struct http_msg *msg;
	struct htx *htx;

	MAY_LJMP(check_args(L, 1, "may_recv"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	htx = htxbuf(&msg->chn->buf);
	lua_pushboolean(L, !htx_expect_more(htx));
	return 1;
}

/* Returns the number of bytes available in the input side of the HTTP
 * message. This function never fails.
 */
__LJMP static int hlua_http_msg_get_in_len(lua_State *L)
{
	struct http_msg *msg;
	size_t output, input;

	MAY_LJMP(check_args(L, 1, "input"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	hlua_http_msg_filter(L, 1, msg, &output, &input);
	lua_pushinteger(L, input);
	return 1;
}

/* Returns the number of bytes available in the output side of the HTTP
 * message. This function never fails.
 */
__LJMP static int hlua_http_msg_get_out_len(lua_State *L)
{
	struct http_msg *msg;
	size_t output, input;

	MAY_LJMP(check_args(L, 1, "output"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	hlua_http_msg_filter(L, 1, msg, &output, &input);
	lua_pushinteger(L, output);
	return 1;
}

/* Copies at most <len> bytes of DATA blocks from the HTTP message <msg>
 * starting at the offset <offset> and put it in a string LUA variables. It
 * returns the built string length. It stops on the first non-DATA HTX
 * block. This function is called during the payload filtering, so the headers
 * are already scheduled for output (from the filter point of view).
 */
static int _hlua_http_msg_dup(struct http_msg *msg, lua_State *L, size_t offset, size_t len)
{
	struct htx *htx = htxbuf(&msg->chn->buf);
	struct htx_blk *blk;
	struct htx_ret htxret;
	luaL_Buffer b;
	int ret = 0;

	luaL_buffinit(L, &b);
	htxret = htx_find_offset(htx, offset);
	for (blk = htxret.blk, offset = htxret.ret; blk && len; blk = htx_get_next_blk(htx, blk)) {
		enum htx_blk_type type = htx_get_blk_type(blk);
		struct ist v;

		switch (type) {
			case HTX_BLK_UNUSED:
				break;

			case HTX_BLK_DATA:
				v = htx_get_blk_value(htx, blk);
				v = istadv(v, offset);
				v = isttrim(v, len);

				luaL_addlstring(&b, v.ptr, v.len);
				ret += v.len;
				break;

			default:
				if (!ret)
					goto no_data;
				goto end;
		}
		offset = 0;
	}

end:
	if (!ret && (htx->flags & HTX_FL_EOM))
		goto no_data;
	luaL_pushresult(&b);
	return ret;

  no_data:
	/* Remove the empty string and push nil on the stack */
	lua_pop(L, 1);
	lua_pushnil(L);
	return 0;
}

/* Copies the string <str> to the HTTP message <msg> at the offset
 * <offset>. This function returns -1 if data cannot be copied. Otherwise, it
 * returns the amount of data written. This function is responsible to update
 * the filter context.
 */
static int _hlua_http_msg_insert(struct http_msg *msg, struct filter *filter, struct ist str, size_t offset)
{
	struct htx *htx = htx_from_buf(&msg->chn->buf);
	struct htx_ret htxret;
	int /*max, */ret = 0;

	/* Nothing to do, just return */
	if (unlikely(istlen(str) == 0))
		goto end;

	if (istlen(str) > htx_free_data_space(htx)) {
		ret = -1;
		goto end;
	}

	htxret = htx_find_offset(htx, offset);
	if (!htxret.blk || htx_get_blk_type(htxret.blk) != HTX_BLK_DATA) {
		if (!htx_add_last_data(htx, str))
			goto end;
	}
	else {
		struct ist v = htx_get_blk_value(htx, htxret.blk);
		v.ptr += htxret.ret;
		v.len  = 0;
		if (!htx_replace_blk_value(htx, htxret.blk, v, str))
			goto end;
	}
	ret = str.len;
	if (ret) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;
		flt_update_offsets(filter, msg->chn, ret);
		flt_ctx->cur_len[CHN_IDX(msg->chn)] += ret;
	}

  end:
	htx_to_buf(htx, &msg->chn->buf);
	return ret;
}

/* Helper function removing at most <len> bytes of DATA blocks at the absolute
 * position <offset>. It stops on the first non-DATA HTX block. This function is
 * called during the payload filtering, so the headers are already scheduled for
 * output (from the filter point of view). This function is responsible to
 * update the filter context.
 */
static void _hlua_http_msg_delete(struct http_msg *msg, struct filter *filter, size_t offset, size_t len)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;
	struct htx *htx = htx_from_buf(&msg->chn->buf);
	struct htx_blk *blk;
	struct htx_ret htxret;
	size_t ret = 0;

	/* Be sure <len> is always the amount of DATA to remove */
	if (htx->data == offset+len && htx_get_tail_type(htx) == HTX_BLK_DATA) {
		/* When htx tail type == HTX_BLK_DATA, no need to take care
		 * of special blocks like HTX_BLK_EOT.
		 * We simply truncate after offset
		 * (truncate targeted blk and discard the following ones)
		 */
		htx_truncate(htx, offset);
		ret = len;
		goto end;
	}

	htxret = htx_find_offset(htx, offset);
	blk = htxret.blk;
	if (htxret.ret) {
		/* dealing with offset: we need to trim targeted blk */
		struct ist v;

		if (htx_get_blk_type(blk) != HTX_BLK_DATA)
			goto end;

		v = htx_get_blk_value(htx, blk);
		v = istadv(v, htxret.ret);

		v = isttrim(v, len);
		/* trimming data in blk: discard everything after the offset
		 * (replace 'v' with 'IST_NULL')
		 */
		blk = htx_replace_blk_value(htx, blk, v, IST_NULL);
		if (blk && v.len < len) {
			/* In this case, caller wants to keep removing data,
			 * but we need to spare current blk
			 * because it was already trimmed
			 */
			blk = htx_get_next_blk(htx, blk);
		}
		len -= v.len;
		ret += v.len;
	}


	while (blk && len) {
		/* there is more data that needs to be discarded */
		enum htx_blk_type type = htx_get_blk_type(blk);
		uint32_t sz = htx_get_blksz(blk);

		switch (type) {
			case HTX_BLK_UNUSED:
				break;

			case HTX_BLK_DATA:
				if (len < sz) {
					/* don't discard whole blk, only part of it
					 * (from the beginning)
					 */
					htx_cut_data_blk(htx, blk, len);
					ret += len;
					goto end;
				}
				break;

			default:
				/* HTX_BLK_EOT blk won't be removed */
				goto end;
		}

		/* Remove all the data block */
		len -= sz;
		ret += sz;
		blk = htx_remove_blk(htx, blk);
	}

end:
	flt_update_offsets(filter, msg->chn, -ret);
	flt_ctx->cur_len[CHN_IDX(msg->chn)] -= ret;
	/* WARNING: we don't call htx_to_buf() on purpose, because we don't want
	 *          to loose the EOM flag if the message is empty.
	 */
}

/* Copies input data found in an HTTP message. Unlike the channel function used
 * to duplicate raw data, this one can only be called inside a filter, from
 * http_payload callback. So it cannot yield. An exception is returned if it is
 * called from another callback. If nothing was copied, a nil value is pushed on
 * the stack.
 */
__LJMP static int hlua_http_msg_get_body(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	size_t output, input;
	int offset, len;

	if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "'data' expects at most 2 arguments"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	filter = hlua_http_msg_filter(L, 1, msg, &output, &input);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	if (!ci_data(msg->chn) && channel_input_closed(msg->chn)) {
		lua_pushnil(L);
		return 1;
	}

	offset = output;
	if (lua_gettop(L) > 1) {
		offset = MAY_LJMP(luaL_checkinteger(L, 2));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset < output || offset > input + output) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}
	len = output + input - offset;
	if (lua_gettop(L) == 3) {
		len = MAY_LJMP(luaL_checkinteger(L, 3));
		if (!len)
			goto dup;
		if (len == -1)
			len = c_size(msg->chn);
		if (len < 0) {
			lua_pushfstring(L, "length out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

  dup:
	_hlua_http_msg_dup(msg, L, offset, len);
	return 1;
}

/* Appends a string to the HTTP message, after all existing DATA blocks but
 * before the trailers, if any. It returns the amount of data written or -1 if
 * nothing was copied. Unlike the channel function used to append data, this one
 * can only be called inside a filter, from http_payload callback. So it cannot
 * yield. An exception is returned if it is called from another callback.
 */
__LJMP static int hlua_http_msg_append(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	const char *str;
	size_t offset, len, sz;
	int ret;

	MAY_LJMP(check_args(L, 2, "append"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	filter = hlua_http_msg_filter(L, 1, msg, &offset, &len);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	ret = _hlua_http_msg_insert(msg, filter, ist2(str, sz), offset+len);
	lua_pushinteger(L, ret);
	return 1;
}

/* Prepends a string to the HTTP message, before all existing DATA blocks. It
 * returns the amount of data written or -1 if nothing was copied. Unlike the
 * channel function used to prepend data, this one can only be called inside a
 * filter, from http_payload callback. So it cannot yield. An exception is
 * returned if it is called from another callback.
 */
__LJMP static int hlua_http_msg_prepend(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	const char *str;
	size_t offset, len, sz;
	int ret;

	MAY_LJMP(check_args(L, 2, "prepend"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	filter = hlua_http_msg_filter(L, 1, msg, &offset, &len);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	ret = _hlua_http_msg_insert(msg, filter, ist2(str, sz), offset);
	lua_pushinteger(L, ret);
	return 1;
}

/* Inserts a string to the HTTP message at a given offset. By default the string
 * is appended at the end of DATA blocks. It returns the amount of data written
 * or -1 if nothing was copied. Unlike the channel function used to insert data,
 * this one can only be called inside a filter, from http_payload callback. So
 * it cannot yield. An exception is returned if it is called from another
 * callback.
 */
__LJMP static int hlua_http_msg_insert_data(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	const char *str;
	size_t input, output, sz;
	int offset;
	int ret;

	if (lua_gettop(L) < 2 || lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "'insert' expects at least 1 argument and at most 2 arguments"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	filter = hlua_http_msg_filter(L, 1, msg, &output, &input);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 2) {
		offset = MAY_LJMP(luaL_checkinteger(L, 3));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset > output + input) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	ret = _hlua_http_msg_insert(msg, filter, ist2(str, sz), offset);
	lua_pushinteger(L, ret);
	return 1;
}

/* Removes a given amount of data from the HTTP message at a given offset. By
 * default all DATA blocks are removed. It returns the amount of data
 * removed. Unlike the channel function used to remove data, this one can only
 * be called inside a filter, from http_payload callback. So it cannot yield. An
 * exception is returned if it is called from another callback.
 */
__LJMP static int hlua_http_msg_del_data(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	size_t input, output;
	int offset, len;

	if (lua_gettop(L) < 1 || lua_gettop(L) > 3)
		WILL_LJMP(luaL_error(L, "'remove' expects at most 2 arguments"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	filter = hlua_http_msg_filter(L, 1, msg, &output, &input);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 1) {
		offset = MAY_LJMP(luaL_checkinteger(L, 2));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset > output + input) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	len = output + input - offset;
	if (lua_gettop(L) == 3) {
		len = MAY_LJMP(luaL_checkinteger(L, 3));
		if (!len)
			goto end;
		if (len == -1)
			len = output + input - offset;
		if (len < 0 || offset + len > output + input) {
			lua_pushfstring(L, "length out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	_hlua_http_msg_delete(msg, filter, offset, len);

  end:
	lua_pushinteger(L, len);
	return 1;
}

/* Replaces a given amount of data at the given offset by a string. By default,
 * all remaining data are removed, accordingly to the filter context. It returns
 * the amount of data written or -1 if nothing was copied. Unlike the channel
 * function used to replace data, this one can only be called inside a filter,
 * from http_payload callback. So it cannot yield. An exception is returned if
 * it is called from another callback.
 */
__LJMP static int hlua_http_msg_set_data(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	struct htx *htx;
	const char *str;
	size_t input, output, sz;
	int offset, len;
	int ret;

	if (lua_gettop(L) < 2 || lua_gettop(L) > 4)
		WILL_LJMP(luaL_error(L, "'set' expects at least 1 argument and at most 3 arguments"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	filter = hlua_http_msg_filter(L, 1, msg, &output, &input);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	offset = output;
	if (lua_gettop(L) > 2) {
		offset = MAY_LJMP(luaL_checkinteger(L, 3));
		if (offset < 0)
			offset = MAX(0, (int)input + offset);
		offset += output;
		if (offset < output || offset > input + output) {
			lua_pushfstring(L, "offset out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

	len = output + input - offset;
	if (lua_gettop(L) == 4) {
		len = MAY_LJMP(luaL_checkinteger(L, 4));
		if (!len)
			goto set;
		if (len == -1)
			len = output + input - offset;
		if (len < 0 || offset + len > output + input) {
			lua_pushfstring(L, "length out of range.");
			WILL_LJMP(lua_error(L));
		}
	}

  set:
	/* Be sure we can copied the string once input data will be removed. */
	htx = htx_from_buf(&msg->chn->buf);
	if (sz > htx_free_data_space(htx) + len)
		lua_pushinteger(L, -1);
	else {
		_hlua_http_msg_delete(msg, filter, offset, len);
		ret = _hlua_http_msg_insert(msg, filter, ist2(str, sz), offset);
		lua_pushinteger(L, ret);
	}
	return 1;
}

/* Prepends data into an HTTP message and forward it, from the filter point of
 * view. It returns the amount of data written or -1 if nothing was sent. Unlike
 * the channel function used to send data, this one can only be called inside a
 * filter, from http_payload callback. So it cannot yield. An exception is
 * returned if it is called from another callback.
 */
__LJMP static int hlua_http_msg_send(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	struct htx *htx;
	const char *str;
	size_t offset, len, sz;
	int ret;

	MAY_LJMP(check_args(L, 2, "send"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	str = MAY_LJMP(luaL_checklstring(L, 2, &sz));
	filter = hlua_http_msg_filter(L, 1, msg, &offset, &len);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	/* Return an error if the channel's output is closed */
	if (unlikely(channel_output_closed(msg->chn))) {
		lua_pushinteger(L, -1);
		return 1;
	}

	htx = htx_from_buf(&msg->chn->buf);
	if (sz > htx_free_data_space(htx)) {
		lua_pushinteger(L, -1);
		return 1;
	}

	ret = _hlua_http_msg_insert(msg, filter, ist2(str, sz), offset);
	if (ret > 0) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;

		FLT_OFF(filter, msg->chn) += ret;
		flt_ctx->cur_len[CHN_IDX(msg->chn)] -= ret;
		flt_ctx->cur_off[CHN_IDX(msg->chn)] += ret;
	}

	lua_pushinteger(L, ret);
	return 1;
}

/* Forwards a given amount of bytes. It return -1 if the channel's output is
 * closed. Otherwise, it returns the number of bytes forwarded. Unlike the
 * channel function used to forward data, this one can only be called inside a
 * filter, from http_payload callback. So it cannot yield. An exception is
 * returned if it is called from another callback. All other functions deal with
 * DATA block, this one not.
*/
__LJMP static int hlua_http_msg_forward(lua_State *L)
{
	struct http_msg *msg;
	struct filter *filter;
	size_t offset, len;
	int fwd, ret = 0;

	MAY_LJMP(check_args(L, 2, "forward"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));

	if (msg->msg_state < HTTP_MSG_DATA)
		WILL_LJMP(lua_error(L));

	fwd = MAY_LJMP(luaL_checkinteger(L, 2));
	filter = hlua_http_msg_filter(L, 1, msg, &offset, &len);
	if (!filter || !hlua_filter_from_payload(filter))
		WILL_LJMP(lua_error(L));

	/* Nothing to do, just return */
	if (!fwd)
		goto end;

	/* Return an error if the channel's output is closed */
	if (unlikely(channel_output_closed(msg->chn))) {
		ret = -1;
		goto end;
	}

	ret = fwd;
	if (ret > len)
		ret = len;

	if (ret) {
		struct hlua_flt_ctx *flt_ctx = filter->ctx;

		FLT_OFF(filter, msg->chn) += ret;
		flt_ctx->cur_off[CHN_IDX(msg->chn)] += ret;
		flt_ctx->cur_len[CHN_IDX(msg->chn)] -= ret;
	}

  end:
	lua_pushinteger(L, ret);
	return 1;
}

/* Set EOM flag on the HTX message.
 *
 * NOTE: Not sure it is a good idea to manipulate this flag but for now I don't
 *       really know how to do without this feature.
 */
__LJMP static int hlua_http_msg_set_eom(lua_State *L)
{
	struct http_msg *msg;
	struct htx *htx;

	MAY_LJMP(check_args(L, 1, "set_eom"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	htx = htxbuf(&msg->chn->buf);
	htx->flags |= HTX_FL_EOM;
	return 0;
}

/* Unset EOM flag on the HTX message.
 *
 * NOTE: Not sure it is a good idea to manipulate this flag but for now I don't
 *       really know how to do without this feature.
 */
__LJMP static int hlua_http_msg_unset_eom(lua_State *L)
{
	struct http_msg *msg;
	struct htx *htx;

	MAY_LJMP(check_args(L, 1, "set_eom"));
	msg = MAY_LJMP(hlua_checkhttpmsg(L, 1));
	htx = htxbuf(&msg->chn->buf);
	htx->flags &= ~HTX_FL_EOM;
	return 0;
}

/*
 *
 *
 * Class HTTPClient
 *
 *
 */
__LJMP static struct hlua_httpclient *hlua_checkhttpclient(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_httpclient_ref));
}


/* stops the httpclient and ask it to kill itself */
__LJMP static int hlua_httpclient_gc(lua_State *L)
{
	struct hlua_httpclient *hlua_hc;

	MAY_LJMP(check_args(L, 1, "__gc"));

	hlua_hc = MAY_LJMP(hlua_checkhttpclient(L, 1));

	if (MT_LIST_DELETE(&hlua_hc->by_hlua)) {
		/* we won the race against hlua_httpclient_destroy_all() */
		httpclient_stop_and_destroy(hlua_hc->hc);
		hlua_hc->hc = NULL;
	}

	return 0;
}


__LJMP static int hlua_httpclient_new(lua_State *L)
{
	struct hlua_httpclient *hlua_hc;
	struct hlua *hlua;

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return 0;

	/* Check stack size. */
	if (!lua_checkstack(L, 3)) {
		hlua_pusherror(L, "httpclient: full stack");
		goto err;
	}
	/* Create the object: obj[0] = userdata. */
	lua_newtable(L);
	hlua_hc = MAY_LJMP(lua_newuserdata(L, sizeof(*hlua_hc)));
	lua_rawseti(L, -2, 0);
	memset(hlua_hc, 0, sizeof(*hlua_hc));

	hlua_hc->hc = httpclient_new(hlua, 0, IST_NULL);
	if (!hlua_hc->hc)
		goto err;

	MT_LIST_APPEND(&hlua->hc_list, &hlua_hc->by_hlua);

	/* Pop a class stream metatable and affect it to the userdata. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_httpclient_ref);
	lua_setmetatable(L, -2);

	return 1;

 err:
	WILL_LJMP(lua_error(L));
	return 0;
}


/*
 * Callback of the httpclient, this callback wakes the lua task up, once the
 * httpclient receives some data
 *
 */

static void hlua_httpclient_cb(struct httpclient *hc)
{
	struct hlua *hlua = hc->caller;

	if (!hlua || !hlua->task)
		return;

	task_wakeup(hlua->task, TASK_WOKEN_MSG);
}

/*
 * Fill the lua stack with headers from the httpclient response
 * This works the same way as the hlua_http_get_headers() function
 */
__LJMP static int hlua_httpclient_get_headers(lua_State *L, struct hlua_httpclient *hlua_hc)
{
	struct http_hdr *hdr;

	lua_newtable(L);

	for (hdr = hlua_hc->hc->res.hdrs; hdr && isttest(hdr->n); hdr++) {
		struct ist n, v;
		int len;

		n = hdr->n;
		v = hdr->v;

		/* Check for existing entry:
		 * assume that the table is on the top of the stack, and
		 * push the key in the stack, the function lua_gettable()
		 * perform the lookup.
		 */

		lua_pushlstring(L, n.ptr, n.len);
		lua_gettable(L, -2);

		switch (lua_type(L, -1)) {
			case LUA_TNIL:
				/* Table not found, create it. */
				lua_pop(L, 1); /* remove the nil value. */
				lua_pushlstring(L, n.ptr, n.len);  /* push the header name as key. */
				lua_newtable(L); /* create and push empty table. */
				lua_pushlstring(L, v.ptr, v.len); /* push header value. */
				lua_rawseti(L, -2, 0); /* index header value (pop it). */
				lua_rawset(L, -3); /* index new table with header name (pop the values). */
				break;

			case LUA_TTABLE:
				/* Entry found: push the value in the table. */
				len = lua_rawlen(L, -1);
				lua_pushlstring(L, v.ptr, v.len); /* push header value. */
				lua_rawseti(L, -2, len+1); /* index header value (pop it). */
				lua_pop(L, 1); /* remove the table (it is stored in the main table). */
				break;

			default:
				/* Other cases are errors. */
				hlua_pusherror(L, "internal error during the parsing of headers.");
				WILL_LJMP(lua_error(L));
		}
	}
	return 1;
}

/*
 * Allocate and return an array of http_hdr ist extracted from the <headers> lua table
 *
 * Caller must free the result
 */
struct http_hdr *hlua_httpclient_table_to_hdrs(lua_State *L)
{
	struct http_hdr hdrs[global.tune.max_http_hdr];
	struct http_hdr *result = NULL;
	uint32_t hdr_num = 0;

	lua_pushnil(L);
	while (lua_next(L, -2) != 0) {
		struct ist name, value;
		const char *n, *v;
		size_t nlen, vlen;

		if (!lua_isstring(L, -2) || !lua_istable(L, -1)) {
			/* Skip element if the key is not a string or if the value is not a table */
			goto next_hdr;
		}

		n = lua_tolstring(L, -2, &nlen);
		name = ist2(n, nlen);

		/* Loop on header's values */
		lua_pushnil(L);
		while (lua_next(L, -2)) {
			if (!lua_isstring(L, -1)) {
				/* Skip the value if it is not a string */
				goto next_value;
			}

			if (hdr_num >= global.tune.max_http_hdr) {
				lua_pop(L, 2);
				goto skip_headers;
			}

			v = lua_tolstring(L, -1, &vlen);
			value = ist2(v, vlen);
			name = ist2(n, nlen);

			hdrs[hdr_num].n = istdup(name);
			hdrs[hdr_num].v = istdup(value);

			hdr_num++;

		  next_value:
			lua_pop(L, 1);
		}

	  next_hdr:
		lua_pop(L, 1);

	}

	if (hdr_num) {
		/* alloc and copy the headers in the httpclient struct */
		result = calloc((hdr_num + 1), sizeof(*result));
		if (!result)
			goto skip_headers;
		memcpy(result, hdrs, sizeof(struct http_hdr) * (hdr_num + 1));

		result[hdr_num].n = IST_NULL;
		result[hdr_num].v = IST_NULL;
	}

skip_headers:

	return result;
}


/*
 * For each yield, checks if there is some data in the httpclient and push them
 * in the lua buffer, once the httpclient finished its job, push the result on
 * the stack
 */
__LJMP static int hlua_httpclient_rcv_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct buffer *tr;
	int res;
	struct hlua *hlua = hlua_gethlua(L);
	struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);


	tr = get_trash_chunk();

	res = httpclient_res_xfer(hlua_hc->hc, tr);
	luaL_addlstring(&hlua_hc->b, b_orig(tr), res);

	if (!httpclient_data(hlua_hc->hc) && httpclient_ended(hlua_hc->hc)) {

		luaL_pushresult(&hlua_hc->b);
		lua_settable(L, -3);

		lua_pushstring(L, "status");
		lua_pushinteger(L, hlua_hc->hc->res.status);
		lua_settable(L, -3);


		lua_pushstring(L, "reason");
		lua_pushlstring(L, hlua_hc->hc->res.reason.ptr, hlua_hc->hc->res.reason.len);
		lua_settable(L, -3);

		lua_pushstring(L, "headers");
		hlua_httpclient_get_headers(L, hlua_hc);
		lua_settable(L, -3);

		return 1;
	}

	if (httpclient_data(hlua_hc->hc))
		task_wakeup(hlua->task, TASK_WOKEN_MSG);

	MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));

	return 0;
}

/*
 * Call this when trying to stream a body during a request
 */
__LJMP static int hlua_httpclient_snd_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct hlua *hlua;
	struct hlua_httpclient *hlua_hc = hlua_checkhttpclient(L, 1);
	const char *body_str = NULL;
	int ret;
	int end = 0;
	size_t buf_len;
	size_t to_send = 0;

	hlua = hlua_gethlua(L);

	if (!hlua || !hlua->task)
		WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
		                     "'frontend', 'backend' or 'task'"));

	ret = lua_getfield(L, -1, "body");
	if (ret != LUA_TSTRING)
		goto rcv;

	body_str = lua_tolstring(L, -1, &buf_len);
	lua_pop(L, 1);

	to_send = buf_len - hlua_hc->sent;

	if ((hlua_hc->sent + to_send) >= buf_len)
		end = 1;

	/* the end flag is always set since we are using the whole remaining size */
	hlua_hc->sent += httpclient_req_xfer(hlua_hc->hc, ist2(body_str + hlua_hc->sent, to_send), end);

	if (buf_len > hlua_hc->sent) {
		/* still need to process the buffer */
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));
	} else {
		goto rcv;
		/* we sent the whole request buffer we can recv */
	}
	return 0;

rcv:

	/* we return a "res" object */
	lua_newtable(L);

	lua_pushstring(L, "body");
	luaL_buffinit(L, &hlua_hc->b);

	task_wakeup(hlua->task, TASK_WOKEN_MSG);
	MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_rcv_yield, TICK_ETERNITY, 0));

	return 1;
}

/*
 * Send an HTTP request and wait for a response
 */

__LJMP static int hlua_httpclient_send(lua_State *L, enum http_meth_t meth)
{
	struct hlua_httpclient *hlua_hc;
	struct http_hdr *hdrs = NULL;
	struct http_hdr *hdrs_i = NULL;
	struct hlua *hlua;
	const char *url_str = NULL;
	const char *body_str = NULL;
	size_t buf_len = 0;
	int ret;

	hlua = hlua_gethlua(L);

	if (!hlua || !hlua->task)
		WILL_LJMP(luaL_error(L, "The 'get' function is only allowed in "
		                     "'frontend', 'backend' or 'task'"));

	if (lua_gettop(L) != 2 || lua_type(L, -1) != LUA_TTABLE)
		WILL_LJMP(luaL_error(L, "'get' needs a table as argument"));

	hlua_hc = hlua_checkhttpclient(L, 1);

	/* An HTTPclient instance must never process more that one request. So
	 * at this stage, it must never have been started.
	 */
	if (httpclient_started(hlua_hc->hc))
		WILL_LJMP(luaL_error(L, "httpclient instance cannot be reused. It must process at most one request"));

	lua_pushnil(L);  /* first key */
	while (lua_next(L, 2)) {
		if (strcmp(lua_tostring(L, -2), "dst") == 0) {
			if (httpclient_set_dst(hlua_hc->hc, lua_tostring(L, -1)) < 0)
				WILL_LJMP(luaL_error(L, "Can't use the 'dst' argument"));

		} else if (strcmp(lua_tostring(L, -2), "url") == 0) {
			if (lua_type(L, -1) != LUA_TSTRING)
				WILL_LJMP(luaL_error(L, "invalid parameter in 'url', must be a string"));
			url_str = lua_tostring(L, -1);

		} else if (strcmp(lua_tostring(L, -2), "timeout") == 0) {
			if (lua_type(L, -1) != LUA_TNUMBER)
				WILL_LJMP(luaL_error(L, "invalid parameter in 'timeout', must be a number"));
			httpclient_set_timeout(hlua_hc->hc, lua_tointeger(L, -1));

		} else if (strcmp(lua_tostring(L, -2), "headers") == 0) {
			if (lua_type(L, -1) != LUA_TTABLE)
				WILL_LJMP(luaL_error(L, "invalid parameter in 'headers', must be a table"));
			hdrs = hlua_httpclient_table_to_hdrs(L);

		} else if (strcmp(lua_tostring(L, -2), "body") == 0) {
			if (lua_type(L, -1) != LUA_TSTRING)
				WILL_LJMP(luaL_error(L, "invalid parameter in 'body', must be a string"));
			body_str = lua_tolstring(L, -1, &buf_len);

		} else {
			WILL_LJMP(luaL_error(L, "'%s' invalid parameter name", lua_tostring(L, -2)));
		}
		/* removes 'value'; keeps 'key' for next iteration */
		lua_pop(L, 1);
	}

	if (!url_str) {
		WILL_LJMP(luaL_error(L, "'get' need a 'url' argument"));
		return 0;
	}

	hlua_hc->sent = 0;

	istfree(&hlua_hc->hc->req.url);
	hlua_hc->hc->req.url = istdup(ist(url_str));
	hlua_hc->hc->req.meth = meth;

	/* update the httpclient callbacks */
	hlua_hc->hc->ops.res_stline = hlua_httpclient_cb;
	hlua_hc->hc->ops.res_headers = hlua_httpclient_cb;
	hlua_hc->hc->ops.res_payload = hlua_httpclient_cb;
	hlua_hc->hc->ops.res_end = hlua_httpclient_cb;

	/* a body is available, it will use the request callback */
	if (body_str && buf_len) {
		hlua_hc->hc->ops.req_payload = hlua_httpclient_cb;
	}

	ret = httpclient_req_gen(hlua_hc->hc, hlua_hc->hc->req.url, meth, hdrs, IST_NULL);

	/* free the temporary headers array */
	hdrs_i = hdrs;
	while (hdrs_i && isttest(hdrs_i->n)) {
		istfree(&hdrs_i->n);
		istfree(&hdrs_i->v);
		hdrs_i++;
	}
	ha_free(&hdrs);


	if (ret != ERR_NONE) {
		WILL_LJMP(luaL_error(L, "Can't generate the HTTP request"));
		return 0;
	}

	if (!httpclient_start(hlua_hc->hc))
		WILL_LJMP(luaL_error(L, "couldn't start the httpclient"));

	MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_httpclient_snd_yield, TICK_ETERNITY, 0));

	return 0;
}

/*
 * Sends an HTTP HEAD request and wait for a response
 *
 * httpclient:head(url, headers, payload)
 */
__LJMP static int hlua_httpclient_head(lua_State *L)
{
	return hlua_httpclient_send(L, HTTP_METH_HEAD);
}

/*
 * Send an HTTP GET request and wait for a response
 *
 * httpclient:get(url, headers, payload)
 */
__LJMP static int hlua_httpclient_get(lua_State *L)
{
	return hlua_httpclient_send(L, HTTP_METH_GET);

}

/*
 * Sends an HTTP PUT request and wait for a response
 *
 * httpclient:put(url, headers, payload)
 */
__LJMP static int hlua_httpclient_put(lua_State *L)
{
	return hlua_httpclient_send(L, HTTP_METH_PUT);
}

/*
 * Send an HTTP POST request and wait for a response
 *
 * httpclient:post(url, headers, payload)
 */
__LJMP static int hlua_httpclient_post(lua_State *L)
{
	return hlua_httpclient_send(L, HTTP_METH_POST);
}


/*
 * Sends an HTTP DELETE request and wait for a response
 *
 * httpclient:delete(url, headers, payload)
 */
__LJMP static int hlua_httpclient_delete(lua_State *L)
{
	return hlua_httpclient_send(L, HTTP_METH_DELETE);
}

/*
 *
 *
 * Class TXN
 *
 *
 */

/* Returns a struct hlua_session if the stack entry "ud" is
 * a class stream, otherwise it throws an error.
 */
__LJMP static struct hlua_txn *hlua_checktxn(lua_State *L, int ud)
{
	return MAY_LJMP(hlua_checkudata(L, ud, class_txn_ref));
}

__LJMP static int hlua_set_var(lua_State *L)
{
	struct hlua_txn *htxn;
	const char *name;
	size_t len;
	struct sample smp;

	if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
		WILL_LJMP(luaL_error(L, "'set_var' needs between 3 and 4 arguments"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));

	/* Converts the third argument in a sample. */
	memset(&smp, 0, sizeof(smp));
	hlua_lua2smp(L, 3, &smp);

	/* Store the sample in a variable. We don't need to dup the smp, vars API
	 * already takes care of duplicating dynamic var data.
	 */
	smp_set_owner(&smp, htxn->p, htxn->s->sess, htxn->s, htxn->dir & SMP_OPT_DIR);

	if (lua_gettop(L) == 4 && lua_toboolean(L, 4))
		lua_pushboolean(L, vars_set_by_name_ifexist(name, len, &smp) != 0);
	else
		lua_pushboolean(L, vars_set_by_name(name, len, &smp) != 0);

	return 1;
}

__LJMP static int hlua_unset_var(lua_State *L)
{
	struct hlua_txn *htxn;
	const char *name;
	size_t len;
	struct sample smp;

	MAY_LJMP(check_args(L, 2, "unset_var"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));

	/* Unset the variable. */
	smp_set_owner(&smp, htxn->p, htxn->s->sess, htxn->s, htxn->dir & SMP_OPT_DIR);
	lua_pushboolean(L, vars_unset_by_name_ifexist(name, len, &smp) != 0);
	return 1;
}

__LJMP static int hlua_get_var(lua_State *L)
{
	struct hlua_txn *htxn;
	const char *name;
	size_t len;
	struct sample smp;

	MAY_LJMP(check_args(L, 2, "get_var"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	name = MAY_LJMP(luaL_checklstring(L, 2, &len));

	smp_set_owner(&smp, htxn->p, htxn->s->sess, htxn->s, htxn->dir & SMP_OPT_DIR);
	if (!vars_get_by_name(name, len, &smp, NULL)) {
		lua_pushnil(L);
		return 1;
	}

	return MAY_LJMP(hlua_smp2lua(L, &smp));
}

__LJMP static int hlua_set_priv(lua_State *L)
{
	struct hlua *hlua;

	MAY_LJMP(check_args(L, 2, "set_priv"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	MAY_LJMP(hlua_checktxn(L, 1));

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua)
		return 0;

	/* Remove previous value. */
	luaL_unref(L, LUA_REGISTRYINDEX, hlua->Mref);

	/* 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_get_priv(lua_State *L)
{
	struct hlua *hlua;

	MAY_LJMP(check_args(L, 1, "get_priv"));

	/* It is useles to retrieve the stream, but this function
	 * runs only in a stream context.
	 */
	MAY_LJMP(hlua_checktxn(L, 1));

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua) {
		lua_pushnil(L);
		return 1;
	}

	/* 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.
 */
__LJMP static int hlua_txn_new(lua_State *L, struct stream *s, struct proxy *p, int dir, int flags)
{
	struct hlua_txn *htxn;

	/* Check stack size. */
	if (!lua_checkstack(L, 3))
		return 0;

	/* NOTE: The allocation never fails. The failure
	 * throw an error, and the function never returns.
	 * if the throw is not available, the process is aborted.
	 */
	/* Create the object: obj[0] = userdata. */
	lua_newtable(L);
	htxn = lua_newuserdata(L, sizeof(*htxn));
	lua_rawseti(L, -2, 0);

	htxn->s = s;
	htxn->p = p;
	htxn->dir = dir;
	htxn->flags = flags;

	/* Create the "f" field that contains a list of fetches. */
	lua_pushstring(L, "f");
	if (!hlua_fetches_new(L, htxn, HLUA_F_MAY_USE_CHANNELS_DATA))
		return 0;
	lua_rawset(L, -3);

	/* Create the "sf" field that contains a list of stringsafe fetches. */
	lua_pushstring(L, "sf");
	if (!hlua_fetches_new(L, htxn, HLUA_F_MAY_USE_CHANNELS_DATA | HLUA_F_AS_STRING))
		return 0;
	lua_rawset(L, -3);

	/* Create the "c" field that contains a list of converters. */
	lua_pushstring(L, "c");
	if (!hlua_converters_new(L, htxn, 0))
		return 0;
	lua_rawset(L, -3);

	/* Create the "sc" field that contains a list of stringsafe converters. */
	lua_pushstring(L, "sc");
	if (!hlua_converters_new(L, htxn, HLUA_F_AS_STRING))
		return 0;
	lua_rawset(L, -3);

	/* Create the "req" field that contains the request channel object. */
	lua_pushstring(L, "req");
	if (!hlua_channel_new(L, &s->req))
		return 0;
	lua_rawset(L, -3);

	/* Create the "res" field that contains the response channel object. */
	lua_pushstring(L, "res");
	if (!hlua_channel_new(L, &s->res))
		return 0;
	lua_rawset(L, -3);

	/* Creates the HTTP object is the current proxy allows http. */
	lua_pushstring(L, "http");
	if (IS_HTX_STRM(s)) {
		if (!hlua_http_new(L, htxn))
			return 0;
	}
	else
		lua_pushnil(L);
	lua_rawset(L, -3);

	if ((htxn->flags & HLUA_TXN_CTX_MASK) == HLUA_TXN_FLT_CTX) {
		/* HTTPMessage object are created when a lua TXN is created from
		 * a filter context only
		 */

		/* Creates the HTTP-Request object is the current proxy allows http. */
		lua_pushstring(L, "http_req");
		if (p->mode == PR_MODE_HTTP) {
			if (!hlua_http_msg_new(L, &s->txn.http->req))
				return 0;
		}
		else
			lua_pushnil(L);
		lua_rawset(L, -3);

		/* Creates the HTTP-Response object is the current proxy allows http. */
		lua_pushstring(L, "http_res");
		if (p->mode == PR_MODE_HTTP) {
			if (!hlua_http_msg_new(L, &s->txn.http->rsp))
				return 0;
		}
		else
			lua_pushnil(L);
		lua_rawset(L, -3);
	}

	/* 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;
}

__LJMP static int hlua_txn_deflog(lua_State *L)
{
	const char *msg;
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "deflog"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	msg = MAY_LJMP(luaL_checkstring(L, 2));

	hlua_sendlog(htxn->s->be, htxn->s->logs.level, msg);
	return 0;
}

__LJMP static int hlua_txn_log(lua_State *L)
{
	int level;
	const char *msg;
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 3, "log"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	level = MAY_LJMP(luaL_checkinteger(L, 2));
	msg = MAY_LJMP(luaL_checkstring(L, 3));

	if (level < 0 || level >= NB_LOG_LEVELS)
		WILL_LJMP(luaL_argerror(L, 1, "Invalid loglevel."));

	hlua_sendlog(htxn->s->be, level, msg);
	return 0;
}

__LJMP static int hlua_txn_log_debug(lua_State *L)
{
	const char *msg;
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "Debug"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	msg = MAY_LJMP(luaL_checkstring(L, 2));
	hlua_sendlog(htxn->s->be, LOG_DEBUG, msg);
	return 0;
}

__LJMP static int hlua_txn_log_info(lua_State *L)
{
	const char *msg;
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "Info"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	msg = MAY_LJMP(luaL_checkstring(L, 2));
	hlua_sendlog(htxn->s->be, LOG_INFO, msg);
	return 0;
}

__LJMP static int hlua_txn_log_warning(lua_State *L)
{
	const char *msg;
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "Warning"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	msg = MAY_LJMP(luaL_checkstring(L, 2));
	hlua_sendlog(htxn->s->be, LOG_WARNING, msg);
	return 0;
}

__LJMP static int hlua_txn_log_alert(lua_State *L)
{
	const char *msg;
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "Alert"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	msg = MAY_LJMP(luaL_checkstring(L, 2));
	hlua_sendlog(htxn->s->be, LOG_ALERT, msg);
	return 0;
}

__LJMP static int hlua_txn_set_fc_mark(lua_State *L)
{
	struct hlua_txn *htxn;
	int mark;

	MAY_LJMP(check_args(L, 2, "set_fc_mark"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	mark = MAY_LJMP(luaL_checkinteger(L, 2));

	conn_set_mark(objt_conn(htxn->s->sess->origin), mark);
	return 0;
}

__LJMP static int hlua_txn_set_fc_tos(lua_State *L)
{
	struct hlua_txn *htxn;
	int tos;

	MAY_LJMP(check_args(L, 2, "set_fc_tos"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	tos = MAY_LJMP(luaL_checkinteger(L, 2));

	conn_set_tos(objt_conn(htxn->s->sess->origin), tos);
	return 0;
}

__LJMP static int hlua_txn_set_loglevel(lua_State *L)
{
	struct hlua_txn *htxn;
	int ll;

	MAY_LJMP(check_args(L, 2, "set_loglevel"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	ll = MAY_LJMP(luaL_checkinteger(L, 2));

	if (ll < -1 || ll > NB_LOG_LEVELS)
		WILL_LJMP(luaL_argerror(L, 2, "Bad log level. It must be one of the following value:"
					" core.silent(-1), core.emerg(0), core.alert(1), core.crit(2), core.error(3),"
					" core.warning(4), core.notice(5), core.info(6) or core.debug(7)"));

	htxn->s->logs.level = (ll == -1) ? ll : ll + 1;
	return 0;
}

__LJMP static int hlua_txn_set_priority_class(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "set_priority_class"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	htxn->s->priority_class = queue_limit_class(MAY_LJMP(luaL_checkinteger(L, 2)));
	return 0;
}

__LJMP static int hlua_txn_set_priority_offset(lua_State *L)
{
	struct hlua_txn *htxn;

	MAY_LJMP(check_args(L, 2, "set_priority_offset"));
	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	htxn->s->priority_offset = queue_limit_offset(MAY_LJMP(luaL_checkinteger(L, 2)));
	return 0;
}

/* Forward the Reply object to the client. This function converts the reply in
 * HTX an push it to into the response channel. It is response to forward the
 * message and terminate the transaction. It returns 1 on success and 0 on
 * error. The Reply must be on top of the stack.
 */
__LJMP static int hlua_txn_forward_reply(lua_State *L, struct stream *s)
{
	struct htx *htx;
	struct htx_sl *sl;
	struct h1m h1m;
	const char *status, *reason, *body;
	size_t status_len, reason_len, body_len;
	int ret, code, flags;

	code = 200;
	status = "200";
	status_len = 3;
	ret = lua_getfield(L, -1, "status");
	if (ret == LUA_TNUMBER) {
		code = lua_tointeger(L, -1);
		status = lua_tolstring(L, -1, &status_len);
	}
	lua_pop(L, 1);

	reason = http_get_reason(code);
	reason_len = strlen(reason);
	ret = lua_getfield(L, -1, "reason");
	if (ret == LUA_TSTRING)
		reason = lua_tolstring(L, -1, &reason_len);
	lua_pop(L, 1);

	body = NULL;
	body_len = 0;
	ret = lua_getfield(L, -1, "body");
	if (ret == LUA_TSTRING)
		body = lua_tolstring(L, -1, &body_len);
	lua_pop(L, 1);

	/* Prepare the response before inserting the headers */
	h1m_init_res(&h1m);
	htx = htx_from_buf(&s->res.buf);
	channel_htx_truncate(&s->res, htx);
	if (s->txn.http->req.flags & HTTP_MSGF_VER_11) {
		flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11);
		sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"),
				    ist2(status, status_len), ist2(reason, reason_len));
	}
	else {
		flags = HTX_SL_F_IS_RESP;
		sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.0"),
				    ist2(status, status_len), ist2(reason, reason_len));
	}
	if (!sl)
		goto fail;
	sl->info.res.status = code;

	/* Push in the stack the "headers" entry. */
	ret = lua_getfield(L, -1, "headers");
	if (ret != LUA_TTABLE)
		goto skip_headers;

	lua_pushnil(L);
	while (lua_next(L, -2) != 0) {
		struct ist name, value;
		const char *n, *v;
		size_t nlen, vlen;

		if (!lua_isstring(L, -2) || !lua_istable(L, -1)) {
			/* Skip element if the key is not a string or if the value is not a table */
			goto next_hdr;
		}

		n = lua_tolstring(L, -2, &nlen);
		name = ist2(n, nlen);
		if (isteqi(name, ist("content-length"))) {
			/* Always skip content-length header. It will be added
			 * later with the correct len
			 */
			goto next_hdr;
		}

		/* Loop on header's values */
		lua_pushnil(L);
		while (lua_next(L, -2)) {
			if (!lua_isstring(L, -1)) {
				/* Skip the value if it is not a string */
				goto next_value;
			}

			v = lua_tolstring(L, -1, &vlen);
			value = ist2(v, vlen);

			if (isteqi(name, ist("transfer-encoding")))
				h1_parse_xfer_enc_header(&h1m, value);
			if (!htx_add_header(htx, ist2(n, nlen), ist2(v, vlen)))
				goto fail;

		  next_value:
			lua_pop(L, 1);
		}

	  next_hdr:
		lua_pop(L, 1);
	}
  skip_headers:
	lua_pop(L, 1);

	/* Update h1m flags: CLEN is set if CHNK is not present */
	if (!(h1m.flags & H1_MF_CHNK)) {
		const char *clen = ultoa(body_len);

		h1m.flags |= H1_MF_CLEN;
		if (!htx_add_header(htx, ist("content-length"), ist(clen)))
			goto fail;
	}
	if (h1m.flags & (H1_MF_CLEN|H1_MF_CHNK))
		h1m.flags |= H1_MF_XFER_LEN;

	/* Update HTX start-line flags */
	if (h1m.flags & H1_MF_XFER_ENC)
		flags |= HTX_SL_F_XFER_ENC;
	if (h1m.flags & H1_MF_XFER_LEN) {
		flags |= HTX_SL_F_XFER_LEN;
		if (h1m.flags & H1_MF_CHNK)
			flags |= HTX_SL_F_CHNK;
		else if (h1m.flags & H1_MF_CLEN)
			flags |= HTX_SL_F_CLEN;
		if (h1m.body_len == 0)
			flags |= HTX_SL_F_BODYLESS;
	}
	sl->flags |= flags;


	if (!htx_add_endof(htx, HTX_BLK_EOH) ||
	    (body_len && !htx_add_data_atonce(htx, ist2(body, body_len))))
		goto fail;

	htx->flags |= HTX_FL_EOM;

	/* Now, forward the response and terminate the transaction */
	s->txn.http->status = code;
	htx_to_buf(htx, &s->res.buf);
	if (!http_forward_proxy_resp(s, 1))
		goto fail;

	return 1;

  fail:
	channel_htx_truncate(&s->res, htx);
	return 0;
}

/* Terminate a transaction if called from a lua action. For TCP streams,
 * processing is just aborted. Nothing is returned to the client and all
 * arguments are ignored. For HTTP streams, if a reply is passed as argument, it
 * is forwarded to the client before terminating the transaction. On success,
 * the function exits with ACT_RET_DONE code. If an error occurred, it exits
 * with ACT_RET_ERR code. If this function is not called from a lua action, it
 * just exits without any processing.
 */
__LJMP static int hlua_txn_done(lua_State *L)
{
	struct hlua_txn *htxn;
	struct stream *s;
	int finst;

	htxn = MAY_LJMP(hlua_checktxn(L, 1));

	/* If the flags NOTERM is set, we cannot terminate the session, so we
	 * just end the execution of the current lua code. */
	if (htxn->flags & HLUA_TXN_NOTERM)
		WILL_LJMP(hlua_done(L));

	s = htxn->s;
	if (!IS_HTX_STRM(htxn->s)) {
		struct channel *req = &s->req;
		struct channel *res = &s->res;

		channel_auto_read(req);
		channel_abort(req);
		channel_erase(req);

		channel_auto_read(res);
		channel_auto_close(res);
		sc_schedule_abort(s->scb);

		finst = ((htxn->dir == SMP_OPT_DIR_REQ) ? SF_FINST_R : SF_FINST_D);
		goto done;
	}

	if (lua_gettop(L) == 1 || !lua_istable(L, 2)) {
		/* No reply or invalid reply */
		s->txn.http->status = 0;
		http_reply_and_close(s, 0, NULL);
	}
	else {
		/* Remove extra args to have the reply on top of the stack */
		if (lua_gettop(L) > 2)
			lua_pop(L, lua_gettop(L) - 2);

		if (!hlua_txn_forward_reply(L, s)) {
			if (!(s->flags & SF_ERR_MASK))
				s->flags |= SF_ERR_PRXCOND;
			lua_pushinteger(L, ACT_RET_ERR);
			WILL_LJMP(hlua_done(L));
			return 0; /* Never reached */
		}
	}

	finst = ((htxn->dir == SMP_OPT_DIR_REQ) ? SF_FINST_R : SF_FINST_H);
	if (htxn->dir == SMP_OPT_DIR_REQ) {
		/* let's log the request time */
		s->logs.request_ts = now_ns;
		/* report it if the request was intercepted by the frontend */
		if (s->sess->fe == s->be && s->sess->fe_tgcounters)
			_HA_ATOMIC_INC(&s->sess->fe_tgcounters->intercepted_req);
	}

  done:
	if (!(s->flags & SF_ERR_MASK))
		s->flags |= SF_ERR_LOCAL;
	if (!(s->flags & SF_FINST_MASK))
		s->flags |= finst;

	if ((htxn->flags & HLUA_TXN_CTX_MASK) == HLUA_TXN_FLT_CTX)
		lua_pushinteger(L, -1);
	else
		lua_pushinteger(L, ACT_RET_ABRT);
	WILL_LJMP(hlua_done(L));
	return 0;
}

/*
 *
 *
 * Class REPLY
 *
 *
 */

/* Pushes the TXN reply onto the top of the stack. If the stask does not have a
 * free slots, the function fails and returns 0;
 */
__LJMP static int hlua_txn_reply_new(lua_State *L)
{
	struct hlua_txn *htxn;
	const char *reason, *body = NULL;
	int ret, status;

	htxn = MAY_LJMP(hlua_checktxn(L, 1));
	if (!IS_HTX_STRM(htxn->s)) {
		hlua_pusherror(L, "txn object is not an HTTP transaction.");
		WILL_LJMP(lua_error(L));
	}

	/* Default value */
	status = 200;
	reason = http_get_reason(status);

	if (lua_istable(L, 2)) {
		/* load status and reason from the table argument at index 2 */
		ret = lua_getfield(L, 2, "status");
		if (ret == LUA_TNIL)
			goto reason;
		else if (ret != LUA_TNUMBER) {
			/* invalid status: ignore the reason */
			goto body;
		}
		status = lua_tointeger(L, -1);

	  reason:
		lua_pop(L, 1); /* restore the stack: remove status */
		ret = lua_getfield(L, 2, "reason");
		if (ret == LUA_TSTRING)
			reason = lua_tostring(L, -1);

	  body:
		lua_pop(L, 1); /* restore the stack: remove invalid status or reason */
		ret = lua_getfield(L, 2, "body");
		if (ret == LUA_TSTRING)
			body = lua_tostring(L, -1);
		lua_pop(L, 1); /* restore the stack: remove  body */
	}

	/* Create the Reply table */
	lua_newtable(L);

	/* Add status element */
	lua_pushstring(L, "status");
	lua_pushinteger(L, status);
	lua_settable(L, -3);

	/* Add reason element */
	reason = http_get_reason(status);
	lua_pushstring(L, "reason");
	lua_pushstring(L, reason);
	lua_settable(L, -3);

	/* Add body element, nil if undefined */
	lua_pushstring(L, "body");
	if (body)
		lua_pushstring(L, body);
	else
		lua_pushnil(L);
	lua_settable(L, -3);

	/* Add headers element */
	lua_pushstring(L, "headers");
	lua_newtable(L);

	/* stack: [ txn, <Arg:table>, <Reply:table>, "headers", <headers:table> ] */
	if (lua_istable(L, 2)) {
		/* load headers from the table argument at index 2. If it is a table, copy it. */
		ret = lua_getfield(L, 2, "headers");
		if (ret == LUA_TTABLE) {
			/* stack: [ ... <headers:table>, <table> ] */
			lua_pushnil(L);
			while (lua_next(L, -2) != 0) {
				/* stack: [ ... <headers:table>, <table>, k, v] */
				if (!lua_isstring(L, -1) && !lua_istable(L, -1)) {
					/* invalid value type, skip it */
					lua_pop(L, 1);
					continue;
				}


				/* Duplicate the key and swap it with the value. */
				lua_pushvalue(L, -2);
				lua_insert(L, -2);
				/* stack: [ ... <headers:table>, <table>, k, k, v ] */

				lua_newtable(L);
				lua_insert(L, -2);
				/* stack: [ ... <headers:table>, <table>, k, k, <inner:table>, v ] */

				if (lua_isstring(L, -1)) {
					/* push the value in the inner table */
					lua_rawseti(L, -2, 1);
				}
				else { /* table */
					lua_pushnil(L);
					while (lua_next(L, -2) != 0) {
						/* stack: [ ... <headers:table>, <table>, k, k, <inner:table>, <v:table>, k2, v2 ] */
						if (!lua_isstring(L, -1)) {
							/* invalid value type, skip it*/
							lua_pop(L, 1);
							continue;
						}
						/* push the value in the inner table */
						lua_rawseti(L, -4, lua_rawlen(L, -4) + 1);
						/* stack: [ ... <headers:table>, <table>, k, k, <inner:table>, <v:table>, k2 ] */
					}
					lua_pop(L, 1);
					/* stack: [ ... <headers:table>, <table>, k, k, <inner:table> ] */
				}

				/* push (k,v) on the stack in the headers table:
				 * stack: [ ... <headers:table>, <table>, k, k, v ]
				 */
				lua_settable(L, -5);
				/* stack: [ ... <headers:table>, <table>, k ] */
			}
		}
		lua_pop(L, 1);
	}
	/* stack: [ txn, <Arg:table>, <Reply:table>, "headers", <headers:table> ] */
	lua_settable(L, -3);
	/* stack: [ txn, <Arg:table>, <Reply:table> ] */

	/* Pop a class sesison metatable and affect it to the userdata. */
	lua_rawgeti(L, LUA_REGISTRYINDEX, class_txn_reply_ref);
	lua_setmetatable(L, -2);
	return 1;
}

/* Set the reply status code, and optionally the reason. If no reason is
 * provided, the default one corresponding to the status code is used.
 */
__LJMP static int hlua_txn_reply_set_status(lua_State *L)
{
	int status = MAY_LJMP(luaL_checkinteger(L, 2));
	const char *reason = MAY_LJMP(luaL_optlstring(L, 3, NULL, NULL));

	/* First argument (self) must be a table */
	MAY_LJMP(luaL_checktype(L, 1, LUA_TTABLE));

	if (status < 100 || status > 599) {
		lua_pushboolean(L, 0);
		return 1;
	}
	if (!reason)
		reason = http_get_reason(status);

	lua_pushinteger(L, status);
	lua_setfield(L, 1, "status");

	lua_pushstring(L, reason);
	lua_setfield(L, 1, "reason");

	lua_pushboolean(L, 1);
	return 1;
}

/* Add a header into the reply object. Each header name is associated to an
 * array of values in the "headers" table. If the header name is not found, a
 * new entry is created.
 */
__LJMP static int hlua_txn_reply_add_header(lua_State *L)
{
	const char *name = MAY_LJMP(luaL_checkstring(L, 2));
	const char *value = MAY_LJMP(luaL_checkstring(L, 3));
	int ret;

	/* First argument (self) must be a table */
	MAY_LJMP(luaL_checktype(L, 1, LUA_TTABLE));

	/* Push in the stack the "headers" entry. */
	ret = lua_getfield(L, 1, "headers");
	if (ret != LUA_TTABLE) {
		hlua_pusherror(L, "Reply['headers'] is expected to a an array. %s found", lua_typename(L, ret));
		WILL_LJMP(lua_error(L));
	}

	/* check if the header is already registered. If not, register it. */
	ret = lua_getfield(L, -1, name);
	if (ret == LUA_TNIL) {
		/* Entry not found. */
		lua_pop(L, 1); /* remove the nil. The "headers" table is the top of the stack. */

		/* Insert the new header name in the array in the top of the stack.
		 * It left the new array in the top of the stack.
		 */
		lua_newtable(L);
		lua_pushstring(L, name);
		lua_pushvalue(L, -2);
		lua_settable(L, -4);
	}
	else if (ret != LUA_TTABLE) {
		hlua_pusherror(L, "Reply['headers']['%s'] is expected to be an array. %s found", name, lua_typename(L, ret));
		WILL_LJMP(lua_error(L));
	}

	/* Now the top of thestack is an array of values. We push
	 * the header value as new entry.
	 */
	lua_pushstring(L, value);
	ret = lua_rawlen(L, -2);
	lua_rawseti(L, -2, ret + 1);

	lua_pushboolean(L, 1);
	return 1;
}

/* Remove all occurrences of a given header name. */
__LJMP static int hlua_txn_reply_del_header(lua_State *L)
{
	const char *name = MAY_LJMP(luaL_checkstring(L, 2));
	int ret;

	/* First argument (self) must be a table */
	MAY_LJMP(luaL_checktype(L, 1, LUA_TTABLE));

	/* Push in the stack the "headers" entry. */
	ret = lua_getfield(L, 1, "headers");
	if (ret != LUA_TTABLE) {
		hlua_pusherror(L, "Reply['headers'] is expected to be an array. %s found", lua_typename(L, ret));
		WILL_LJMP(lua_error(L));
	}

	lua_pushstring(L, name);
	lua_pushnil(L);
	lua_settable(L, -3);

	lua_pushboolean(L, 1);
	return 1;
}

/* Set the reply's body. Overwrite any existing entry. */
__LJMP static int hlua_txn_reply_set_body(lua_State *L)
{
	const char *payload = MAY_LJMP(luaL_checkstring(L, 2));

	/* First argument (self) must be a table */
	MAY_LJMP(luaL_checktype(L, 1, LUA_TTABLE));

	lua_pushstring(L, payload);
	lua_setfield(L, 1, "body");

	lua_pushboolean(L, 1);
	return 1;
}

__LJMP static int hlua_log(lua_State *L)
{
	int level;
	const char *msg;

	MAY_LJMP(check_args(L, 2, "log"));
	level = MAY_LJMP(luaL_checkinteger(L, 1));
	msg = MAY_LJMP(luaL_checkstring(L, 2));

	if (level < 0 || level >= NB_LOG_LEVELS)
		WILL_LJMP(luaL_argerror(L, 1, "Invalid loglevel."));

	hlua_sendlog(NULL, level, msg);
	return 0;
}

__LJMP static int hlua_log_debug(lua_State *L)
{
	const char *msg;

	MAY_LJMP(check_args(L, 1, "debug"));
	msg = MAY_LJMP(luaL_checkstring(L, 1));
	hlua_sendlog(NULL, LOG_DEBUG, msg);
	return 0;
}

__LJMP static int hlua_log_info(lua_State *L)
{
	const char *msg;

	MAY_LJMP(check_args(L, 1, "info"));
	msg = MAY_LJMP(luaL_checkstring(L, 1));
	hlua_sendlog(NULL, LOG_INFO, msg);
	return 0;
}

__LJMP static int hlua_log_warning(lua_State *L)
{
	const char *msg;

	MAY_LJMP(check_args(L, 1, "warning"));
	msg = MAY_LJMP(luaL_checkstring(L, 1));
	hlua_sendlog(NULL, LOG_WARNING, msg);
	return 0;
}

__LJMP static int hlua_log_alert(lua_State *L)
{
	const char *msg;

	MAY_LJMP(check_args(L, 1, "alert"));
	msg = MAY_LJMP(luaL_checkstring(L, 1));
	hlua_sendlog(NULL, LOG_ALERT, msg);
	return 0;
}

__LJMP static int hlua_sleep_yield(lua_State *L, int status, lua_KContext ctx)
{
	int wakeup_ms = lua_tointeger(L, -1);
	if (!tick_is_expired(wakeup_ms, now_ms))
		MAY_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;
	int wakeup_ms; // tick value

	MAY_LJMP(check_args(L, 1, "sleep"));

	delay = MAY_LJMP(luaL_checkinteger(L, 1)) * 1000;
	wakeup_ms = tick_add(now_ms, delay);
	lua_pushinteger(L, wakeup_ms);

	MAY_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;
	int wakeup_ms; // tick value

	MAY_LJMP(check_args(L, 1, "msleep"));

	delay = MAY_LJMP(luaL_checkinteger(L, 1));
	wakeup_ms = tick_add(now_ms, delay);
	lua_pushinteger(L, wakeup_ms);

	MAY_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, int status, lua_KContext ctx)
{
	return 0;
}

__LJMP static int hlua_yield(lua_State *L)
{
	MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_yield_yield, TICK_ETERNITY, HLUA_CTRLYIELD));
	return 0;
}

/* same as hlua_yield() but doesn't enforce CTRLYIELD so the Lua task won't be
 * automatically woken up to resume ASAP, instead it means we will wait for
 * an event to occur to wake the task. Only use when you're confident that
 * something or someone will wake the task at some point.
 *
 * Takes optional <delay> argument as parameter, in which case an automatic
 * wake will be performed after <delay>
 */
__LJMP static int hlua_wait(lua_State *L)
{
	int nb_arg;
	unsigned int delay;
	int wakeup_ms = TICK_ETERNITY; // tick value

	nb_arg = lua_gettop(L);
	if (nb_arg >= 1) {
		delay = MAY_LJMP(luaL_checkinteger(L, 1));
		wakeup_ms = tick_add(now_ms, delay);
	}

	MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_yield_yield, wakeup_ms, 0));
	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_set_nice(lua_State *L)
{
	struct hlua *hlua;
	int nice;

	MAY_LJMP(check_args(L, 1, "set_nice"));
	nice = MAY_LJMP(luaL_checkinteger(L, 1));

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);

	/* If the 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;
}

/* safe lua coroutine.create() function:
 *
 * This is a simple wrapper for coroutine.create() that
 * ensures the current hlua state ctx is available from
 * the new subroutine state
 */
__LJMP static int hlua_coroutine_create(lua_State *L)
{
	lua_State *new; /* new coroutine state */
	struct hlua **hlua_store;
	struct hlua *hlua = hlua_gethlua(L);

	new = lua_newthread(L);
	if (!new)
		return 0;

	hlua_store = lua_getextraspace(new);
	/* Expose current hlua ctx on new lua thread
	 * (hlua_gethlua() will properly return the last "known"
	 *  hlua ctx instead of NULL when it is called from such coroutines)
	 */
	*hlua_store = hlua;

	/* new lua thread is on the top of the stack, we
	 * need to duplicate first stack argument (<f> from coroutine.create(<f>))
	 * on the top of the stack to be able to use xmove() to move it on the new
	 * stack
	 */
	lua_pushvalue(L, 1);
	/* move <f> function to the new stack */
	lua_xmove(L, new, 1);
	/* new lua thread is back at the top of the stack */
	return 1;
}

/* This function is used as a callback 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.
 *
 * Task wrapper are longjmp safe because the only one Lua code
 * executed is the safe hlua_ctx_resume();
 */
struct task *hlua_process_task(struct task *task, void *context, unsigned int state)
{
	struct hlua *hlua = context;
	enum hlua_exec status;

	if (task->tid < 0)
		task->tid = tid;

	/* If it is the first call to the task, we must initialize the
	 * execution timeouts.
	 */
	if (!HLUA_IS_RUNNING(hlua))
		hlua_timer_init(&hlua->timer, 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_destroy(task);
		task = NULL;
		break;

	case HLUA_E_AGAIN: /* co process or timeout wake me later. */
		notification_gc(&hlua->com);
		task->expire = hlua->wake_time;
		break;

	/* finished with error. */
	case HLUA_E_ETMOUT:
		SEND_ERR(NULL, "Lua task: execution timeout.\n");
		goto err_task_abort;
	case HLUA_E_BTMOUT:
		SEND_ERR(NULL, "Lua task: burst timeout.\n");
		goto err_task_abort;
	case HLUA_E_ERRMSG:
		hlua_lock(hlua);
		SEND_ERR(NULL, "Lua task: %s.\n", hlua_tostring_safe(hlua->T, -1));
		hlua_unlock(hlua);
		goto err_task_abort;
	case HLUA_E_ERR:
	default:
		SEND_ERR(NULL, "Lua task: unknown error.\n");
	err_task_abort:
		hlua_ctx_destroy(hlua);
		task_destroy(task);
		task = NULL;
		break;
	}
	return task;
}

/* 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"));

	if (hlua_gethlua(L)) {
		/* runtime processing */
		WILL_LJMP(luaL_error(L, "register_init: not available outside of body context"));
	}

	ref = MAY_LJMP(hlua_checkfunction(L, 1));

	init = calloc(1, sizeof(*init));
	if (!init) {
		hlua_unref(L, ref);
		WILL_LJMP(luaL_error(L, "Lua out of memory error."));
	}

	init->function_ref = ref;
	LIST_APPEND(&hlua_init_functions[hlua_state_id], &init->l);
	return 0;
}

/* This function 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>[, <arg1>[, <arg2>[, ...[, <arg4>]]]])
 *
 * <arg1..4> are optional arguments that will be provided to <function>
 */
__LJMP static int hlua_register_task(lua_State *L)
{
	struct hlua *hlua = NULL;
	struct task *task = NULL;
	int ref;
	int nb_arg;
	int it;
	int arg_ref[4]; /* optional arguments */
	int state_id;

	nb_arg = lua_gettop(L);
	if (nb_arg < 1)
		WILL_LJMP(luaL_error(L, "register_task: <func> argument is required"));
	else if (nb_arg > 5)
		WILL_LJMP(luaL_error(L, "register_task: no more that 4 optional arguments may be provided"));

	/* first arg: function ref */
	ref = MAY_LJMP(hlua_checkfunction(L, 1));

	/* extract optional args (if any) */
	it = 0;
	while (--nb_arg) {
		lua_pushvalue(L, 2 + it);
		arg_ref[it] = hlua_ref(L); /* get arg reference */
		it += 1;
	}
	nb_arg = it;

	/* Get the reference state. If the reference is NULL, L is the master
	 * state, otherwise hlua->T is.
	 */
	hlua = hlua_gethlua(L);
	if (hlua)
		/* we are in runtime processing */
		state_id = hlua->state_id;
	else
		/* we are in initialization mode */
		state_id = hlua_state_id;

	hlua = pool_alloc(pool_head_hlua);
	if (!hlua)
		goto alloc_error;
	HLUA_INIT(hlua);

	/* We are in the common lua state, execute the task anywhere,
	 * otherwise, inherit the current thread identifier
	 */
	if (state_id == 0)
		task = task_new_anywhere();
	else
		task = task_new_here();
	if (!task)
		goto alloc_error;

	task->context = hlua;
	task->process = hlua_process_task;

	if (!hlua_ctx_init(hlua, state_id, task))
		goto alloc_error;

	/* Ensure there is enough space on the stack for the function
	 * plus optional arguments
	 */
	if (!lua_checkstack(hlua->T, (1 + nb_arg)))
		goto alloc_error;

	/* Restore the function in the stack. */
	hlua_pushref(hlua->T, ref);
	/* function ref not needed anymore since it was pushed to the substack */
	hlua_unref(L, ref);

	hlua->nargs = nb_arg;

	/* push optional arguments to the function */
	for (it = 0; it < nb_arg; it++) {
		/* push arg to the stack */
		hlua_pushref(hlua->T, arg_ref[it]);
		/* arg ref not needed anymore since it was pushed to the substack */
		hlua_unref(L, arg_ref[it]);
	}

	/* Schedule task. */
	task_wakeup(task, TASK_WOKEN_INIT);

	return 0;

  alloc_error:
	task_destroy(task);
	hlua_unref(L, ref);
	for (it = 0; it < nb_arg; it++) {
		hlua_unref(L, arg_ref[it]);
	}
	hlua_ctx_destroy(hlua);
	WILL_LJMP(luaL_error(L, "Lua out of memory error."));
	return 0; /* Never reached */
}

/* called from unsafe location */
static void hlua_event_subscription_destroy(struct hlua_event_sub *hlua_sub)
{
	/* hlua cleanup */

	hlua_lock(hlua_sub->hlua);
	/* registry is shared between coroutines */
	hlua_unref(hlua_sub->hlua->T, hlua_sub->fcn_ref);
	hlua_unlock(hlua_sub->hlua);

	hlua_ctx_destroy(hlua_sub->hlua);

	/* free */
	pool_free(pool_head_hlua_event_sub, hlua_sub);
}

/* single event handler: hlua ctx is shared between multiple events handlers
 * issued from the same subscription. Thus, it is not destroyed when the event
 * is processed: it is destroyed when no more events are expected for the
 * subscription (ie: when the subscription ends).
 *
 * Moreover, events are processed sequentially within the subscription:
 * one event must be fully processed before another one may be processed.
 * This ensures proper consistency for lua event handling from an ordering
 * point of view. This is especially useful with server events for example
 * where ADD/DEL/UP/DOWN events ordering really matters to trigger specific
 * actions from lua (e.g.: sending emails or making API calls).
 *
 * Due to this design, each lua event handler is pleased to process the event
 * as fast as possible to prevent the event queue from growing up.
 * Strictly speaking, there is no runtime limit for the callback function
 * (timeout set to default task timeout), but if the event queue goes past
 * the limit of unconsumed events an error will be reported and the
 * susbscription will pause itself for as long as it takes for the handler to
 * catch up (events will be lost as a result).
 * If the event handler does not need the sequential ordering and wants to
 * process multiple events at a time, it may spawn a new side-task using
 * 'core.register_task' to delegate the event handling and make parallel event
 * processing within the same subscription set.
 */
static void hlua_event_handler(struct hlua *hlua)
{
	enum hlua_exec status;

	/* If it is the first call to the task, we must initialize the
	 * execution timeouts.
	 */
	if (!HLUA_IS_RUNNING(hlua))
		hlua_timer_init(&hlua->timer, hlua_timeout_task);

	/* make sure to reset the task expiry before each hlua_ctx_resume()
	 * since the task is reused for multiple cb function calls
	 * We couldn't risk to have t->expire pointing to a past date because
	 * it was set during last function invocation but was never reset since
	 * (ie: E_AGAIN)
	 */
	hlua->task->expire = TICK_ETERNITY;

	/* Execute the Lua code. */
	status = hlua_ctx_resume(hlua, 1);

	switch (status) {
	/* finished or yield */
	case HLUA_E_OK:
		break;

	case HLUA_E_AGAIN: /* co process or timeout wake me later. */
		notification_gc(&hlua->com);
		hlua->task->expire = hlua->wake_time;
		break;

	/* finished with error. */
	case HLUA_E_ETMOUT:
		SEND_ERR(NULL, "Lua event_hdl: execution timeout.\n");
		break;

	case HLUA_E_BTMOUT:
		SEND_ERR(NULL, "Lua event_hdl: burst timeout.\n");
		break;

	case HLUA_E_ERRMSG:
		hlua_lock(hlua);
		SEND_ERR(NULL, "Lua event_hdl: %s.\n", hlua_tostring_safe(hlua->T, -1));
		hlua_unlock(hlua);
		break;

	case HLUA_E_ERR:
	default:
		SEND_ERR(NULL, "Lua event_hdl: unknown error.\n");
		break;
	}
}

__LJMP static void hlua_event_hdl_cb_push_event_checkres(lua_State *L,
                                                         struct event_hdl_cb_data_server_checkres *check)
{
	lua_pushstring(L, "agent");
	lua_pushboolean(L, check->agent);
	lua_settable(L, -3);
	lua_pushstring(L, "result");
	switch (check->result) {
		case CHK_RES_FAILED:
			lua_pushstring(L, "FAILED");
			break;
		case CHK_RES_PASSED:
			lua_pushstring(L, "PASSED");
			break;
		case CHK_RES_CONDPASS:
			lua_pushstring(L, "CONDPASS");
			break;
		default:
			lua_pushnil(L);
			break;
	}
	lua_settable(L, -3);

	lua_pushstring(L, "duration");
	lua_pushinteger(L, check->duration);
	lua_settable(L, -3);

	lua_pushstring(L, "reason");
	lua_newtable(L);

	lua_pushstring(L, "short");
	lua_pushstring(L, get_check_status_info(check->reason.status));
	lua_settable(L, -3);
	lua_pushstring(L, "desc");
	lua_pushstring(L, get_check_status_description(check->reason.status));
	lua_settable(L, -3);
	if (check->reason.status >= HCHK_STATUS_L57DATA) {
		/* code only available when the check reached data analysis stage */
		lua_pushstring(L, "code");
		lua_pushinteger(L, check->reason.code);
		lua_settable(L, -3);
	}

	lua_settable(L, -3); /* reason table */

	lua_pushstring(L, "health");
	lua_newtable(L);

	lua_pushstring(L, "cur");
	lua_pushinteger(L, check->health.cur);
	lua_settable(L, -3);
	lua_pushstring(L, "rise");
	lua_pushinteger(L, check->health.rise);
	lua_settable(L, -3);
	lua_pushstring(L, "fall");
	lua_pushinteger(L, check->health.fall);
	lua_settable(L, -3);

	lua_settable(L, -3); /* health table */
}

/* This function pushes various arguments such as event type and event data to
 * the lua function that will be called to consume the event.
 */
__LJMP static void hlua_event_hdl_cb_push_args(struct hlua_event_sub *hlua_sub,
                                               struct event_hdl_async_event *e)
{
	struct hlua *hlua = hlua_sub->hlua;
	struct event_hdl_sub_type event = e->type;
	void *data = e->data;

	/* push event type */
	hlua->nargs = 1;
	lua_pushstring(hlua->T, event_hdl_sub_type_to_string(event));

	/* push event data (according to event type) */
	if (event_hdl_sub_family_equal(EVENT_HDL_SUB_SERVER, event)) {
		struct event_hdl_cb_data_server *e_server = data;
		struct proxy *px;
		struct server *server;

		hlua->nargs += 1;
		lua_newtable(hlua->T);
		/* Add server name */
		lua_pushstring(hlua->T, "name");
		lua_pushstring(hlua->T, e_server->safe.name);
		lua_settable(hlua->T, -3);
		/* Add server puid */
		lua_pushstring(hlua->T, "puid");
		lua_pushinteger(hlua->T, e_server->safe.puid);
		lua_settable(hlua->T, -3);
		/* Add server rid */
		lua_pushstring(hlua->T, "rid");
		lua_pushinteger(hlua->T, e_server->safe.rid);
		lua_settable(hlua->T, -3);
		/* Add server proxy name */
		lua_pushstring(hlua->T, "proxy_name");
		lua_pushstring(hlua->T, e_server->safe.proxy_name);
		lua_settable(hlua->T, -3);
		/* Add server proxy uuid */
		lua_pushstring(hlua->T, "proxy_uuid");
		lua_pushinteger(hlua->T, e_server->safe.proxy_uuid);
		lua_settable(hlua->T, -3);

		/* special events, fetch additional info with explicit type casting */
		if (event_hdl_sub_type_equal(EVENT_HDL_SUB_SERVER_STATE, event)) {
			struct event_hdl_cb_data_server_state *state = data;
			int it;

			if (!lua_checkstack(hlua->T, 20))
				WILL_LJMP(luaL_error(hlua->T, "Lua out of memory error."));

			/* state subclass */
			lua_pushstring(hlua->T, "state");
			lua_newtable(hlua->T);

			lua_pushstring(hlua->T, "admin");
			lua_pushboolean(hlua->T, state->safe.type);
			lua_settable(hlua->T, -3);

			/* is it because of a check ? */
			if (!state->safe.type &&
			    (state->safe.op_st_chg.cause == SRV_OP_STCHGC_HEALTH ||
			     state->safe.op_st_chg.cause == SRV_OP_STCHGC_AGENT)) {
				/* yes, provide check result */
				lua_pushstring(hlua->T, "check");
				lua_newtable(hlua->T);
				hlua_event_hdl_cb_push_event_checkres(hlua->T, &state->safe.op_st_chg.check);
				lua_settable(hlua->T, -3); /* check table */
			}

			lua_pushstring(hlua->T, "cause");
			if (state->safe.type)
				lua_pushstring(hlua->T, srv_adm_st_chg_cause(state->safe.adm_st_chg.cause));
			else
				lua_pushstring(hlua->T, srv_op_st_chg_cause(state->safe.op_st_chg.cause));
			lua_settable(hlua->T, -3);

			/* old_state, new_state */
			for (it = 0; it < 2; it++) {
				enum srv_state srv_state = (!it) ? state->safe.old_state : state->safe.new_state;

				lua_pushstring(hlua->T, (!it) ? "old_state" : "new_state");
				switch (srv_state) {
					case SRV_ST_STOPPED:
						lua_pushstring(hlua->T, "STOPPED");
						break;
					case SRV_ST_STOPPING:
						lua_pushstring(hlua->T, "STOPPING");
						break;
					case SRV_ST_STARTING:
						lua_pushstring(hlua->T, "STARTING");
						break;
					case SRV_ST_RUNNING:
						lua_pushstring(hlua->T, "RUNNING");
						break;
					default:
						lua_pushnil(hlua->T);
						break;
				}
				lua_settable(hlua->T, -3);
			}

			/* requeued */
			lua_pushstring(hlua->T, "requeued");
			lua_pushinteger(hlua->T, state->safe.requeued);
			lua_settable(hlua->T, -3);

			lua_settable(hlua->T, -3); /* state table */
		}
		else if (event_hdl_sub_type_equal(EVENT_HDL_SUB_SERVER_ADMIN, event)) {
			struct event_hdl_cb_data_server_admin *admin = data;
			int it;

			if (!lua_checkstack(hlua->T, 20))
				WILL_LJMP(luaL_error(hlua->T, "Lua out of memory error."));

			/* admin subclass */
			lua_pushstring(hlua->T, "admin");
			lua_newtable(hlua->T);

			lua_pushstring(hlua->T, "cause");
			lua_pushstring(hlua->T, srv_adm_st_chg_cause(admin->safe.cause));
			lua_settable(hlua->T, -3);

			/* old_admin, new_admin */
			for (it = 0; it < 2; it++) {
				enum srv_admin srv_admin = (!it) ? admin->safe.old_admin : admin->safe.new_admin;

				lua_pushstring(hlua->T, (!it) ? "old_admin" : "new_admin");

				/* admin state matrix */
				lua_newtable(hlua->T);

				lua_pushstring(hlua->T, "MAINT");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_MAINT);
				lua_settable(hlua->T, -3);
				lua_pushstring(hlua->T, "FMAINT");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_FMAINT);
				lua_settable(hlua->T, -3);
				lua_pushstring(hlua->T, "IMAINT");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_IMAINT);
				lua_settable(hlua->T, -3);
				lua_pushstring(hlua->T, "RMAINT");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_RMAINT);
				lua_settable(hlua->T, -3);
				lua_pushstring(hlua->T, "CMAINT");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_CMAINT);
				lua_settable(hlua->T, -3);

				lua_pushstring(hlua->T, "DRAIN");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_DRAIN);
				lua_settable(hlua->T, -3);
				lua_pushstring(hlua->T, "FDRAIN");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_FDRAIN);
				lua_settable(hlua->T, -3);
				lua_pushstring(hlua->T, "IDRAIN");
				lua_pushboolean(hlua->T, srv_admin & SRV_ADMF_IDRAIN);
				lua_settable(hlua->T, -3);

				lua_settable(hlua->T, -3); /* matrix table */
			}
			/* requeued */
			lua_pushstring(hlua->T, "requeued");
			lua_pushinteger(hlua->T, admin->safe.requeued);
			lua_settable(hlua->T, -3);

			lua_settable(hlua->T, -3); /* admin table */
		}
		else if (event_hdl_sub_type_equal(EVENT_HDL_SUB_SERVER_CHECK, event)) {
			struct event_hdl_cb_data_server_check *check = data;

			if (!lua_checkstack(hlua->T, 20))
				WILL_LJMP(luaL_error(hlua->T, "Lua out of memory error."));

			/* check subclass */
			lua_pushstring(hlua->T, "check");
			lua_newtable(hlua->T);

			/* check result snapshot */
			hlua_event_hdl_cb_push_event_checkres(hlua->T, &check->safe.res);

			lua_settable(hlua->T, -3); /* check table */
		}

		/* attempt to provide reference server object
		 * (if it wasn't removed yet, SERVER_DEL will never succeed here)
		 */
		px = proxy_find_by_id(e_server->safe.proxy_uuid, PR_CAP_BE, 0);
		BUG_ON(!px);
		server = server_find_by_id_unique(px, e_server->safe.puid, e_server->safe.rid);
		if (server) {
			lua_pushstring(hlua->T, "reference");
			hlua_fcn_new_server(hlua->T, server);
			lua_settable(hlua->T, -3);
		}
	}
	/* sub mgmt */
	hlua->nargs += 1;
	hlua_fcn_new_event_sub(hlua->T, hlua_sub->sub);

	/* when? */
	hlua->nargs += 1;
	lua_pushinteger(hlua->T, e->when.tv_sec);
}

/* events runner: if there's an ongoing hlua event handling process, finish it
 * then, check if there are new events waiting to be processed
 * (events are processed sequentially)
 *
 * We have a safety measure to warn/guard if the event queue is growing up
 * too much due to many events being generated and lua handler is unable to
 * keep up the pace (e.g.: when the event queue grows past 100 unconsumed events).
 * TODO: make it tunable
 */
static struct task *hlua_event_runner(struct task *task, void *context, unsigned int state)
{
	struct hlua_event_sub *hlua_sub = context;
	struct event_hdl_async_event *event;
	const char *error = NULL;

	if (!hlua_sub->paused && event_hdl_async_equeue_size(&hlua_sub->equeue) > 100) {
		const char *trace = NULL;

		/* We reached the limit of pending events in the queue: we should
		 * warn the user, and temporarily pause the subscription to give a chance
		 * to the handler to catch up? (it also prevents resource shortage since
		 * the queue could grow indefinitely otherwise)
		 * TODO: find a way to inform the handler that it missed some events
		 * (example: stats within the subscription in event_hdl api exposed via lua api?)
		 *
		 * Nonetheless, reaching this limit means that the handler is not fast enough
		 * and/or that it subscribed to events that happen too frequently and did not
		 * expect it. This could come from an inadequate design in the user's script.
		 */
		event_hdl_pause(hlua_sub->sub);
		hlua_sub->paused = 1;

		trace = hlua_traceback(hlua_sub->hlua->T, ", ");

		ha_warning("Lua event_hdl: pausing the subscription because the handler fails "
			   "to keep up the pace (%u unconsumed events) from %s.\n",
			   event_hdl_async_equeue_size(&hlua_sub->equeue), trace);
	}

	if (HLUA_IS_RUNNING(hlua_sub->hlua)) {
		/* ongoing hlua event handler, resume it */
		hlua_event_handler(hlua_sub->hlua);
	} else if ((event = event_hdl_async_equeue_pop(&hlua_sub->equeue))) { /* check for new events */
		if (event_hdl_sub_type_equal(event->type, EVENT_HDL_SUB_END)) {
			/* ending event: no more events to come */
			event_hdl_async_free_event(event);
			task_destroy(task);
			hlua_event_subscription_destroy(hlua_sub);
			return NULL;
		}
		/* new event: start processing it */

		/* The following Lua calls can fail. */
		if (!SET_SAFE_LJMP(hlua_sub->hlua)) {
			hlua_lock(hlua_sub->hlua);
			if (lua_type(hlua_sub->hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(hlua_sub->hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(NULL, "Lua event_hdl: %s.\n", error);
			hlua_unlock(hlua_sub->hlua);
			goto skip_event;
		}

		/* Check stack available size. */
		if (!lua_checkstack(hlua_sub->hlua->T, 5)) {
			SEND_ERR(NULL, "Lua event_hdl: full stack.\n");
			RESET_SAFE_LJMP(hlua_sub->hlua);
			goto skip_event;
		}

		/* Restore the function in the stack. */
		hlua_pushref(hlua_sub->hlua->T, hlua_sub->fcn_ref);

		/* push args */
		hlua_sub->hlua->nargs = 0;
		MAY_LJMP(hlua_event_hdl_cb_push_args(hlua_sub, event));

		/* At this point the execution is safe. */
		RESET_SAFE_LJMP(hlua_sub->hlua);

		/* At this point the event was successfully translated into hlua ctx,
		 * or hlua error occurred, so we can safely discard it
		 */
		event_hdl_async_free_event(event);
		event = NULL;

		hlua_event_handler(hlua_sub->hlua);
 skip_event:
		if (event)
			event_hdl_async_free_event(event);

	}

	if (!HLUA_IS_RUNNING(hlua_sub->hlua)) {
		/* we just finished the processing of one event..
		 * check for new events before becoming idle
		 */
		if (!event_hdl_async_equeue_isempty(&hlua_sub->equeue)) {
			/* more events to process, make sure the task
			 * will be resumed ASAP to process pending events
			 */
			task_wakeup(task, TASK_WOKEN_OTHER);
		}
		else if (hlua_sub->paused) {
			/* empty queue, the handler caught up: resume the subscription */
			event_hdl_resume(hlua_sub->sub);
			hlua_sub->paused = 0;
		}
	}

	return task;
}

/* Must be called directly under lua protected/safe environment
 * (not from external callback)
 * <fcn_ref> should NOT be dropped after the function successfully returns:
 * it will be done automatically in hlua_event_subscription_destroy() when the
 * subscription ends.
 *
 * Returns the new subscription on success and NULL on failure (memory error)
 */
static struct event_hdl_sub *hlua_event_subscribe(event_hdl_sub_list *list, struct event_hdl_sub_type e_type,
                                                  int state_id, int fcn_ref)
{
	struct hlua_event_sub *hlua_sub;
	struct task *task = NULL;

	hlua_sub = pool_alloc(pool_head_hlua_event_sub);
	if (!hlua_sub)
		goto mem_error;
	hlua_sub->task = NULL;
	hlua_sub->hlua = NULL;
	hlua_sub->paused = 0;
	if ((task = task_new_here()) == NULL)
		goto mem_error;
	task->process = hlua_event_runner;
	task->context = hlua_sub;
	event_hdl_async_equeue_init(&hlua_sub->equeue);
	hlua_sub->task = task;
	hlua_sub->fcn_ref = fcn_ref;
	hlua_sub->state_id = state_id;
	hlua_sub->hlua = pool_alloc(pool_head_hlua);
	if (!hlua_sub->hlua)
		goto mem_error;
	HLUA_INIT(hlua_sub->hlua);
	if (!hlua_ctx_init(hlua_sub->hlua, hlua_sub->state_id, task))
		goto mem_error;

	hlua_sub->sub = event_hdl_subscribe_ptr(list, e_type,
	                                        EVENT_HDL_ASYNC_TASK(&hlua_sub->equeue,
	                                                             task,
	                                                             hlua_sub,
	                                                             NULL));
	if (!hlua_sub->sub)
		goto mem_error;

	return hlua_sub->sub; /* returns pointer to event_hdl_sub struct */

 mem_error:
	if (hlua_sub) {
		task_destroy(hlua_sub->task);
		if (hlua_sub->hlua)
			hlua_ctx_destroy(hlua_sub->hlua);
		pool_free(pool_head_hlua_event_sub, hlua_sub);
	}

	return NULL;
}

/* looks for an array of strings referring to a composition of event_hdl subscription
 * types at <index> in <L> stack
 */
__LJMP static struct event_hdl_sub_type hlua_check_event_sub_types(lua_State *L, int index)
{
	struct event_hdl_sub_type subscriptions;
	const char *msg;

	if (lua_type(L, index) != LUA_TTABLE) {
		msg = lua_pushfstring(L, "table of strings expected, got %s", luaL_typename(L, index));
		luaL_argerror(L, index, msg);
	}

	subscriptions = EVENT_HDL_SUB_NONE;

	/* browse the argument as an array. */
	lua_pushnil(L);
	while (lua_next(L, index) != 0) {
		if (lua_type(L, -1) != LUA_TSTRING) {
			msg = lua_pushfstring(L, "table of strings expected, got %s", luaL_typename(L, index));
			luaL_argerror(L, index, msg);
		}

		if (event_hdl_sub_type_equal(EVENT_HDL_SUB_NONE, event_hdl_string_to_sub_type(lua_tostring(L, -1)))) {
			msg = lua_pushfstring(L, "'%s' event type is unknown", lua_tostring(L, -1));
			luaL_argerror(L, index, msg);
		}

		/* perform subscriptions |= current sub */
		subscriptions = event_hdl_sub_type_add(subscriptions, event_hdl_string_to_sub_type(lua_tostring(L, -1)));

		/* pop the current value. */
		lua_pop(L, 1);
	}

	return subscriptions;
}

/* Wrapper for hlua_fcn_new_event_sub(): catch errors raised by
 * the function to prevent LJMP
 *
 * If no error occurred, the function returns 1, else it returns 0 and
 * the error message is pushed at the top of the stack
 */
__LJMP static int _hlua_new_event_sub_safe(lua_State *L)
{
	struct event_hdl_sub *sub = lua_touserdata(L, 1);

	/* this function may raise errors */
	return MAY_LJMP(hlua_fcn_new_event_sub(L, sub));
}
static int hlua_new_event_sub_safe(lua_State *L, struct event_hdl_sub *sub)
{
	if (!lua_checkstack(L, 2))
		return 0;
	lua_pushcfunction(L, _hlua_new_event_sub_safe);
	lua_pushlightuserdata(L, sub);
	switch (lua_pcall(L, 1, 1, 0)) {
		case LUA_OK:
			return 1;
		default:
			/* error was caught */
			lua_pop(L, 1); // consume the lua object pushed on the stack since we ignore it
			return 0;
	}
}

/* This function is a LUA helper used for registering lua event callbacks.
 * It expects an event subscription array and the function to be executed
 * when subscribed events occur (stack arguments).
 * It can be called from the "init" section, "post init" or during the runtime.
 *
 * <sub_list> is the subscription list where the subscription will be attempted
 *
 * Pushes the newly allocated subscription on the stack on success
 */
__LJMP int hlua_event_sub(lua_State *L, event_hdl_sub_list *sub_list)
{
	struct hlua *hlua;
	struct event_hdl_sub *sub;
	struct event_hdl_sub_type subscriptions;
	int fcn_ref;
	int state_id;

	MAY_LJMP(check_args(L, 2, "event_sub"));

	/* Get the reference state */
	hlua = hlua_gethlua(L);
	if (hlua)
		/* we are in runtime processing, any thread may subscribe to events:
		 * subscription events will be handled by the thread who performed
		 * the registration.
		 */
		state_id = hlua->state_id;
	else {
		/* we are in initialization mode, only thread 0 (actual calling thread)
		 * may subscribe to events to prevent the same handler (from different lua
		 * stacks) from being registered multiple times
		 *
		 * hlua_state_id == 0: monostack (lua-load)
		 * hlua_state_id > 0: hlua_state_id=tid+1, multi-stack (lua-load-per-thread)
		 * (thus if hlua_state_id > 1, it means we are not in primary thread ctx)
		 */
		if (hlua_state_id > 1)
			return 0; /* skip registration */
		state_id = hlua_state_id;
	}

	/* First argument : event subscriptions. */
	subscriptions = MAY_LJMP(hlua_check_event_sub_types(L, 1));

	if (event_hdl_sub_type_equal(subscriptions, EVENT_HDL_SUB_NONE)) {
		WILL_LJMP(luaL_error(L, "event_sub: no valid event types were provided"));
		return 0; /* Never reached */
	}

	/* Second argument : lua function. */
	fcn_ref = MAY_LJMP(hlua_checkfunction(L, 2));

	/* try to subscribe */
	sub = hlua_event_subscribe(sub_list, subscriptions, state_id, fcn_ref);
	if (!sub) {
		hlua_unref(L, fcn_ref);
		WILL_LJMP(luaL_error(L, "event_sub: lua out of memory error"));
		return 0; /* Never reached */
	}

	/* push the subscription to the stack
	 *
	 * Here we use the safe function so that lua errors will be
	 * handled explicitly to prevent 'sub' from being lost
	 */
	if (!hlua_new_event_sub_safe(L, sub)) {
		/* Some events could already be pending in the handler's queue.
		 * However it is wiser to cancel the subscription since we are unable to
		 * provide a valid reference to it.
		 * Pending events will be delivered (unless lua keeps raising errors).
		 */
		event_hdl_unsubscribe(sub); /* cancel the subscription */
		WILL_LJMP(luaL_error(L, "event_sub: cannot push the subscription (%s)", lua_tostring(L, -1)));
		return 0; /* Never reached */
	}
	event_hdl_drop(sub); /* sub has been duplicated, discard old ref */

	return 1;
}

/* This function is a LUA wrapper used for registering global lua event callbacks
 * The new subscription is pushed onto the stack on success
 * Returns the number of arguments pushed to the stack (1 for success)
 */
__LJMP static int hlua_event_global_sub(lua_State *L)
{
	/* NULL <sub_list> = global subscription list */
	return MAY_LJMP(hlua_event_sub(L, NULL));
}

/* 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(const struct arg *arg_p, struct sample *smp, void *private)
{
	struct hlua_function *fcn = private;
	struct stream *stream = smp->strm;
	struct hlua *hlua = NULL;
	const char *error;

	if (!stream)
		return 0;

	if (!(hlua = hlua_stream_ctx_prepare(stream, fcn_ref_to_stack_id(fcn)))) {
		SEND_ERR(stream->be, "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(hlua)) {

		/* The following Lua calls can fail. */
		if (!SET_SAFE_LJMP(hlua)) {
			hlua_lock(hlua);
			if (lua_type(hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(stream->be, "Lua converter '%s': %s.\n", fcn->name, error);
			hlua_unlock(hlua);
			return 0;
		}

		/* Check stack available size. */
		if (!lua_checkstack(hlua->T, 1)) {
			SEND_ERR(stream->be, "Lua converter '%s': full stack.\n", fcn->name);
			RESET_SAFE_LJMP(hlua);
			return 0;
		}

		/* Restore the function in the stack. */
		hlua_pushref(hlua->T, fcn->function_ref[hlua->state_id]);

		/* convert input sample and pust-it in the stack. */
		if (!lua_checkstack(hlua->T, 1)) {
			SEND_ERR(stream->be, "Lua converter '%s': full stack.\n", fcn->name);
			RESET_SAFE_LJMP(hlua);
			return 0;
		}
		MAY_LJMP(hlua_smp2lua(hlua->T, smp));
		hlua->nargs = 1;

		/* push keywords in the stack. */
		if (arg_p) {
			for (; arg_p->type != ARGT_STOP; arg_p++) {
				if (!lua_checkstack(hlua->T, 1)) {
					SEND_ERR(stream->be, "Lua converter '%s': full stack.\n", fcn->name);
					RESET_SAFE_LJMP(hlua);
					return 0;
				}
				MAY_LJMP(hlua_arg2lua(hlua->T, arg_p));
				hlua->nargs++;
			}
		}

		/* We must initialize the execution timeouts. */
		hlua_timer_init(&hlua->timer, hlua_timeout_session);

		/* At this point the execution is safe. */
		RESET_SAFE_LJMP(hlua);
	}

	/* Execute the function. */
	switch (hlua_ctx_resume(hlua, 0)) {
	/* finished. */
	case HLUA_E_OK:
		hlua_lock(hlua);
		/* If the stack is empty, the function fails. */
		if (lua_gettop(hlua->T) <= 0) {
			hlua_unlock(hlua);
			return 0;
		}

		/* Convert the returned value in sample. */
		hlua_lua2smp(hlua->T, -1, smp);
		/* dup the smp before popping the related lua value and
		 * returning it to haproxy
		 */
		smp_dup(smp);
		lua_pop(hlua->T, 1);
		hlua_unlock(hlua);
		return 1;

	/* yield. */
	case HLUA_E_AGAIN:
		SEND_ERR(stream->be, "Lua converter '%s': cannot use yielded functions.\n", fcn->name);
		return 0;

	/* finished with error. */
	case HLUA_E_ERRMSG:
		/* Display log. */
		hlua_lock(hlua);
		SEND_ERR(stream->be, "Lua converter '%s': %s.\n",
		         fcn->name, hlua_tostring_safe(hlua->T, -1));
		lua_pop(hlua->T, 1);
		hlua_unlock(hlua);
		return 0;

	case HLUA_E_ETMOUT:
		SEND_ERR(stream->be, "Lua converter '%s': execution timeout.\n", fcn->name);
		return 0;

	case HLUA_E_BTMOUT:
		SEND_ERR(stream->be, "Lua converter '%s': burst timeout.\n", fcn->name);
		return 0;

	case HLUA_E_NOMEM:
		SEND_ERR(stream->be, "Lua converter '%s': out of memory error.\n", fcn->name);
		return 0;

	case HLUA_E_YIELD:
		SEND_ERR(stream->be, "Lua converter '%s': yield functions like core.tcp() or core.sleep() are not allowed.\n", fcn->name);
		return 0;

	case HLUA_E_ERR:
		/* Display log. */
		SEND_ERR(stream->be, "Lua converter '%s' returns an unknown error.\n", fcn->name);
		__fallthrough;

	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. This function will be called by the sample
 * fetch engine to call lua-based fetch operations.
 */
static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp,
                                     const char *kw, void *private)
{
	struct hlua_function *fcn = private;
	struct stream *stream = smp->strm;
	struct hlua *hlua = NULL;
	const char *error;
	unsigned int hflags = HLUA_TXN_NOTERM | HLUA_TXN_SMP_CTX;

	if (!stream)
		return 0;

	if (!(hlua = hlua_stream_ctx_prepare(stream, fcn_ref_to_stack_id(fcn)))) {
		SEND_ERR(stream->be, "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(hlua)) {

		/* The following Lua calls can fail. */
		if (!SET_SAFE_LJMP(hlua)) {
			hlua_lock(hlua);
			if (lua_type(hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(smp->px, "Lua sample-fetch '%s': %s.\n", fcn->name, error);
			hlua_unlock(hlua);
			return 0;
		}

		/* Check stack available size. */
		if (!lua_checkstack(hlua->T, 2)) {
			SEND_ERR(smp->px, "Lua sample-fetch '%s': full stack.\n", fcn->name);
			RESET_SAFE_LJMP(hlua);
			return 0;
		}

		/* Restore the function in the stack. */
		hlua_pushref(hlua->T, fcn->function_ref[hlua->state_id]);

		/* push arguments in the stack. */
		if (!hlua_txn_new(hlua->T, stream, smp->px, smp->opt & SMP_OPT_DIR, hflags)) {
			SEND_ERR(smp->px, "Lua sample-fetch '%s': full stack.\n", fcn->name);
			RESET_SAFE_LJMP(hlua);
			return 0;
		}
		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(hlua->T, 1)) {
				SEND_ERR(smp->px, "Lua sample-fetch '%s': full stack.\n", fcn->name);
				RESET_SAFE_LJMP(hlua);
				return 0;
			}
			MAY_LJMP(hlua_arg2lua(hlua->T, arg_p));
			hlua->nargs++;
		}

		/* We must initialize the execution timeouts. */
		hlua_timer_init(&hlua->timer, hlua_timeout_session);

		/* At this point the execution is safe. */
		RESET_SAFE_LJMP(hlua);
	}

	/* Execute the function. */
	switch (hlua_ctx_resume(hlua, 0)) {
	/* finished. */
	case HLUA_E_OK:
		hlua_lock(hlua);
		/* If the stack is empty, the function fails. */
		if (lua_gettop(hlua->T) <= 0) {
			hlua_unlock(hlua);
			return 0;
		}

		/* Convert the returned value in sample. */
		hlua_lua2smp(hlua->T, -1, smp);
		/* dup the smp before popping the related lua value and
		 * returning it to haproxy
		 */
		smp_dup(smp);
		lua_pop(hlua->T, 1);
		hlua_unlock(hlua);

		/* Set the end of execution flag. */
		smp->flags &= ~SMP_F_MAY_CHANGE;
		return 1;

	/* yield. */
	case HLUA_E_AGAIN:
		SEND_ERR(smp->px, "Lua sample-fetch '%s': cannot use yielded functions.\n", fcn->name);
		return 0;

	/* finished with error. */
	case HLUA_E_ERRMSG:
		/* Display log. */
		hlua_lock(hlua);
		SEND_ERR(smp->px, "Lua sample-fetch '%s': %s.\n",
		         fcn->name, hlua_tostring_safe(hlua->T, -1));
		lua_pop(hlua->T, 1);
		hlua_unlock(hlua);
		return 0;

	case HLUA_E_ETMOUT:
		SEND_ERR(smp->px, "Lua sample-fetch '%s': execution timeout.\n", fcn->name);
		return 0;

	case HLUA_E_BTMOUT:
		SEND_ERR(smp->px, "Lua sample-fetch '%s': burst timeout.\n", fcn->name);
		return 0;

	case HLUA_E_NOMEM:
		SEND_ERR(smp->px, "Lua sample-fetch '%s': out of memory error.\n", fcn->name);
		return 0;

	case HLUA_E_YIELD:
		SEND_ERR(smp->px, "Lua sample-fetch '%s': yield not allowed.\n", fcn->name);
		return 0;

	case HLUA_E_ERR:
		/* Display log. */
		SEND_ERR(smp->px, "Lua sample-fetch '%s' returns an unknown error.\n", fcn->name);
		__fallthrough;

	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 = NULL;
	struct sample_conv *sc;
	struct buffer *trash;

	MAY_LJMP(check_args(L, 2, "register_converters"));

	if (hlua_gethlua(L)) {
		/* runtime processing */
		WILL_LJMP(luaL_error(L, "register_converters: not available outside of body context"));
	}

	/* First argument : converter name. */
	name = MAY_LJMP(luaL_checkstring(L, 1));

	/* Second argument : lua function. */
	ref = MAY_LJMP(hlua_checkfunction(L, 2));

	/* Check if the converter is already registered */
	trash = get_trash_chunk();
	chunk_printf(trash, "lua.%s", name);
	sc = find_sample_conv(trash->area, trash->data);
	if (sc != NULL) {
		fcn = sc->private;
		if (fcn->function_ref[hlua_state_id] != -1) {
			ha_warning("Trying to register converter 'lua.%s' more than once. "
			           "This will become a hard error in version 2.5.\n", name);
			hlua_unref(L, fcn->function_ref[hlua_state_id]);
		}
		fcn->function_ref[hlua_state_id] = ref;
		return 0;
	}

	/* Allocate and fill the sample fetch keyword struct. */
	sck = calloc(1, sizeof(*sck) + sizeof(struct sample_conv) * 2);
	if (!sck)
		goto alloc_error;
	fcn = new_hlua_function();
	if (!fcn)
		goto alloc_error;

	/* Fill fcn. */
	fcn->name = strdup(name);
	if (!fcn->name)
		goto alloc_error;
	fcn->function_ref[hlua_state_id] = ref;

	/* List head */
	sck->list.n = sck->list.p = NULL;

	/* converter keyword. */
	len = strlen("lua.") + strlen(name) + 1;
	sck->kw[0].kw = calloc(1, len);
	if (!sck->kw[0].kw)
		goto alloc_error;

	snprintf((char *)sck->kw[0].kw, len, "lua.%s", name);
	sck->kw[0].process = hlua_sample_conv_wrapper;
	sck->kw[0].arg_mask = ARG12(0,STR,STR,STR,STR,STR,STR,STR,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;

	/* Register this new converter */
	sample_register_convs(sck);

	return 0;

  alloc_error:
	release_hlua_function(fcn);
	hlua_unref(L, ref);
	ha_free(&sck);
	WILL_LJMP(luaL_error(L, "Lua out of memory error."));
	return 0; /* Never reached */
}

/* This function 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 = NULL;
	struct sample_fetch *sf;
	struct buffer *trash;

	MAY_LJMP(check_args(L, 2, "register_fetches"));

	if (hlua_gethlua(L)) {
		/* runtime processing */
		WILL_LJMP(luaL_error(L, "register_fetches: not available outside of body context"));
	}

	/* First argument : sample-fetch name. */
	name = MAY_LJMP(luaL_checkstring(L, 1));

	/* Second argument : lua function. */
	ref = MAY_LJMP(hlua_checkfunction(L, 2));

	/* Check if the sample-fetch is already registered */
	trash = get_trash_chunk();
	chunk_printf(trash, "lua.%s", name);
	sf = find_sample_fetch(trash->area, trash->data);
	if (sf != NULL) {
		fcn = sf->private;
		if (fcn->function_ref[hlua_state_id] != -1) {
			ha_warning("Trying to register sample-fetch 'lua.%s' more than once. "
			           "This will become a hard error in version 2.5.\n", name);
			hlua_unref(L, fcn->function_ref[hlua_state_id]);
		}
		fcn->function_ref[hlua_state_id] = ref;
		return 0;
	}

	/* Allocate and fill the sample fetch keyword struct. */
	sfk = calloc(1, sizeof(*sfk) + sizeof(struct sample_fetch) * 2);
	if (!sfk)
		goto alloc_error;
	fcn = new_hlua_function();
	if (!fcn)
		goto alloc_error;

	/* Fill fcn. */
	fcn->name = strdup(name);
	if (!fcn->name)
		goto alloc_error;
	fcn->function_ref[hlua_state_id] = ref;

	/* List head */
	sfk->list.n = sfk->list.p = NULL;

	/* sample-fetch keyword. */
	len = strlen("lua.") + strlen(name) + 1;
	sfk->kw[0].kw = calloc(1, len);
	if (!sfk->kw[0].kw)
		goto alloc_error;

	snprintf((char *)sfk->kw[0].kw, len, "lua.%s", name);
	sfk->kw[0].process = hlua_sample_fetch_wrapper;
	sfk->kw[0].arg_mask = ARG12(0,STR,STR,STR,STR,STR,STR,STR,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;

	/* Register this new fetch. */
	sample_register_fetches(sfk);

	return 0;

  alloc_error:
	release_hlua_function(fcn);
	hlua_unref(L, ref);
	ha_free(&sfk);
	WILL_LJMP(luaL_error(L, "Lua out of memory error."));
	return 0; /* Never reached */
}

/* This function is a lua binding to set the wake_time.
 */
__LJMP static int hlua_set_wake_time(lua_State *L)
{
	struct hlua *hlua;
	unsigned int delay;
	int wakeup_ms; // tick value

	/* Get hlua struct, or NULL if we execute from main lua state */
	hlua = hlua_gethlua(L);
	if (!hlua) {
		return 0;
	}

	MAY_LJMP(check_args(L, 1, "wake_time"));

	delay = MAY_LJMP(luaL_checkinteger(L, 1));
	wakeup_ms = tick_add(now_ms, delay);
	hlua->wake_time = wakeup_ms;
	return 0;
}

/* This function is a wrapper to execute each LUA function declared as an action
 * wrapper during the initialisation period. This function may return any
 * ACT_RET_* value. On error ACT_RET_CONT is returned and the action is
 * ignored. If the lua action yields, ACT_RET_YIELD is returned. On success, the
 * return value is the first element on the stack.
 */
static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
                                   struct session *sess, struct stream *s, int flags)
{
	char **arg;
	unsigned int hflags = HLUA_TXN_ACT_CTX;
	int dir, act_ret = ACT_RET_CONT;
	const char *error;
	struct hlua *hlua = NULL;

	switch (rule->from) {
	case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
	case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
	case ACT_F_HTTP_REQ:    dir = SMP_OPT_DIR_REQ; break;
	case ACT_F_HTTP_RES:    dir = SMP_OPT_DIR_RES; break;
	default:
		SEND_ERR(px, "Lua: internal error while execute action.\n");
		goto end;
	}

	if (!(hlua = hlua_stream_ctx_prepare(s, fcn_ref_to_stack_id(rule->arg.hlua_rule->fcn)))) {
		SEND_ERR(px, "Lua action '%s': can't initialize Lua context.\n",
		         rule->arg.hlua_rule->fcn->name);
		goto end;
	}

	/* If it is the first run, initialize the data for the call. */
	if (!HLUA_IS_RUNNING(hlua)) {

		/* The following Lua calls can fail. */
		if (!SET_SAFE_LJMP(hlua)) {
			hlua_lock(hlua);
			if (lua_type(hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(px, "Lua function '%s': %s.\n",
			         rule->arg.hlua_rule->fcn->name, error);
			hlua_unlock(hlua);
			goto end;
		}

		/* Check stack available size. */
		if (!lua_checkstack(hlua->T, 1)) {
			SEND_ERR(px, "Lua function '%s': full stack.\n",
			         rule->arg.hlua_rule->fcn->name);
			RESET_SAFE_LJMP(hlua);
			goto end;
		}

		/* Restore the function in the stack. */
		hlua_pushref(hlua->T, rule->arg.hlua_rule->fcn->function_ref[hlua->state_id]);

		/* Create and and push object stream in the stack. */
		if (!hlua_txn_new(hlua->T, s, px, dir, hflags)) {
			SEND_ERR(px, "Lua function '%s': full stack.\n",
			         rule->arg.hlua_rule->fcn->name);
			RESET_SAFE_LJMP(hlua);
			goto end;
		}
		hlua->nargs = 1;

		/* push keywords in the stack. */
		for (arg = rule->arg.hlua_rule->args; arg && *arg; arg++) {
			if (!lua_checkstack(hlua->T, 1)) {
				SEND_ERR(px, "Lua function '%s': full stack.\n",
				         rule->arg.hlua_rule->fcn->name);
				RESET_SAFE_LJMP(hlua);
				goto end;
			}
			lua_pushstring(hlua->T, *arg);
			hlua->nargs++;
		}

		/* Now the execution is safe. */
		RESET_SAFE_LJMP(hlua);

		/* We must initialize the execution timeouts. */
		hlua_timer_init(&hlua->timer, hlua_timeout_session);
	}

	/* Execute the function. */
	switch (hlua_ctx_resume(hlua, !(flags & ACT_OPT_FINAL))) {
	/* finished. */
	case HLUA_E_OK:
		/* Catch the return value */
		hlua_lock(hlua);
		if (lua_gettop(hlua->T) > 0)
			act_ret = lua_tointeger(hlua->T, -1);
		hlua_unlock(hlua);

		/* Set timeout in the required channel. */
		if (act_ret == ACT_RET_YIELD) {
			if (flags & ACT_OPT_FINAL)
				goto err_yield;

			if (dir == SMP_OPT_DIR_REQ)
				s->req.analyse_exp = tick_first((tick_is_expired(s->req.analyse_exp, now_ms) ? 0 : s->req.analyse_exp),
								hlua->wake_time);
			else
				s->res.analyse_exp = tick_first((tick_is_expired(s->res.analyse_exp, now_ms) ? 0 : s->res.analyse_exp),
								hlua->wake_time);
		}
		goto end;

	/* yield. */
	case HLUA_E_AGAIN:
		/* Set timeout in the required channel. */
		if (dir == SMP_OPT_DIR_REQ)
			s->req.analyse_exp = tick_first((tick_is_expired(s->req.analyse_exp, now_ms) ? 0 : s->req.analyse_exp),
							hlua->wake_time);
		else
			s->res.analyse_exp = tick_first((tick_is_expired(s->res.analyse_exp, now_ms) ? 0 : s->res.analyse_exp),
							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 targeted on the requests.
		 */
		if (HLUA_IS_WAKERESWR(hlua))
			s->res.flags |= CF_WAKE_WRITE;
		if (HLUA_IS_WAKEREQWR(hlua))
			s->req.flags |= CF_WAKE_WRITE;
		act_ret = ACT_RET_YIELD;
		goto end;

	/* finished with error. */
	case HLUA_E_ERRMSG:
		/* Display log. */
		hlua_lock(hlua);
		SEND_ERR(px, "Lua function '%s': %s.\n",
		         rule->arg.hlua_rule->fcn->name, hlua_tostring_safe(hlua->T, -1));
		lua_pop(hlua->T, 1);
		hlua_unlock(hlua);
		goto end;

	case HLUA_E_ETMOUT:
		SEND_ERR(px, "Lua function '%s': execution timeout.\n", rule->arg.hlua_rule->fcn->name);
		goto end;

	case HLUA_E_BTMOUT:
		SEND_ERR(px, "Lua function '%s': burst timeout.\n", rule->arg.hlua_rule->fcn->name);
		goto end;

	case HLUA_E_NOMEM:
		SEND_ERR(px, "Lua function '%s': out of memory error.\n", rule->arg.hlua_rule->fcn->name);
		goto end;

	case HLUA_E_YIELD:
	  err_yield:
		act_ret = ACT_RET_CONT;
		if (flags & ACT_OPT_FINAL_EARLY) {
			char *msg = NULL;

			/* yield not allowed, but it is only caused because ruleset was ended earlier
			 * than expected, so it is not a misuse of yield in the Lua script: simply emit
			 * a log
			 */
			memprintf(&msg, "Lua function '%s': unable to yield, aborted Lua execution.\n",
			          rule->arg.hlua_rule->fcn->name);
			if (msg)
				hlua_sendlog(px, LOG_WARNING, msg);
			ha_free(&msg);
		}
		else {
			/* invalid yield use */
			SEND_ERR(px, "Lua function '%s': yield not allowed.\n",
			         rule->arg.hlua_rule->fcn->name);
		}
		goto end;

	case HLUA_E_ERR:
		/* Display log. */
		SEND_ERR(px, "Lua function '%s' return an unknown error.\n",
		         rule->arg.hlua_rule->fcn->name);

	default:
		goto end;
	}

 end:
	if (act_ret != ACT_RET_YIELD && hlua)
		hlua->wake_time = TICK_ETERNITY;
	return act_ret;
}

struct task *hlua_applet_wakeup(struct task *t, void *context, unsigned int state)
{
	struct appctx *ctx = context;

	appctx_wakeup(ctx);
	t->expire = TICK_ETERNITY;
	return t;
}

static int hlua_applet_tcp_init(struct appctx *ctx)
{
	struct hlua_tcp_ctx *tcp_ctx = applet_reserve_svcctx(ctx, sizeof(*tcp_ctx));
	struct stconn *sc = appctx_sc(ctx);
	struct stream *strm = __sc_strm(sc);
	struct act_rule *rule = strm->current_rule;
	struct hlua *hlua;
	struct task *task;
	char **arg;
	const char *error;

	hlua = pool_alloc(pool_head_hlua);
	if (!hlua) {
		SEND_ERR(strm->be, "Lua applet tcp '%s': out of memory.\n",
		         ctx->applet->name);
		return -1;
	}
	HLUA_INIT(hlua);
	tcp_ctx->hlua = hlua;
	tcp_ctx->flags = 0;

	/* Create task used by signal to wakeup applets. */
	task = task_new_here();
	if (!task) {
		SEND_ERR(strm->be, "Lua applet tcp '%s': out of memory.\n",
		         ctx->applet->name);
		return -1;
	}
	task->nice = 0;
	task->context = ctx;
	task->process = hlua_applet_wakeup;
	tcp_ctx->task = task;

	/* In the execution wrappers linked with a stream, the
	 * Lua context can be not initialized. This behavior
	 * permits to save performances because a systematic
	 * Lua initialization cause 5% performances loss.
	 */
	if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(rule->arg.hlua_rule->fcn), task)) {
		SEND_ERR(strm->be, "Lua applet tcp '%s': can't initialize Lua context.\n",
		         ctx->applet->name);
		return -1;
	}

	/* Set timeout according with the applet configuration. */
	hlua_timer_init(&hlua->timer, ctx->applet->timeout);

	/* The following Lua calls can fail. */
	if (!SET_SAFE_LJMP(hlua)) {
		hlua_lock(hlua);
		if (lua_type(hlua->T, -1) == LUA_TSTRING)
			error = hlua_tostring_safe(hlua->T, -1);
		else
			error = "critical error";
		SEND_ERR(strm->be, "Lua applet tcp '%s': %s.\n",
		         ctx->applet->name, error);
		hlua_unlock(hlua);
		return -1;
	}

	/* Check stack available size. */
	if (!lua_checkstack(hlua->T, 1)) {
		SEND_ERR(strm->be, "Lua applet tcp '%s': full stack.\n",
		         ctx->applet->name);
		RESET_SAFE_LJMP(hlua);
		return -1;
	}

	/* Restore the function in the stack. */
	hlua_pushref(hlua->T, rule->arg.hlua_rule->fcn->function_ref[hlua->state_id]);

	/* Create and and push object stream in the stack. */
	if (!hlua_applet_tcp_new(hlua->T, ctx)) {
		SEND_ERR(strm->be, "Lua applet tcp '%s': full stack.\n",
		         ctx->applet->name);
		RESET_SAFE_LJMP(hlua);
		return -1;
	}
	hlua->nargs = 1;

	/* push keywords in the stack. */
	for (arg = rule->arg.hlua_rule->args; arg && *arg; arg++) {
		if (!lua_checkstack(hlua->T, 1)) {
			SEND_ERR(strm->be, "Lua applet tcp '%s': full stack.\n",
			         ctx->applet->name);
			RESET_SAFE_LJMP(hlua);
			return -1;
		}
		lua_pushstring(hlua->T, *arg);
		hlua->nargs++;
	}

	RESET_SAFE_LJMP(hlua);

	/* Wakeup the applet ASAP. */
	applet_need_more_data(ctx);
	applet_have_more_data(ctx);

	return 0;
}

void hlua_applet_tcp_fct(struct appctx *ctx)
{
	struct hlua_tcp_ctx *tcp_ctx = ctx->svcctx;
	struct stconn *sc = appctx_sc(ctx);
	struct stream *strm = __sc_strm(sc);
	struct proxy *px = strm->be;
	struct hlua *hlua = tcp_ctx->hlua;
	int yield = 0;

	if (unlikely(applet_fl_test(ctx, APPCTX_FL_EOS|APPCTX_FL_ERROR)))
		goto out;

	/* The applet execution is already done. */
	if (tcp_ctx->flags & APPLET_DONE) {
		/* Restart to consume to drain request data */
		applet_will_consume(ctx);
		goto out;
	}

	/* Execute the function. */
	switch (hlua_ctx_resume(hlua, 1)) {
	/* finished. */
	case HLUA_E_OK:
		tcp_ctx->flags |= APPLET_DONE;
		applet_set_eos(ctx);

		/* Restart to consume to drain request data */
		applet_will_consume(ctx);
		break;

	/* yield. */
	case HLUA_E_AGAIN:
		yield = 1;
		if (hlua->wake_time != TICK_ETERNITY)
			task_schedule(tcp_ctx->task, hlua->wake_time);
		break;

	/* finished with error. */
	case HLUA_E_ERRMSG:
		/* Display log. */
		hlua_lock(hlua);
		SEND_ERR(px, "Lua applet tcp '%s': %s.\n",
		         ctx->applet->name, hlua_tostring_safe(hlua->T, -1));
		lua_pop(hlua->T, 1);
		hlua_unlock(hlua);
		goto error;

	case HLUA_E_ETMOUT:
		SEND_ERR(px, "Lua applet tcp '%s': execution timeout.\n",
		         ctx->applet->name);
		goto error;

	case HLUA_E_BTMOUT:
		SEND_ERR(px, "Lua applet tcp '%s': burst timeout.\n",
		         ctx->applet->name);
		goto error;

	case HLUA_E_NOMEM:
		SEND_ERR(px, "Lua applet tcp '%s': out of memory error.\n",
		         ctx->applet->name);
		goto error;

	case HLUA_E_YIELD: /* unexpected */
		SEND_ERR(px, "Lua applet tcp '%s': yield not allowed.\n",
		         ctx->applet->name);
		goto error;

	case HLUA_E_ERR:
		/* Display log. */
		SEND_ERR(px, "Lua applet tcp '%s' return an unknown error.\n",
		         ctx->applet->name);
		goto error;

	default:
		goto error;
	}

out:
	/* eat the whole request unless yield was requested which means
	 * we are not done yet
	 */
	if (yield)
		return;

	applet_reset_input(ctx);
	return;

error:
	applet_set_error(ctx);
	tcp_ctx->flags |= APPLET_DONE;
	goto out;
}

static void hlua_applet_tcp_release(struct appctx *ctx)
{
	struct hlua_tcp_ctx *tcp_ctx = ctx->svcctx;

	task_destroy(tcp_ctx->task);
	tcp_ctx->task = NULL;
	hlua_ctx_destroy(tcp_ctx->hlua);
	tcp_ctx->hlua = NULL;
}

/* The function returns 0 if the initialisation is complete or -1 if
 * an errors occurs. It also reserves the appctx for an hlua_http_ctx.
 */
static int hlua_applet_http_init(struct appctx *ctx)
{
	struct hlua_http_ctx *http_ctx = applet_reserve_svcctx(ctx, sizeof(*http_ctx));
	struct stconn *sc = appctx_sc(ctx);
	struct stream *strm = __sc_strm(sc);
	struct act_rule *rule = strm->current_rule;
	struct http_txn *txn;
	struct hlua *hlua;
	char **arg;
	struct task *task;
	const char *error;

	txn = strm->txn.http;
	hlua = pool_alloc(pool_head_hlua);
	if (!hlua) {
		SEND_ERR(strm->be, "Lua applet http '%s': out of memory.\n",
		         ctx->applet->name);
		return -1;
	}
	HLUA_INIT(hlua);
	http_ctx->hlua = hlua;
	http_ctx->left_bytes = -1;
	http_ctx->flags = 0;

	if (txn->req.flags & HTTP_MSGF_VER_11)
		http_ctx->flags |= APPLET_HTTP11;

	/* Create task used by signal to wakeup applets. */
	task = task_new_here();
	if (!task) {
		SEND_ERR(strm->be, "Lua applet http '%s': out of memory.\n",
		         ctx->applet->name);
		return -1;
	}
	task->nice = 0;
	task->context = ctx;
	task->process = hlua_applet_wakeup;
	http_ctx->task = task;

	/* In the execution wrappers linked with a stream, the
	 * Lua context can be not initialized. This behavior
	 * permits to save performances because a systematic
	 * Lua initialization cause 5% performances loss.
	 */
	if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(rule->arg.hlua_rule->fcn), task)) {
		SEND_ERR(strm->be, "Lua applet http '%s': can't initialize Lua context.\n",
		         ctx->applet->name);
		return -1;
	}

	/* Set timeout according with the applet configuration. */
	hlua_timer_init(&hlua->timer, ctx->applet->timeout);

	/* The following Lua calls can fail. */
	if (!SET_SAFE_LJMP(hlua)) {
		hlua_lock(hlua);
		if (lua_type(hlua->T, -1) == LUA_TSTRING)
			error = hlua_tostring_safe(hlua->T, -1);
		else
			error = "critical error";
		SEND_ERR(strm->be, "Lua applet http '%s': %s.\n",
		         ctx->applet->name, error);
		hlua_unlock(hlua);
		return -1;
	}

	/* Check stack available size. */
	if (!lua_checkstack(hlua->T, 1)) {
		SEND_ERR(strm->be, "Lua applet http '%s': full stack.\n",
		         ctx->applet->name);
		RESET_SAFE_LJMP(hlua);
		return -1;
	}

	/* Restore the function in the stack. */
	hlua_pushref(hlua->T, rule->arg.hlua_rule->fcn->function_ref[hlua->state_id]);

	/* Create and and push object stream in the stack. */
	if (!hlua_applet_http_new(hlua->T, ctx)) {
		SEND_ERR(strm->be, "Lua applet http '%s': full stack.\n",
		         ctx->applet->name);
		RESET_SAFE_LJMP(hlua);
		return -1;
	}
	hlua->nargs = 1;

	/* push keywords in the stack. */
	for (arg = rule->arg.hlua_rule->args; arg && *arg; arg++) {
		if (!lua_checkstack(hlua->T, 1)) {
			SEND_ERR(strm->be, "Lua applet http '%s': full stack.\n",
			         ctx->applet->name);
			RESET_SAFE_LJMP(hlua);
			return -1;
		}
		lua_pushstring(hlua->T, *arg);
		hlua->nargs++;
	}

	RESET_SAFE_LJMP(hlua);

	/* Wakeup the applet when data is ready for read. */
	applet_need_more_data(ctx);

	return 0;
}

void hlua_applet_http_fct(struct appctx *ctx)
{
	struct hlua_http_ctx *http_ctx = ctx->svcctx;
	struct stconn *sc = appctx_sc(ctx);
	struct stream *strm = __sc_strm(sc);
	struct proxy *px = strm->be;
	struct hlua *hlua = http_ctx->hlua;
	struct buffer *outbuf;
	struct htx *res_htx;
	const char *error;
	int yield = 0;


	if (unlikely(applet_fl_test(ctx, APPCTX_FL_EOS|APPCTX_FL_ERROR)))
		goto out;

	/* The applet execution is already done. */
	if (http_ctx->flags & APPLET_DONE)
		goto out;

	outbuf = applet_get_outbuf(ctx);
	if (!outbuf) {
		applet_have_more_data(ctx);
		yield = 1;
		goto out;
	}

	/* Set the currently running flag. */
	if (!HLUA_IS_RUNNING(hlua)) {
		struct buffer *inbuf;
		struct htx *req_htx;
		struct htx_blk *blk;
		struct htx_sl *sl;
		struct ist path;
		unsigned long long len = 0;
		struct http_uri_parser parser;
		int app_idx = 1 - hlua->nargs; /* index of the HTTP applet object in the lua stask */

		inbuf = applet_get_inbuf(ctx);
		if (!inbuf) {
			applet_need_more_data(ctx);
			yield = 1;
			goto out;
		}

		if (!SET_SAFE_LJMP(hlua)) {
			hlua_lock(hlua);
			if (lua_type(hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(NULL, "Lua applet HTTP: %s.\n", error);
			hlua_unlock(hlua);
			goto error;
		}

		req_htx = htx_from_buf(inbuf);

		blk = htx_get_first_blk(req_htx);
		BUG_ON(!blk || htx_get_blk_type(blk) != HTX_BLK_REQ_SL);
		sl = htx_get_blk_ptr(req_htx, blk);

		/* Stores the request method. */
		lua_pushstring(hlua->T, "method");
		lua_pushlstring(hlua->T, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl));
		lua_settable(hlua->T, app_idx - 3);

		/* Stores the http version. */
		lua_pushstring(hlua->T, "version");
		lua_pushlstring(hlua->T, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl));
		lua_settable(hlua->T, app_idx - 3);

		/* creates an array of headers. hlua_http_get_headers() crates and push
		 * the array on the top of the stack.
		 */
		lua_pushstring(hlua->T, "headers");
		if (!hlua_http_get_headers(hlua->T, inbuf))
			goto error;
		lua_settable(hlua->T, app_idx - 3);

		parser = http_uri_parser_init(htx_sl_req_uri(sl));
		path = http_parse_path(&parser);
		if (isttest(path)) {
			char *p, *q, *end;

			p = path.ptr;
			end = istend(path);
			q = p;
			while (q < end && *q != '?')
				q++;

			/* Stores the request path. */
			lua_pushstring(hlua->T, "path");
			lua_pushlstring(hlua->T, p, q - p);
			lua_settable(hlua->T, app_idx - 3);

			/* Stores the query string. */
			lua_pushstring(hlua->T, "qs");
			if (*q == '?')
				q++;
			lua_pushlstring(hlua->T, q, end - q);
			lua_settable(hlua->T, app_idx - 3);
		}

		/* Stores the request path. */
		len = ctx->sedesc->kip;
		lua_pushstring(hlua->T, "length");
		lua_pushinteger(hlua->T, len);
		lua_settable(hlua->T, app_idx - 3);

		/* Create an empty array of HTTP request headers. */
		lua_pushstring(hlua->T, "response");
		lua_newtable(hlua->T);
		lua_settable(hlua->T, app_idx - 3);

		/* At this point the execution is safe. */
		RESET_SAFE_LJMP(hlua);
	}

	/* Execute the function. */
	switch (hlua_ctx_resume(hlua, 1)) {
		/* finished. */
		case HLUA_E_OK:
			http_ctx->flags |= APPLET_DONE;

			/* Restart to consume to drain the request */
			applet_will_consume(ctx);
			break;

		/* yield. */
		case HLUA_E_AGAIN:
			yield = 1;
			if (hlua->wake_time != TICK_ETERNITY)
				task_schedule(http_ctx->task, hlua->wake_time);
			goto out;

		/* finished with error. */
		case HLUA_E_ERRMSG:
			/* Display log. */
			hlua_lock(hlua);
			SEND_ERR(px, "Lua applet http '%s': %s.\n",
				 ctx->applet->name, hlua_tostring_safe(hlua->T, -1));
			lua_pop(hlua->T, 1);
			hlua_unlock(hlua);
			goto error;

		case HLUA_E_ETMOUT:
			SEND_ERR(px, "Lua applet http '%s': execution timeout.\n",
				 ctx->applet->name);
			goto error;

		case HLUA_E_BTMOUT:
			SEND_ERR(px, "Lua applet http '%s': burst timeout.\n",
				 ctx->applet->name);
			goto error;

		case HLUA_E_NOMEM:
			SEND_ERR(px, "Lua applet http '%s': out of memory error.\n",
				 ctx->applet->name);
			goto error;

		case HLUA_E_YIELD: /* unexpected */
			SEND_ERR(px, "Lua applet http '%s': yield not allowed.\n",
				 ctx->applet->name);
			goto error;

		case HLUA_E_ERR:
			/* Display log. */
			SEND_ERR(px, "Lua applet http '%s' return an unknown error.\n",
				 ctx->applet->name);
			goto error;

		default:
			goto error;
	}

	if (http_ctx->flags & APPLET_DONE) {
		if (http_ctx->flags & APPLET_RSP_SENT)
			goto out;

		if (!(http_ctx->flags & APPLET_HDR_SENT))
			goto error;

		/* no more data are expected. If the response buffer is empty
		 * for a chunked message, be sure to add something (EOT block in
		 * this case) to have something to send. It is important to be
		 * sure the EOM flags will be handled by the endpoint.
		 */
		res_htx = htx_from_buf(outbuf);
		if (htx_is_empty(res_htx)) {
			if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
				applet_have_more_data(ctx);
				yield = 1;
				goto out;
			}
		}
		res_htx->flags |= HTX_FL_EOM;
		htx_to_buf(res_htx, outbuf);
		applet_set_eoi(ctx);
		http_ctx->flags |= APPLET_RSP_SENT;
	}

  out:
	/* eat the whole request unless yield was requested which means
	 * we are not done yet
	 */
	if (yield)
		return;

	applet_reset_input(ctx);
	return;

  error:

	/* If we are in HTTP mode, and we are not send any
	 * data, return a 500 server error in best effort:
	 * if there is no room available in the buffer,
	 * just close the connection.
	 */
	if (!(http_ctx->flags & APPLET_HDR_SENT)) {
		struct buffer *err = &http_err_chunks[HTTP_ERR_500];

		res_htx = htx_from_buf(outbuf);
		htx_reset(res_htx);
		outbuf->data = b_data(err);
		memcpy(ctx->outbuf.area, b_head(err), b_data(err));
		res_htx = htx_from_buf(outbuf);
		applet_set_eoi(ctx);
		applet_set_eos(ctx);
	}
	else
		applet_set_error(ctx);

	if (!(strm->flags & SF_ERR_MASK))
		strm->flags |= SF_ERR_RESOURCE;
	http_ctx->flags |= APPLET_DONE;

	/* Restart to consume to drain the request */
	applet_will_consume(ctx);
	goto out;
}

static void hlua_applet_http_release(struct appctx *ctx)
{
	struct hlua_http_ctx *http_ctx = ctx->svcctx;

	task_destroy(http_ctx->task);
	http_ctx->task = NULL;
	hlua_ctx_destroy(http_ctx->hlua);
	http_ctx->hlua = NULL;
}

/* global {tcp|http}-request parser. Return ACT_RET_PRS_OK in
 * success case, else return ACT_RET_PRS_ERR.
 *
 * This function can fail with an abort() due to an Lua critical error.
 * We are in the configuration parsing process of HAProxy, this abort() is
 * tolerated.
 */
static enum act_parse_ret action_register_lua(const char **args, int *cur_arg, struct proxy *px,
                                              struct act_rule *rule, char **err)
{
	struct hlua_function *fcn = rule->kw->private;
	int i;

	/* Memory for the rule. */
	rule->arg.hlua_rule = calloc(1, sizeof(*rule->arg.hlua_rule));
	if (!rule->arg.hlua_rule) {
		memprintf(err, "out of memory error");
		goto error;
	}

	/* Memory for arguments. */
	rule->arg.hlua_rule->args = calloc(fcn->nargs + 1,
					   sizeof(*rule->arg.hlua_rule->args));
	if (!rule->arg.hlua_rule->args) {
		memprintf(err, "out of memory error");
		goto error;
	}

	/* Reference the Lua function and store the reference. */
	rule->arg.hlua_rule->fcn = fcn;

	/* Expect some arguments */
	for (i = 0; i < fcn->nargs; i++) {
		if (*args[*cur_arg] == '\0') {
			memprintf(err, "expect %d arguments", fcn->nargs);
			goto error;
		}
		rule->arg.hlua_rule->args[i] = strdup(args[*cur_arg]);
		if (!rule->arg.hlua_rule->args[i]) {
			memprintf(err, "out of memory error");
			goto error;
		}
		(*cur_arg)++;
	}
	rule->arg.hlua_rule->args[i] = NULL;

	rule->action = ACT_CUSTOM;
	rule->action_ptr = hlua_action;
	return ACT_RET_PRS_OK;

  error:
	if (rule->arg.hlua_rule) {
		if (rule->arg.hlua_rule->args) {
			for (i = 0; i < fcn->nargs; i++)
				ha_free(&rule->arg.hlua_rule->args[i]);
			ha_free(&rule->arg.hlua_rule->args);
		}
		ha_free(&rule->arg.hlua_rule);
	}
	return ACT_RET_PRS_ERR;
}

static enum act_parse_ret action_register_service_http(const char **args, int *cur_arg, struct proxy *px,
                                                       struct act_rule *rule, char **err)
{
	struct hlua_function *fcn = rule->kw->private;

	/* HTTP applets are forbidden in tcp-request rules.
	 * HTTP applet request requires everything initialized by
	 * "http_process_request" (analyzer flag AN_REQ_HTTP_INNER).
	 * The applet will be immediately initialized, but its before
	 * the call of this analyzer.
	 */
	if (rule->from != ACT_F_HTTP_REQ) {
		memprintf(err, "HTTP applets are forbidden from 'tcp-request' rulesets");
		return ACT_RET_PRS_ERR;
	}

	/* Memory for the rule. */
	rule->arg.hlua_rule = calloc(1, sizeof(*rule->arg.hlua_rule));
	if (!rule->arg.hlua_rule) {
		memprintf(err, "out of memory error");
		return ACT_RET_PRS_ERR;
	}

	/* Reference the Lua function and store the reference. */
	rule->arg.hlua_rule->fcn = fcn;

	/* TODO: later accept arguments. */
	rule->arg.hlua_rule->args = NULL;

	/* Add applet pointer in the rule. */
	rule->applet.obj_type = OBJ_TYPE_APPLET;
	rule->applet.flags = APPLET_FL_NEW_API|APPLET_FL_HTX;
	rule->applet.name = fcn->name;
	rule->applet.init = hlua_applet_http_init;
	rule->applet.rcv_buf = appctx_htx_rcv_buf;
	rule->applet.snd_buf = appctx_htx_snd_buf;
	rule->applet.fct = hlua_applet_http_fct;
	rule->applet.release = hlua_applet_http_release;
	rule->applet.timeout = hlua_timeout_applet;

	return ACT_RET_PRS_OK;
}

/* 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_action(lua_State *L)
{
	struct action_kw_list *akl = NULL;
	const char *name;
	int ref;
	int len;
	struct hlua_function *fcn = NULL;
	int nargs;
	struct buffer *trash;
	struct action_kw *akw;

	/* Initialise the number of expected arguments at 0. */
	nargs = 0;

	if (lua_gettop(L) < 3 || lua_gettop(L) > 4)
		WILL_LJMP(luaL_error(L, "'register_action' needs between 3 and 4 arguments"));

	if (hlua_gethlua(L)) {
		/* runtime processing */
		WILL_LJMP(luaL_error(L, "register_action: not available outside of body context"));
	}

	/* First argument : converter name. */
	name = MAY_LJMP(luaL_checkstring(L, 1));

	/* Second argument : environment. */
	if (lua_type(L, 2) != LUA_TTABLE)
		WILL_LJMP(luaL_error(L, "register_action: second argument must be a table of strings"));

	/* Third argument : lua function. */
	ref = MAY_LJMP(hlua_checkfunction(L, 3));

	/* Fourth argument : number of mandatory arguments expected on the configuration line. */
	if (lua_gettop(L) >= 4)
		nargs = MAY_LJMP(luaL_checkinteger(L, 4));

	/* browse the second argument as an array. */
	lua_pushnil(L);
	while (lua_next(L, 2) != 0) {
		if (lua_type(L, -1) != LUA_TSTRING) {
			hlua_unref(L, ref);
			WILL_LJMP(luaL_error(L, "register_action: second argument must be a table of strings"));
		}

		/* Check if action exists */
		trash = get_trash_chunk();
		chunk_printf(trash, "lua.%s", name);
		if (strcmp(lua_tostring(L, -1), "tcp-req") == 0) {
			akw = tcp_req_cont_action(trash->area);
		} else if (strcmp(lua_tostring(L, -1), "tcp-res") == 0) {
			akw = tcp_res_cont_action(trash->area);
		} else if (strcmp(lua_tostring(L, -1), "http-req") == 0) {
			akw = action_http_req_custom(trash->area);
		} else if (strcmp(lua_tostring(L, -1), "http-res") == 0) {
			akw = action_http_res_custom(trash->area);
		} else if (strcmp(lua_tostring(L, -1), "http-after-res") == 0) {
			akw = action_http_after_res_custom(trash->area);
		} else {
			akw = NULL;
		}
		if (akw != NULL) {
			fcn = akw->private;
			if (fcn->function_ref[hlua_state_id] != -1) {
				ha_warning("Trying to register action 'lua.%s' more than once. "
				           "This will become a hard error in version 2.5.\n", name);
				hlua_unref(L, fcn->function_ref[hlua_state_id]);
			}
			fcn->function_ref[hlua_state_id] = ref;

			/* pop the environment string. */
			lua_pop(L, 1);
			continue;
		}

		/* Check required environment. Only accepted "http" or "tcp". */
		/* Allocate and fill the sample fetch keyword struct. */
		akl = calloc(1, sizeof(*akl) + sizeof(struct action_kw) * 2);
		if (!akl)
			goto alloc_error;;
		fcn = new_hlua_function();
		if (!fcn)
			goto alloc_error;

		/* Fill fcn. */
		fcn->name = strdup(name);
		if (!fcn->name)
			goto alloc_error;
		fcn->function_ref[hlua_state_id] = ref;

		/* Set the expected number of arguments. */
		fcn->nargs = nargs;

		/* List head */
		akl->list.n = akl->list.p = NULL;

		/* action keyword. */
		len = strlen("lua.") + strlen(name) + 1;
		akl->kw[0].kw = calloc(1, len);
		if (!akl->kw[0].kw)
			goto alloc_error;

		snprintf((char *)akl->kw[0].kw, len, "lua.%s", name);

		akl->kw[0].flags = 0;
		akl->kw[0].private = fcn;
		akl->kw[0].parse = action_register_lua;

		/* select the action registering point. */
		if (strcmp(lua_tostring(L, -1), "tcp-req") == 0)
			tcp_req_cont_keywords_register(akl);
		else if (strcmp(lua_tostring(L, -1), "tcp-res") == 0)
			tcp_res_cont_keywords_register(akl);
		else if (strcmp(lua_tostring(L, -1), "http-req") == 0)
			http_req_keywords_register(akl);
		else if (strcmp(lua_tostring(L, -1), "http-res") == 0)
			http_res_keywords_register(akl);
		else if (strcmp(lua_tostring(L, -1), "http-after-res") == 0)
			http_after_res_keywords_register(akl);
		else {
			release_hlua_function(fcn);
			hlua_unref(L, ref);
			if (akl)
				ha_free((char **)&(akl->kw[0].kw));
			ha_free(&akl);
			WILL_LJMP(luaL_error(L, "Lua action environment '%s' is unknown. "
			                        "'tcp-req', 'tcp-res', 'http-req', 'http-res' "
			                        "or 'http-after-res' "
			                        "are expected.", lua_tostring(L, -1)));
		}

		/* pop the environment string. */
		lua_pop(L, 1);

		/* reset for next loop */
		akl = NULL;
		fcn = NULL;
	}
	return ACT_RET_PRS_OK;

  alloc_error:
	release_hlua_function(fcn);
	hlua_unref(L, ref);
	ha_free(&akl);
	WILL_LJMP(luaL_error(L, "Lua out of memory error."));
	return 0; /* Never reached */
}

static enum act_parse_ret action_register_service_tcp(const char **args, int *cur_arg, struct proxy *px,
                                                      struct act_rule *rule, char **err)
{
	struct hlua_function *fcn = rule->kw->private;

	if (px->mode == PR_MODE_HTTP) {
		memprintf(err, "Lua TCP services cannot be used on HTTP proxies");
		return ACT_RET_PRS_ERR;
	}

	/* Memory for the rule. */
	rule->arg.hlua_rule = calloc(1, sizeof(*rule->arg.hlua_rule));
	if (!rule->arg.hlua_rule) {
		memprintf(err, "out of memory error");
		return ACT_RET_PRS_ERR;
	}

	/* Reference the Lua function and store the reference. */
	rule->arg.hlua_rule->fcn = fcn;

	/* TODO: later accept arguments. */
	rule->arg.hlua_rule->args = NULL;

	/* Add applet pointer in the rule. */
	rule->applet.obj_type = OBJ_TYPE_APPLET;
	rule->applet.flags = APPLET_FL_NEW_API;
	rule->applet.name = fcn->name;
	rule->applet.init = hlua_applet_tcp_init;
	rule->applet.rcv_buf = appctx_raw_rcv_buf;
	rule->applet.snd_buf = appctx_raw_snd_buf;
	rule->applet.fct = hlua_applet_tcp_fct;
	rule->applet.release = hlua_applet_tcp_release;
	rule->applet.timeout = hlua_timeout_applet;

	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_service(lua_State *L)
{
	struct action_kw_list *akl;
	const char *name;
	const char *env;
	int ref;
	int len;
	struct hlua_function *fcn = NULL;
	struct buffer *trash;
	struct action_kw *akw;

	MAY_LJMP(check_args(L, 3, "register_service"));

	if (hlua_gethlua(L)) {
		/* runtime processing */
		WILL_LJMP(luaL_error(L, "register_service: not available outside of body context"));
	}

	/* First argument : converter name. */
	name = MAY_LJMP(luaL_checkstring(L, 1));

	/* Second argument : environment. */
	env = MAY_LJMP(luaL_checkstring(L, 2));

	/* Third argument : lua function. */
	ref = MAY_LJMP(hlua_checkfunction(L, 3));

	/* Check for service already registered */
	trash = get_trash_chunk();
	chunk_printf(trash, "lua.%s", name);
	akw = service_find(trash->area);
	if (akw != NULL) {
		fcn = akw->private;
		if (fcn->function_ref[hlua_state_id] != -1) {
			ha_warning("Trying to register service 'lua.%s' more than once. "
			           "This will become a hard error in version 2.5.\n", name);
			hlua_unref(L, fcn->function_ref[hlua_state_id]);
		}
		fcn->function_ref[hlua_state_id] = ref;
		return 0;
	}

	/* Allocate and fill the sample fetch keyword struct. */
	akl = calloc(1, sizeof(*akl) + sizeof(struct action_kw) * 2);
	if (!akl)
		goto alloc_error;
	fcn = new_hlua_function();
	if (!fcn)
		goto alloc_error;

	/* Fill fcn. */
	len = strlen("<lua.>") + strlen(name) + 1;
	fcn->name = calloc(1, len);
	if (!fcn->name)
		goto alloc_error;
	snprintf((char *)fcn->name, len, "<lua.%s>", name);
	fcn->function_ref[hlua_state_id] = ref;

	/* List head */
	akl->list.n = akl->list.p = NULL;

	/* converter keyword. */
	len = strlen("lua.") + strlen(name) + 1;
	akl->kw[0].kw = calloc(1, len);
	if (!akl->kw[0].kw)
		goto alloc_error;

	snprintf((char *)akl->kw[0].kw, len, "lua.%s", name);

	/* Check required environment. Only accepted "http" or "tcp". */
	if (strcmp(env, "tcp") == 0)
		akl->kw[0].parse = action_register_service_tcp;
	else if (strcmp(env, "http") == 0)
		akl->kw[0].parse = action_register_service_http;
	else {
		release_hlua_function(fcn);
		hlua_unref(L, ref);
		if (akl)
			ha_free((char **)&(akl->kw[0].kw));
		ha_free(&akl);
		WILL_LJMP(luaL_error(L, "Lua service environment '%s' is unknown. "
		                        "'tcp' or 'http' are expected.", env));
	}

	akl->kw[0].flags = 0;
	akl->kw[0].private = fcn;

	/* End of array. */
	memset(&akl->kw[1], 0, sizeof(*akl->kw));

	/* Register this new converter */
	service_keywords_register(akl);

	return 0;

  alloc_error:
	release_hlua_function(fcn);
	hlua_unref(L, ref);
	ha_free(&akl);
	WILL_LJMP(luaL_error(L, "Lua out of memory error."));
	return 0; /* Never reached */
}

/* This function initialises Lua cli handler. It copies the
 * arguments in the Lua stack and create channel IO objects.
 */
static int hlua_cli_parse_fct(char **args, char *payload, struct appctx *appctx, void *private)
{
	struct hlua_cli_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
	struct hlua *hlua;
	struct hlua_function *fcn;
	int i;
	const char *error;

	fcn = private;
	ctx->fcn = private;

	hlua = pool_alloc(pool_head_hlua);
	if (!hlua) {
		SEND_ERR(NULL, "Lua cli '%s': out of memory.\n", fcn->name);
		return 1;
	}
	HLUA_INIT(hlua);
	ctx->hlua = hlua;

	/* Create task used by signal to wakeup applets.
	 * We use the same wakeup function than the Lua applet_tcp and
	 * applet_http. It is absolutely compatible.
	 */
	ctx->task = task_new_here();
	if (!ctx->task) {
		SEND_ERR(NULL, "Lua cli '%s': out of memory.\n", fcn->name);
		goto error;
	}
	ctx->task->nice = 0;
	ctx->task->context = appctx;
	ctx->task->process = hlua_applet_wakeup;

	/* Initialises the Lua context */
	if (!hlua_ctx_init(hlua, fcn_ref_to_stack_id(fcn), ctx->task)) {
		SEND_ERR(NULL, "Lua cli '%s': can't initialize Lua context.\n", fcn->name);
		goto error;
	}

	/* The following Lua calls can fail. */
	if (!SET_SAFE_LJMP(hlua)) {
		hlua_lock(hlua);
		if (lua_type(hlua->T, -1) == LUA_TSTRING)
			error = hlua_tostring_safe(hlua->T, -1);
		else
			error = "critical error";
		SEND_ERR(NULL, "Lua cli '%s': %s.\n", fcn->name, error);
		hlua_unlock(hlua);
		goto error;
	}

	/* Check stack available size. */
	if (!lua_checkstack(hlua->T, 2)) {
		SEND_ERR(NULL, "Lua cli '%s': full stack.\n", fcn->name);
		goto error;
	}

	/* Restore the function in the stack. */
	hlua_pushref(hlua->T, fcn->function_ref[hlua->state_id]);

	/* Once the arguments parsed, the CLI is like an AppletTCP,
	 * so push AppletTCP in the stack.
	 */
	if (!hlua_applet_tcp_new(hlua->T, appctx)) {
		SEND_ERR(NULL, "Lua cli '%s': full stack.\n", fcn->name);
		goto error;
	}
	hlua->nargs = 1;

	/* push keywords in the stack. */
	for (i = 0; *args[i]; i++) {
		/* Check stack available size. */
		if (!lua_checkstack(hlua->T, 1)) {
			SEND_ERR(NULL, "Lua cli '%s': full stack.\n", fcn->name);
			goto error;
		}
		lua_pushstring(hlua->T, args[i]);
		hlua->nargs++;
	}

	/* We must initialize the execution timeouts. */
	hlua_timer_init(&hlua->timer, hlua_timeout_session);

	/* At this point the execution is safe. */
	RESET_SAFE_LJMP(hlua);

	/* It's ok */
	return 0;

	/* It's not ok. */
error:
	RESET_SAFE_LJMP(hlua);
	hlua_ctx_destroy(hlua);
	ctx->hlua = NULL;
	return 1;
}

static int hlua_cli_io_handler_fct(struct appctx *appctx)
{
	struct hlua_cli_ctx *ctx = appctx->svcctx;
	struct hlua *hlua;
	struct hlua_function *fcn;

	hlua = ctx->hlua;
	fcn = ctx->fcn;

	/* Execute the function. */
	switch (hlua_ctx_resume(hlua, 1)) {

	/* finished. */
	case HLUA_E_OK:
		return 1;

	/* yield. */
	case HLUA_E_AGAIN:
		/* We want write. */
		if (HLUA_IS_WAKERESWR(hlua))
			applet_have_more_data(appctx);
		/* Set the timeout. */
		if (hlua->wake_time != TICK_ETERNITY)
			task_schedule(hlua->task, hlua->wake_time);

		applet_will_consume(appctx);
		applet_expect_data(appctx);
		return 0;

	/* finished with error. */
	case HLUA_E_ERRMSG:
		/* Display log. */
		hlua_lock(hlua);
		SEND_ERR(NULL, "Lua cli '%s': %s.\n",
		         fcn->name, hlua_tostring_safe(hlua->T, -1));
		lua_pop(hlua->T, 1);
		hlua_unlock(hlua);
		return 1;

	case HLUA_E_ETMOUT:
		SEND_ERR(NULL, "Lua cli '%s': execution timeout.\n",
		         fcn->name);
		return 1;

	case HLUA_E_BTMOUT:
		SEND_ERR(NULL, "Lua cli '%s': burst timeout.\n",
		         fcn->name);
		return 1;

	case HLUA_E_NOMEM:
		SEND_ERR(NULL, "Lua cli '%s': out of memory error.\n",
		         fcn->name);
		return 1;

	case HLUA_E_YIELD: /* unexpected */
		SEND_ERR(NULL, "Lua cli '%s': yield not allowed.\n",
		         fcn->name);
		return 1;

	case HLUA_E_ERR:
		/* Display log. */
		SEND_ERR(NULL, "Lua cli '%s' return an unknown error.\n",
		         fcn->name);
		return 1;

	default:
		return 1;
	}

	return 1;
}

static void hlua_cli_io_release_fct(struct appctx *appctx)
{
	struct hlua_cli_ctx *ctx = appctx->svcctx;

	task_destroy(ctx->task);
	ctx->task = NULL;
	hlua_ctx_destroy(ctx->hlua);
	ctx->hlua = NULL;
}

/* This function is an LUA binding used for registering
 * new keywords in the cli. It expects a list of keywords
 * which are the "path". It is limited to 5 keywords. A
 * description of the command, a function to be executed
 * for the parsing and a function for io handlers.
 */
__LJMP static int hlua_register_cli(lua_State *L)
{
	struct cli_kw_list *cli_kws;
	const char *message;
	int ref_io;
	int len;
	struct hlua_function *fcn = NULL;
	int index;
	int i;
	struct buffer *trash;
	const char *kw[5];
	struct cli_kw *cli_kw;
	const char *errmsg;
	char *end;

	MAY_LJMP(check_args(L, 3, "register_cli"));

	if (hlua_gethlua(L)) {
		/* runtime processing */
		WILL_LJMP(luaL_error(L, "register_cli: not available outside of body context"));
	}

	/* First argument : an array of maximum 5 keywords. */
	if (!lua_istable(L, 1))
		WILL_LJMP(luaL_argerror(L, 1, "1st argument must be a table"));

	/* Second argument : string with contextual message. */
	message = MAY_LJMP(luaL_checkstring(L, 2));

	/* Third and fourth argument : lua function. */
	ref_io = MAY_LJMP(hlua_checkfunction(L, 3));

	/* Check for CLI service already registered */
	trash = get_trash_chunk();
	index = 0;
	lua_pushnil(L);
	memset(kw, 0, sizeof(kw));
	while (lua_next(L, 1) != 0) {
		if (index >= CLI_PREFIX_KW_NB) {
			hlua_unref(L, ref_io);
			WILL_LJMP(luaL_argerror(L, 1, "1st argument must be a table with a maximum of 5 entries"));
		}
		if (lua_type(L, -1) != LUA_TSTRING) {
			hlua_unref(L, ref_io);
			WILL_LJMP(luaL_argerror(L, 1, "1st argument must be a table filled with strings"));
		}
		kw[index] = lua_tostring(L, -1);
		if (index == 0)
			chunk_printf(trash, "%s", kw[index]);
		else
			chunk_appendf(trash, " %s", kw[index]);
		index++;
		lua_pop(L, 1);
	}
	cli_kw = cli_find_kw_exact((char **)kw);
	if (cli_kw != NULL) {
		fcn = cli_kw->private;
		if (fcn->function_ref[hlua_state_id] != -1) {
			ha_warning("Trying to register CLI keyword 'lua.%s' more than once. "
			           "This will become a hard error in version 2.5.\n", trash->area);
			hlua_unref(L, fcn->function_ref[hlua_state_id]);
		}
		fcn->function_ref[hlua_state_id] = ref_io;
		return 0;
	}

	/* Allocate and fill the sample fetch keyword struct. */
	cli_kws = calloc(1, sizeof(*cli_kws) + sizeof(struct cli_kw) * 2);
	if (!cli_kws) {
		errmsg = "Lua out of memory error.";
		goto error;
	}
	fcn = new_hlua_function();
	if (!fcn) {
		errmsg = "Lua out of memory error.";
		goto error;
	}

	/* Fill path. */
	index = 0;
	lua_pushnil(L);
	while(lua_next(L, 1) != 0) {
		if (index >= 5) {
			errmsg = "1st argument must be a table with a maximum of 5 entries";
			goto error;
		}
		if (lua_type(L, -1) != LUA_TSTRING) {
			errmsg = "1st argument must be a table filled with strings";
			goto error;
		}
		cli_kws->kw[0].str_kw[index] = strdup(lua_tostring(L, -1));
		if (!cli_kws->kw[0].str_kw[index]) {
			errmsg = "Lua out of memory error.";
			goto error;
		}
		index++;
		lua_pop(L, 1);
	}

	/* Copy help message. */
	cli_kws->kw[0].usage = strdup(message);
	if (!cli_kws->kw[0].usage) {
		errmsg = "Lua out of memory error.";
		goto error;
	}

	/* Fill fcn io handler. */
	len = strlen("<lua.cli>") + 1;
	for (i = 0; i < index; i++)
		len += strlen(cli_kws->kw[0].str_kw[i]) + 1;
	fcn->name = calloc(1, len);
	if (!fcn->name) {
		errmsg = "Lua out of memory error.";
		goto error;
	}

	end = fcn->name;
	len = 8;
	memcpy(end, "<lua.cli", len);
	end += len;

	for (i = 0; i < index; i++) {
		*(end++) = '.';
		len = strlen(cli_kws->kw[0].str_kw[i]);
		memcpy(end, cli_kws->kw[0].str_kw[i], len);
		end += len;
	}
	*(end++) = '>';
	*(end++) = 0;

	fcn->function_ref[hlua_state_id] = ref_io;

	/* Fill last entries. */
	cli_kws->kw[0].private = fcn;
	cli_kws->kw[0].parse = hlua_cli_parse_fct;
	cli_kws->kw[0].io_handler = hlua_cli_io_handler_fct;
	cli_kws->kw[0].io_release = hlua_cli_io_release_fct;

	/* Register this new converter */
	cli_register_kw(cli_kws);

	return 0;

  error:
	release_hlua_function(fcn);
	hlua_unref(L, ref_io);
	if (cli_kws) {
		for (i = 0; i < index; i++)
			ha_free((char **)&(cli_kws->kw[0].str_kw[i]));
		ha_free((char **)&(cli_kws->kw[0].usage));
	}
	ha_free(&cli_kws);
	WILL_LJMP(luaL_error(L, errmsg));
	return 0; /* Never reached */
}

static int hlua_filter_init_per_thread(struct proxy *px, struct flt_conf *fconf)
{
	struct hlua_flt_config *conf = fconf->conf;
	lua_State *L;
	int error, pos, state_id, flt_ref;

	state_id = reg_flt_to_stack_id(conf->reg);
	L = hlua_states[state_id];
	pos = lua_gettop(L);

	/* The filter parsing function */
	hlua_pushref(L, conf->reg->fun_ref[state_id]);

	/* Push the filter class on the stack and resolve all callbacks */
	hlua_pushref(L, conf->reg->flt_ref[state_id]);

	/* Duplicate the filter class so each filter will have its own copy */
	lua_newtable(L);
	lua_pushnil(L);

        while (lua_next(L, pos+2)) {
		lua_pushvalue(L, -2);
		lua_insert(L, -2);
		lua_settable(L, -4);
	}
	flt_ref = hlua_ref(L);

	/* Remove the original lua filter class from the stack */
	lua_pop(L, 1);

	/* Push the copy on the stack */
	hlua_pushref(L, flt_ref);

	/* extra args are pushed in a table */
	lua_newtable(L);
	for (pos = 0; conf->args[pos]; pos++) {
		/* Check stack available size. */
		if (!lua_checkstack(L, 1)) {
			ha_alert("Lua filter '%s' : Lua error : full stack.", conf->reg->name);
			goto error;
		}
		lua_pushstring(L, conf->args[pos]);
		lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);
	}

	error = lua_pcall(L, 2, LUA_MULTRET, 0);
	switch (error) {
	case LUA_OK:
		/* replace the filter ref */
		conf->ref[state_id] = flt_ref;
		break;
	case LUA_ERRRUN:
		ha_alert("Lua filter '%s' : runtime error : %s", conf->reg->name, hlua_tostring_safe(L, -1));
		goto error;
	case LUA_ERRMEM:
		ha_alert("Lua filter '%s' : out of memory error", conf->reg->name);
		goto error;
	case LUA_ERRERR:
		ha_alert("Lua filter '%s' : message handler error : %s", conf->reg->name, hlua_tostring_safe(L, -1));
		goto error;
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 503
	case LUA_ERRGCMM:
		ha_alert("Lua filter '%s' : garbage collector error : %s", conf->reg->name, hlua_tostring_safe(L, -1));
		goto error;
#endif
	default:
		ha_alert("Lua filter '%s' : unknown error : %s", conf->reg->name, hlua_tostring_safe(L, -1));
		goto error;
	}

	lua_settop(L, 0);
	return 0;

  error:
	lua_settop(L, 0);
	return -1;
}

static void hlua_filter_deinit_per_thread(struct proxy *px, struct flt_conf *fconf)
{
	struct hlua_flt_config *conf = fconf->conf;
	lua_State *L;
	int state_id;

	if (!conf)
		return;

	state_id = reg_flt_to_stack_id(conf->reg);
	L = hlua_states[state_id];
	hlua_unref(L, conf->ref[state_id]);
}

static int hlua_filter_init(struct proxy *px, struct flt_conf *fconf)
{
	struct hlua_flt_config *conf = fconf->conf;
	int state_id = reg_flt_to_stack_id(conf->reg);

	/* Rely on per-thread init for global scripts */
	if (!state_id)
		return hlua_filter_init_per_thread(px, fconf);
	return 0;
}

static void hlua_filter_deinit(struct proxy *px, struct flt_conf *fconf)
{

	if (fconf->conf) {
		struct hlua_flt_config *conf = fconf->conf;
		int state_id = reg_flt_to_stack_id(conf->reg);
		int pos;

		/* Rely on per-thread deinit for global scripts */
		if (!state_id)
			hlua_filter_deinit_per_thread(px, fconf);

		for (pos = 0; conf->args[pos]; pos++)
			free(conf->args[pos]);
		free(conf->args);
	}
	ha_free(&fconf->conf);
	ha_free((char **)&fconf->id);
	ha_free(&fconf->ops);
}

static int hlua_filter_new(struct stream *s, struct filter *filter)
{
	struct hlua_flt_config *conf = FLT_CONF(filter);
	struct hlua_flt_ctx *flt_ctx = NULL;
	struct hlua *hlua = NULL;
	int ret = 1;

	if (!(hlua = hlua_stream_ctx_prepare(s, reg_flt_to_stack_id(conf->reg)))) {
		SEND_ERR(s->be, "Lua filter '%s': can't initialize filter Lua context.\n",
			 conf->reg->name);
		ret = 0;
		goto end;
	}

	flt_ctx = pool_zalloc(pool_head_hlua_flt_ctx);
	if (!flt_ctx) {
		SEND_ERR(s->be, "Lua filter '%s': can't initialize filter Lua context.\n",
			 conf->reg->name);
		ret = 0;
		goto end;
	}

	if ((flt_ctx->hlua[0] = pool_alloc(pool_head_hlua)))
		HLUA_INIT(flt_ctx->hlua[0]);
	if ((flt_ctx->hlua[1] = pool_alloc(pool_head_hlua)))
		HLUA_INIT(flt_ctx->hlua[1]);
	if (!flt_ctx->hlua[0] || !flt_ctx->hlua[1]) {
		SEND_ERR(s->be, "Lua filter '%s': can't initialize filter Lua context.\n",
			 conf->reg->name);
		ret = 0;
		goto end;
	}

	if (!hlua_ctx_init(flt_ctx->hlua[0], reg_flt_to_stack_id(conf->reg), s->task) ||
	    !hlua_ctx_init(flt_ctx->hlua[1], reg_flt_to_stack_id(conf->reg), s->task)) {
		SEND_ERR(s->be, "Lua filter '%s': can't initialize filter Lua context.\n",
			 conf->reg->name);
		ret = 0;
		goto end;
	}

	if (!HLUA_IS_RUNNING(hlua)) {
		/* The following Lua calls can fail. */
		if (!SET_SAFE_LJMP(hlua)) {
			const char *error;

			hlua_lock(hlua);
			if (lua_type(hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(s->be, "Lua filter '%s': %s.\n", conf->reg->name, error);
			hlua_unlock(hlua);
			ret = 0;
			goto end;
		}

		/* Check stack size. */
		if (!lua_checkstack(hlua->T, 1)) {
			SEND_ERR(s->be, "Lua filter '%s': full stack.\n", conf->reg->name);
			RESET_SAFE_LJMP(hlua);
			ret = 0;
			goto end;
		}

		hlua_pushref(hlua->T, conf->ref[hlua->state_id]);
		if (lua_getfield(hlua->T, -1, "new") != LUA_TFUNCTION) {
			SEND_ERR(s->be, "Lua filter '%s': 'new' field is not a function.\n",
				 conf->reg->name);
			RESET_SAFE_LJMP(hlua);
			ret = 0;
			goto end;
		}
		lua_insert(hlua->T, -2);

		/* Push the copy on the stack */
		hlua->nargs = 1;

		/* We must initialize the execution timeouts. */
		hlua_timer_init(&hlua->timer, hlua_timeout_session);

		/* At this point the execution is safe. */
		RESET_SAFE_LJMP(hlua);
	}

	switch (hlua_ctx_resume(hlua, 0)) {
	case HLUA_E_OK:
		/* The following Lua calls can fail. */
		if (!SET_SAFE_LJMP(hlua)) {
			const char *error;

			hlua_lock(hlua);
			if (lua_type(hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(s->be, "Lua filter '%s': %s.\n", conf->reg->name, error);
			hlua_unlock(hlua);
			ret = 0;
			goto end;
		}

		/* Nothing returned or not a table, ignore the filter for current stream */
		if (!lua_gettop(hlua->T) || !lua_istable(hlua->T, 1)) {
			ret = 0;
			RESET_SAFE_LJMP(hlua);
			goto end;
		}

		/* Attached the filter pointer to the ctx */
		lua_pushstring(hlua->T, "__filter");
		lua_pushlightuserdata(hlua->T, filter);
		lua_settable(hlua->T, -3);

		/* Save a ref on the filter ctx */
		lua_pushvalue(hlua->T, 1);
		flt_ctx->ref = hlua_ref(hlua->T);

		/* At this point the execution is safe. */
		RESET_SAFE_LJMP(hlua);

		/* save main hlua ctx (from the stream) */
		flt_ctx->_hlua = hlua;

		filter->ctx = flt_ctx;
		break;
	case HLUA_E_ERRMSG:
		hlua_lock(hlua);
		SEND_ERR(s->be, "Lua filter '%s' : %s.\n", conf->reg->name, hlua_tostring_safe(hlua->T, -1));
		hlua_unlock(hlua);
		ret = -1;
		goto end;
	case HLUA_E_ETMOUT:
		SEND_ERR(s->be, "Lua filter '%s' : 'new' execution timeout.\n", conf->reg->name);
		ret = 0;
		goto end;
	case HLUA_E_BTMOUT:
		SEND_ERR(s->be, "Lua filter '%s' : 'new' burst timeout.\n", conf->reg->name);
		ret = 0;
		goto end;
	case HLUA_E_NOMEM:
		SEND_ERR(s->be, "Lua filter '%s' : out of memory error.\n", conf->reg->name);
		ret = 0;
		goto end;
	case HLUA_E_AGAIN:
	case HLUA_E_YIELD:
		SEND_ERR(s->be, "Lua filter '%s': yield functions like core.tcp() or core.sleep()"
			 " are not allowed from 'new' function.\n", conf->reg->name);
		ret = 0;
		goto end;
	case HLUA_E_ERR:
		SEND_ERR(s->be, "Lua filter '%s': 'new' returns an unknown error.\n", conf->reg->name);
		ret = 0;
		goto end;
	default:
		ret = 0;
		goto end;
	}

  end:
	if (hlua) {
		hlua_lock(hlua);
		lua_settop(hlua->T, 0);
		hlua_unlock(hlua);
	}
	if (ret <= 0) {
		if (flt_ctx) {
			hlua_ctx_destroy(flt_ctx->hlua[0]);
			hlua_ctx_destroy(flt_ctx->hlua[1]);
			pool_free(pool_head_hlua_flt_ctx, flt_ctx);
		}
	}
	return ret;
}

static void hlua_filter_delete(struct stream *s, struct filter *filter)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;
	struct hlua *hlua = hlua_stream_ctx_get(s, flt_ctx->_hlua->state_id);

	BUG_ON(!hlua); /* hlua_filter_new() is responsible for preparing
	                * hlua stream_ctx, thus hlua_stream_ctx_get() should
	                * not return NULL!
	                */
	hlua_lock(hlua);
	hlua_unref(hlua->T, flt_ctx->ref);
	hlua_unlock(hlua);
	hlua_ctx_destroy(flt_ctx->hlua[0]);
	hlua_ctx_destroy(flt_ctx->hlua[1]);
	pool_free(pool_head_hlua_flt_ctx, flt_ctx);
	filter->ctx = NULL;
}

static int hlua_filter_from_payload(struct filter *filter)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;

	return (flt_ctx && !!(flt_ctx->flags & HLUA_FLT_CTX_FL_PAYLOAD));
}

static int hlua_filter_callback(struct stream *s, struct filter *filter, const char *fun,
				int dir, unsigned int flags)
{
	struct hlua *flt_hlua;
	struct hlua_flt_config *conf = FLT_CONF(filter);
	struct hlua_flt_ctx *flt_ctx = filter->ctx;
	unsigned int hflags = HLUA_TXN_FLT_CTX;
	int ret = 1;

	flt_hlua = flt_ctx->hlua[(dir == SMP_OPT_DIR_REQ ? 0 : 1)];
	if (!flt_hlua)
		goto end;

	if (!HLUA_IS_RUNNING(flt_hlua)) {
		int extra_idx;

		/* The following Lua calls can fail. */
		if (!SET_SAFE_LJMP(flt_hlua)) {
			const char *error;

			hlua_lock(flt_hlua);
			if (lua_type(flt_hlua->T, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(flt_hlua->T, -1);
			else
				error = "critical error";
			SEND_ERR(s->be, "Lua filter '%s': %s.\n", conf->reg->name, error);
			hlua_unlock(flt_hlua);
			goto end;
		}

		extra_idx = lua_gettop(flt_hlua->T);

		/* Check stack size. */
		if (!lua_checkstack(flt_hlua->T, 3)) {
			SEND_ERR(s->be, "Lua filter '%s': full stack.\n", conf->reg->name);
			RESET_SAFE_LJMP(flt_hlua);
			goto end;
		}

		hlua_pushref(flt_hlua->T, flt_ctx->ref);
		if (lua_getfield(flt_hlua->T, -1, fun) != LUA_TFUNCTION) {
			RESET_SAFE_LJMP(flt_hlua);
			goto end;
		}
		lua_insert(flt_hlua->T, -2);

		if (!hlua_txn_new(flt_hlua->T, s, s->be, dir, hflags)) {
			SEND_ERR(s->be, "Lua filter '%s': full stack.\n", conf->reg->name);
			RESET_SAFE_LJMP(flt_hlua);
			goto end;
		}
		flt_hlua->nargs = 2;

		if (flags & HLUA_FLT_CB_ARG_CHN) {
			if (dir == SMP_OPT_DIR_REQ)
				lua_getfield(flt_hlua->T, -1, "req");
			else
				lua_getfield(flt_hlua->T, -1, "res");
			if (lua_type(flt_hlua->T, -1) == LUA_TTABLE) {
				lua_pushstring(flt_hlua->T, "__filter");
				lua_pushlightuserdata(flt_hlua->T, filter);
				lua_settable(flt_hlua->T, -3);
			}
			flt_hlua->nargs++;
		}
		else if (flags & HLUA_FLT_CB_ARG_HTTP_MSG) {
			if (dir == SMP_OPT_DIR_REQ)
				lua_getfield(flt_hlua->T, -1, "http_req");
			else
				lua_getfield(flt_hlua->T, -1, "http_res");
			if (lua_type(flt_hlua->T, -1) == LUA_TTABLE) {
				lua_pushstring(flt_hlua->T, "__filter");
				lua_pushlightuserdata(flt_hlua->T, filter);
				lua_settable(flt_hlua->T, -3);
			}
			flt_hlua->nargs++;
		}

		/* Check stack size. */
		if (!lua_checkstack(flt_hlua->T, 1)) {
			SEND_ERR(s->be, "Lua filter '%s': full stack.\n", conf->reg->name);
			RESET_SAFE_LJMP(flt_hlua);
			goto end;
		}

		while (extra_idx--) {
			lua_pushvalue(flt_hlua->T, 1);
			lua_remove(flt_hlua->T, 1);
			flt_hlua->nargs++;
		}

		/* We must initialize the execution timeouts. */
		hlua_timer_init(&flt_hlua->timer, hlua_timeout_session);

		/* At this point the execution is safe. */
		RESET_SAFE_LJMP(flt_hlua);
	}

	switch (hlua_ctx_resume(flt_hlua, !(flags & HLUA_FLT_CB_FINAL))) {
	case HLUA_E_OK:
		/* Catch the return value if it required */
		hlua_lock(flt_hlua);
		if ((flags & HLUA_FLT_CB_RETVAL) && lua_gettop(flt_hlua->T) > 0) {
			ret = lua_tointeger(flt_hlua->T, -1);
			lua_settop(flt_hlua->T, 0); /* Empty the stack. */
		}
		hlua_unlock(flt_hlua);

		/* Set timeout in the required channel. */
		if (flt_hlua->wake_time != TICK_ETERNITY) {
			if (dir == SMP_OPT_DIR_REQ)
				s->req.analyse_exp = flt_hlua->wake_time;
			else
				s->res.analyse_exp = flt_hlua->wake_time;
		}
		break;
	case HLUA_E_AGAIN:
		/* Set timeout in the required channel. */
		if (flt_hlua->wake_time != TICK_ETERNITY) {
			if (dir == SMP_OPT_DIR_REQ)
				s->req.analyse_exp = flt_hlua->wake_time;
			else
				s->res.analyse_exp = flt_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 targeted on the requests.
		 */
		if (HLUA_IS_WAKERESWR(flt_hlua))
			s->res.flags |= CF_WAKE_WRITE;
		if (HLUA_IS_WAKEREQWR(flt_hlua))
			s->req.flags |= CF_WAKE_WRITE;
		ret = 0;
		goto end;
	case HLUA_E_ERRMSG:
		hlua_lock(flt_hlua);
		SEND_ERR(s->be, "Lua filter '%s' : %s.\n", conf->reg->name, hlua_tostring_safe(flt_hlua->T, -1));
		hlua_unlock(flt_hlua);
		ret = -1;
		goto end;
	case HLUA_E_ETMOUT:
		SEND_ERR(s->be, "Lua filter '%s' : '%s' callback execution timeout.\n", conf->reg->name, fun);
		goto end;
	case HLUA_E_BTMOUT:
		SEND_ERR(s->be, "Lua filter '%s' : '%s' callback burst timeout.\n", conf->reg->name, fun);
		goto end;
	case HLUA_E_NOMEM:
		SEND_ERR(s->be, "Lua filter '%s' : out of memory error.\n", conf->reg->name);
		goto end;
	case HLUA_E_YIELD:
		SEND_ERR(s->be, "Lua filter '%s': yield functions like core.tcp() or core.sleep()"
			 " are not allowed from '%s' callback.\n", conf->reg->name, fun);
		goto end;
	case HLUA_E_ERR:
		SEND_ERR(s->be, "Lua filter '%s': '%s' returns an unknown error.\n", conf->reg->name, fun);
		goto end;
	default:
		goto end;
	}


  end:
	return ret;
}

static int  hlua_filter_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;

	flt_ctx->flags = 0;
	return hlua_filter_callback(s, filter, "start_analyze",
				    (!(chn->flags & CF_ISRESP) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES),
				    (HLUA_FLT_CB_FINAL | HLUA_FLT_CB_RETVAL | HLUA_FLT_CB_ARG_CHN));
}

static int  hlua_filter_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;

	flt_ctx->flags &= ~HLUA_FLT_CTX_FL_PAYLOAD;
	return hlua_filter_callback(s, filter, "end_analyze",
				    (!(chn->flags & CF_ISRESP) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES),
				    (HLUA_FLT_CB_FINAL | HLUA_FLT_CB_RETVAL | HLUA_FLT_CB_ARG_CHN));
}

static int  hlua_filter_http_headers(struct stream *s, struct filter *filter, struct http_msg *msg)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;

	flt_ctx->flags &= ~HLUA_FLT_CTX_FL_PAYLOAD;
	return hlua_filter_callback(s, filter, "http_headers",
				    (!(msg->chn->flags & CF_ISRESP) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES),
				    (HLUA_FLT_CB_FINAL | HLUA_FLT_CB_RETVAL | HLUA_FLT_CB_ARG_HTTP_MSG));
}

static int  hlua_filter_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
				     unsigned int offset, unsigned int len)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;
	struct hlua *flt_hlua;
	int dir = (!(msg->chn->flags & CF_ISRESP) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
	int idx = (dir == SMP_OPT_DIR_REQ ? 0 : 1);
	int ret;

	flt_hlua = flt_ctx->hlua[idx];
	flt_ctx->cur_off[idx] = offset;
	flt_ctx->cur_len[idx] = len;
	flt_ctx->flags |= HLUA_FLT_CTX_FL_PAYLOAD;
	ret = hlua_filter_callback(s, filter, "http_payload", dir, (HLUA_FLT_CB_FINAL | HLUA_FLT_CB_ARG_HTTP_MSG));
	if (ret != -1) {
		ret = flt_ctx->cur_len[idx];
		if (lua_gettop(flt_hlua->T) > 0) {
			ret = lua_tointeger(flt_hlua->T, -1);
			if (ret > flt_ctx->cur_len[idx])
				ret = flt_ctx->cur_len[idx];
			lua_settop(flt_hlua->T, 0); /* Empty the stack. */
		}
	}
	return ret;
}

static int  hlua_filter_http_end(struct stream *s, struct filter *filter, struct http_msg *msg)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;

	flt_ctx->flags &= ~HLUA_FLT_CTX_FL_PAYLOAD;
	return hlua_filter_callback(s, filter, "http_end",
				    (!(msg->chn->flags & CF_ISRESP) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES),
				    (HLUA_FLT_CB_FINAL | HLUA_FLT_CB_RETVAL | HLUA_FLT_CB_ARG_HTTP_MSG));
}

static int  hlua_filter_tcp_payload(struct stream *s, struct filter *filter, struct channel *chn,
				    unsigned int offset, unsigned int len)
{
	struct hlua_flt_ctx *flt_ctx = filter->ctx;
	struct hlua *flt_hlua;
	int dir = (!(chn->flags & CF_ISRESP) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
	int idx = (dir == SMP_OPT_DIR_REQ ? 0 : 1);
	int ret;

	flt_hlua = flt_ctx->hlua[idx];
	flt_ctx->cur_off[idx] = offset;
	flt_ctx->cur_len[idx] = len;
	flt_ctx->flags |= HLUA_FLT_CTX_FL_PAYLOAD;
	ret = hlua_filter_callback(s, filter, "tcp_payload", dir, (HLUA_FLT_CB_FINAL | HLUA_FLT_CB_ARG_CHN));
	if (ret != -1) {
		ret = flt_ctx->cur_len[idx];
		if (lua_gettop(flt_hlua->T) > 0) {
			ret = lua_tointeger(flt_hlua->T, -1);
			if (ret > flt_ctx->cur_len[idx])
				ret = flt_ctx->cur_len[idx];
			lua_settop(flt_hlua->T, 0); /* Empty the stack. */
		}
	}
	return ret;
}

static int hlua_filter_parse_fct(char **args, int *cur_arg, struct proxy *px,
				 struct flt_conf *fconf, char **err, void *private)
{
	struct hlua_reg_filter *reg_flt = private;
	lua_State *L;
	struct hlua_flt_config *conf = NULL;
	const char *flt_id = NULL;
	int state_id, pos, flt_flags = 0;
	struct flt_ops *hlua_flt_ops = NULL;

	state_id = reg_flt_to_stack_id(reg_flt);
	L = hlua_states[state_id];

	/* Initialize the filter ops with default callbacks */
	hlua_flt_ops = calloc(1, sizeof(*hlua_flt_ops));
	if (!hlua_flt_ops)
		goto error;
	hlua_flt_ops->init              = hlua_filter_init;
	hlua_flt_ops->deinit            = hlua_filter_deinit;
	if (state_id) {
		/* Set per-thread callback if script is loaded per-thread */
		hlua_flt_ops->init_per_thread   = hlua_filter_init_per_thread;
		hlua_flt_ops->deinit_per_thread = hlua_filter_deinit_per_thread;
	}
	hlua_flt_ops->attach            = hlua_filter_new;
	hlua_flt_ops->detach            = hlua_filter_delete;

	/* Push the filter class on the stack and resolve all callbacks */
	hlua_pushref(L, reg_flt->flt_ref[state_id]);

	if (lua_getfield(L, -1, "start_analyze") == LUA_TFUNCTION)
		hlua_flt_ops->channel_start_analyze = hlua_filter_start_analyze;
	lua_pop(L, 1);
	if (lua_getfield(L, -1, "end_analyze") == LUA_TFUNCTION)
		hlua_flt_ops->channel_end_analyze = hlua_filter_end_analyze;
	lua_pop(L, 1);
	if (lua_getfield(L, -1, "http_headers") == LUA_TFUNCTION)
		hlua_flt_ops->http_headers = hlua_filter_http_headers;
	lua_pop(L, 1);
	if (lua_getfield(L, -1, "http_payload") == LUA_TFUNCTION)
		hlua_flt_ops->http_payload = hlua_filter_http_payload;
	lua_pop(L, 1);
	if (lua_getfield(L, -1, "http_end") == LUA_TFUNCTION)
		hlua_flt_ops->http_end = hlua_filter_http_end;
	lua_pop(L, 1);
	if (lua_getfield(L, -1, "tcp_payload") == LUA_TFUNCTION)
		hlua_flt_ops->tcp_payload = hlua_filter_tcp_payload;
	lua_pop(L, 1);

	/* Get id and flags of the filter class */
	if (lua_getfield(L, -1, "id") == LUA_TSTRING)
		flt_id  = lua_tostring(L, -1);
	lua_pop(L, 1);
	if (lua_getfield(L, -1, "flags") == LUA_TNUMBER)
		flt_flags  = lua_tointeger(L, -1);
	lua_pop(L, 1);

	/* Create the filter config */
	conf = calloc(1, sizeof(*conf));
	if (!conf)
		goto error;
	conf->reg = reg_flt;

	/* duplicate args */
	for (pos = 0; *args[*cur_arg + 1 + pos]; pos++);
	conf->args = calloc(pos + 1, sizeof(*conf->args));
	if (!conf->args)
		goto error;
	for (pos = 0; *args[*cur_arg + 1 + pos]; pos++) {
		conf->args[pos] = strdup(args[*cur_arg + 1 + pos]);
		if (!conf->args[pos])
			goto error;
	}
	conf->args[pos] = NULL;
	*cur_arg += pos + 1;

	if (flt_id) {
		fconf->id    = strdup(flt_id);
		if (!fconf->id)
			goto error;
	}
	fconf->flags = flt_flags;
	fconf->conf  = conf;
	fconf->ops   = hlua_flt_ops;

	lua_settop(L, 0);
	return 0;

  error:
	memprintf(err, "Lua filter '%s' : Lua out of memory error", reg_flt->name);
	free(hlua_flt_ops);
	if (conf && conf->args) {
		for (pos = 0; conf->args[pos]; pos++)
			free(conf->args[pos]);
		free(conf->args);
	}
	free(conf);
	free((char *)fconf->id);
	lua_settop(L, 0);
	return -1;
}

__LJMP static int hlua_register_data_filter(lua_State *L)
{
	struct filter *filter;
	struct channel *chn;

	MAY_LJMP(check_args(L, 2, "register_data_filter"));
	MAY_LJMP(luaL_checktype(L, 1, LUA_TTABLE));
	chn = MAY_LJMP(hlua_checkchannel(L, 2));

	lua_getfield(L, 1, "__filter");
	MAY_LJMP(luaL_checktype(L, -1, LUA_TLIGHTUSERDATA));
	filter = lua_touserdata (L, -1);
	lua_pop(L, 1);

	register_data_filter(chn_strm(chn), chn, filter);
	return 1;
}

__LJMP static int hlua_unregister_data_filter(lua_State *L)
{
	struct filter *filter;
	struct channel *chn;

	MAY_LJMP(check_args(L, 2, "unregister_data_filter"));
	MAY_LJMP(luaL_checktype(L, 1, LUA_TTABLE));
	chn = MAY_LJMP(hlua_checkchannel(L, 2));

	lua_getfield(L, 1, "__filter");
	MAY_LJMP(luaL_checktype(L, -1, LUA_TLIGHTUSERDATA));
	filter = lua_touserdata (L, -1);
	lua_pop(L, 1);

	unregister_data_filter(chn_strm(chn), chn, filter);
	return 1;
}

/* This function is an LUA binding used for registering a filter. It expects a
 * filter name used in the haproxy configuration file and a LUA function to
 * parse configuration arguments.
 */
__LJMP static int hlua_register_filter(lua_State *L)
{
	struct buffer *trash;
	struct flt_kw_list *fkl;
	struct flt_kw *fkw;
	const char *name;
	struct hlua_reg_filter *reg_flt= NULL;
	int flt_ref, fun_ref;
	int len;

	MAY_LJMP(check_args(L, 3, "register_filter"));

	if (hlua_gethlua(L)) {
		/* runtime processing */
		WILL_LJMP(luaL_error(L, "register_filter: not available outside of body context"));
	}

	/* First argument : filter name. */
	name = MAY_LJMP(luaL_checkstring(L, 1));

	/* Second argument : The filter class */
	flt_ref = MAY_LJMP(hlua_checktable(L, 2));

	/* Third argument : lua function. */
	fun_ref = MAY_LJMP(hlua_checkfunction(L, 3));

	trash = get_trash_chunk();
	chunk_printf(trash, "lua.%s", name);
	fkw = flt_find_kw(trash->area);
	if (fkw != NULL) {
		reg_flt = fkw->private;
		if (reg_flt->flt_ref[hlua_state_id] != -1 ||  reg_flt->fun_ref[hlua_state_id] != -1) {
			ha_warning("Trying to register filter 'lua.%s' more than once. "
				   "This will become a hard error in version 2.5.\n", name);
			if (reg_flt->flt_ref[hlua_state_id] != -1)
				hlua_unref(L, reg_flt->flt_ref[hlua_state_id]);
			if (reg_flt->fun_ref[hlua_state_id] != -1)
				hlua_unref(L, reg_flt->fun_ref[hlua_state_id]);
		}
		reg_flt->flt_ref[hlua_state_id] = flt_ref;
		reg_flt->fun_ref[hlua_state_id] = fun_ref;
		return 0;
	}

	fkl = calloc(1, sizeof(*fkl) + sizeof(struct flt_kw) * 2);
	if (!fkl)
		goto alloc_error;
	fkl->scope = "HLUA";

	reg_flt = new_hlua_reg_filter(name);
	if (!reg_flt)
		goto alloc_error;

	reg_flt->flt_ref[hlua_state_id] = flt_ref;
	reg_flt->fun_ref[hlua_state_id] = fun_ref;

	/* The filter keyword */
	len = strlen("lua.") + strlen(name) + 1;
	fkl->kw[0].kw = calloc(1, len);
	if (!fkl->kw[0].kw)
		goto alloc_error;

	snprintf((char *)fkl->kw[0].kw, len, "lua.%s", name);

	fkl->kw[0].parse = hlua_filter_parse_fct;
	fkl->kw[0].private = reg_flt;
	memset(&fkl->kw[1], 0, sizeof(*fkl->kw));

	/* Register this new filter */
	flt_register_keywords(fkl);

	return 0;

  alloc_error:
	release_hlua_reg_filter(reg_flt);
	hlua_unref(L, flt_ref);
	hlua_unref(L, fun_ref);
	ha_free(&fkl);
	WILL_LJMP(luaL_error(L, "Lua out of memory error."));
	return 0; /* Never reached */
}

static int hlua_read_timeout(char **args, int section_type, struct proxy *curpx,
                             const 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 == PARSE_TIME_OVER) {
		memprintf(err, "timer overflow in argument <%s> to <%s> (maximum value is 2147483647 ms or ~24.8 days)",
			  args[1], args[0]);
		return -1;
	}
	else if (error == PARSE_TIME_UNDER) {
		memprintf(err, "timer underflow in argument <%s> to <%s> (minimum non-null value is 1 ms)",
			  args[1], args[0]);
		return -1;
	}
	else if (error) {
		memprintf(err, "%s: invalid timeout", args[0]);
		return -1;
	}
	return 0;
}

static int hlua_burst_timeout(char **args, int section_type, struct proxy *curpx,
                              const struct proxy *defpx, const char *file, int line,
                              char **err)
{
	return hlua_read_timeout(args, section_type, curpx, defpx,
	                         file, line, err, &hlua_timeout_burst);
}

static int hlua_session_timeout(char **args, int section_type, struct proxy *curpx,
                                const 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,
                             const 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_applet_timeout(char **args, int section_type, struct proxy *curpx,
                               const struct proxy *defpx, const char *file, int line,
                               char **err)
{
	return hlua_read_timeout(args, section_type, curpx, defpx,
	                         file, line, err, &hlua_timeout_applet);
}

static int hlua_forced_yield(char **args, int section_type, struct proxy *curpx,
                             const 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;
}

static int hlua_parse_maxmem(char **args, int section_type, struct proxy *curpx,
                             const struct proxy *defpx, const char *file, int line,
                             char **err)
{
	char *error;

	if (*(args[1]) == 0) {
		memprintf(err, "'%s' expects an integer argument (Lua memory size in MB).", args[0]);
		return -1;
	}
	hlua_global_allocator.limit = strtoll(args[1], &error, 10) * 1024L * 1024L;
	if (*error != '\0') {
		memprintf(err, "%s: invalid number %s (error at '%c')", args[0], args[1], *error);
		return -1;
	}
	return 0;
}

static int hlua_cfg_parse_log_loggers(char **args, int section_type, struct proxy *curpx,
                              const struct proxy *defpx, const char *file, int line,
                              char **err)
{
	if (too_many_args(1, args, err, NULL))
		return -1;

	if (strcmp(args[1], "on") == 0)
		hlua_log_opts |= HLUA_LOG_LOGGERS_ON;
	else if (strcmp(args[1], "off") == 0)
		hlua_log_opts &= ~HLUA_LOG_LOGGERS_ON;
	else {
		memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]);
		return -1;
	}
	return 0;
}

static int hlua_cfg_parse_log_stderr(char **args, int section_type, struct proxy *curpx,
                                     const struct proxy *defpx, const char *file, int line,
                                    char **err)
{
	if (too_many_args(1, args, err, NULL))
		return -1;

	if (strcmp(args[1], "on") == 0)
		hlua_log_opts = (hlua_log_opts & ~HLUA_LOG_STDERR_MASK) | HLUA_LOG_STDERR_ON;
	else if (strcmp(args[1], "auto") == 0)
		hlua_log_opts = (hlua_log_opts & ~HLUA_LOG_STDERR_MASK) | HLUA_LOG_STDERR_AUTO;
	else if (strcmp(args[1], "off") == 0)
		hlua_log_opts &= ~HLUA_LOG_STDERR_MASK;
	else {
		memprintf(err, "'%s' expects either 'on', 'auto', or 'off' but got '%s'.", args[0], args[1]);
		return -1;
	}
	return 0;
}

static int hlua_cfg_parse_bool_sample_conversion(char **args, int section_type, struct proxy *curpx,
                                                 const struct proxy *defpx, const char *file, int line,
                                                 char **err)
{
	uint8_t set;

	if (too_many_args(1, args, err, NULL))
		return -1;

	if (strcmp(args[1], "normal") == 0)
		set = HLUA_BOOL_SAMPLE_CONVERSION_NORMAL;
	else if (strcmp(args[1], "pre-3.1-bug") == 0)
		set = HLUA_BOOL_SAMPLE_CONVERSION_BUG;
	else {
		memprintf(err, "'%s' expects either 'normal' or 'pre-3.1-bug' but got '%s'.", args[0], args[1]);
		return -1;
	}

	if (hlua_loaded)
		ha_warning("parsing [%s:%d] : %s", file, line, "ignored as some Lua was loaded "
		           "already. \"tune.lua.bool-sample-conversion\" must be set before "
		           "any \"lua-load\" or \"lua-load-per-thread\" directive for it to be "
		           "considered.\n");
	else
		hlua_bool_sample_conversion = set;

	return 0;
}

static int hlua_cfg_parse_openlibs(char **args, int section_type, struct proxy *curpx,
                                   const struct proxy *defpx, const char *file, int line,
                                   char **err)
{
	char *token, *save, *str;
	uint flags = 0;
	int has_none = 0;

	if (too_many_args(1, args, err, NULL))
		return -1;

	if (!*args[1]) {
		memprintf(err, "'%s' expects 'all', 'none', or a comma-separated list of "
		          "libraries: table,io,os,string,math,utf8,package,debug", args[0]);
		return -1;
	}

	if (hlua_loaded) {
		memprintf(err, "'%s' must be set before any \"lua-load\" or "
		          "\"lua-load-per-thread\" directive.", args[0]);
		return -1;
	}

	str = args[1];
	while ((token = strtok_r(str, ",", &save))) {
		int i;

		if (strcmp(token, "all") == 0) {
			flags = HLUA_OPENLIBS_ALL;
		}
		else if (strcmp(token, "none") == 0) {
			has_none = 1;
		}
		else {
			for (i = 0; hlua_openlibs_tbl[i].name; i++) {
				if (strcmp(token, hlua_openlibs_tbl[i].name) == 0) {
					flags |= hlua_openlibs_tbl[i].flag;
					break;
				}
			}
			if (!hlua_openlibs_tbl[i].name) {
				int j;
				memprintf(err, "'%s': unknown library '%s', expected one of: all,none", args[0], token);
				for (j = 0; hlua_openlibs_tbl[j].name; j++)
					memprintf(err, "%s,%s", *err, hlua_openlibs_tbl[j].name);
				return -1;
			}
		}
		str = NULL;
	}

	if (has_none && flags) {
		memprintf(err, "'%s': 'none' cannot be combined with other libraries.", args[0]);
		return -1;
	}

	hlua_openlibs_flags = flags;
	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 function runs with the HAProxy keywords API. It returns -1 if an error
 * occurs, 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.
 *
 * This function can fail with an abort() due to an Lua critical error.
 * We are in the configuration parsing process of HAProxy, this abort() is
 * tolerated.
 */
static int hlua_load_state(char **args, lua_State *L, char **err)
{
	int error;
	int nargs;

	if (ONLY_ONCE()) {
		hlua_loaded = 1;

		/* we know we get there if "lua-load" or "lua-load-per-thread" was
		 * used in the config
		 */
		if (hlua_bool_sample_conversion == HLUA_BOOL_SAMPLE_CONVERSION_UNK) {
			/* hlua_bool_sample_conversion tunable must be explicitly set to
			 * avoid ambiguity, so we raise a warning (but only if lua
			 * is actually used
			 */
			ha_warning("hlua: please set \"tune.lua.bool-sample-conversion\" tunable "
			           "to either \"normal\" or \"pre-3.1-bug\" explicitly to avoid "
			           "ambiguities. This must be set before any \"lua-load\" or "
			           "\"lua-load-per-thread\" directive. Defaulting to \"pre-3.1-bug\".\n");
		}
	}

	/* Just load and compile the file. */
	error = luaL_loadfile(L, args[0]);
	if (error) {
		memprintf(err, "error in Lua file '%s': %s", args[0], hlua_tostring_safe(L, -1));
		lua_pop(L, 1);
		return -1;
	}

	/* Push args in the Lua stack, except the first one which is the filename */
	for (nargs = 1; *(args[nargs]) != 0; nargs++) {
		/* Check stack size. */
		if (!lua_checkstack(L, 1)) {
			memprintf(err, "Lua runtime error while loading arguments: stack is full.");
			return -1;
		}
		lua_pushstring(L, args[nargs]);
	}
	nargs--;

	/* If no syntax error where detected, execute the code. */
	error = lua_pcall(L, nargs, LUA_MULTRET, 0);
	switch (error) {
	case LUA_OK:
		break;
	case LUA_ERRRUN:
		memprintf(err, "Lua runtime error: %s", hlua_tostring_safe(L, -1));
		lua_pop(L, 1);
		return -1;
	case LUA_ERRMEM:
		memprintf(err, "Lua out of memory error");
		return -1;
	case LUA_ERRERR:
		memprintf(err, "Lua message handler error: %s", hlua_tostring_safe(L, -1));
		lua_pop(L, 1);
		return -1;
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 503
	case LUA_ERRGCMM:
		memprintf(err, "Lua garbage collector error: %s", hlua_tostring_safe(L, -1));
		lua_pop(L, 1);
		return -1;
#endif
	default:
		memprintf(err, "Lua unknown error: %s", hlua_tostring_safe(L, -1));
		lua_pop(L, 1);
		return -1;
	}

	return 0;
}

static int hlua_load(char **args, int section_type, struct proxy *curpx,
                     const struct proxy *defpx, const char *file, int line,
                     char **err)
{
	if (*(args[1]) == 0) {
		memprintf(err, "'%s' expects a file name as parameter.", args[0]);
		return -1;
	}

	/* loading for global state */
	hlua_state_id = 0;
	ha_set_thread(NULL);
	return hlua_load_state(&args[1], hlua_states[0], err);
}

static int hlua_load_per_thread(char **args, int section_type, struct proxy *curpx,
                                const struct proxy *defpx, const char *file, int line,
                                char **err)
{
	int len;
	int i;

	if (*(args[1]) == 0) {
		memprintf(err, "'%s' expects a file as parameter.", args[0]);
		return -1;
	}

	if (per_thread_load == NULL) {
		/* allocate the first entry large enough to store the final NULL */
		per_thread_load = calloc(1, sizeof(*per_thread_load));
		if (per_thread_load == NULL) {
			memprintf(err, "out of memory error");
			return -1;
		}
	}

	/* count used entries */
	for (len = 0; per_thread_load[len] != NULL; len++)
		;

	per_thread_load = realloc(per_thread_load, (len + 2) * sizeof(*per_thread_load));
	if (per_thread_load == NULL) {
		memprintf(err, "out of memory error");
		return -1;
	}
	per_thread_load[len + 1] = NULL;

	/* count args excepting the first, allocate array and copy args */
	for (i = 0; *(args[i + 1]) != 0; i++);
	per_thread_load[len] = calloc(i + 1, sizeof(*per_thread_load[len]));
	if (per_thread_load[len] == NULL) {
		memprintf(err, "out of memory error");
		return -1;
	}
	for (i = 1; *(args[i]) != 0; i++) {
		/* first arg is filename */
		if (i == 1 && args[1][0] != '/') {
			char *curpath;
			char *fullpath = NULL;

			/* filename is provided using relative path, store the absolute path
			 * to take current chdir into account for other threads file load
			 * which occur later
			 */
			curpath = getcwd(trash.area, trash.size);
			if (!curpath) {
				memprintf(err, "failed to retrieve cur path");
				return -1;
			}
			per_thread_load[len][i - 1] = memprintf(&fullpath, "%s/%s", curpath, args[1]);
		}
		else
			per_thread_load[len][i - 1] = strdup(args[i]);
		if (per_thread_load[len][i - 1] == NULL) {
			memprintf(err, "out of memory error");
			return -1;
		}
	}
	per_thread_load[len][i - 1] = strdup("");
	if (per_thread_load[len][i - 1]  == NULL) {
		memprintf(err, "out of memory error");
		return -1;
	}

	/* loading for thread 1 only */
	hlua_state_id = 1;
	ha_set_thread(NULL);
	return hlua_load_state(per_thread_load[len], hlua_states[1], err);
}

/* Prepend the given <path> followed by a semicolon to the `package.<type>` variable
 * in the given <ctx>.
 */
static int hlua_prepend_path(lua_State *L, char *type, char *path)
{
	lua_getglobal(L, "package"); /* push package variable   */
	lua_pushstring(L, path);     /* push given path         */
	lua_pushstring(L, ";");      /* push semicolon          */
	lua_getfield(L, -3, type);   /* push old path           */
	lua_concat(L, 3);            /* concatenate to new path */
	lua_setfield(L, -2, type);   /* store new path          */
	lua_pop(L, 1);               /* pop package variable    */

	return 0;
}

static int hlua_config_prepend_path(char **args, int section_type, struct proxy *curpx,
                                    const struct proxy *defpx, const char *file, int line,
                                    char **err)
{
	char *path;
	char *type = "path";
	struct prepend_path *p = NULL;
	size_t i;

	if (too_many_args(2, args, err, NULL)) {
		goto err;
	}

	if (!(*args[1])) {
		memprintf(err, "'%s' expects to receive a <path> as argument", args[0]);
		goto err;
	}
	path = args[1];

	if (*args[2]) {
		if (strcmp(args[2], "path") != 0 && strcmp(args[2], "cpath") != 0) {
			memprintf(err, "'%s' expects <type> to either be 'path' or 'cpath'", args[0]);
			goto err;
		}
		type = args[2];
	}

	p = calloc(1, sizeof(*p));
	if (p == NULL) {
		memprintf(err, "memory allocation failed");
		goto err;
	}
	p->path = strdup(path);
	if (p->path == NULL) {
		memprintf(err, "memory allocation failed");
		goto err2;
	}
	p->type = strdup(type);
	if (p->type == NULL) {
		memprintf(err, "memory allocation failed");
		goto err2;
	}
	LIST_APPEND(&prepend_path_list, &p->l);

	/* Handle the global state and the per-thread state for the first
	 * thread. The remaining threads will be initialized based on
	 * prepend_path_list.
	 */
	for (i = 0; i < 2; i++) {
		lua_State *L = hlua_states[i];
		const char *error;

		if (setjmp(safe_ljmp_env) != 0) {
			lua_atpanic(L, hlua_panic_safe);
			if (lua_type(L, -1) == LUA_TSTRING)
				error = hlua_tostring_safe(L, -1);
			else
				error = "critical error";
			fprintf(stderr, "lua-prepend-path: %s.\n", error);
			exit(1);
		} else {
			lua_atpanic(L, hlua_panic_ljmp);
		}

		hlua_prepend_path(L, type, path);

		lua_atpanic(L, hlua_panic_safe);
	}

	return 0;

err2:
	free(p->type);
	free(p->path);
err:
	free(p);
	return -1;
}

/* configuration keywords declaration */
static struct cfg_kw_list cfg_kws = {{ },{
	{ CFG_GLOBAL, "lua-prepend-path",         hlua_config_prepend_path },
	{ CFG_GLOBAL, "lua-load",                 hlua_load },
	{ CFG_GLOBAL, "lua-load-per-thread",      hlua_load_per_thread },
	{ CFG_GLOBAL, "tune.lua.session-timeout", hlua_session_timeout },
	{ CFG_GLOBAL, "tune.lua.task-timeout",    hlua_task_timeout },
	{ CFG_GLOBAL, "tune.lua.service-timeout", hlua_applet_timeout },
	{ CFG_GLOBAL, "tune.lua.burst-timeout",   hlua_burst_timeout },
	{ CFG_GLOBAL, "tune.lua.forced-yield",    hlua_forced_yield },
	{ CFG_GLOBAL, "tune.lua.maxmem",          hlua_parse_maxmem },
	{ CFG_GLOBAL, "tune.lua.log.loggers",     hlua_cfg_parse_log_loggers },
	{ CFG_GLOBAL, "tune.lua.log.stderr",      hlua_cfg_parse_log_stderr },
	{ CFG_GLOBAL, "tune.lua.bool-sample-conversion", hlua_cfg_parse_bool_sample_conversion },
	{ CFG_GLOBAL, "tune.lua.openlibs",               hlua_cfg_parse_openlibs },
	{ 0, NULL, NULL },
}};

INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);

#ifdef USE_OPENSSL

/*
 * This function replace a ckch_store by another one, and rebuild the ckch_inst and all its dependencies.
 * It does the sam as "cli_io_handler_commit_cert" but for lua, the major
 * difference is that the yield in lua and for the CLI is not handled the same
 * way.
 */
__LJMP static int hlua_ckch_commit_yield(lua_State *L, int status, lua_KContext ctx)
{
	struct ckch_inst **lua_ckchi = lua_touserdata(L, -1);
	struct ckch_store **lua_ckchs = lua_touserdata(L, -2);
	struct ckch_store *old_ckchs = lua_ckchs[0];
	struct ckch_store *new_ckchs = lua_ckchs[1];
	struct hlua *hlua;
	char *err = NULL;
	int y = 1;
	int retval = 0;

	hlua = hlua_gethlua(L);

	/* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
	retval = ckch_store_rebuild_instances(old_ckchs, new_ckchs, lua_ckchi,
					      hlua ? 10 : -1, &y, &err);

	if (retval < 0)
		goto error;
	else if (retval == 0) {
		/* yield */
		task_wakeup(hlua->task, TASK_WOKEN_MSG);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_ckch_commit_yield, TICK_ETERNITY, 0));
	}

	/* The generation is finished, we can insert everything */
	ckch_store_replace(old_ckchs, new_ckchs);

	lua_pop(L, 2); /* pop the lua_ckchs and ckchi */

	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);

	return 0;

error:
	ckch_store_free(new_ckchs);
	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
	hlua_pushfstring_safe(L, "%s", err);
	free(err);
	WILL_LJMP(lua_error(L));

	return 0;
}

/*
 * Replace a ckch_store <filename> in the ckchs_tree with a ckch_store created
 * from the table in parameter.
 *
 * This is equivalent to  "set ssl cert" + "commit ssl cert" over the CLI, which
 * means it does not need to have a transaction since everything is done in the
 * same function.
 *
 * CertCache.set{filename="", crt="", key="", sctl="", ocsp="", issuer=""}
 *
 */
__LJMP static int hlua_ckch_set(lua_State *L)
{
	struct hlua *hlua;
	struct ckch_inst **lua_ckchi;
	struct ckch_store **lua_ckchs;
	struct ckch_store *old_ckchs = NULL;
	struct ckch_store *new_ckchs = NULL;
	int errcode = 0;
	char *err = NULL;
	struct cert_exts *cert_ext = NULL;
	char *filename;
	struct ckch_data *data;
	int ret;

	if (lua_type(L, -1) != LUA_TTABLE)
		WILL_LJMP(luaL_error(L, "'CertCache.set' needs a table as argument"));

	hlua = hlua_gethlua(L);
	if (hlua && HLUA_CANT_YIELD(hlua)) {
		/* using hlua_ckch_set() during runtime from a context that
		 * doesn't allow yielding (e.g.: fetches) is not supported
		 * as it may cause contention.
		 */
		WILL_LJMP(luaL_error(L, "Cannot use CertCache.set from a "
		                        "non-yield capable runtime context"));
	}

	/* FIXME: this should not return an error but should come back later */
	if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
		WILL_LJMP(luaL_error(L, "CertCache already under lock"));

	ret = lua_getfield(L, -1, "filename");
	if (ret != LUA_TSTRING) {
		memprintf(&err, "%sNo filename specified!", err ? err : "");
		errcode |= ERR_ALERT | ERR_FATAL;
		goto end;
	}
	filename = (char *)lua_tostring(L, -1);


	/* look for the filename in the tree */
	old_ckchs = ckchs_lookup(filename);
	if (!old_ckchs) {
		memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!", err ? err : "");
		errcode |= ERR_ALERT | ERR_FATAL;
		goto end;
	}
	/* TODO: handle extra_files_noext */

	new_ckchs = ckchs_dup(old_ckchs);
	if (!new_ckchs) {
		memprintf(&err, "%sCannot allocate memory!", err ? err : "");
		errcode |= ERR_ALERT | ERR_FATAL;
		goto end;
	}

	data = new_ckchs->data;

	/* loop on the field in the table, which have the same name as the
	 * possible extensions of files */
	lua_pushnil(L);
	while (lua_next(L, 1)) {
		int i;
		const char *field = lua_tostring(L, -2);
		char *payload = (char *)lua_tostring(L, -1);

		if (!field || strcmp(field, "filename") == 0) {
			lua_pop(L, 1);
			continue;
		}

		for (i = 0; field && cert_exts[i].ext != NULL; i++) {
			if (strcmp(field, cert_exts[i].ext) == 0) {
				cert_ext = &cert_exts[i];
				break;
			}
		}

		/* this is the default type, the field is not supported */
		if (cert_ext == NULL) {
			memprintf(&err, "%sUnsupported field '%s'", err ? err : "", field);
			errcode |= ERR_ALERT | ERR_FATAL;
			goto end;
		}
#ifdef HAVE_SSL_OCSP
		/* Reset the OCSP CID */
		if (cert_ext->type == CERT_TYPE_PEM || cert_ext->type == CERT_TYPE_KEY ||
		    cert_ext->type == CERT_TYPE_ISSUER) {
			OCSP_CERTID_free(new_ckchs->data->ocsp_cid);
			new_ckchs->data->ocsp_cid = NULL;
		}
#endif
		/* apply the change on the duplicate */
		if (cert_ext->load(filename, payload, data, &err) != 0) {
			memprintf(&err, "%sCan't load the payload for '%s'", err ? err : "", cert_ext->ext);
			errcode |= ERR_ALERT | ERR_FATAL;
			goto end;
		}
		lua_pop(L, 1);
	}

	/* store the pointers on the lua stack */
        lua_ckchs = lua_newuserdata(L, sizeof(struct ckch_store *) * 2);
	lua_ckchs[0] = old_ckchs;
	lua_ckchs[1] = new_ckchs;
	lua_ckchi = lua_newuserdata(L, sizeof(struct ckch_inst *));
	*lua_ckchi = NULL;

	if (hlua) {
		/* yield right away to let hlua_ckch_commit_yield() benefit from
		 * a fresh task cycle on next wakeup
		 */
		task_wakeup(hlua->task, TASK_WOKEN_MSG);
		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_ckch_commit_yield, TICK_ETERNITY, 0));
	} else {
		/* body/init context: yielding not available, perform the commit as a
		 * 1-shot operation (may be slow, but haproxy process is starting so
		 * it is acceptable)
		 */
		hlua_ckch_commit_yield(L, LUA_OK, 0);
	}

end:
	HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);

	if (errcode & ERR_CODE) {
		ckch_store_free(new_ckchs);
		hlua_pushfstring_safe(L, "%s", err);
		free(err);
		WILL_LJMP(lua_error(L));
	}
	free(err);

	return 0;
}

#else

__LJMP static int hlua_ckch_set(lua_State *L)
{
	WILL_LJMP(luaL_error(L, "'CertCache.set' needs an HAProxy built with OpenSSL"));

	return 0;
}
#endif /* ! USE_OPENSSL */



/* This function can fail with an abort() due to an Lua critical error.
 * We are in the initialisation process of HAProxy, this abort() is
 * tolerated.
 */
int hlua_post_init_state(lua_State *L)
{
	struct hlua_init_function *init;
	const char *msg;
	enum hlua_exec ret;
	const char *error;
	const char *kind;
	const char *trace;
	int return_status = 1;
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
	int nres;
#endif

	/* disable memory limit checks if limit is not set */
	if (!hlua_global_allocator.limit)
		hlua_global_allocator.limit = ~hlua_global_allocator.limit;

	/* Call post initialisation function in safe environment. */
	if (setjmp(safe_ljmp_env) != 0) {
		lua_atpanic(L, hlua_panic_safe);
		if (lua_type(L, -1) == LUA_TSTRING)
			error = hlua_tostring_safe(L, -1);
		else
			error = "critical error";
		fprintf(stderr, "Lua post-init: %s.\n", error);
		exit(1);
	} else {
		lua_atpanic(L, hlua_panic_ljmp);
	}

	list_for_each_entry(init, &hlua_init_functions[hlua_state_id], l) {
		hlua_pushref(L, init->function_ref);
		/* function ref should be released right away since it was pushed
		 * on the stack and will not be used anymore
		 */
		hlua_unref(L, init->function_ref);

#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
		ret = lua_resume(L, NULL, 0, &nres);
#else
		ret = lua_resume(L, NULL, 0);
#endif
		kind = NULL;
		switch (ret) {

		case LUA_OK:
			lua_pop(L, -1);
			break;

		case LUA_ERRERR:
			kind = "message handler error";
			__fallthrough;
		case LUA_ERRRUN:
			if (!kind)
				kind = "runtime error";
			msg = hlua_tostring_safe(L, -1);
			trace = hlua_traceback(L, ", ");
			if (msg)
				ha_alert("Lua init: %s: '%s' from %s\n", kind, msg, trace);
			else
				ha_alert("Lua init: unknown %s from %s\n", kind, trace);

			lua_settop(L, 0); /* Empty the stack. */
			return_status = 0;
			break;

		default:
			/* Unknown error */
			kind = "Unknown error";
			__fallthrough;
		case LUA_YIELD:
			/* yield is not configured at this step, this state doesn't happen */
			if (!kind)
				kind = "yield not allowed";
			__fallthrough;
		case LUA_ERRMEM:
			if (!kind)
				kind = "out of memory error";
			lua_settop(L, 0); /* Empty the stack. */
			trace = hlua_traceback(L, ", ");
			ha_alert("Lua init: %s: %s\n", kind, trace);
			return_status = 0;
			break;
		}
		if (!return_status)
			break;
	}

	lua_atpanic(L, hlua_panic_safe);
	return return_status;
}

int hlua_post_init()
{
	int ret;
	int i;
	int errors;
	char *err = NULL;
	struct hlua_function *fcn;
	struct hlua_reg_filter *reg_flt;

	hlua_body = 0;

#if defined(USE_OPENSSL)
	/* Initialize SSL server. */
	if (socket_ssl->xprt->prepare_srv) {
		int saved_used_backed = global.ssl_used_backend;
		// don't affect maxconn automatic computation
		socket_ssl->xprt->prepare_srv(socket_ssl);
		global.ssl_used_backend = saved_used_backed;
	}
#endif

	/* Perform post init of common thread */
	hlua_state_id = 0;
	ha_set_thread(&ha_thread_info[0]);
	ret = hlua_post_init_state(hlua_states[hlua_state_id]);
	if (ret == 0)
		return 0;

	/* init remaining lua states and load files */
	for (hlua_state_id = 2; hlua_state_id < global.nbthread + 1; hlua_state_id++) {

		/* set thread context */
		ha_set_thread(&ha_thread_info[hlua_state_id - 1]);

		/* Init lua state */
		hlua_states[hlua_state_id] = hlua_init_state(hlua_state_id);

		/* Load lua files */
		for (i = 0; per_thread_load && per_thread_load[i]; i++) {
			ret = hlua_load_state(per_thread_load[i], hlua_states[hlua_state_id], &err);
			if (ret != 0) {
				ha_alert("Lua init: %s\n", err);
				return 0;
			}
		}
	}

	/* Reset thread context */
	ha_set_thread(NULL);

	/* Execute post init for all states */
	for (hlua_state_id = 1; hlua_state_id < global.nbthread + 1; hlua_state_id++) {

		/* set thread context */
		ha_set_thread(&ha_thread_info[hlua_state_id - 1]);

		/* run post init */
		ret = hlua_post_init_state(hlua_states[hlua_state_id]);
		if (ret == 0)
			return 0;
	}

	/* Reset thread context */
	ha_set_thread(NULL);

	/* control functions registering. Each function must have:
	 *  - only the function_ref[0] set positive and all other to -1
	 *  - only the function_ref[0] set to -1 and all other positive
	 * This ensure a same reference is not used both in shared
	 * lua state and thread dedicated lua state. Note: is the case
	 * reach, the shared state is priority, but the bug will be
	 * complicated to found for the end user.
	 */
	errors = 0;
	list_for_each_entry(fcn, &referenced_functions, l) {
		ret = 0;
		for (i = 1; i < global.nbthread + 1; i++) {
			if (fcn->function_ref[i] == -1)
				ret--;
			else
				ret++;
		}
		if (abs(ret) != global.nbthread) {
			ha_alert("Lua function '%s' is not referenced in all thread. "
			         "Expect function in all thread or in none thread.\n", fcn->name);
			errors++;
			continue;
		}

		if ((fcn->function_ref[0] == -1) == (ret < 0)) {
			ha_alert("Lua function '%s' is referenced both ins shared Lua context (through lua-load) "
			         "and per-thread Lua context (through lua-load-per-thread). these two context "
			         "exclusive.\n", fcn->name);
			errors++;
		}
	}

	/* Do the same with registered filters */
	list_for_each_entry(reg_flt, &referenced_filters, l) {
		ret = 0;
		for (i = 1; i < global.nbthread + 1; i++) {
			if (reg_flt->flt_ref[i] == -1)
				ret--;
			else
				ret++;
		}
		if (abs(ret) != global.nbthread) {
			ha_alert("Lua filter '%s' is not referenced in all thread. "
			         "Expect function in all thread or in none thread.\n", reg_flt->name);
			errors++;
			continue;
		}

		if ((reg_flt->flt_ref[0] == -1) == (ret < 0)) {
			ha_alert("Lua filter '%s' is referenced both ins shared Lua context (through lua-load) "
			         "and per-thread Lua context (through lua-load-per-thread). these two context "
			         "exclusive.\n", reg_flt->name);
			errors++;
		}
	}


	if (errors > 0)
		return 0;

	/* after this point, this global will no longer be used, so set to
	 * -1 in order to have probably a segfault if someone use it
	 */
	hlua_state_id = -1;

	return 1;
}

/* The memory allocator used by the Lua stack. <ud> is a pointer to the
 * allocator's context. <ptr> is the pointer to alloc/free/realloc. <osize>
 * is the previously allocated size or the kind of object in case of a new
 * allocation. <nsize> is the requested new size. A new allocation is
 * indicated by <ptr> being NULL. A free is indicated by <nsize> being
 * zero. This one verifies that the limits are respected but is optimized
 * for the fast case where limits are not used, hence stats are not updated.
 *
 * Warning: while this API ressembles glibc's realloc() a lot, glibc surpasses
 * POSIX by making realloc(ptr,0) an effective free(), but others do not do
 * that and will simply allocate zero as if it were the result of malloc(0),
 * so mapping this onto realloc() will lead to memory leaks on non-glibc
 * systems.
 */
static void *hlua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
	struct hlua_mem_allocator *zone = ud;
	size_t limit, old, new;

	/* a limit of ~0 means unlimited and boot complete, so there's no need
	 * for accounting anymore.
	 */
	if (likely(~zone->limit == 0)) {
		if (!nsize)
			ha_free(&ptr);
		else
			ptr = realloc(ptr, nsize);
		return ptr;
	}

	if (!ptr)
		osize = 0;

	/* enforce strict limits across all threads */
	limit = zone->limit;
	old = _HA_ATOMIC_LOAD(&zone->allocated);
	do {
		new = old + nsize - osize;
		if (unlikely(nsize && limit && new > limit))
			return NULL;
	} while (!_HA_ATOMIC_CAS(&zone->allocated, &old, new));

	if (!nsize)
		ha_free(&ptr);
	else
		ptr = realloc(ptr, nsize);

	if (unlikely(!ptr && nsize)) // failed
		_HA_ATOMIC_SUB(&zone->allocated, nsize - osize);

	__ha_barrier_atomic_store();
	return ptr;
}

/* This function can fail with an abort() due to a Lua critical error.
 * We are in the initialisation process of HAProxy, this abort() is
 * tolerated.
 */
lua_State *hlua_init_state(int thread_num)
{
	int i;
	int idx;
	struct sample_fetch *sf;
	struct sample_conv *sc;
	char *p;
	const char *error_msg;
	void **context;
	lua_State *L;
	struct prepend_path *pp;

	/* Init main lua stack. */
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 505
	L = lua_newstate(hlua_alloc, &hlua_global_allocator, luaL_makeseed(0));
#else
	L = lua_newstate(hlua_alloc, &hlua_global_allocator);
#endif

	if (!L) {
		fprintf(stderr,
		        "Lua init: critical error: lua_newstate() returned NULL."
		        " This may possibly be caused by a memory allocation error.\n");
		exit(1);
	}

	/* Initialise Lua context to NULL */
	context = lua_getextraspace(L);
	*context = NULL;

	/* From this point, until the end of the initialisation function,
	 * the Lua function can fail with an abort. We are in the initialisation
	 * process of HAProxy, this abort() is tolerated.
	 */

	/* Call post initialisation function in safe environment. */
	if (setjmp(safe_ljmp_env) != 0) {
		lua_atpanic(L, hlua_panic_safe);
		if (lua_type(L, -1) == LUA_TSTRING)
			error_msg = hlua_tostring_safe(L, -1);
		else
			error_msg = "critical error";
		fprintf(stderr, "Lua init: %s.\n", error_msg);
		exit(1);
	} else {
		lua_atpanic(L, hlua_panic_ljmp);
	}

	/* Initialise lua: open standard libraries according to tune.lua.openlibs.
	 * The base and coroutine libraries are always loaded: base provides the
	 * core Lua functions HAProxy relies on; coroutine.create() is overridden
	 * by HAProxy's own safe wrapper right after.
	 */
	if (hlua_openlibs_flags == HLUA_OPENLIBS_ALL) {
		luaL_openlibs(L);
	} else {
		int i;

		luaL_requiref(L, LUA_GNAME,     luaopen_base,      1); lua_pop(L, 1);
		luaL_requiref(L, LUA_COLIBNAME, luaopen_coroutine, 1); lua_pop(L, 1);
		for (i = 0; hlua_openlibs_tbl[i].name; i++) {
			if (hlua_openlibs_flags & hlua_openlibs_tbl[i].flag) {
				luaL_requiref(L, hlua_openlibs_tbl[i].name, hlua_openlibs_tbl[i].open, 1);
				lua_pop(L, 1);
			}
		}
	}
#define HLUA_PREPEND_PATH_TOSTRING1(x) #x
#define HLUA_PREPEND_PATH_TOSTRING(x) HLUA_PREPEND_PATH_TOSTRING1(x)
#ifdef HLUA_PREPEND_PATH
	hlua_prepend_path(L, "path", HLUA_PREPEND_PATH_TOSTRING(HLUA_PREPEND_PATH));
#endif
#ifdef HLUA_PREPEND_CPATH
	hlua_prepend_path(L, "cpath", HLUA_PREPEND_PATH_TOSTRING(HLUA_PREPEND_CPATH));
#endif
#undef HLUA_PREPEND_PATH_TOSTRING
#undef HLUA_PREPEND_PATH_TOSTRING1

	/* Apply configured prepend path */
	list_for_each_entry(pp, &prepend_path_list, l)
		hlua_prepend_path(L, pp->type, pp->path);

	/*
	 * Override some lua functions.
	 *
	 */

	/* push our "safe" coroutine.create() function */
	lua_getglobal(L, "coroutine");
	lua_pushcclosure(L, hlua_coroutine_create, 0);
	lua_setfield(L, -2, "create");

	/*
	 *
	 * Create "core" object.
	 *
	 */

	/* This table entry is the object "core" base. */
	lua_newtable(L);

	/* set the thread id */
	hlua_class_const_int(L, "thread", thread_num);

	/* Push the loglevel constants. */
		hlua_class_const_int(L, "silent", -1);
	for (i = 0; i < NB_LOG_LEVELS; i++)
		hlua_class_const_int(L, log_levels[i], i);

	/* Register special functions. */
	hlua_class_function(L, "register_init", hlua_register_init);
	hlua_class_function(L, "register_task", hlua_register_task);
	hlua_class_function(L, "register_fetches", hlua_register_fetches);
	hlua_class_function(L, "register_converters", hlua_register_converters);
	hlua_class_function(L, "register_action", hlua_register_action);
	hlua_class_function(L, "register_service", hlua_register_service);
	hlua_class_function(L, "register_cli", hlua_register_cli);
	hlua_class_function(L, "register_filter", hlua_register_filter);
	hlua_class_function(L, "yield", hlua_yield);
	hlua_class_function(L, "wait", hlua_wait);
	hlua_class_function(L, "set_nice", hlua_set_nice);
	hlua_class_function(L, "sleep", hlua_sleep);
	hlua_class_function(L, "msleep", hlua_msleep);
	hlua_class_function(L, "add_acl", hlua_add_acl);
	hlua_class_function(L, "del_acl", hlua_del_acl);
	hlua_class_function(L, "set_map", hlua_set_map);
	hlua_class_function(L, "del_map", hlua_del_map);
	hlua_class_function(L, "get_patref", hlua_get_patref);
	hlua_class_function(L, "get_var", hlua_core_get_var);
	hlua_class_function(L, "tcp", hlua_socket_new);
	hlua_class_function(L, "httpclient", hlua_httpclient_new);
	hlua_class_function(L, "event_sub", hlua_event_global_sub);
	hlua_class_function(L, "log", hlua_log);
	hlua_class_function(L, "Debug", hlua_log_debug);
	hlua_class_function(L, "Info", hlua_log_info);
	hlua_class_function(L, "Warning", hlua_log_warning);
	hlua_class_function(L, "Alert", hlua_log_alert);
	hlua_class_function(L, "done", hlua_done);
	hlua_class_function(L, "use_native_mailers_config", hlua_use_native_mailers_config);
	hlua_fcn_reg_core_fcn(L);

	lua_setglobal(L, "core");

	/*
	 *
	 * Create "act" object.
	 *
	 */

	/* This table entry is the object "act" base. */
	lua_newtable(L);

	/* push action return constants */
	hlua_class_const_int(L, "CONTINUE", ACT_RET_CONT);
	hlua_class_const_int(L, "STOP",     ACT_RET_STOP);
	hlua_class_const_int(L, "YIELD",    ACT_RET_YIELD);
	hlua_class_const_int(L, "ERROR",    ACT_RET_ERR);
	hlua_class_const_int(L, "DONE",     ACT_RET_DONE);
	hlua_class_const_int(L, "DENY",     ACT_RET_DENY);
	hlua_class_const_int(L, "ABORT",    ACT_RET_ABRT);
	hlua_class_const_int(L, "INVALID",  ACT_RET_INV);

	hlua_class_function(L, "wake_time", hlua_set_wake_time);

	lua_setglobal(L, "act");

	/*
	 *
	 * Create "Filter" object.
	 *
	 */

	/* This table entry is the object "filter" base. */
	lua_newtable(L);

	/* push flags and constants */
	hlua_class_const_int(L, "CONTINUE", 1);
	hlua_class_const_int(L, "WAIT",     0);
	hlua_class_const_int(L, "ERROR",    -1);

	hlua_class_const_int(L, "FLT_CFG_FL_HTX", FLT_CFG_FL_HTX);

	hlua_class_function(L, "wake_time", hlua_set_wake_time);
	hlua_class_function(L, "register_data_filter", hlua_register_data_filter);
	hlua_class_function(L, "unregister_data_filter", hlua_unregister_data_filter);

	lua_setglobal(L, "filter");

	/*
	 *
	 * Register class Map
	 *
	 */

	/* This table entry is the object "Map" base. */
	lua_newtable(L);

	/* register pattern types. */
	for (i=0; i<PAT_MATCH_NUM; i++)
		hlua_class_const_int(L, pat_match_names[i], i);
	for (i=0; i<PAT_MATCH_NUM; i++) {
		snprintf(trash.area, trash.size, "_%s", pat_match_names[i]);
		hlua_class_const_int(L, trash.area, i);
	}

	/* register constructor. */
	hlua_class_function(L, "new", hlua_map_new);

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Register . */
	hlua_class_function(L, "lookup", hlua_map_lookup);
	hlua_class_function(L, "slookup", hlua_map_slookup);

	lua_rawset(L, -3);

	/* Register previous table in the registry with reference and named entry.
	 * The function hlua_register_metatable() pops the stack, so we
	 * previously create a copy of the table.
	 */
	lua_pushvalue(L, -1); /* Copy the -1 entry and push it on the stack. */
	class_map_ref = hlua_register_metatable(L, CLASS_MAP);

	/* Assign the metatable to the mai Map object. */
	lua_setmetatable(L, -2);

	/* Set a name to the table. */
	lua_setglobal(L, "Map");

	/*
	 *
	 * Register "CertCache" class
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);
	/* Register */
	hlua_class_function(L, "set",         hlua_ckch_set);
	lua_setglobal(L, CLASS_CERTCACHE); /* Create global object called CertCache */

	/*
	 *
	 * Register class Channel
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Register . */
	hlua_class_function(L, "data",        hlua_channel_get_data);
	hlua_class_function(L, "line",        hlua_channel_get_line);
	hlua_class_function(L, "set",         hlua_channel_set_data);
	hlua_class_function(L, "remove",      hlua_channel_del_data);
	hlua_class_function(L, "append",      hlua_channel_append);
	hlua_class_function(L, "prepend",     hlua_channel_prepend);
	hlua_class_function(L, "insert",      hlua_channel_insert_data);
	hlua_class_function(L, "send",        hlua_channel_send);
	hlua_class_function(L, "forward",     hlua_channel_forward);
	hlua_class_function(L, "input",       hlua_channel_get_in_len);
	hlua_class_function(L, "output",      hlua_channel_get_out_len);
	hlua_class_function(L, "may_recv",    hlua_channel_may_recv);
	hlua_class_function(L, "is_full",     hlua_channel_is_full);
	hlua_class_function(L, "is_resp",     hlua_channel_is_resp);

	/* Deprecated API */
	hlua_class_function(L, "get",         hlua_channel_get);
	hlua_class_function(L, "dup",         hlua_channel_dup);
	hlua_class_function(L, "getline",     hlua_channel_getline);
	hlua_class_function(L, "get_in_len",  hlua_channel_get_in_len);
	hlua_class_function(L, "get_out_len", hlua_channel_get_out_len);

	lua_rawset(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_channel_ref = hlua_register_metatable(L, CLASS_CHANNEL);

	/*
	 *
	 * Register class Fetches
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Browse existing fetches and create the associated
	 * object method.
	 */
	sf = NULL;
	while ((sf = sample_fetch_getnext(sf, &idx)) != NULL) {
		/* gL.Tua doesn't support '.' and '-' in the function names, replace it
		 * by an underscore.
		 */
		strlcpy2(trash.area, sf->kw, trash.size);
		for (p = trash.area; *p; p++)
			if (*p == '.' || *p == '-' || *p == '+')
				*p = '_';

		/* Register the function. */
		lua_pushstring(L, trash.area);
		lua_pushlightuserdata(L, sf);
		lua_pushcclosure(L, hlua_run_sample_fetch, 1);
		lua_rawset(L, -3);
	}

	lua_rawset(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_fetches_ref = hlua_register_metatable(L, CLASS_FETCHES);

	/*
	 *
	 * Register class Converters
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Browse existing converters and create the associated
	 * object method.
	 */
	sc = NULL;
	while ((sc = sample_conv_getnext(sc, &idx)) != NULL) {
		/* gL.Tua doesn't support '.' and '-' in the function names, replace it
		 * by an underscore.
		 */
		strlcpy2(trash.area, sc->kw, trash.size);
		for (p = trash.area; *p; p++)
			if (*p == '.' || *p == '-' || *p == '+')
				*p = '_';

		/* Register the function. */
		lua_pushstring(L, trash.area);
		lua_pushlightuserdata(L, sc);
		lua_pushcclosure(L, hlua_run_sample_conv, 1);
		lua_rawset(L, -3);
	}

	lua_rawset(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_converters_ref = hlua_register_metatable(L, CLASS_CONVERTERS);

	/*
	 *
	 * Register class HTTP
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Register Lua functions. */
	hlua_class_function(L, "req_get_headers",hlua_http_req_get_headers);
	hlua_class_function(L, "req_del_header", hlua_http_req_del_hdr);
	hlua_class_function(L, "req_rep_header", hlua_http_req_rep_hdr);
	hlua_class_function(L, "req_rep_value",  hlua_http_req_rep_val);
	hlua_class_function(L, "req_add_header", hlua_http_req_add_hdr);
	hlua_class_function(L, "req_set_header", hlua_http_req_set_hdr);
	hlua_class_function(L, "req_set_method", hlua_http_req_set_meth);
	hlua_class_function(L, "req_set_path",   hlua_http_req_set_path);
	hlua_class_function(L, "req_set_query",  hlua_http_req_set_query);
	hlua_class_function(L, "req_set_uri",    hlua_http_req_set_uri);

	hlua_class_function(L, "res_get_headers",hlua_http_res_get_headers);
	hlua_class_function(L, "res_del_header", hlua_http_res_del_hdr);
	hlua_class_function(L, "res_rep_header", hlua_http_res_rep_hdr);
	hlua_class_function(L, "res_rep_value",  hlua_http_res_rep_val);
	hlua_class_function(L, "res_add_header", hlua_http_res_add_hdr);
	hlua_class_function(L, "res_set_header", hlua_http_res_set_hdr);
	hlua_class_function(L, "res_set_status", hlua_http_res_set_status);

	lua_rawset(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_http_ref = hlua_register_metatable(L, CLASS_HTTP);

	/*
	 *
	 * Register class HTTPMessage
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Register Lua functions. */
	hlua_class_function(L, "is_resp",     hlua_http_msg_is_resp);
	hlua_class_function(L, "get_stline",  hlua_http_msg_get_stline);
	hlua_class_function(L, "get_headers", hlua_http_msg_get_headers);
	hlua_class_function(L, "del_header",  hlua_http_msg_del_hdr);
	hlua_class_function(L, "rep_header",  hlua_http_msg_rep_hdr);
	hlua_class_function(L, "rep_value",   hlua_http_msg_rep_val);
	hlua_class_function(L, "add_header",  hlua_http_msg_add_hdr);
	hlua_class_function(L, "set_header",  hlua_http_msg_set_hdr);
	hlua_class_function(L, "set_method",  hlua_http_msg_set_meth);
	hlua_class_function(L, "set_path",    hlua_http_msg_set_path);
	hlua_class_function(L, "set_query",   hlua_http_msg_set_query);
	hlua_class_function(L, "set_uri",     hlua_http_msg_set_uri);
	hlua_class_function(L, "set_status",  hlua_http_msg_set_status);
	hlua_class_function(L, "set_body_len",hlua_http_msg_set_body_len);
	hlua_class_function(L, "is_full",     hlua_http_msg_is_full);
	hlua_class_function(L, "may_recv",    hlua_http_msg_may_recv);
	hlua_class_function(L, "eom",         hlua_http_msg_is_eom);
	hlua_class_function(L, "input",       hlua_http_msg_get_in_len);
	hlua_class_function(L, "output",      hlua_http_msg_get_out_len);

	hlua_class_function(L, "body",        hlua_http_msg_get_body);
	hlua_class_function(L, "set",         hlua_http_msg_set_data);
	hlua_class_function(L, "remove",      hlua_http_msg_del_data);
	hlua_class_function(L, "append",      hlua_http_msg_append);
	hlua_class_function(L, "prepend",     hlua_http_msg_prepend);
	hlua_class_function(L, "insert",      hlua_http_msg_insert_data);
	hlua_class_function(L, "set_eom",     hlua_http_msg_set_eom);
	hlua_class_function(L, "unset_eom",   hlua_http_msg_unset_eom);

	hlua_class_function(L, "send",        hlua_http_msg_send);
	hlua_class_function(L, "forward",     hlua_http_msg_forward);

	lua_rawset(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_http_msg_ref = hlua_register_metatable(L, CLASS_HTTP_MSG);

	/*
	 *
	 * Register class HTTPClient
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);
	lua_pushstring(L, "__index");
	lua_newtable(L);
	hlua_class_function(L, "get",         hlua_httpclient_get);
	hlua_class_function(L, "head",        hlua_httpclient_head);
	hlua_class_function(L, "put",         hlua_httpclient_put);
	hlua_class_function(L, "post",        hlua_httpclient_post);
	hlua_class_function(L, "delete",      hlua_httpclient_delete);
	lua_settable(L, -3); /* Sets the __index entry. */
	/* Register the garbage collector entry. */
	lua_pushstring(L, "__gc");
	lua_pushcclosure(L, hlua_httpclient_gc, 0);
	lua_settable(L, -3); /* Push the last 2 entries in the table at index -3 */



	class_httpclient_ref = hlua_register_metatable(L, CLASS_HTTPCLIENT);
	/*
	 *
	 * Register class AppletTCP
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Register Lua functions. */
	hlua_class_function(L, "getline",   hlua_applet_tcp_getline);
	hlua_class_function(L, "receive",   hlua_applet_tcp_recv);
	hlua_class_function(L, "try_receive", hlua_applet_tcp_try_recv);
	hlua_class_function(L, "send",      hlua_applet_tcp_send);
	hlua_class_function(L, "set_priv",  hlua_applet_tcp_set_priv);
	hlua_class_function(L, "get_priv",  hlua_applet_tcp_get_priv);
	hlua_class_function(L, "set_var",   hlua_applet_tcp_set_var);
	hlua_class_function(L, "unset_var", hlua_applet_tcp_unset_var);
	hlua_class_function(L, "get_var",   hlua_applet_tcp_get_var);

	lua_settable(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_applet_tcp_ref = hlua_register_metatable(L, CLASS_APPLET_TCP);

	/*
	 *
	 * Register class AppletHTTP
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Register Lua functions. */
	hlua_class_function(L, "set_priv",       hlua_applet_http_set_priv);
	hlua_class_function(L, "get_priv",       hlua_applet_http_get_priv);
	hlua_class_function(L, "set_var",        hlua_applet_http_set_var);
	hlua_class_function(L, "unset_var",      hlua_applet_http_unset_var);
	hlua_class_function(L, "get_var",        hlua_applet_http_get_var);
	hlua_class_function(L, "getline",        hlua_applet_http_getline);
	hlua_class_function(L, "receive",        hlua_applet_http_recv);
	hlua_class_function(L, "send",           hlua_applet_http_send);
	hlua_class_function(L, "add_header",     hlua_applet_http_addheader);
	hlua_class_function(L, "set_status",     hlua_applet_http_status);
	hlua_class_function(L, "start_response", hlua_applet_http_start_response);

	lua_settable(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_applet_http_ref = hlua_register_metatable(L, CLASS_APPLET_HTTP);

	/*
	 *
	 * Register class TXN
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

	/* Register Lua functions. */
	hlua_class_function(L, "set_priv",            hlua_set_priv);
	hlua_class_function(L, "get_priv",            hlua_get_priv);
	hlua_class_function(L, "set_var",             hlua_set_var);
	hlua_class_function(L, "unset_var",           hlua_unset_var);
	hlua_class_function(L, "get_var",             hlua_get_var);
	hlua_class_function(L, "done",                hlua_txn_done);
	hlua_class_function(L, "reply",               hlua_txn_reply_new);
	hlua_class_function(L, "set_fc_mark",         hlua_txn_set_fc_mark);
	hlua_class_function(L, "set_fc_tos",          hlua_txn_set_fc_tos);
	hlua_class_function(L, "set_loglevel",        hlua_txn_set_loglevel);
	hlua_class_function(L, "set_mark",            hlua_txn_set_fc_mark); // DEPRECATED, use set_fc_mark
	hlua_class_function(L, "set_tos",             hlua_txn_set_fc_tos);  // DEPRECATED, use set_fc_tos
	hlua_class_function(L, "set_priority_class",  hlua_txn_set_priority_class);
	hlua_class_function(L, "set_priority_offset", hlua_txn_set_priority_offset);
	hlua_class_function(L, "deflog",              hlua_txn_deflog);
	hlua_class_function(L, "log",                 hlua_txn_log);
	hlua_class_function(L, "Debug",               hlua_txn_log_debug);
	hlua_class_function(L, "Info",                hlua_txn_log_info);
	hlua_class_function(L, "Warning",             hlua_txn_log_warning);
	hlua_class_function(L, "Alert",               hlua_txn_log_alert);

	lua_rawset(L, -3);

	/* Register previous table in the registry with reference and named entry. */
	class_txn_ref = hlua_register_metatable(L, CLASS_TXN);

	/*
	 *
	 * Register class reply
	 *
	 */
	lua_newtable(L);
	lua_pushstring(L, "__index");
	lua_newtable(L);
	hlua_class_function(L, "set_status", hlua_txn_reply_set_status);
	hlua_class_function(L, "add_header", hlua_txn_reply_add_header);
	hlua_class_function(L, "del_header", hlua_txn_reply_del_header);
	hlua_class_function(L, "set_body",   hlua_txn_reply_set_body);
	lua_settable(L, -3); /* Sets the __index entry. */
	class_txn_reply_ref = luaL_ref(L, LUA_REGISTRYINDEX);


	/*
	 *
	 * Register class Socket
	 *
	 */

	/* Create and fill the metatable. */
	lua_newtable(L);

	/* Create and fill the __index entry. */
	lua_pushstring(L, "__index");
	lua_newtable(L);

#ifdef USE_OPENSSL
	hlua_class_function(L, "connect_ssl", hlua_socket_connect_ssl);
#endif
	hlua_class_function(L, "connect",     hlua_socket_connect);
	hlua_class_function(L, "send",        hlua_socket_send);
	hlua_class_function(L, "receive",     hlua_socket_receive);
	hlua_class_function(L, "close",       hlua_socket_close);
	hlua_class_function(L, "getpeername", hlua_socket_getpeername);
	hlua_class_function(L, "getsockname", hlua_socket_getsockname);
	hlua_class_function(L, "setoption",   hlua_socket_setoption);
	hlua_class_function(L, "settimeout",  hlua_socket_settimeout);

	lua_rawset(L, -3); /* Push the last 2 entries in the table at index -3 */

	/* Register the garbage collector entry. */
	lua_pushstring(L, "__gc");
	lua_pushcclosure(L, hlua_socket_gc, 0);
	lua_rawset(L, -3); /* Push the last 2 entries in the table at index -3 */

	/* Register previous table in the registry with reference and named entry. */
	class_socket_ref = hlua_register_metatable(L, CLASS_SOCKET);

	lua_atpanic(L, hlua_panic_safe);

	return L;
}

void hlua_init(void) {
	int i;
	char *errmsg = NULL;
#ifdef USE_OPENSSL
	struct srv_kw *kw;
	int tmp_error;
	char *error;
	char *args[] = { /* SSL client configuration. */
		"ssl",
		"verify",
		"none",
		NULL
	};
#endif

	/* Init post init function list head */
	for (i = 0; i < MAX_THREADS + 1; i++)
		LIST_INIT(&hlua_init_functions[i]);

	/* Init state for common/shared lua parts */
	hlua_state_id = 0;
	ha_set_thread(NULL);
	hlua_states[0] = hlua_init_state(0);

	/* Init state 1 for thread 0. We have at least one thread. */
	hlua_state_id = 1;
	ha_set_thread(NULL);
	hlua_states[1] = hlua_init_state(1);

	/* Proxy and server configuration initialisation. */
	socket_proxy = alloc_new_proxy("LUA-SOCKET", PR_CAP_FE|PR_CAP_BE|PR_CAP_INT, &errmsg);
	if (!socket_proxy) {
		fprintf(stderr, "Lua init: %s\n", errmsg);
		exit(1);
	}

	/* Init TCP server: unchanged parameters */
	socket_tcp = new_server(socket_proxy);
	if (!socket_tcp) {
		fprintf(stderr, "Lua init: failed to allocate tcp server socket\n");
		exit(1);
	}

#ifdef USE_OPENSSL
	/* Init TCP server: unchanged parameters */
	socket_ssl = new_server(socket_proxy);
	if (!socket_ssl) {
		fprintf(stderr, "Lua init: failed to allocate ssl server socket\n");
		exit(1);
	}

	socket_ssl->use_ssl = 1;
	socket_ssl->xprt = xprt_get(XPRT_SSL);

	for (i = 0; args[i] != NULL; i++) {
		if ((kw = srv_find_kw(args[i])) != NULL) { /* Maybe it's registered server keyword */
			/*
			 *
			 * If the keyword is not known, we can search in the registered
			 * server keywords. This is useful to configure special SSL
			 * features like client certificates and ssl_verify.
			 *
			 */
			tmp_error = kw->parse(args, &i, 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. */
			}
			i += kw->skip;
		}
	}
#endif

}

static void hlua_deinit()
{
	int thr;
	struct hlua_reg_filter *reg_flt, *reg_flt_bck;

	list_for_each_entry_safe(reg_flt, reg_flt_bck, &referenced_filters, l)
		release_hlua_reg_filter(reg_flt);

	for (thr = 0; thr < MAX_THREADS+1; thr++) {
		if (hlua_states[thr])
			lua_close(hlua_states[thr]);
	}

	proxy_drop(socket_proxy);
}

REGISTER_POST_DEINIT(hlua_deinit);

static void hlua_register_build_options(void)
{
	char *ptr = NULL;

	memprintf(&ptr, "Built with Lua version : %s", LUA_RELEASE);
	hap_register_build_opts(ptr, 1);
}

INITCALL0(STG_REGISTER, hlua_register_build_options);
