MAJOR: filters: Adapt filters API to be compatible with the HTX represenation

First, to be called on HTX streams, a filter must explicitly be declared as
compatible by setting the flag STRM_FLT_FL_HAS_FILTERS on the filter's config at
HAProxy startup. This flag is checked when a filter implementation is attached
to a stream.

Then, some changes have been made on HTTP callbacks. The callback http_payload
has been added to filter HTX data. It will be called on HTX streams only. It
replaces the callbacks http_data, http_chunk_trailers and http_forward_data,
called on legacy HTTP streams only and marked as deprecated. The documention
(once updated)) will give all information to implement this new callback. Other
HTTP callbacks will be called for HTX and HTTP legacy streams. So it is the
filter's responsibility to known which kind of data it handles. The macro
IS_HTX_STRM should be used in such cases.

There is at least a noticeable changes in the way data are forwarded. In HTX,
after the call to the callback http_headers, all the headers are considered as
forwarded. So, in http_payload, only the body and eventually the trailers will
be filtered.
diff --git a/src/filters.c b/src/filters.c
index d29a0c0..2b82389 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -27,6 +27,8 @@
 #include <proto/compression.h>
 #include <proto/filters.h>
 #include <proto/flt_http_comp.h>
+#include <proto/http_htx.h>
+#include <proto/htx.h>
 #include <proto/proto_http.h>
 #include <proto/stream.h>
 #include <proto/stream_interface.h>
@@ -335,15 +337,6 @@
 			err += fconf->ops->check(proxy, fconf);
 	}
 	err += check_legacy_http_comp_flt(proxy);
-
-	if (!LIST_ISEMPTY(&proxy->filter_configs) &&
-	    (proxy->options2 & PR_O2_USE_HTX)) {
-		ha_alert("config: %s '%s' : filters cannot be used when "
-			 "the HTX internal representation is enabled.\n",
-			 proxy_type_str(proxy), proxy->id);
-		err++;
-	}
-
 	return err;
 }
 
@@ -394,8 +387,12 @@
 static int
 flt_stream_add_filter(struct stream *s, struct flt_conf *fconf, unsigned int flags)
 {
-	struct filter *f = pool_alloc(pool_head_filter);
+	struct filter *f;
 
+	if ((strm_fe(s)->options2 & PR_O2_USE_HTX) && !(fconf->flags & FLT_CFG_FL_HTX))
+		return 0;
+
+	f = pool_alloc(pool_head_filter);
 	if (!f) /* not enough memory */
 		return -1;
 	memset(f, 0, sizeof(*f));
@@ -540,6 +537,8 @@
  * filters and adjusts available data to be sure that a filter cannot parse more
  * data than its predecessors. A filter can choose to not consume all available
  * data. Returns -1 if an error occurs, the number of consumed bytes otherwise.
+ *
+ * DEPRECATED FUNCTION - CALLED FROM LEGACY HTTP ANALYZERS
  */
 int
 flt_http_data(struct stream *s, struct http_msg *msg)
@@ -602,6 +601,8 @@
  * analyzers. Filters can know how much data were parsed by the HTTP parsing
  * until the last call with the msg->sol value. Returns a negative value if an
  * error occurs, any other value otherwise.
+ *
+ * DEPRECATED FUNCTION - CALLED FROM LEGACY HTTP ANALYZERS
  */
 int
 flt_http_chunk_trailers(struct stream *s, struct http_msg *msg)
@@ -640,6 +641,10 @@
  * functions is called when all data were parsed and forwarded. 'http_end'
  * callback is resumable, so this function returns a negative value if an error
  * occurs, 0 if it needs to wait for some reason, any other value otherwise.
+ *
+ * Be carefull, this function can be called from the HTTP legacy analyzers or
+ * from HTX analyzers. If your filter is compatible with the two modes, use
+ * IS_HTX_STRM macro on the stream.
  */
 int
 flt_http_end(struct stream *s, struct http_msg *msg)
@@ -660,6 +665,10 @@
 /*
  * Calls 'http_reset' callback for all filters attached to a stream. This
  * happens when a 100-continue response is received.
+ *
+ * Be carefull, this function can be called from the HTTP legacy analyzers or
+ * from HTX analyzers. If your filter is compatible with the two modes, use
+ * IS_HTX_STRM macro on the stream.
  */
 void
 flt_http_reset(struct stream *s, struct http_msg *msg)
@@ -675,6 +684,10 @@
 /*
  * Calls 'http_reply' callback for all filters attached to a stream when HA
  * decides to stop the HTTP message processing.
+ *
+ * Be carefull, this function can be called from the HTTP legacy analyzers or
+ * from HTX analyzers. If your filter is compatible with the two modes, use
+ * IS_HTX_STRM macro on the stream.
  */
 void
 flt_http_reply(struct stream *s, short status, const struct buffer *msg)
@@ -688,13 +701,15 @@
 }
 
 /*
- * Calls 'http_forward_data' callback for all "data" filters attached to a
- * stream. This function is called when some data can be forwarded in the
+ * Calls 'http_forward_data' callback for all "data" filters attached to a HTTP
+ * legacy stream. This function is called when some data can be forwarded in the
  * AN_REQ_HTTP_XFER_BODY and AN_RES_HTTP_XFER_BODY analyzers. It takes care to
  * update the forward offset of filters and adjusts "forwardable" data to be
  * sure that a filter cannot forward more data than its predecessors. A filter
  * can choose to not forward all parsed data. Returns a negative value if an
  * error occurs, else the number of forwarded bytes.
+ *
+ * DEPRECATED FUNCTION - CALLED FROM LEGACY HTTP ANALYZERS
  */
 int
 flt_http_forward_data(struct stream *s, struct http_msg *msg, unsigned int len)
@@ -750,6 +765,48 @@
 }
 
 /*
+ * Calls 'http_payload' callback for all "data" filters attached to a
+ * stream. This function is called when some data can be forwarded in the
+ * AN_REQ_HTTP_XFER_BODY and AN_RES_HTTP_XFER_BODY analyzers. It takes care to
+ * update the filters and the stream offset to be sure that a filter cannot
+ * forward more data than its predecessors. A filter can choose to not forward
+ * all data. Returns a negative value if an error occurs, else the number of
+ * forwarded bytes.
+ *
+ * Be carefull, this callback is only called from HTX analyzers. So the
+ * channel's buffer must be considered as an HTX structured. Of course, your
+ * filter must support HTX streams.
+ */
+int
+flt_http_payload(struct stream *s, struct http_msg *msg, unsigned int len)
+{
+	struct filter *filter;
+	unsigned long long *strm_off = &FLT_STRM_OFF(s, msg->chn);
+	unsigned int out = co_data(msg->chn);
+	int ret = len - out;
+
+	list_for_each_entry(filter, &strm_flt(s)->filters, list) {
+		/* Call "data" filters only */
+		if (!IS_DATA_FILTER(filter, msg->chn))
+			continue;
+		if (FLT_OPS(filter)->http_payload) {
+			unsigned long long *flt_off = &FLT_OFF(filter, msg->chn);
+			unsigned int offset = *flt_off - *strm_off;
+
+			ret = FLT_OPS(filter)->http_payload(s, filter, msg, out + offset, ret - offset);
+			if (ret < 0)
+				goto end;
+			*flt_off += ret;
+			ret += offset;
+		}
+	}
+	*strm_off += ret;
+
+ end:
+	return ret;
+}
+
+/*
  * Calls 'channel_start_analyze' callback for all filters attached to a
  * stream. This function is called when we start to analyze a request or a
  * response. For frontend filters, it is called before all other analyzers. For
@@ -851,6 +908,10 @@
  * This function is the AN_REQ/RES_FLT_HTTP_HDRS analyzer, used to filter HTTP
  * headers or a request or a response. Returns 0 if an error occurs or if it
  * needs to wait, any other value otherwise.
+ *
+ * Be carefull, this function can be called from the HTTP legacy analyzers or
+ * from HTX analyzers. If your filter is compatible with the two modes, use
+ * IS_HTX_STRM macro on the stream.
  */
 int
 flt_analyze_http_headers(struct stream *s, struct channel *chn, unsigned int an_bit)
@@ -868,15 +929,28 @@
 		}
 	} RESUME_FILTER_END;
 
-	/* We increase next offset of all "data" filters after all processing on
-	 * headers because any filter can alter them. So the definitive size of
-	 * headers (msg->sov) is only known when all filters have been
-	 * called. */
-	list_for_each_entry(filter, &strm_flt(s)->filters, list) {
-		/* Handle "data" filters only */
-		if (!IS_DATA_FILTER(filter, chn))
-			continue;
-		FLT_NXT(filter, chn) = msg->sov;
+	if (IS_HTX_STRM(s)) {
+		struct htx *htx = htx_from_buf(&chn->buf);
+		int32_t pos;
+
+		for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) {
+			struct htx_blk *blk = htx_get_blk(htx, pos);
+			c_adv(chn, htx_get_blksz(blk));
+			if (htx_get_blk_type(blk) == HTX_BLK_EOH)
+				break;
+		}
+	}
+	else {
+		/* We increase next offset of all "data" filters after all processing on
+		 * headers because any filter can alter them. So the definitive size of
+		 * headers (msg->sov) is only known when all filters have been
+		 * called. */
+		list_for_each_entry(filter, &strm_flt(s)->filters, list) {
+			/* Handle "data" filters only */
+			if (!IS_DATA_FILTER(filter, chn))
+				continue;
+			FLT_NXT(filter, chn) = msg->sov;
+		}
 	}
 
  check_result: