MINOR: http_ext: add rfc7239_field converter

Adding new http converter: rfc7239_field.

Takes a string representing 7239 forwarded header single value as
input and extracts a single field/parameter from the header according
to user selection.

  Example:
    # extract host field from forwarded header and store it in req.fhost var
    http-request set-var(req.fhost) req.hdr(forwarded),rfc7239_field(host)
    #input: "proto=https;host=\"haproxy.org:80\""
    #  output: "haproxy.org:80"

    # extract for field from forwarded header and store it in req.ffor var
    http-request set-var(req.ffor) req.hdr(forwarded),rfc7239_field(for)
    #input: "proto=https;host=\"haproxy.org:80\";for=\"127.0.0.1:9999\""
    #  output: "127.0.0.1:9999"

Depends on:
  - "MINOR: http_ext: introduce http ext converters"
diff --git a/doc/configuration.txt b/doc/configuration.txt
index a40f090..b0c64a5 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -17228,6 +17228,29 @@
     #input: "proto=custom"
     #  output: FALSE
 
+rfc7239_field(<field>)
+  Extracts a single field/parameter from RFC 7239 compliant header value input.
+
+  Supported fields are:
+    - proto: either 'http' or 'https'
+    - host: http compliant host
+    - for: RFC7239 node
+    - by: RFC7239 node
+
+  More info here:
+    https://www.rfc-editor.org/rfc/rfc7239.html#section-6
+
+  Example:
+    # extract host field from forwarded header and store it in req.fhost var
+    http-request set-var(req.fhost) req.hdr(forwarded),rfc7239_field(host)
+    #input: "proto=https;host=\"haproxy.org:80\""
+    #  output: "haproxy.org:80"
+
+    # extract for field from forwarded header and store it in req.ffor var
+    http-request set-var(req.ffor) req.hdr(forwarded),rfc7239_field(for)
+    #input: "proto=https;host=\"haproxy.org:80\";for=\"127.0.0.1:9999\""
+    #  output: "127.0.0.1:9999"
+
 add(<value>)
   Adds <value> to the input value of type signed integer, and returns the
   result as a signed integer. <value> can be a numeric value or a variable
diff --git a/src/http_ext.c b/src/http_ext.c
index 1b89947..ba8bec8 100644
--- a/src/http_ext.c
+++ b/src/http_ext.c
@@ -1383,9 +1383,60 @@
 	return 1;
 }
 
+/* input: string representing 7239 forwarded header single value
+ * argument: parameter name to look for in the header
+ * output: header parameter raw value, as a string
+ */
+static int sample_conv_7239_field(const struct arg *args, struct sample *smp, void *private)
+{
+	struct ist input = ist2(smp->data.u.str.area, smp->data.u.str.data);
+	struct buffer *output;
+	struct forwarded_header_ctx ctx;
+	int validate;
+	int field = 0;
+
+	if (strcmp(args->data.str.area, "proto") == 0)
+		field = FORWARDED_HEADER_PROTO;
+	else if (strcmp(args->data.str.area, "host") == 0)
+		field = FORWARDED_HEADER_HOST;
+	else if (strcmp(args->data.str.area, "for") == 0)
+		field = FORWARDED_HEADER_FOR;
+	else if (strcmp(args->data.str.area, "by") == 0)
+		field = FORWARDED_HEADER_BY;
+
+	validate = http_validate_7239_header(input, FORWARDED_HEADER_ALL, &ctx);
+	if (!(validate & field))
+		return 0; /* invalid header or header does not contain field */
+	output = get_trash_chunk();
+	switch (field) {
+		case FORWARDED_HEADER_PROTO:
+			if (ctx.proto == FORWARDED_HEADER_HTTP)
+				chunk_appendf(output, "http");
+			else if (ctx.proto == FORWARDED_HEADER_HTTPS)
+				chunk_appendf(output, "https");
+			break;
+		case FORWARDED_HEADER_HOST:
+			chunk_istcat(output, ctx.host);
+			break;
+		case FORWARDED_HEADER_FOR:
+			chunk_istcat(output, ctx.nfor.raw);
+			break;
+		case FORWARDED_HEADER_BY:
+			chunk_istcat(output, ctx.nby.raw);
+			break;
+		default:
+			break;
+	}
+	smp->flags &= ~SMP_F_CONST;
+	smp->data.type = SMP_T_STR;
+	smp->data.u.str = *output;
+	return 1;
+}
+
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct sample_conv_kw_list sample_conv_kws = {ILH, {
 	{ "rfc7239_is_valid",  sample_conv_7239_valid,   0,                NULL,   SMP_T_STR,  SMP_T_BOOL},
+	{ "rfc7239_field",     sample_conv_7239_field,   ARG1(1,STR),      NULL,   SMP_T_STR,  SMP_T_STR},
 	{ NULL, NULL, 0, 0, 0 },
 }};