MEDIUM: htx: Store the first block position instead of the start-line one

We don't store the start-line position anymore in the HTX message. Instead we
store the first block position to analyze. For now, it is almost the same. But
once all changes will be made on this part, this position will have to be used
by HTX analyzers, and only in the analysis context, to know where the analyse
should start.

When new blocks are added in an HTX message, if the first block position is not
defined, it is set. When the block pointed by it is removed, it is set to the
block following it. -1 remains the value to unset the position. the first block
position is unset when the HTX message is empty. It may also be unset on a
non-empty message, meaning every blocks were already analyzed.

From HTX analyzers point of view, this position is always set during headers
analysis. When they are waiting for a request or a response, if it is unset, it
means the analysis should wait. But once the analysis is started, and as long as
headers are not forwarded, it points to the message start-line.

As mentionned, outside the HTX analysis, no code must rely on the first block
position. So multiplexers and applets must always use the head position to start
a loop on an HTX message.
diff --git a/include/common/htx.h b/include/common/htx.h
index ec99098..25c28c8 100644
--- a/include/common/htx.h
+++ b/include/common/htx.h
@@ -161,7 +161,7 @@
 	uint64_t extra;  /* known bytes amount remaining to receive */
 	uint32_t flags;  /* HTX_FL_* */
 
-	int32_t sl_pos; /* position of the start-line of the HTTP message. -1 if unset */
+	int32_t  first;  /* position of the first block to (re)start the analyse. -1 if unset. */
 
 	struct htx_blk blocks[0]; /* Blocks representing the HTTP message itself */
 };
@@ -413,8 +413,8 @@
 	return (blk ? htx_get_blk_type(blk) : HTX_BLK_UNUSED);
 }
 
-/* Returns the position of the first block in the HTX message <htx>. It is the
- * sl_pos if set, otherwise it is the head.
+/* Returns the position of the first block in the HTX message <htx>. If unset,
+ * or if <htx> is empty, -1 is returned.
  *
  * An signed 32-bits integer is returned to handle -1 case. Blocks position are
  * store on unsigned 32-bits integer, but it is impossible to have so much
@@ -422,13 +422,13 @@
  */
 static inline int32_t htx_get_first(const struct htx *htx)
 {
-	if (htx->sl_pos != -1)
-		return htx->sl_pos;
-	return htx->head;
+	if (!htx->used)
+		return -1;
+	return htx->first;
 }
 
-/* Returns the first HTX block in the HTX message <htx>. If <blk> is the head,
- * NULL returned.
+/* Returns the first HTX block in the HTX message <htx>. If unset or if <htx> is
+ * empty, NULL returned.
  */
 static inline struct htx_blk *htx_get_first_blk(const struct htx *htx)
 {
@@ -717,7 +717,7 @@
 	htx->data = htx->used = htx->tail = htx->head  = htx->front = 0;
 	htx->extra = 0;
 	htx->flags = HTX_FL_NONE;
-	htx->sl_pos = -1;
+	htx->first = -1;
 }
 
 /* returns the available room for raw data in buffer <buf> once HTX overhead is
@@ -832,8 +832,8 @@
 	fprintf(stderr, "htx:%p [ size=%u - data=%u - used=%u - wrap=%s - extra=%llu]\n",
 		htx, htx->size, htx->data, htx->used, (htx->tail >= htx->head) ? "NO" : "YES",
 		(unsigned long long)htx->extra);
-	fprintf(stderr, "\tsl_pos=%d - head=%u, tail=%u - front=%u\n",
-		htx->sl_pos, htx->head, htx->tail, htx->front);
+	fprintf(stderr, "\tfirst=%d - head=%u, tail=%u - front=%u\n",
+		htx->first, htx->head, htx->tail, htx->front);
 
 	for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
 		struct htx_sl     *sl;
diff --git a/include/proto/channel.h b/include/proto/channel.h
index 077b3ae..3ebaf7f 100644
--- a/include/proto/channel.h
+++ b/include/proto/channel.h
@@ -923,7 +923,7 @@
 		struct htx_blk *blk = htx_get_blk(htx, pos);
 		data += htx_get_blksz(blk);
 		if (htx_get_blk_type(blk) == HTX_BLK_EOH) {
-			htx->sl_pos = htx_get_next(htx, pos);
+			htx->first = htx_get_next(htx, pos);
 			break;
 		}
 	}
diff --git a/src/cache.c b/src/cache.c
index 1fec57e..4371910 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -913,10 +913,6 @@
 		if (!blk)
 			return 0;
 
-		/* Set the start-line offset */
-		if (type == HTX_BLK_RES_SL)
-			htx->sl_pos = htx_get_blk_pos(htx, blk);
-
 		/* Copy info and data */
 		blk->info = info;
 		memcpy(htx_get_blk_ptr(htx, blk), b_peek(tmp, offset+4), sz);
diff --git a/src/hlua.c b/src/hlua.c
index bb8c93d..3c9c70a 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -3944,11 +3944,16 @@
 	if (IS_HTX_STRM(s)) {
 		/* HTX version */
 		struct htx *htx = htxbuf(&s->req.buf);
-		struct htx_sl *sl = http_get_stline(htx);
+		struct htx_blk *blk;
+		struct htx_sl *sl;
 		struct ist path;
 		unsigned long long len = 0;
 		int32_t pos;
 
+		blk = htx_get_first_blk(htx);
+		BUG_ON(htx_get_blk_type(blk) != HTX_BLK_REQ_SL);
+		sl = htx_get_blk_ptr(htx, blk);
+
 		/* Stores the request method. */
 		lua_pushstring(L, "method");
 		lua_pushlstring(L, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl));
@@ -4366,7 +4371,7 @@
 	htx = htx_from_buf(&req->buf);
 	len = MAY_LJMP(luaL_checkinteger(L, 2));
 	count = co_data(req);
-	blk = htx_get_first_blk(htx);
+	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);
diff --git a/src/http_fetch.c b/src/http_fetch.c
index 05074d6..a184f2f 100644
--- a/src/http_fetch.c
+++ b/src/http_fetch.c
@@ -203,7 +203,7 @@
 
 		if (msg->msg_state < HTTP_MSG_BODY) {
 			/* Analyse not yet started */
-			if (htx_is_empty(htx) || htx->sl_pos == -1) {
+			if (htx_is_empty(htx) || htx->first == -1) {
 				/* Parsing is done by the mux, just wait */
 				smp->flags |= SMP_F_MAY_CHANGE;
 				return NULL;
diff --git a/src/http_htx.c b/src/http_htx.c
index 15865d5..b9501be 100644
--- a/src/http_htx.c
+++ b/src/http_htx.c
@@ -11,6 +11,7 @@
  */
 
 #include <common/config.h>
+#include <common/debug.h>
 #include <common/cfgparse.h>
 #include <common/h1.h>
 #include <common/http.h>
@@ -21,19 +22,18 @@
 struct buffer htx_err_chunks[HTTP_ERR_SIZE];
 
 /* Returns the next unporocessed start line in the HTX message. It returns NULL
- * is the start-line is undefined (sl_pos == 1). Otherwise, it returns the
+ * if the start-line is undefined (first == -1). Otherwise, it returns the
  * pointer on the htx_sl structure.
  */
 struct htx_sl *http_get_stline(struct htx *htx)
 {
 	struct htx_blk *blk;
 
-	if (htx->sl_pos == -1)
-		return NULL;
-
-	blk = htx_get_blk(htx, htx->sl_pos);
+	BUG_ON(htx->first == -1);
+	blk = htx_get_first_blk(htx);
 	if (!blk)
 		return NULL;
+	BUG_ON(htx_get_blk_type(blk) != HTX_BLK_REQ_SL && htx_get_blk_type(blk) != HTX_BLK_RES_SL);
 	return htx_get_blk_ptr(htx, blk);
 }
 
@@ -143,7 +143,7 @@
 
 	/* <blk> is the head, swap it iteratively with its predecessor to place
 	 * it just before the end-of-header block. So blocks remains ordered. */
-	for (prev = htx_get_prev(htx, htx->tail); prev != htx->sl_pos; prev = htx_get_prev(htx, prev)) {
+	for (prev = htx_get_prev(htx, htx->tail); prev != htx->first; prev = htx_get_prev(htx, prev)) {
 		struct htx_blk   *pblk = htx_get_blk(htx, prev);
 		enum htx_blk_type type = htx_get_blk_type(pblk);
 
@@ -169,18 +169,14 @@
 }
 
 /* Replaces parts of the start-line of the HTX message <htx>. It returns 1 on
- * success, otherwise it returns 0. The right block is search in the HTX
- * message.
+ * success, otherwise it returns 0.
  */
 int http_replace_stline(struct htx *htx, const struct ist p1, const struct ist p2, const struct ist p3)
 {
 	struct htx_blk *blk;
 
-	if (htx->sl_pos == -1)
-		return 0;
-
-	blk = htx_get_blk(htx, htx->sl_pos);
-	if (!htx_replace_stline(htx, blk, p1, p2, p3))
+	blk = htx_get_first_blk(htx);
+	if (!blk || !htx_replace_stline(htx, blk, p1, p2, p3))
 		return 0;
 	return 1;
 }
diff --git a/src/htx.c b/src/htx.c
index a7623b6..dbf8dea 100644
--- a/src/htx.c
+++ b/src/htx.c
@@ -28,7 +28,7 @@
 	struct htx_blk *newblk, *oldblk;
 	uint32_t new, old, blkpos;
 	uint32_t addr, blksz;
-	int32_t sl_pos = -1;
+	int32_t first = -1;
 
 	if (!htx->used)
 		return NULL;
@@ -51,8 +51,8 @@
 		blksz = htx_get_blksz(oldblk);
 
 		/* update the start-line position */
-		if (htx->sl_pos == old)
-			sl_pos = new;
+		if (htx->first == old)
+			first = new;
 
 		/* if <blk> is defined, set its new position */
 		if (blk != NULL && blk == oldblk)
@@ -65,7 +65,7 @@
 	}
 
 	htx->used = new;
-	htx->sl_pos = sl_pos;
+	htx->first = first;
 	htx->head = 0;
 	htx->front = htx->tail = new - 1;
 	memcpy((void *)htx->blocks, (void *)tmp->blocks, htx->size);
@@ -94,7 +94,7 @@
 
 	if (!htx->used) {
 		/* Empty message */
-		htx->front = htx->head = htx->tail = 0;
+		htx->front = htx->head = htx->tail = htx->first = 0;
 		htx->used = 1;
 		blk = htx_get_blk(htx, htx->tail);
 		blk->addr = 0;
@@ -188,6 +188,9 @@
 	htx->tail  = tail;
 	htx->used  = used;
 	htx->data += blksz;
+	/* Set first position if not already set */
+	if (htx->first == -1)
+		htx->first = tail;
 	return blk;
 }
 
@@ -220,15 +223,11 @@
 		/* Mark the block as unused, decrement allocated size */
 		htx->data -= htx_get_blksz(blk);
 		blk->info = ((uint32_t)HTX_BLK_UNUSED << 28);
-		if (htx->sl_pos == pos)
-			htx->sl_pos = -1;
 	}
 
 	/* This is the last block in use */
-	if (htx->used == 1/* || !htx->data */) {
-		htx->front = htx->head = htx->tail = 0;
-		htx->used = 0;
-		htx->data = 0;
+	if (htx->used == 1) {
+		htx_reset(htx);
 		return NULL;
 	}
 
@@ -271,13 +270,9 @@
 	}
 
 	blk = htx_get_blk(htx, next);
-	if (htx->sl_pos == -1) {
-		/* Try to update the start-line payload addr, if possible */
-		type = htx_get_blk_type(blk);
-		if (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL)
-			htx->sl_pos = htx_get_blk_pos(htx, blk);
-	}
   end:
+	if (pos == htx->first)
+		htx->first = (blk ? htx_get_blk_pos(htx, blk) : -1);
 	if (pos == htx->front)
 		htx->front = htx_find_front(htx);
 	return blk;
@@ -542,9 +537,6 @@
 			htx_cut_data_blk(src, blk, sz);
 			break;
 		}
-
-		if (dst->sl_pos == -1 && src->sl_pos == htx_get_blk_pos(src, blk))
-			dst->sl_pos = htx_get_blk_pos(dst, dstblk);
 	  next:
 		blk = htx_remove_blk(src, blk);
 		if (type == mark)
@@ -682,8 +674,6 @@
 	blk->info += size;
 
 	sl = htx_get_blk_ptr(htx, blk);
-	if (htx->sl_pos == -1)
-		htx->sl_pos = htx_get_blk_pos(htx, blk);
 	sl->hdrs_bytes = -1;
 	sl->flags = flags;
 
diff --git a/src/proto_htx.c b/src/proto_htx.c
index dc0762d..7cc26ba 100644
--- a/src/proto_htx.c
+++ b/src/proto_htx.c
@@ -132,7 +132,7 @@
 	 * a timeout or connection reset is not counted as an error. However
 	 * a bad request is.
 	 */
-	if (unlikely(htx_is_empty(htx) || htx->sl_pos == -1)) {
+	if (unlikely(htx_is_empty(htx) || htx->first == -1)) {
 		if (htx->flags & HTX_FL_UPGRADE)
 			goto failed_keep_alive;
 
@@ -279,6 +279,7 @@
 	txn->flags &= ~TX_WAIT_NEXT_RQ;
 	req->analyse_exp = TICK_ETERNITY;
 
+	BUG_ON(htx_get_first_type(htx) != HTX_BLK_REQ_SL);
 	sl = http_get_stline(htx);
 
 	/* 0: we might have to print this header in debug mode */
@@ -1470,7 +1471,7 @@
 	 * errors somewhere else.
 	 */
   next_one:
-	if (unlikely(htx_is_empty(htx) || htx->sl_pos == -1)) {
+	if (unlikely(htx_is_empty(htx) || htx->first == -1)) {
 		/* 1: have we encountered a read error ? */
 		if (rep->flags & CF_READ_ERROR) {
 			struct connection *conn = NULL;
@@ -1625,6 +1626,7 @@
 	 */
 
 	msg->msg_state = HTTP_MSG_BODY;
+	BUG_ON(htx_get_first_type(htx) != HTX_BLK_RES_SL);
 	sl = http_get_stline(htx);
 
 	/* 0: we might have to print this header in debug mode */