MAJOR: lua/htx: Adapt HTTP applets to support HTX messages

This patch is a bit huge but nothing special here. Some functions have been
duplicated to support the HTX, some others have a switch inside to do so. So,
now, it is possible to create HTTP applets from HTX proxies. However, TCP
applets remains unsupported.
diff --git a/src/hlua.c b/src/hlua.c
index 245997b..de4649a 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -3885,10 +3885,6 @@
 	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))
@@ -3931,57 +3927,129 @@
 		return 0;
 	lua_settable(L, -3);
 
-	/* Stores the request method. */
-	lua_pushstring(L, "method");
-	lua_pushlstring(L, ci_head(txn->req.chn), txn->req.sl.rq.m_l);
-	lua_settable(L, -3);
+	if (IS_HTX_STRM(s)) {
+		/* HTX version */
+		struct htx *htx = htxbuf(&s->req.buf);
+		struct htx_sl *sl = http_find_stline(htx);
+		struct ist path;
+		unsigned long long len = 0;
+		int32_t pos;
 
-	/* Stores the http version. */
-	lua_pushstring(L, "version");
-	lua_pushlstring(L, ci_head(txn->req.chn) + txn->req.sl.rq.v, txn->req.sl.rq.v_l);
-	lua_settable(L, -3);
+		/* Stores the request method. */
+		lua_pushstring(L, "method");
+		lua_pushlstring(L, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl));
+		lua_settable(L, -3);
 
-	/* creates an array of headers. hlua_http_get_headers() crates and push
-	 * the array on the top of the stack.
-	 */
-	lua_pushstring(L, "headers");
-	htxn.s = s;
-	htxn.p = px;
-	htxn.dir = SMP_OPT_DIR_REQ;
-	if (!hlua_http_get_headers(L, &htxn, &htxn.s->txn->req))
-		return 0;
-	lua_settable(L, -3);
+		/* Stores the http version. */
+		lua_pushstring(L, "version");
+		lua_pushlstring(L, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl));
+		lua_settable(L, -3);
 
-	/* Get path and qs */
-	path = http_txn_get_path(txn);
-	if (path) {
-		end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
-		p = path;
-		while (p < end && *p != '?')
-			p++;
+		/* creates an array of headers. hlua_http_get_headers() crates and push
+		 * the array on the top of the stack.
+		 */
+		lua_pushstring(L, "headers");
+		htxn.s = s;
+		htxn.p = px;
+		htxn.dir = SMP_OPT_DIR_REQ;
+		if (!hlua_http_get_headers(L, &htxn, &htxn.s->txn->req))
+			return 0;
+		lua_settable(L, -3);
+
+		path = http_get_path(htx_sl_req_uri(sl));
+		if (path.ptr) {
+			char *p, *q, *end;
+
+			p = path.ptr;
+			end = path.ptr + path.len;
+			q = p;
+			while (q < end && *q != '?')
+				q++;
+
+			/* Stores the request path. */
+			lua_pushstring(L, "path");
+			lua_pushlstring(L, p, q - p);
+			lua_settable(L, -3);
+
+			/* Stores the query string. */
+			lua_pushstring(L, "qs");
+			if (*q == '?')
+				q++;
+			lua_pushlstring(L, q, end - q);
+			lua_settable(L, -3);
+		}
+
+		for (pos = htx_get_head(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);
+
+			if (type == HTX_BLK_EOM || type == HTX_BLK_EOD)
+				break;
+			if (type == HTX_BLK_DATA)
+				len += htx_get_blksz(blk);
+		}
+		if (htx->extra != ULLONG_MAX)
+			len += htx->extra;
 
 		/* Stores the request path. */
-		lua_pushstring(L, "path");
-		lua_pushlstring(L, path, p - path);
+		lua_pushstring(L, "length");
+		lua_pushinteger(L, len);
+		lua_settable(L, -3);
+	}
+	else {
+		/* Legacy HTTP version */
+		struct http_txn *txn = s->txn;
+		const char *path;
+		const char *end;
+		const char *p;
+
+		/* Stores the request method. */
+		lua_pushstring(L, "method");
+		lua_pushlstring(L, ci_head(txn->req.chn), txn->req.sl.rq.m_l);
 		lua_settable(L, -3);
 
-		/* Stores the query string. */
-		lua_pushstring(L, "qs");
-		if (*p == '?')
-			p++;
-		lua_pushlstring(L, p, end - p);
+		/* Stores the http version. */
+		lua_pushstring(L, "version");
+		lua_pushlstring(L, ci_head(txn->req.chn) + txn->req.sl.rq.v, txn->req.sl.rq.v_l);
 		lua_settable(L, -3);
-	}
 
-	/* Stores the request path. */
-	lua_pushstring(L, "length");
-	lua_pushinteger(L, txn->req.body_len);
-	lua_settable(L, -3);
+		/* creates an array of headers. hlua_http_get_headers() crates and push
+		 * the array on the top of the stack.
+		 */
+		lua_pushstring(L, "headers");
+		htxn.s = s;
+		htxn.p = px;
+		htxn.dir = SMP_OPT_DIR_REQ;
+		if (!hlua_http_get_headers(L, &htxn, &htxn.s->txn->req))
+			return 0;
+		lua_settable(L, -3);
 
-	/* 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);
+		/* Get path and qs */
+		path = http_txn_get_path(txn);
+		if (path) {
+			end = ci_head(txn->req.chn) + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+			p = path;
+			while (p < end && *p != '?')
+				p++;
+
+			/* Stores the request path. */
+			lua_pushstring(L, "path");
+			lua_pushlstring(L, path, p - path);
+			lua_settable(L, -3);
+
+			/* Stores the query string. */
+			lua_pushstring(L, "qs");
+			if (*p == '?')
+				p++;
+			lua_pushlstring(L, p, end - p);
+			lua_settable(L, -3);
+		}
+
+		/* Stores the request path. */
+		lua_pushstring(L, "length");
+		lua_pushinteger(L, txn->req.body_len);
+		lua_settable(L, -3);
+	}
 
 	/* Create an empty array of HTTP request headers. */
 	lua_pushstring(L, "response");
@@ -4112,10 +4180,123 @@
 	return 1;
 }
 
+__LJMP static void hlua_applet_htx_reply_100_continue(lua_State *L)
+{
+	struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+	struct stream_interface *si = appctx->appctx->owner;
+	struct channel *res = si_ic(si);
+	struct htx *htx = htx_from_buf(&res->buf);
+	struct htx_sl *sl;
+	unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
+			      HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
+	size_t data;
+
+	sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
+			    ist("HTTP/1.1"), ist("100"), ist("Continue"));
+	if (!sl)
+		goto fail;
+	sl->info.res.status = 100;
+	if (!htx_add_endof(htx, HTX_BLK_EOH) || !htx_add_endof(htx, HTX_BLK_EOM))
+		goto fail;
+
+	data = htx->data - co_data(res);
+	res->total += data;
+	res->flags |= CF_READ_PARTIAL;
+	appctx->appctx->ctx.hlua_apphttp.flags &= ~APPLET_100C;
+	return;
+
+  fail:
+	hlua_pusherror(L, "Lua applet http '%s': Failed to create 100-Continue response.\n",
+		       appctx->appctx->rule->arg.hlua_rule->fcn.name);
+	WILL_LJMP(lua_error(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_htx_getline_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 *req = si_oc(si);
+	struct htx *htx;
+	struct htx_blk *blk;
+	size_t count;
+	int stop = 0;
+
+	/* Maybe we cant send a 100-continue ? */
+	if (appctx->appctx->ctx.hlua_apphttp.flags & APPLET_100C)
+		MAY_LJMP(hlua_applet_htx_reply_100_continue(L));
+
+	htx = htx_from_buf(&req->buf);
+	count = co_data(req);
+	blk = htx_get_head_blk(htx);
+	while (count && !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;
+		if (vlen > count) {
+			if (type != HTX_BLK_DATA)
+				break;
+			vlen = count;
+		}
+
+		switch (type) {
+			case HTX_BLK_UNUSED:
+				break;
+
+			case HTX_BLK_DATA:
+				v = htx_get_blk_value(htx, blk);
+				v.len = vlen;
+				nl = istchr(v, '\n');
+				if (nl != NULL) {
+					stop = 1;
+					vlen = nl - v.ptr + 1;
+				}
+				luaL_addlstring(&appctx->b, v.ptr, vlen);
+				break;
+
+			case HTX_BLK_EOD:
+			case HTX_BLK_TLR:
+			case HTX_BLK_EOM:
+				stop = 1;
+				break;
+
+			default:
+				break;
+		}
+
+		co_set_data(req, co_data(req) - vlen);
+		count -= vlen;
+		if (sz == vlen)
+			blk = htx_remove_blk(htx, blk);
+		else {
+			htx_cut_data_blk(htx, blk, vlen);
+			break;
+		}
+	}
+
+	if (!stop) {
+		si_cant_get(si);
+		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_htx_getline_yield, TICK_ETERNITY, 0));
+	}
+
+	/* return the result. */
+	luaL_pushresult(&appctx->b);
+	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 *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
@@ -4194,13 +4375,99 @@
 	/* Initialise the string catenation. */
 	luaL_buffinit(L, &appctx->b);
 
-	return MAY_LJMP(hlua_applet_http_getline_yield(L, 0, 0));
+	if (IS_HTX_STRM(si_strm(appctx->appctx->owner)))
+		return MAY_LJMP(hlua_applet_htx_getline_yield(L, 0, 0));
+	else
+		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_htx_recv_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 *req = si_oc(si);
+	struct htx *htx;
+	struct htx_blk *blk;
+	size_t count;
+	int len;
+
+	/* Maybe we cant send a 100-continue ? */
+	if (appctx->appctx->ctx.hlua_apphttp.flags & APPLET_100C)
+		MAY_LJMP(hlua_applet_htx_reply_100_continue(L));
+
+	htx = htx_from_buf(&req->buf);
+	len = MAY_LJMP(luaL_checkinteger(L, 2));
+	count = co_data(req);
+	blk = htx_get_head_blk(htx);
+	while (count && 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;
+		if (len > 0 && vlen > len)
+			vlen = len;
+		if (vlen > count) {
+			if (type != HTX_BLK_DATA)
+				break;
+			vlen = count;
+		}
+
+		switch (type) {
+			case HTX_BLK_UNUSED:
+				break;
+
+			case HTX_BLK_DATA:
+				v = htx_get_blk_value(htx, blk);
+				luaL_addlstring(&appctx->b, v.ptr, vlen);
+				break;
+
+			case HTX_BLK_EOD:
+			case HTX_BLK_TLR:
+			case HTX_BLK_EOM:
+				len = 0;
+				break;
+
+			default:
+				break;
+		}
+
+		co_set_data(req, co_data(req) - vlen);
+		count -= vlen;
+		if (len > 0)
+			len -= vlen;
+		if (sz == vlen)
+			blk = htx_remove_blk(htx, blk);
+		else {
+			htx_cut_data_blk(htx, blk, vlen);
+			break;
+		}
+	}
+
+	/* 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);
+		}
+		si_cant_get(si);
+		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_htx_recv_yield, TICK_ETERNITY, 0));
+	}
+
+	/* return the result. */
+	luaL_pushresult(&appctx->b);
+	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_recv_yield(lua_State *L, int status, lua_KContext ctx)
 {
 	struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
@@ -4292,15 +4559,80 @@
 		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);
+	if (IS_HTX_STRM(si_strm(appctx->appctx->owner))) {
+		/* HTX version */
+		lua_pushinteger(L, len);
 
-	/* Initialise the string catenation. */
-	luaL_buffinit(L, &appctx->b);
+		/* Initialise the string catenation. */
+		luaL_buffinit(L, &appctx->b);
+
+		return MAY_LJMP(hlua_applet_htx_recv_yield(L, 0, 0));
+	}
+	else {
+		/* Legacy HTTP version */
+		/* 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 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_htx_send_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 *res = si_ic(si);
+	struct htx *htx = htx_from_buf(&res->buf);
+	const char *data;
+	size_t len;
+	int l = MAY_LJMP(luaL_checkinteger(L, 3));
+	int max;
+
+	max = htx_free_space(htx);
+        if (channel_recv_limit(res) < b_size(&res->buf)) {
+                if (max < global.tune.maxrewrite) {
+			si_rx_room_blk(si);
+			MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_htx_send_yield, TICK_ETERNITY, 0));
+		}
+                max -= global.tune.maxrewrite;
+        }
+
+	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. */
+	htx_add_data(htx, ist2(data + l, max));
+	res->total += l;
+	res->flags |= CF_READ_PARTIAL;
+	htx_to_buf(htx, &res->buf);
+
+	/* update counters. */
+	l += max;
+	lua_pop(L, 1);
+	lua_pushinteger(L, l);
 
-	return MAY_LJMP(hlua_applet_http_recv_yield(L, 0, 0));
+	/* If some data is not send, declares the situation to the
+	 * applet, and returns a yield.
+	 */
+	if (l < len) {
+		si_rx_room_blk(si);
+		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_htx_send_yield, TICK_ETERNITY, 0));
+	}
+
+	return 1;
 }
 
 /* Append data in the output side of the buffer. This data is immediately
@@ -4349,32 +4681,43 @@
 __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));
 	}
+
+	if (IS_HTX_STRM(si_strm(appctx->appctx->owner))) {
+		/* HTX version */
+		/* This interger is used for followinf the amount of data sent. */
+		lua_pushinteger(L, 0);
+
+		return MAY_LJMP(hlua_applet_htx_send_yield(L, 0, 0));
+	}
+	else {
+		/* Legacy HTTP version */
+		size_t len;
+		char hex[10];
 
-	return MAY_LJMP(hlua_applet_http_send_yield(L, 0, 0));
+		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);
+
+		return MAY_LJMP(hlua_applet_http_send_yield(L, 0, 0));
+	}
 }
 
 __LJMP static int hlua_applet_http_addheader(lua_State *L)
@@ -4446,10 +4789,219 @@
 	return 1;
 }
 
+
+__LJMP static int hlua_applet_htx_send_response(lua_State *L)
+{
+	struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
+	struct stream_interface *si = appctx->appctx->owner;
+	struct channel *res = si_ic(si);
+	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;
+
+	/* Send the message at once. */
+	htx = htx_from_buf(&res->buf);
+	h1m_init_res(&h1m);
+
+	/* Use the same http version than the request. */
+	status = ultoa_r(appctx->appctx->ctx.hlua_apphttp.status, trash.area, trash.size);
+	reason = appctx->appctx->ctx.hlua_apphttp.reason;
+	if (reason == NULL)
+		reason = http_get_reason(appctx->appctx->ctx.hlua_apphttp.status);
+	if (appctx->appctx->ctx.hlua_apphttp.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",
+		               appctx->appctx->rule->arg.hlua_rule->fcn.name);
+		WILL_LJMP(lua_error(L));
+	}
+	sl->info.res.status = 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_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",
+				       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) {
+			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",
+					       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_tolstring(L, -1, &vlen);
+
+			/* Simple Protocol checks. */
+			if (isteqi(ist2(name, nlen), ist("transfer-encoding")))
+				h1_parse_xfer_enc_header(&h1m, ist2(name, nlen));
+			else if (isteqi(ist2(name, nlen), ist("content-length"))) {
+				struct ist v = ist2(value, vlen);
+				int ret;
+
+				ret = h1_parse_cont_len_header(&h1m, &v);
+				if (ret < 0) {
+					hlua_pusherror(L, "Lua applet http '%s': Invalid '%s' header.\n",
+						       appctx->appctx->rule->arg.hlua_rule->fcn.name,
+						       name);
+					WILL_LJMP(lua_error(L));
+				}
+				else if (ret == 0)
+					goto next; /* Skip it */
+			}
+
+			/* 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",
+					       appctx->appctx->rule->arg.hlua_rule->fcn.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 dont 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 annouces a transfer-encoding
+	 * chunked itslef, don't do anything.
+	 */
+	if ((flags & (HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN)) == HTX_SL_F_VER_11 &&
+	    appctx->appctx->ctx.hlua_apphttp.status >= 200 &&
+	    appctx->appctx->ctx.hlua_apphttp.status != 204 &&
+	    appctx->appctx->ctx.hlua_apphttp.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",
+				       appctx->appctx->rule->arg.hlua_rule->fcn.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",
+			       appctx->appctx->rule->arg.hlua_rule->fcn.name);
+		WILL_LJMP(lua_error(L));
+	}
+
+	if (htx_used_space(htx) > b_size(&res->buf) - global.tune.maxrewrite) {
+		b_reset(&res->buf);
+		hlua_pusherror(L, "Lua: 'start_response': response header block too big");
+		WILL_LJMP(lua_error(L));
+	}
+
+	htx_to_buf(htx, &res->buf);
+	res->total += htx->data;
+	res->flags |= CF_READ_PARTIAL;
+
+	/* Headers sent, set the flag. */
+	appctx->appctx->ctx.hlua_apphttp.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_htx_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 *res = si_ic(si);
+
+	if (co_data(res)) {
+		si_rx_room_blk(si);
+		MAY_LJMP(hlua_yieldk(L, 0, 0, hlua_applet_htx_start_response_yield, TICK_ETERNITY, 0));
+	}
+	return MAY_LJMP(hlua_applet_htx_send_response(L));
+}
+
+
+__LJMP static int hlua_applet_htx_start_response(lua_State *L)
+{
+	return MAY_LJMP(hlua_applet_htx_start_response_yield(L, 0, 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 *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
@@ -4486,7 +5038,7 @@
 
 __LJMP static int hlua_applet_http_start_response(lua_State *L)
 {
-	struct buffer *tmp = get_trash_chunk();
+	struct buffer *tmp;
 	struct hlua_appctx *appctx = MAY_LJMP(hlua_checkapplet_http(L, 1));
 	const char *name;
 	size_t name_len;
@@ -4495,11 +5047,17 @@
 	int id;
 	long long hdr_contentlength = -1;
 	int hdr_chunked = 0;
-	const char *reason = appctx->appctx->ctx.hlua_apphttp.reason;
+	const char *reason;
+
+	if (IS_HTX_STRM(si_strm(appctx->appctx->owner)))
+		return MAY_LJMP(hlua_applet_htx_start_response(L));
 
+	reason = appctx->appctx->ctx.hlua_apphttp.reason;
 	if (reason == NULL)
 		reason = http_get_reason(appctx->appctx->ctx.hlua_apphttp.status);
 
+	tmp = get_trash_chunk();
+
 	/* Use the same http version than the request. */
 	chunk_appendf(tmp, "HTTP/1.%c %d %s\r\n",
 	              appctx->appctx->ctx.hlua_apphttp.flags & APPLET_HTTP11 ? '1' : '0',
@@ -6607,17 +7165,9 @@
 	struct http_txn *txn;
 	struct hlua *hlua;
 	char **arg;
-	struct hdr_ctx hdr;
 	struct task *task;
-	struct sample smp; /* just used for a valid call to smp_prefetch_http. */
 	const char *error;
 
-	/* 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;
 
@@ -6703,10 +7253,27 @@
 
 	/* Look for a 100-continue expected. */
 	if (msg->flags & HTTP_MSGF_VER_11) {
-		hdr.idx = 0;
-		if (http_find_header2("Expect", 6, ci_head(req), &txn->hdr_idx, &hdr) &&
-		    unlikely(hdr.vlen == 12 && strncasecmp(hdr.line+hdr.val, "100-continue", 12) == 0))
-			ctx->ctx.hlua_apphttp.flags |= APPLET_100C;
+		if (IS_HTX_STRM(si_strm(si))) {
+			/* HTX version */
+			struct htx *htx = htxbuf(&req->buf);
+			struct ist hdr = { .ptr = "Expect", .len = 6 };
+                        struct http_hdr_ctx hdr_ctx;
+
+                        hdr_ctx.blk = NULL;
+                        /* Expect is allowed in 1.1, look for it */
+                        if (http_find_header(htx, hdr, &hdr_ctx, 0) &&
+                            unlikely(isteqi(hdr_ctx.value, ist2("100-continue", 12))))
+				ctx->ctx.hlua_apphttp.flags |= APPLET_100C;
+		}
+		else {
+			/* Legacy HTTP version */
+			struct hdr_ctx hdr;
+
+			hdr.idx = 0;
+			if (http_find_header2("Expect", 6, ci_head(req), &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. */
@@ -6729,6 +7296,187 @@
 	return 1;
 }
 
+static void hlua_applet_htx_fct(struct appctx *ctx)
+{
+	struct stream_interface *si = ctx->owner;
+	struct stream *strm = si_strm(si);
+	struct channel *req = si_oc(si);
+	struct channel *res = si_ic(si);
+	struct act_rule *rule = ctx->rule;
+	struct proxy *px = strm->be;
+	struct hlua *hlua = ctx->ctx.hlua_apphttp.hlua;
+	struct htx *req_htx, *res_htx;
+
+	res_htx = htx_from_buf(&res->buf);
+
+	/* If the stream is disconnect or closed, ldo nothing. */
+	if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
+		goto out;
+
+	/* Check if the input buffer is avalaible. */
+	if (!b_size(&res->buf)) {
+		si_rx_room_blk(si);
+		goto out;
+	}
+	/* check that the output is not closed */
+	if (res->flags & (CF_SHUTW|CF_SHUTW_NOW))
+		ctx->ctx.hlua_apphttp.flags |= APPLET_DONE;
+
+	/* Set the currently running flag. */
+	if (!HLUA_IS_RUNNING(hlua) &&
+	    !(ctx->ctx.hlua_apphttp.flags & APPLET_DONE)) {
+		struct htx_blk *blk;
+		size_t count = co_data(req);
+
+		if (!count) {
+			si_cant_get(si);
+			goto out;
+		}
+
+		/* We need to flush the request header. This left the body for
+		 * the Lua.
+		 */
+		req_htx = htx_from_buf(&req->buf);
+		blk = htx_get_head_blk(req_htx);
+		while (count && blk) {
+			enum htx_blk_type type = htx_get_blk_type(blk);
+			uint32_t sz = htx_get_blksz(blk);
+
+			if (sz > count) {
+				si_cant_get(si);
+				goto out;
+			}
+
+			count -= sz;
+			co_set_data(req, co_data(req) - sz);
+			blk = htx_remove_blk(req_htx, blk);
+
+			if (type == HTX_BLK_EOH)
+				break;
+		}
+	}
+
+	/* 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:
+			if (hlua->wake_time != TICK_ETERNITY)
+				task_schedule(ctx->ctx.hlua_apphttp.task, hlua->wake_time);
+			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_ETMOUT:
+			SEND_ERR(px, "Lua applet http '%s': execution timeout.\n",
+			         rule->arg.hlua_rule->fcn.name);
+			goto error;
+
+		case HLUA_E_NOMEM:
+			SEND_ERR(px, "Lua applet http '%s': out of memory error.\n",
+			         rule->arg.hlua_rule->fcn.name);
+			goto error;
+
+		case HLUA_E_YIELD: /* unexpected */
+			SEND_ERR(px, "Lua applet http '%s': yield not allowed.\n",
+			         rule->arg.hlua_rule->fcn.name);
+			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) {
+		if (!(ctx->ctx.hlua_apphttp.flags & APPLET_HDR_SENT))
+			goto error;
+
+		/* Don't add EOD and TLR because mux-h1 will take care of it */
+		if (!htx_add_endof(res_htx, HTX_BLK_EOM)) {
+			si_rx_room_blk(si);
+			goto out;
+		}
+                res->total++;
+		res->flags |= CF_READ_PARTIAL;
+	}
+
+  done:
+	if (ctx->ctx.hlua_apphttp.flags & APPLET_DONE) {
+		/* eat the whole request */
+		req_htx = htxbuf(&req->buf);
+		htx_reset(req_htx);
+		htx_to_buf(req_htx, &req->buf);
+		co_set_data(req, 0);
+		res->flags |= CF_READ_NULL;
+		si_shutr(si);
+	}
+
+	if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
+		si_shutw(si);
+
+	if (ctx->ctx.hlua_apphttp.flags & APPLET_DONE) {
+		if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST)) {
+			si_shutr(si);
+			res->flags |= CF_READ_NULL;
+		}
+	}
+
+  out:
+	/* we have left the request in the buffer for the case where we
+	 * process a POST, and this automatically re-enables activity on
+	 * read. It's better to indicate that we want to stop reading when
+	 * we're sending, so that we know there's at most one direction
+	 * deciding to wake the applet up. It saves it from looping when
+	 * emitting large blocks into small TCP windows.
+	 */
+	htx_to_buf(res_htx, &res->buf);
+	if (!channel_is_empty(res))
+		si_stop_get(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 is no room available in the buffer,
+	 * just close the connection.
+	 */
+	if (!(ctx->ctx.hlua_apphttp.flags & APPLET_HDR_SENT)) {
+		struct buffer *err = &htx_err_chunks[HTTP_ERR_500];
+
+		channel_erase(res);
+		res->buf.data = b_data(err);
+                memcpy(res->buf.area, b_head(err), b_data(err));
+                res_htx = htx_from_buf(&res->buf);
+
+                res->total += res_htx->data;
+		res->flags |= CF_READ_PARTIAL;
+	}
+	if (!(strm->flags & SF_ERR_MASK))
+		strm->flags |= SF_ERR_RESOURCE;
+	ctx->ctx.hlua_apphttp.flags |= APPLET_DONE;
+	goto done;
+}
+
 static void hlua_applet_http_fct(struct appctx *ctx)
 {
 	struct stream_interface *si = ctx->owner;
@@ -6743,6 +7491,9 @@
 	size_t len2;
 	int ret;
 
+	if (IS_HTX_STRM(strm))
+		return hlua_applet_htx_fct(ctx);
+
 	/* If the stream is disconnect or closed, ldo nothing. */
 	if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
 		return;
@@ -6951,11 +7702,6 @@
 {
 	struct hlua_function *fcn = rule->kw->private;
 
-	if (px->options2 & PR_O2_USE_HTX) {
-		memprintf(err, "Lua services cannot be used when the HTX internal representation is enabled");
-		return ACT_RET_PRS_ERR;
-	}
-
 	/* HTTP applets are forbidden in tcp-request rules.
 	 * HTTP applet request requires everything initilized by
 	 * "http_process_request" (analyzer flag AN_REQ_HTTP_INNER).