MEDIUM: da: HTX mode support.

The DeviceAtlas module now can support both the legacy
mode and the new HTX's with the known set of support headers
for the latter.
diff --git a/src/da.c b/src/da.c
index 50862a1..6c7ce81 100644
--- a/src/da.c
+++ b/src/da.c
@@ -7,6 +7,7 @@
 #include <types/global.h>
 #include <proto/arg.h>
 #include <proto/http_fetch.h>
+#include <proto/http_htx.h>
 #include <proto/log.h>
 #include <proto/proto_http.h>
 #include <proto/sample.h>
@@ -285,83 +286,164 @@
 
 static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
-	struct hdr_idx *hidx;
-	struct hdr_ctx hctx;
-	const struct http_msg *hmsg;
 	da_evidence_t ev[DA_MAX_HEADERS];
 	da_deviceinfo_t devinfo;
 	da_status_t status;
+	struct channel *chn;
 	char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }};
 	int i, nbh = 0;
 
 	if (global_deviceatlas.daset == 0) {
-		return 1;
+		return 0;
 	}
 
-	CHECK_HTTP_MESSAGE_FIRST((smp->strm ? &smp->strm->req : NULL));
-	smp->data.type = SMP_T_STR;
+	chn = (smp->strm ? &smp->strm->req : NULL);
+	/* HTX Mode check */
+	if (smp->px->options2 & PR_O2_USE_HTX) {
+		struct htx_blk *blk;
+		struct htx *htx = smp_prefetch_htx(smp, chn, 1);
+		if (!htx) {
+			return 0;
+		}
 
-	/**
-	 * Here we go through the whole list of headers from start
-	 * they will be filtered via the DeviceAtlas API itself
-	 */
-	hctx.idx = 0;
-	hidx = &smp->strm->txn->hdr_idx;
-	hmsg = &smp->strm->txn->req;
+		i = 0;
+		for (blk = htx_get_head_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) {
+			size_t vlen;
+			char *pval;
+			da_evidence_id_t evid;
+			enum htx_blk_type type;
+			struct ist n, v;
+			char hbuf[24] = { 0 };
+			char tval[1024] = { 0 };
 
-	while (http_find_next_header(ci_head(hmsg->chn), hidx, &hctx) == 1 &&
-	        nbh < DA_MAX_HEADERS) {
-		char *pval;
-		size_t vlen;
-		da_evidence_id_t evid = -1;
-		char hbuf[24] = { 0 };
+			type = htx_get_blk_type(blk);
 
-		/* The HTTP headers used by the DeviceAtlas API are not longer */
-		if (hctx.del >= sizeof(hbuf) || hctx.del <= 0 || hctx.vlen <= 0) {
-			continue;
-		}
+			if (type == HTX_BLK_HDR) {
+				n = htx_get_blk_name(htx, blk);
+				v = htx_get_blk_value(htx, blk);
+			} else if (type == HTX_BLK_EOH) {
+				break;
+			} else {
+				continue;
+			}
+
+			/* The HTTP headers used by the DeviceAtlas API are not longer */
+			if (n.len >= sizeof(hbuf)) {
+				continue;
+			}
+
+			memcpy(hbuf, n.ptr, n.len);
+			hbuf[n.len] = 0;
+			pval = v.ptr;
+			vlen = v.len;
+			evid = -1;
+			i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len;
+			memcpy(tval, v.ptr, i);
+			tval[i] = 0;
+			pval = tval;
+
+			if (strcasecmp(hbuf, "Accept-Language") == 0) {
+				evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas);
+			} else if (strcasecmp(hbuf, "Cookie") == 0) {
+				char *p, *eval;
+				size_t pl;
 
-		vlen = hctx.vlen;
-		memcpy(hbuf, hctx.line, hctx.del);
-		hbuf[hctx.del] = 0;
-		pval = (hctx.line + hctx.val);
+				eval = pval + vlen;
+				/**
+				 * The cookie value, if it exists, is located between the current header's
+				 * value position and the next one
+				 */
+				if (http_extract_cookie_value(pval, eval, global_deviceatlas.cookiename,
+							global_deviceatlas.cookienamelen, 1, &p, &pl) == NULL) {
+					continue;
+				}
 
-		if (strcmp(hbuf, "Accept-Language") == 0) {
-			evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.
-				atlas);
-		} else if (strcmp(hbuf, "Cookie") == 0) {
-			char *p, *eval;
-			size_t pl;
+				vlen -= global_deviceatlas.cookienamelen - 1;
+				pval = p;
+				evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
+			} else {
+				evid = da_atlas_header_evidence_id(&global_deviceatlas.atlas, hbuf);
+			}
 
-			eval = pval + hctx.vlen;
-			/**
-			 * The cookie value, if it exists, is located between the current header's
-			 * value position and the next one
-			 */
-			if (http_extract_cookie_value(pval, eval, global_deviceatlas.cookiename,
-				global_deviceatlas.cookienamelen, 1, &p, &pl) == NULL) {
+			if (evid == -1) {
 				continue;
 			}
 
-			vlen = (size_t)pl;
-			pval = p;
-			evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
-		} else {
-			evid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
-				hbuf);
+			i = vlen > sizeof(vbuf[nbh]) - 1 ? sizeof(vbuf[nbh]) - 1 : vlen;
+			memcpy(vbuf[nbh], pval, i);
+			vbuf[nbh][i] = 0;
+			ev[nbh].key = evid;
+			ev[nbh].value = vbuf[nbh];
+			++ nbh;
 		}
+	} else {
+		struct hdr_idx *hidx;
+		struct hdr_ctx hctx;
+		const struct http_msg *hmsg;
+		CHECK_HTTP_MESSAGE_FIRST(chn);
+		smp->data.type = SMP_T_STR;
 
-		if (evid == -1) {
-			continue;
-		}
+		/**
+		 * Here we go through the whole list of headers from start
+		 * they will be filtered via the DeviceAtlas API itself
+		 */
+		hctx.idx = 0;
+		hidx = &smp->strm->txn->hdr_idx;
+		hmsg = &smp->strm->txn->req;
+
+		while (http_find_next_header(ci_head(hmsg->chn), hidx, &hctx) == 1 &&
+				nbh < DA_MAX_HEADERS) {
+			char *pval;
+			size_t vlen;
+			da_evidence_id_t evid = -1;
+			char hbuf[24] = { 0 };
+
+			/* The HTTP headers used by the DeviceAtlas API are not longer */
+			if (hctx.del >= sizeof(hbuf) || hctx.del <= 0 || hctx.vlen <= 0) {
+				continue;
+			}
+
+			vlen = hctx.vlen;
+			memcpy(hbuf, hctx.line, hctx.del);
+			hbuf[hctx.del] = 0;
+			pval = (hctx.line + hctx.val);
 
-		i = vlen > sizeof(vbuf[nbh]) ? sizeof(vbuf[nbh]) : vlen;
-		memcpy(vbuf[nbh], pval, i - 1);
-		vbuf[nbh][i - 1] = 0;
-		ev[nbh].key = evid;
-		ev[nbh].value = vbuf[nbh];
-		ev[nbh].value[vlen] = 0;
-		++ nbh;
+			if (strcmp(hbuf, "Accept-Language") == 0) {
+				evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.
+						atlas);
+			} else if (strcmp(hbuf, "Cookie") == 0) {
+				char *p, *eval;
+				size_t pl;
+
+				eval = pval + hctx.vlen;
+				/**
+				 * The cookie value, if it exists, is located between the current header's
+				 * value position and the next one
+				 */
+				if (http_extract_cookie_value(pval, eval, global_deviceatlas.cookiename,
+							global_deviceatlas.cookienamelen, 1, &p, &pl) == NULL) {
+					continue;
+				}
+
+				vlen = (size_t)pl;
+				pval = p;
+				evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
+			} else {
+				evid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
+						hbuf);
+			}
+
+			if (evid == -1) {
+				continue;
+			}
+
+			i = vlen > sizeof(vbuf[nbh]) ? sizeof(vbuf[nbh]) : vlen;
+			memcpy(vbuf[nbh], pval, i - 1);
+			vbuf[nbh][i - 1] = 0;
+			ev[nbh].key = evid;
+			ev[nbh].value = vbuf[nbh];
+			++ nbh;
+		}
 	}
 
 	status = da_searchv(&global_deviceatlas.atlas, &devinfo,
@@ -372,10 +454,10 @@
 
 static struct cfg_kw_list dacfg_kws = {{ }, {
 	{ CFG_GLOBAL, "deviceatlas-json-file",	  da_json_file },
-	{ CFG_GLOBAL, "deviceatlas-log-level",	  da_log_level },
-	{ CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator },
-	{ CFG_GLOBAL, "deviceatlas-properties-cookie", da_properties_cookie },
-	{ 0, NULL, NULL },
+		{ CFG_GLOBAL, "deviceatlas-log-level",	  da_log_level },
+		{ CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator },
+		{ CFG_GLOBAL, "deviceatlas-properties-cookie", da_properties_cookie },
+		{ 0, NULL, NULL },
 }};
 
 INITCALL1(STG_REGISTER, cfg_register_keywords, &dacfg_kws);
@@ -383,7 +465,7 @@
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct sample_fetch_kw_list fetch_kws = {ILH, {
 	{ "da-csv-fetch", da_haproxy_fetch, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
-	{ NULL, NULL, 0, 0, 0 },
+		{ NULL, NULL, 0, 0, 0 },
 }};
 
 INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws);
@@ -391,7 +473,7 @@
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct sample_conv_kw_list conv_kws = {ILH, {
 	{ "da-csv-conv", da_haproxy_conv, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR },
-	{ NULL, NULL, 0, 0, 0 },
+		{ NULL, NULL, 0, 0, 0 },
 }};
 
 static void da_haproxy_register_build_options()