]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: lua: add AppletHTTP class and service
authorThierry FOURNIER <tfournier@arpalert.org>
Fri, 18 Sep 2015 07:04:27 +0000 (09:04 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 27 Sep 2015 23:03:48 +0000 (01:03 +0200)
This class is used by Lua code for running as an applet called in HTTP mode
It defines also the associated lua service

include/types/applet.h
include/types/hlua.h
src/hlua.c

index 2254b0ac2d1129ed7b82854f15b37af5e85fad1a..2e1626a79d1fd65d3eea0345ea6e070402764b7a 100644 (file)
@@ -120,6 +120,13 @@ struct appctx {
                        int flags;
                        struct task *task;
                } hlua_apptcp;
+               struct {
+                       struct hlua hlua;
+                       int left_bytes; /* The max amount of bytes that we can read. */
+                       int flags;
+                       int status;
+                       struct task *task;
+               } hlua_apphttp;
                struct {
                        struct dns_resolvers *ptr;
                } resolvers;
index eccfc6cce452de8f79e10ec18190d52c5af94005..ac56c2ab307094a41a3e677ccf6e7ff64aeec446 100644 (file)
@@ -18,6 +18,7 @@
 #define CLASS_HTTP         "HTTP"
 #define CLASS_MAP          "Map"
 #define CLASS_APPLET_TCP   "AppletTCP"
+#define CLASS_APPLET_HTTP  "AppletHTTP"
 
 struct stream;
 
index 36bd1001393a3bd25246bc5f9f80e988e7b5f791..12ffe75436cfd5c832b70ba4237e131b8e44145f 100644 (file)
@@ -110,8 +110,13 @@ static int hlua_panic_ljmp(lua_State *L) { longjmp(safe_ljmp_env, 1); }
        } while(0)
 
 /* Applet status flags */
-#define APPLET_DONE 0x1 /* applet processing is done. */
-#define APPLET_SENT 0x2 /* some data are sent. */
+#define APPLET_DONE     0x01 /* applet processing is done. */
+#define APPLET_100C     0x02 /* 100 continue expected. */
+#define APPLET_HDR_SENT 0x04 /* Response header sent. */
+#define APPLET_CHUNKED  0x08 /* Use transfer encoding chunked. */
+#define APPLET_LAST_CHK 0x10 /* Last chunk sent. */
+
+#define HTTP_100C "HTTP/1.1 100 Continue\r\n\r\n"
 
 /* The main Lua execution context. */
 struct hlua gL;
@@ -143,6 +148,7 @@ static int class_converters_ref;
 static int class_http_ref;
 static int class_map_ref;
 static int class_applet_tcp_ref;
+static int class_applet_http_ref;
 
 /* Global Lua execution timeout. By default Lua, execution linked
  * with stream (actions, sample-fetches and converters) have a
@@ -190,6 +196,14 @@ struct hlua_mem_allocator {
 
 static struct hlua_mem_allocator hlua_global_allocator;
 
+static const char error_500[] =
+       "HTTP/1.0 500 Server Error\r\n"
+       "Cache-Control: no-cache\r\n"
+       "Connection: close\r\n"
+       "Content-Type: text/html\r\n"
+       "\r\n"
+       "<html><body><h1>500 Server Error</h1>\nAn internal server error occured.\n</body></html>\n";
+
 /* 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
@@ -203,6 +217,8 @@ static int hlua_smp2lua(lua_State *L, struct sample *smp);
 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 hlua_txn *htxn, struct http_msg *msg);
+
 #define SEND_ERR(__be, __fmt, __args...) \
        do { \
                send_log(__be, LOG_ERR, __fmt, ## __args); \
@@ -224,6 +240,16 @@ __LJMP unsigned int hlua_checkfunction(lua_State *L, int argno)
        return luaL_ref(L, LUA_REGISTRYINDEX);
 }
 
+/* Return the string that is of the top of the stack. */
+const char *hlua_get_top_error_string(lua_State *L)
+{
+       if (lua_gettop(L) < 1)
+               return "unknown error";
+       if (lua_type(L, -1) != LUA_TSTRING)
+               return "unknown error";
+       return lua_tostring(L, -1);
+}
+
 /* The three following functions are useful for adding entries
  * in a table. These functions takes a string and respectively an
  * integer, a string or a function and add it to the table in the
@@ -3424,7 +3450,7 @@ __LJMP static int hlua_applet_tcp_send(lua_State *L)
 /*
  *
  *
- * Class HTTP
+ * Class AppletHTTP
  *
  *
  */
@@ -3432,17 +3458,24 @@ __LJMP static int hlua_applet_tcp_send(lua_State *L)
 /* 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)
+__LJMP static struct hlua_appctx *hlua_checkapplet_http(lua_State *L, int ud)
 {
-       return (struct hlua_txn *)MAY_LJMP(hlua_checkudata(L, ud, class_http_ref));
+       return (struct hlua_appctx *)MAY_LJMP(hlua_checkudata(L, ud, class_applet_http_ref));
 }
 
-/* This function creates and push in the stack a HTTP object
+/* This function creates and push in the stack an Applet object
  * according with a current TXN.
  */
-static int hlua_http_new(lua_State *L, struct hlua_txn *txn)
+static int hlua_applet_http_new(lua_State *L, struct appctx *ctx)
 {
-       struct hlua_txn *htxn;
+       struct hlua_appctx *appctx;
+       struct stream_interface *si = ctx->owner;
+       struct stream *s = si_strm(si);
+       struct proxy *px = s->be;
+       struct http_txn *txn = s->txn;
+       const char *path;
+       const char *end;
+       const char *p;
 
        /* Check stack size. */
        if (!lua_checkstack(L, 3))
@@ -3453,195 +3486,816 @@ static int hlua_http_new(lua_State *L, struct hlua_txn *txn)
         * same than the TXN object.
         */
        lua_newtable(L);
-       htxn = lua_newuserdata(L, sizeof(*htxn));
+       appctx = lua_newuserdata(L, sizeof(*appctx));
        lua_rawseti(L, -2, 0);
+       appctx->appctx = ctx;
+       appctx->appctx->ctx.hlua_apphttp.status = 200; /* Default status code returned. */
+       appctx->htxn.s = s;
+       appctx->htxn.p = px;
 
-       htxn->s = txn->s;
-       htxn->p = txn->p;
-
-       /* 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;
-}
+       /* Create the "f" field that contains a list of fetches. */
+       lua_pushstring(L, "f");
+       if (!hlua_fetches_new(L, &appctx->htxn, 0))
+               return 0;
+       lua_settable(L, -3);
 
-/* 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 hlua_txn *htxn, struct http_msg *msg)
-{
-       const char *cur_ptr, *cur_next, *p;
-       int old_idx, cur_idx;
-       struct hdr_idx_elem *cur_hdr;
-       const char *hn, *hv;
-       int hnl, hvl;
-       int type;
-       const char *in;
-       char *out;
-       int len;
+       /* Create the "sf" field that contains a list of stringsafe fetches. */
+       lua_pushstring(L, "sf");
+       if (!hlua_fetches_new(L, &appctx->htxn, 1))
+               return 0;
+       lua_settable(L, -3);
 
-       /* Create the table. */
-       lua_newtable(L);
+       /* Create the "c" field that contains a list of converters. */
+       lua_pushstring(L, "c");
+       if (!hlua_converters_new(L, &appctx->htxn, 0))
+               return 0;
+       lua_settable(L, -3);
 
-       if (!htxn->s->txn)
-               return 1;
+       /* Create the "sc" field that contains a list of stringsafe converters. */
+       lua_pushstring(L, "sc");
+       if (!hlua_converters_new(L, &appctx->htxn, 1))
+               return 0;
+       lua_settable(L, -3);
 
-       /* Build array of headers. */
-       old_idx = 0;
-       cur_next = msg->chn->buf->p + hdr_idx_first_pos(&htxn->s->txn->hdr_idx);
+       /* Stores the request method. */
+       lua_pushstring(L, "method");
+       lua_pushlstring(L, txn->req.chn->buf->p, txn->req.sl.rq.m_l);
+       lua_settable(L, -3);
 
-       while (1) {
-               cur_idx = htxn->s->txn->hdr_idx.v[old_idx].next;
-               if (!cur_idx)
-                       break;
-               old_idx = cur_idx;
+       /* Stores the http version. */
+       lua_pushstring(L, "version");
+       lua_pushlstring(L, txn->req.chn->buf->p + txn->req.sl.rq.v, txn->req.sl.rq.v_l);
+       lua_settable(L, -3);
 
-               cur_hdr  = &htxn->s->txn->hdr_idx.v[cur_idx];
-               cur_ptr  = cur_next;
-               cur_next = cur_ptr + cur_hdr->len + cur_hdr->cr + 1;
+       /* Get path and qs */
+       path = http_get_path(txn);
+       end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+       p = path;
+       while (p < end && *p != '?')
+               p++;
 
-               /* Now we have one full header at cur_ptr of len cur_hdr->len,
-                * and the next header starts at cur_next. We'll check
-                * this header in the list as well as against the default
-                * rule.
-                */
+       /* Stores the request path. */
+       lua_pushstring(L, "path");
+       lua_pushlstring(L, path, p - path);
+       lua_settable(L, -3);
 
-               /* look for ': *'. */
-               hn = cur_ptr;
-               for (p = cur_ptr; p < cur_ptr + cur_hdr->len && *p != ':'; p++);
-               if (p >= cur_ptr+cur_hdr->len)
-                       continue;
-               hnl = p - hn;
+       /* Stores the query string. */
+       lua_pushstring(L, "qs");
+       if (*p == '?')
                p++;
-               while (p < cur_ptr+cur_hdr->len && ( *p == ' ' || *p == '\t' ))
-                       p++;
-               if (p >= cur_ptr+cur_hdr->len)
-                       continue;
-               hv = p;
-               hvl = cur_ptr+cur_hdr->len-p;
-
-               /* Lowercase the key. Don't check the size of trash, it have
-                * the size of one buffer and the input data contains in one
-                * buffer.
-                */
-               out = trash.str;
-               for (in=hn; in<hn+hnl; in++, out++)
-                       *out = tolower(*in);
-               *out = '\0';
+       lua_pushlstring(L, p, end - p);
+       lua_settable(L, -3);
 
-               /* 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, trash.str, hnl);
-               lua_gettable(L, -2);
-               type = lua_type(L, -1);
+       /* Stores the request path. */
+       lua_pushstring(L, "length");
+       lua_pushinteger(L, txn->req.body_len);
+       lua_settable(L, -3);
 
-               switch (type) {
-               case LUA_TNIL:
-                       /* Table not found, create it. */
-                       lua_pop(L, 1); /* remove the nil value. */
-                       lua_pushlstring(L, trash.str, hnl);  /* push the header name as key. */
-                       lua_newtable(L); /* create and push empty table. */
-                       lua_pushlstring(L, hv, hvl); /* 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;
+       /* Create an array of HTTP request headers. */
+       lua_pushstring(L, "headers");
+       MAY_LJMP(hlua_http_get_headers(L, &appctx->htxn, &appctx->htxn.s->txn->req));
+       lua_settable(L, -3);
 
-               case LUA_TTABLE:
-                       /* Entry found: push the value in the table. */
-                       len = lua_rawlen(L, -1);
-                       lua_pushlstring(L, hv, hvl); /* 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;
+       /* Create an empty array of HTTP request headers. */
+       lua_pushstring(L, "response");
+       lua_newtable(L);
+       lua_settable(L, -3);
 
-               default:
-                       /* Other cases are errors. */
-                       hlua_pusherror(L, "internal error during the parsing of headers.");
-                       WILL_LJMP(lua_error(L));
-               }
-       }
+       /* 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_http_req_get_headers(lua_State *L)
+/* 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_txn *htxn;
-
-       MAY_LJMP(check_args(L, 1, "req_get_headers"));
-       htxn = MAY_LJMP(hlua_checkhttp(L, 1));
-
-       return hlua_http_get_headers(L, htxn, &htxn->s->txn->req);
-}
+       struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+       struct stream_interface *si = appctx->appctx->owner;
+       struct channel *chn = si_ic(si);
+       int ret;
+       char *blk1;
+       int len1;
+       char *blk2;
+       int len2;
 
-__LJMP static int hlua_http_res_get_headers(lua_State *L)
-{
-       struct hlua_txn *htxn;
+       /* Maybe we cant send a 100-continue ? */
+       if (appctx->appctx->ctx.hlua_apphttp.flags & APPLET_100C) {
+               ret = bi_putblk(chn, HTTP_100C, strlen(HTTP_100C));
+               /* if ret == -2 or -3 the channel closed or the message si too
+                * big for the buffers. We cant send anything. So, we ignoring
+                * the error, considers that the 100-continue is sent, and try
+                * to receive.
+                * If ret is -1, we dont have room in the buffer, so we yield.
+                */
+               if (ret == -1) {
+                       si_applet_cant_put(si);
+                       WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_getline_yield, TICK_ETERNITY, 0));
+               }
+               appctx->appctx->ctx.hlua_apphttp.flags &= ~APPLET_100C;
+       }
 
-       MAY_LJMP(check_args(L, 1, "res_get_headers"));
-       htxn = MAY_LJMP(hlua_checkhttp(L, 1));
+       /* Check for the end of the data. */
+       if (appctx->appctx->ctx.hlua_apphttp.left_bytes <= 0) {
+               luaL_pushresult(&appctx->b);
+               return 1;
+       }
 
-       return hlua_http_get_headers(L, htxn, &htxn->s->txn->rsp);
-}
+       /* Read the maximum amount of data avalaible. */
+       ret = bo_getline_nc(si_oc(si), &blk1, &len1, &blk2, &len2);
 
-/* 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 hlua_txn *htxn,
-                                           struct http_msg *msg, int action)
-{
-       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 my_regex re;
+       /* Data not yet avalaible. return yield. */
+       if (ret == 0) {
+               si_applet_cant_get(si);
+               WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_getline_yield, TICK_ETERNITY, 0));
+       }
 
-       if (!regex_comp(reg, &re, 1, 1, NULL))
-               WILL_LJMP(luaL_argerror(L, 3, "invalid regex"));
+       /* End of data: commit the total strings and return. */
+       if (ret < 0) {
+               luaL_pushresult(&appctx->b);
+               return 1;
+       }
 
-       http_transform_header_str(htxn->s, msg, name, name_len, value, &re, action);
-       regex_free(&re);
-       return 0;
-}
+       /* Ensure that the block 2 length is usable. */
+       if (ret == 1)
+               len2 = 0;
 
-__LJMP static int hlua_http_req_rep_hdr(lua_State *L)
-{
-       struct hlua_txn *htxn;
+       /* Copy the fisrt block caping to the length required. */
+       if (len1 > appctx->appctx->ctx.hlua_apphttp.left_bytes)
+               len1 = appctx->appctx->ctx.hlua_apphttp.left_bytes;
+       luaL_addlstring(&appctx->b, blk1, len1);
+       appctx->appctx->ctx.hlua_apphttp.left_bytes -= len1;
 
-       MAY_LJMP(check_args(L, 4, "req_rep_hdr"));
-       htxn = MAY_LJMP(hlua_checkhttp(L, 1));
+       /* Copy the second block. */
+       if (len2 > appctx->appctx->ctx.hlua_apphttp.left_bytes)
+               len2 = appctx->appctx->ctx.hlua_apphttp.left_bytes;
+       luaL_addlstring(&appctx->b, blk2, len2);
+       appctx->appctx->ctx.hlua_apphttp.left_bytes -= len2;
 
-       return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, ACT_HTTP_REPLACE_HDR));
+       /* Consume input channel output buffer data. */
+       bo_skip(si_oc(si), len1 + len2);
+       luaL_pushresult(&appctx->b);
+       return 1;
 }
 
-__LJMP static int hlua_http_res_rep_hdr(lua_State *L)
+/* Check arguments for the fucntion "hlua_channel_get_yield". */
+__LJMP static int hlua_applet_http_getline(lua_State *L)
 {
-       struct hlua_txn *htxn;
+       struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
 
-       MAY_LJMP(check_args(L, 4, "res_rep_hdr"));
-       htxn = MAY_LJMP(hlua_checkhttp(L, 1));
+       /* Initialise the string catenation. */
+       luaL_buffinit(L, &appctx->b);
 
-       return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->rsp, ACT_HTTP_REPLACE_HDR));
+       return MAY_LJMP(hlua_applet_http_getline_yield(L, 0, 0));
 }
 
-__LJMP static int hlua_http_req_rep_val(lua_State *L)
+/* 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_txn *htxn;
+       struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+       struct stream_interface *si = appctx->appctx->owner;
+       int len = MAY_LJMP(luaL_checkinteger(L, 2));
+       struct channel *chn = si_ic(si);
+       int ret;
+       char *blk1;
+       int len1;
+       char *blk2;
+       int len2;
 
-       MAY_LJMP(check_args(L, 4, "req_rep_hdr"));
-       htxn = MAY_LJMP(hlua_checkhttp(L, 1));
+       /* Maybe we cant send a 100-continue ? */
+       if (appctx->appctx->ctx.hlua_apphttp.flags & APPLET_100C) {
+               ret = bi_putblk(chn, HTTP_100C, strlen(HTTP_100C));
+               /* if ret == -2 or -3 the channel closed or the message si too
+                * big for the buffers. We cant send anything. So, we ignoring
+                * the error, considers that the 100-continue is sent, and try
+                * to receive.
+                * If ret is -1, we dont have room in the buffer, so we yield.
+                */
+               if (ret == -1) {
+                       si_applet_cant_put(si);
+                       WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_recv_yield, TICK_ETERNITY, 0));
+               }
+               appctx->appctx->ctx.hlua_apphttp.flags &= ~APPLET_100C;
+       }
 
-       return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, ACT_HTTP_REPLACE_VAL));
-}
+       /* Read the maximum amount of data avalaible. */
+       ret = bo_getblk_nc(si_oc(si), &blk1, &len1, &blk2, &len2);
 
-__LJMP static int hlua_http_res_rep_val(lua_State *L)
-{
+       /* Data not yet avalaible. return yield. */
+       if (ret == 0) {
+               si_applet_cant_get(si);
+               WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_recv_yield, TICK_ETERNITY, 0));
+       }
+
+       /* End of data: commit the total strings and return. */
+       if (ret < 0) {
+               luaL_pushresult(&appctx->b);
+               return 1;
+       }
+
+       /* Ensure that the block 2 length is usable. */
+       if (ret == 1)
+               len2 = 0;
+
+       /* Copy the fisrt block caping to the length required. */
+       if (len1 > len)
+               len1 = len;
+       luaL_addlstring(&appctx->b, blk1, len1);
+       len -= len1;
+
+       /* Copy the second block. */
+       if (len2 > len)
+               len2 = len;
+       luaL_addlstring(&appctx->b, blk2, len2);
+       len -= len2;
+
+       /* Consume input channel output buffer data. */
+       bo_skip(si_oc(si), len1 + len2);
+       if (appctx->appctx->ctx.hlua_apphttp.left_bytes != -1)
+               appctx->appctx->ctx.hlua_apphttp.left_bytes -= len;
+
+       /* If we are no other data avalaible, yield waiting for new data. */
+       if (len > 0) {
+               lua_pushinteger(L, len);
+               lua_replace(L, 2);
+               si_applet_cant_get(si);
+               WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_recv_yield, TICK_ETERNITY, 0));
+       }
+
+       /* return the result. */
+       luaL_pushresult(&appctx->b);
+       return 1;
+}
+
+/* Check arguments for the fucntion "hlua_channel_get_yield". */
+__LJMP static int hlua_applet_http_recv(lua_State *L)
+{
+       struct hlua_appctx *appctx = 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);
+       }
+
+       /* Check the required length */
+       if (len == -1 || len > appctx->appctx->ctx.hlua_apphttp.left_bytes)
+               len = appctx->appctx->ctx.hlua_apphttp.left_bytes;
+       lua_pushinteger(L, len);
+
+       /* Initialise the string catenation. */
+       luaL_buffinit(L, &appctx->b);
+
+       return MAY_LJMP(hlua_applet_http_recv_yield(L, 0, 0));
+}
+
+/* Append data in the output side of the buffer. This data is immediatly
+ * sent. The fcuntion returns the ammount of data writed. If the buffer
+ * cannot contains the data, the function yield. The function returns -1
+ * if the channel is closed.
+ */
+__LJMP static int hlua_applet_http_send_yield(lua_State *L, int status, lua_KContext ctx)
+{
+       size_t len;
+       struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+       const char *str = MAY_LJMP(luaL_checklstring(L, 2, &len));
+       int l = MAY_LJMP(luaL_checkinteger(L, 3));
+       struct stream_interface *si = appctx->appctx->owner;
+       struct channel *chn = si_ic(si);
+       int max;
+
+       /* Get the max amount of data which can write as input in the channel. */
+       max = channel_recv_max(chn);
+       if (max > (len - l))
+               max = len - l;
+
+       /* Copy data. */
+       bi_putblk(chn, 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) {
+               si_applet_cant_put(si);
+               WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_send_yield, TICK_ETERNITY, 0));
+       }
+
+       return 1;
+}
+
+/* Just a wraper 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 *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+       size_t len;
+       char hex[10];
+
+       MAY_LJMP(luaL_checklstring(L, 2, &len));
+
+       /* If transfer encoding chunked is selected, we surround the data
+        * by chunk data.
+        */
+       if (appctx->appctx->ctx.hlua_apphttp.flags & APPLET_CHUNKED) {
+               snprintf(hex, 9, "%x", (unsigned int)len);
+               lua_pushfstring(L, "%s\r\n", hex);
+               lua_insert(L, 2); /* swap the last 2 entries. */
+               lua_pushstring(L, "\r\n");
+               lua_concat(L, 3);
+       }
+
+       /* This interger is used for followinf the amount of data sent. */
+       lua_pushinteger(L, 0);
+
+       /* We want to send some data. Headers must be sent. */
+       if (!(appctx->appctx->ctx.hlua_apphttp.flags & APPLET_HDR_SENT)) {
+               hlua_pusherror(L, "Lua: 'send' you must call start_response() before sending data.");
+               WILL_LJMP(lua_error(L));
+       }
+
+       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 od 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 *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+       int status = MAY_LJMP(luaL_checkinteger(L, 2));
+
+       if (status < 100 || status > 599) {
+               lua_pushboolean(L, 0);
+               return 1;
+       }
+
+       appctx->appctx->ctx.hlua_apphttp.status = status;
+       lua_pushboolean(L, 1);
+       return 1;
+}
+
+/* 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 *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+       struct stream_interface *si = appctx->appctx->owner;
+       struct channel *chn = si_ic(si);
+       int ret;
+       size_t len;
+       const char *msg;
+
+       /* Get the message as the first argument on the stack. */
+       msg = MAY_LJMP(luaL_checklstring(L, 2, &len));
+
+       /* Send the message at once. */
+       ret = bi_putblk(chn, msg, len);
+
+       /* if ret == -2 or -3 the channel closed or the message si too
+        * big for the buffers.
+        */
+       if (ret == -2 || ret == -3) {
+               hlua_pusherror(L, "Lua: 'start_response': response header block too big");
+               WILL_LJMP(lua_error(L));
+       }
+
+       /* If ret is -1, we dont have room in the buffer, so we yield. */
+       if (ret == -1) {
+               si_applet_cant_put(si);
+               WILL_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_http_start_response_yield, TICK_ETERNITY, 0));
+       }
+
+       /* Headers sent, set the flag. */
+       appctx->appctx->ctx.hlua_apphttp.flags |= APPLET_HDR_SENT;
+       return 0;
+}
+
+__LJMP static int hlua_applet_http_start_response(lua_State *L)
+{
+       struct chunk *tmp = get_trash_chunk();
+       struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+       struct stream_interface *si = appctx->appctx->owner;
+       struct stream *s = si_strm(si);
+       struct http_txn *txn = s->txn;
+       const char *name;
+       const char *value;
+       int id;
+       int hdr_connection = 0;
+       int hdr_contentlength = -1;
+       int hdr_chunked = 0;
+
+       /* Use the same http version than the request. */
+       chunk_appendf(tmp, "HTTP/1.%c %d %s\r\n",
+                     txn->req.flags & HTTP_MSGF_VER_11 ? '1' : '0',
+                     appctx->appctx->ctx.hlua_apphttp.status,
+                     get_reason(appctx->appctx->ctx.hlua_apphttp.status));
+
+       /* Get the array associated to the field "response" in the object AppletHTTP. */
+       lua_pushvalue(L, 0);
+       if (lua_getfield(L, 1, "response") != LUA_TTABLE) {
+               hlua_pusherror(L, "Lua applet http '%s': AppletHTTP['response'] missing.\n",
+                              appctx->appctx->rule->arg.hlua_rule->fcn.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",
+                                                               appctx->appctx->rule->arg.hlua_rule->fcn.name,
+                                      lua_typename(L, lua_type(L, -2)));
+                       WILL_LJMP(lua_error(L));
+               }
+               name = lua_tostring(L, -2);
+
+               /* 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",
+                                                               appctx->appctx->rule->arg.hlua_rule->fcn.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) {
+
+                       /* 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",
+                                                                       appctx->appctx->rule->arg.hlua_rule->fcn.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",
+                                                                       appctx->appctx->rule->arg.hlua_rule->fcn.name,
+                                                                       name, id,
+                                              lua_typename(L, lua_type(L, -1)));
+                               WILL_LJMP(lua_error(L));
+                       }
+                       value = lua_tostring(L, -1);
+
+                       /* Catenate a new header. */
+                       chunk_appendf(tmp, "%s: %s\r\n", name, value);
+
+                       /* Protocol checks. */
+
+                       /* Check if the header conneciton is present. */
+                       if (strcasecmp("connection", name) == 0)
+                               hdr_connection = 1;
+
+                       /* Copy the header content length. The length conversion
+                        * is done without control. If it contains a ad value, this
+                        * is not our problem.
+                        */
+                       if (strcasecmp("content-length", name) == 0)
+                               hdr_contentlength = atoi(value);
+
+                       /* Check if the client annouces a transfer-encoding chunked it self. */
+                       if (strcasecmp("transfer-encoding", name) == 0 &&
+                           strcasecmp("chunked", value) == 0)
+                               hdr_chunked = 1;
+
+                       /* 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 the http protocol version is 1.1, we expect an header "connection" set
+        * to "close" to be HAProxy/keeplive compliant. Otherwise, we expect nothing.
+        * If the header conneciton is present, don't change it, if it is not present,
+        * we must set.
+        *
+        * we set a "connection: close" header for ensuring that the keepalive will be
+        * respected by haproxy. HAProcy considers that the application cloe the connection
+        * and it keep the connection from the client open.
+        */
+       if (txn->req.flags & HTTP_MSGF_VER_11 && !hdr_connection)
+               chunk_appendf(tmp, "Connection: close\r\n");
+
+       /* If we dont have a content-length set, we must announce a transfer enconding
+        * chunked. This is required by haproxy for the keepalive compliance.
+        * If the applet annouce a transfer-encoding chunked itslef, don't
+        * do anything.
+        */
+       if (hdr_contentlength == -1 && hdr_chunked == 0) {
+               chunk_appendf(tmp, "Transfer-encoding: chunked\r\n");
+               appctx->appctx->ctx.hlua_apphttp.flags |= APPLET_CHUNKED;
+       }
+
+       /* Finalize headers. */
+       chunk_appendf(tmp, "\r\n");
+
+       /* Remove the last entry and the array of headers */
+       lua_pop(L, 2);
+
+       /* Push the headers block. */
+       lua_pushlstring(L, tmp->str, tmp->len);
+
+       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 (struct hlua_txn *)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.
+ */
+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;
+
+       /* 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 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 hlua_txn *htxn, struct http_msg *msg)
+{
+       const char *cur_ptr, *cur_next, *p;
+       int old_idx, cur_idx;
+       struct hdr_idx_elem *cur_hdr;
+       const char *hn, *hv;
+       int hnl, hvl;
+       int type;
+       const char *in;
+       char *out;
+       int len;
+
+       /* Create the table. */
+       lua_newtable(L);
+
+       if (!htxn->s->txn)
+               return 1;
+
+       /* Build array of headers. */
+       old_idx = 0;
+       cur_next = msg->chn->buf->p + hdr_idx_first_pos(&htxn->s->txn->hdr_idx);
+
+       while (1) {
+               cur_idx = htxn->s->txn->hdr_idx.v[old_idx].next;
+               if (!cur_idx)
+                       break;
+               old_idx = cur_idx;
+
+               cur_hdr  = &htxn->s->txn->hdr_idx.v[cur_idx];
+               cur_ptr  = cur_next;
+               cur_next = cur_ptr + cur_hdr->len + cur_hdr->cr + 1;
+
+               /* Now we have one full header at cur_ptr of len cur_hdr->len,
+                * and the next header starts at cur_next. We'll check
+                * this header in the list as well as against the default
+                * rule.
+                */
+
+               /* look for ': *'. */
+               hn = cur_ptr;
+               for (p = cur_ptr; p < cur_ptr + cur_hdr->len && *p != ':'; p++);
+               if (p >= cur_ptr+cur_hdr->len)
+                       continue;
+               hnl = p - hn;
+               p++;
+               while (p < cur_ptr+cur_hdr->len && ( *p == ' ' || *p == '\t' ))
+                       p++;
+               if (p >= cur_ptr+cur_hdr->len)
+                       continue;
+               hv = p;
+               hvl = cur_ptr+cur_hdr->len-p;
+
+               /* Lowercase the key. Don't check the size of trash, it have
+                * the size of one buffer and the input data contains in one
+                * buffer.
+                */
+               out = trash.str;
+               for (in=hn; in<hn+hnl; in++, out++)
+                       *out = tolower(*in);
+               *out = '\0';
+
+               /* 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, trash.str, hnl);
+               lua_gettable(L, -2);
+               type = lua_type(L, -1);
+
+               switch (type) {
+               case LUA_TNIL:
+                       /* Table not found, create it. */
+                       lua_pop(L, 1); /* remove the nil value. */
+                       lua_pushlstring(L, trash.str, hnl);  /* push the header name as key. */
+                       lua_newtable(L); /* create and push empty table. */
+                       lua_pushlstring(L, hv, hvl); /* 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, hv, hvl); /* 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));
+
+       return hlua_http_get_headers(L, htxn, &htxn->s->txn->req);
+}
+
+__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));
+
+       return hlua_http_get_headers(L, htxn, &htxn->s->txn->rsp);
+}
+
+/* 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 hlua_txn *htxn,
+                                           struct http_msg *msg, int action)
+{
+       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 my_regex re;
+
+       if (!regex_comp(reg, &re, 1, 1, NULL))
+               WILL_LJMP(luaL_argerror(L, 3, "invalid regex"));
+
+       http_transform_header_str(htxn->s, msg, name, name_len, value, &re, action);
+       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));
+
+       return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, ACT_HTTP_REPLACE_HDR));
+}
+
+__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));
+
+       return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->rsp, ACT_HTTP_REPLACE_HDR));
+}
+
+__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));
+
+       return MAY_LJMP(hlua_http_rep_hdr(L, htxn, &htxn->s->txn->req, ACT_HTTP_REPLACE_VAL));
+}
+
+__LJMP static int hlua_http_res_rep_val(lua_State *L)
+{
        struct hlua_txn *htxn;
 
        MAY_LJMP(check_args(L, 4, "res_rep_val"));
@@ -5081,6 +5735,272 @@ static void hlua_applet_tcp_release(struct appctx *ctx)
        hlua_ctx_destroy(&ctx->ctx.hlua_apptcp.hlua);
 }
 
+/* The function returns 1 if the initialisation is complete, 0 if
+ * an errors occurs and -1 if more data are required for initializing
+ * the applet.
+ */
+static int hlua_applet_http_init(struct appctx *ctx, struct proxy *px, struct stream *strm)
+{
+       struct stream_interface *si = ctx->owner;
+       struct channel *req = si_oc(si);
+       struct http_msg *msg;
+       struct http_txn *txn;
+       struct hlua *hlua = &ctx->ctx.hlua_apphttp.hlua;
+       char **arg;
+       struct hdr_ctx hdr;
+       struct task *task;
+       struct sample smp; /* just used for a valid call to smp_prefetch_http. */
+
+       /* Wait for a full HTTP request. */
+       if (!smp_prefetch_http(px, strm, 0, NULL, &smp, 0)) {
+               if (smp.flags & SMP_F_MAY_CHANGE)
+                       return -1;
+               return 0;
+       }
+       txn = strm->txn;
+       msg = &txn->req;
+
+       HLUA_INIT(hlua);
+       ctx->ctx.hlua_apphttp.left_bytes = -1;
+       ctx->ctx.hlua_apphttp.flags = 0;
+
+       /* Create task used by signal to wakeup applets. */
+       task = task_new();
+       if (!task) {
+               SEND_ERR(px, "Lua applet http '%s': out of memory.\n",
+                        ctx->rule->arg.hlua_rule->fcn.name);
+               return 0;
+       }
+       task->nice = 0;
+       task->context = ctx;
+       task->process = hlua_applet_wakeup;
+       ctx->ctx.hlua_apphttp.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, task)) {
+               SEND_ERR(px, "Lua applet http '%s': can't initialize Lua context.\n",
+                        ctx->rule->arg.hlua_rule->fcn.name);
+               return 0;
+       }
+
+       /* Set timeout according with the applet configuration. */
+       hlua->expire = tick_add_ifset(now_ms, ctx->applet->timeout);
+
+       /* The following Lua calls can fail. */
+       if (!SET_SAFE_LJMP(hlua->T)) {
+               SEND_ERR(px, "Lua applet http '%s': critical error.\n",
+                        ctx->rule->arg.hlua_rule->fcn.name);
+               return 0;
+       }
+
+       /* Check stack available size. */
+       if (!lua_checkstack(hlua->T, 1)) {
+               SEND_ERR(px, "Lua applet http '%s': full stack.\n",
+                        ctx->rule->arg.hlua_rule->fcn.name);
+               RESET_SAFE_LJMP(hlua->T);
+               return 0;
+       }
+
+       /* Restore the function in the stack. */
+       lua_rawgeti(hlua->T, LUA_REGISTRYINDEX, ctx->rule->arg.hlua_rule->fcn.function_ref);
+
+       /* Create and and push object stream in the stack. */
+       if (!hlua_applet_http_new(hlua->T, ctx)) {
+               SEND_ERR(px, "Lua applet http '%s': full stack.\n",
+                        ctx->rule->arg.hlua_rule->fcn.name);
+               RESET_SAFE_LJMP(hlua->T);
+               return 0;
+       }
+       hlua->nargs = 1;
+
+       /* Look for a 100-continue expected. */
+       if (msg->flags & HTTP_MSGF_VER_11) {
+               hdr.idx = 0;
+               if (http_find_header2("Expect", 6, req->buf->p, &txn->hdr_idx, &hdr) &&
+                   unlikely(hdr.vlen == 12 && strncasecmp(hdr.line+hdr.val, "100-continue", 12) == 0))
+                       ctx->ctx.hlua_apphttp.flags |= APPLET_100C;
+       }
+
+       /* push keywords in the stack. */
+       for (arg = ctx->rule->arg.hlua_rule->args; arg && *arg; arg++) {
+               if (!lua_checkstack(hlua->T, 1)) {
+                       SEND_ERR(px, "Lua applet http '%s': full stack.\n",
+                                ctx->rule->arg.hlua_rule->fcn.name);
+                       RESET_SAFE_LJMP(hlua->T);
+                       return 0;
+               }
+               lua_pushstring(hlua->T, *arg);
+               hlua->nargs++;
+       }
+
+       RESET_SAFE_LJMP(hlua->T);
+
+       /* Wakeup the applet when data is ready for read. */
+       si_applet_cant_get(si);
+
+       return 1;
+}
+
+static void hlua_applet_http_fct(struct appctx *ctx)
+{
+       struct stream_interface *si = ctx->owner;
+       struct stream *strm = si_strm(si);
+       struct channel *res = si_ic(si);
+       struct channel *req = si_oc(si);
+       struct act_rule *rule = ctx->rule;
+       struct proxy *px = strm->be;
+       struct hlua *hlua = &ctx->ctx.hlua_apphttp.hlua;
+       char *blk1;
+       int len1;
+       char *blk2;
+       int len2;
+       int ret;
+
+       /* If the stream is disconnect or closed, ldo nothing. */
+       if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
+               return;
+
+       /* Set the currently running flag. */
+       if (!HLUA_IS_RUNNING(hlua) &&
+           !(ctx->ctx.hlua_apphttp.flags & APPLET_DONE)) {
+
+               /* enable the minimally required analyzers to handle keep-alive
+                * and compression on the HTTP response
+                */
+               req->analysers = (req->analysers & AN_REQ_HTTP_BODY) |
+                                AN_REQ_HTTP_XFER_BODY | AN_REQ_HTTP_INNER;
+
+               /* Wait for full HTTP analysys. */
+               if (unlikely(strm->txn->req.msg_state < HTTP_MSG_BODY)) {
+                       si_applet_cant_get(si);
+                       return;
+               }
+
+               /* Store the max amount of bytes that we can read. */
+               ctx->ctx.hlua_apphttp.left_bytes = strm->txn->req.body_len;
+
+               /* We need to flush the request header. This left the body
+                * for the Lua.
+                */
+
+               /* Read the maximum amount of data avalaible. */
+               ret = bo_getblk_nc(si_oc(si), &blk1, &len1, &blk2, &len2);
+               if (ret == -1)
+                       return;
+
+               /* No data available, ask for more data. */
+               if (ret == 1)
+                       len2 = 0;
+               if (ret == 0)
+                       len1 = 0;
+               if (len1 + len2 < strm->txn->req.eoh + 2) {
+                       si_applet_cant_get(si);
+                       return;
+               }
+
+               /* skip the requests bytes. */
+               bo_skip(si_oc(si), strm->txn->req.eoh + 2);
+       }
+
+       /* Executes The applet if it is not done. */
+       if (!(ctx->ctx.hlua_apphttp.flags & APPLET_DONE)) {
+
+               /* Execute the function. */
+               switch (hlua_ctx_resume(hlua, 1)) {
+               /* finished. */
+               case HLUA_E_OK:
+                       ctx->ctx.hlua_apphttp.flags |= APPLET_DONE;
+                       break;
+
+               /* yield. */
+               case HLUA_E_AGAIN:
+                       return;
+
+               /* finished with error. */
+               case HLUA_E_ERRMSG:
+                       /* Display log. */
+                       SEND_ERR(px, "Lua applet http '%s': %s.\n",
+                                rule->arg.hlua_rule->fcn.name, lua_tostring(hlua->T, -1));
+                       lua_pop(hlua->T, 1);
+                       goto error;
+
+               case HLUA_E_ERR:
+                       /* Display log. */
+                       SEND_ERR(px, "Lua applet http '%s' return an unknown error.\n",
+                                rule->arg.hlua_rule->fcn.name);
+                       goto error;
+
+               default:
+                       goto error;
+               }
+       }
+
+       if (ctx->ctx.hlua_apphttp.flags & APPLET_DONE) {
+
+               /* We must send the final chunk. */
+               if (ctx->ctx.hlua_apphttp.flags & APPLET_CHUNKED &&
+                   !(ctx->ctx.hlua_apphttp.flags & APPLET_LAST_CHK)) {
+
+                       /* sent last chunk at once. */
+                       ret = bi_putblk(res, "0\r\n\r\n", 5);
+
+                       /* critical error. */
+                       if (ret == -2 || ret == -3) {
+                               SEND_ERR(px, "Lua applet http '%s'cannont send last chunk.\n",
+                                        rule->arg.hlua_rule->fcn.name);
+                               goto error;
+                       }
+
+                       /* no enough space error. */
+                       if (ret == -1) {
+                               si_applet_cant_put(si);
+                               return;
+                       }
+
+                       /* set the last chunk sent. */
+                       ctx->ctx.hlua_apphttp.flags |= APPLET_LAST_CHK;
+               }
+
+               /* close the connection. */
+
+               /* status / log */
+               strm->txn->status = ctx->ctx.hlua_apphttp.status;
+               strm->logs.tv_request = now;
+
+               /* eat the whole request */
+               bo_skip(si_oc(si), si_ob(si)->o);
+               res->flags |= CF_READ_NULL;
+               si_shutr(si);
+
+               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 are no room avalaible in the buffer,
+        * just close the connection.
+        */
+       bi_putblk(res, error_500, strlen(error_500));
+       if (!(strm->flags & SF_ERR_MASK))
+               strm->flags |= SF_ERR_RESOURCE;
+       si_shutw(si);
+       si_shutr(si);
+       ctx->ctx.hlua_apphttp.flags |= APPLET_DONE;
+}
+
+static void hlua_applet_http_release(struct appctx *ctx)
+{
+       task_free(ctx->ctx.hlua_apphttp.task);
+       ctx->ctx.hlua_apphttp.task = NULL;
+       hlua_ctx_destroy(&ctx->ctx.hlua_apphttp.hlua);
+}
+
 /* global {tcp|http}-request parser. Return ACT_RET_PRS_OK in
  * succes case, else return ACT_RET_PRS_ERR.
  *
@@ -5111,6 +6031,35 @@ static enum act_parse_ret action_register_lua(const char **args, int *cur_arg, s
        return ACT_RET_PRS_OK;
 }
 
+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 = (struct hlua_function *)rule->kw->private;
+
+       /* 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.name = fcn->name;
+       rule->applet.init = hlua_applet_http_init;
+       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.
@@ -5274,8 +6223,11 @@ __LJMP static int hlua_register_service(lua_State *L)
 
        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
-               WILL_LJMP(luaL_error(L, "lua service environment '%s' is unknown. 'tcp' is expected."));
+               WILL_LJMP(luaL_error(L, "lua service environment '%s' is unknown. "
+                                       "'tcp' or 'http' are expected."));
 
        akl->kw[0].match_pfx = 0;
        akl->kw[0].private = fcn;
@@ -5857,6 +6809,40 @@ void hlua_init(void)
        lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_APPLET_TCP); /* register class session. */
        class_applet_tcp_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
 
+       /*
+        *
+        * Register class AppletHTTP
+        *
+        */
+
+       /* Create and fill the metatable. */
+       lua_newtable(gL.T);
+
+       /* Create the __tostring identifier */
+       lua_pushstring(gL.T, "__tostring");
+       lua_pushstring(gL.T, CLASS_APPLET_HTTP);
+       lua_pushcclosure(gL.T, hlua_dump_object, 1);
+       lua_rawset(gL.T, -3);
+
+       /* Create and fille the __index entry. */
+       lua_pushstring(gL.T, "__index");
+       lua_newtable(gL.T);
+
+       /* Register Lua functions. */
+       hlua_class_function(gL.T, "getline",        hlua_applet_http_getline);
+       hlua_class_function(gL.T, "receive",        hlua_applet_http_recv);
+       hlua_class_function(gL.T, "send",           hlua_applet_http_send);
+       hlua_class_function(gL.T, "add_header",     hlua_applet_http_addheader);
+       hlua_class_function(gL.T, "set_status",     hlua_applet_http_status);
+       hlua_class_function(gL.T, "start_response", hlua_applet_http_start_response);
+
+       lua_settable(gL.T, -3);
+
+       /* Register previous table in the registry with reference and named entry. */
+       lua_pushvalue(gL.T, -1); /* Copy the -1 entry and push it on the stack. */
+       lua_setfield(gL.T, LUA_REGISTRYINDEX, CLASS_APPLET_HTTP); /* register class session. */
+       class_applet_http_ref = luaL_ref(gL.T, LUA_REGISTRYINDEX); /* reference class session. */
+
        /*
         *
         * Register class TXN