CONTRIB: opentracing: add the OpenTracing filter

This commit adds the OpenTracing filter (hereinafter we will use the
abbreviated name 'the OT filter') to the contrib tree.

The OT filter adds native support for using distributed tracing in HAProxy.
This is enabled by sending an OpenTracing compliant request to one of the
supported tracers; such as Datadog, Jaeger, Lightstep and Zipkin tracers.
Please note: tracers are not listed by any preference, but alphabetically.

The OT filter is a standard HAProxy filter, so what applies to others also
applies to this one (of course, by that I mean what is described in the
documentation, more precisely in the doc/internals/filters.txt file).

The OT filter activation is done explicitly by specifying it in the HAProxy
configuration.  If this is not done, the OT filter in no way participates
in the work of HAProxy.

As for the impact on HAProxy speed, this is documented with several tests
located in the test directory, and the result is found in the README-speed-*
files.  In short, the speed of operation depends on the way it is used and
the complexity of the configuration, from an almost immeasurable impact to
a significant deceleration (5x and more).  I think that in some normal use
the speed of HAProxy with the filter on will be quite satisfactory with a
slowdown of less than 4%.

The OT filter allows intensive use of ACLs, which can be defined anywhere in
the configuration.  Thus, it is possible to use the filter only for those
connections that are of interest to us.

More detailed documentation related to the operation, configuration and use
of the filter can be found in the contrib/opentracing directory.

To make the OpenTracing filter easier to configure and compile, several
entries have been added to the Makefile.  When running the make utility,
it is possible to use several new arguments:

  USE_OT=1     : enable the OpenTracing filter
  OT_DEBUG=1   : compile the OpenTracing filter in debug mode
  OT_INC=path  : force the include path to libopentracing-c-wrapper
  OT_LIB=path  : force the lib path to libopentracing-c-wrapper
  OT_RUNPATH=1 : add libopentracing-c-wrapper RUNPATH to haproxy executable

If USE_OT is set, then an additional Makefile from the contrib/opentracing
directory is included in the compilation process.
diff --git a/contrib/opentracing/src/cli.c b/contrib/opentracing/src/cli.c
new file mode 100644
index 0000000..529c687
--- /dev/null
+++ b/contrib/opentracing/src/cli.c
@@ -0,0 +1,395 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+/***
+ * NAME
+ *   flt_ot_cli_set_msg -
+ *
+ * ARGUMENTS
+ *   appctx    -
+ *   err       -
+ *   msg       -
+ *   cli_state -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void cmn_cli_set_msg(struct appctx *appctx, char *err, char *msg, int cli_state)
+{
+	FLT_OT_FUNC("%p, %p, %p, %d", appctx, err, msg, cli_state);
+
+	if ((appctx == NULL) || ((err == NULL) && (msg == NULL)))
+		FLT_OT_RETURN();
+
+	appctx->ctx.cli.err = (err == NULL) ? msg : err;
+	appctx->st0         = (appctx->ctx.cli.err == NULL) ? CLI_ST_PROMPT : cli_state;
+
+	FLT_OT_DBG(1, "err(%d): \"%s\"", appctx->st0, appctx->ctx.cli.err);
+
+	FLT_OT_RETURN();
+}
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_cli_parse_debug -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   payload -
+ *   appctx  -
+ *   private -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_cli_parse_debug(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	char    *err = NULL, *msg = NULL;
+	uint8_t  value;
+	int      retval = 0;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p", args, payload, appctx, private);
+
+	FLT_OT_ARGS_DUMP();
+
+	if (FLT_OT_ARG_ISVALID(2)) {
+		value = flt_ot_strtoll(args[2], 0, 255, &err);
+		if (err == NULL) {
+			_HA_ATOMIC_STORE(&(flt_ot_debug.level), value);
+
+			(void)memprintf(&msg, FLT_OT_CLI_CMD " : debug level set to %hhu", value);
+		} else {
+			retval = 1;
+		}
+	} else {
+		value = _HA_ATOMIC_LOAD(&(flt_ot_debug.level));
+
+		(void)memprintf(&msg, FLT_OT_CLI_CMD " : current debug level is %hhu", value);
+	}
+
+	cmn_cli_set_msg(appctx, err, msg, CLI_ST_PRINT_FREE);
+
+	FLT_OT_RETURN(retval);
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   flt_ot_cli_parse_disabled -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   payload -
+ *   appctx  -
+ *   private -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_cli_parse_disabled(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	char *msg = NULL;
+	bool  value = (uintptr_t)private;
+	int   retval = 0;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p", args, payload, appctx, private);
+
+	FLT_OT_ARGS_DUMP();
+
+	FLT_OT_PROXIES_LIST_START() {
+		_HA_ATOMIC_STORE(&(conf->tracer->flag_disabled), value);
+
+		(void)memprintf(&msg, "%s%s" FLT_OT_CLI_CMD " : filter %sabled", FLT_OT_CLI_MSG_CAT(msg), value ? "dis" : "en");
+	} FLT_OT_PROXIES_LIST_END();
+
+	cmn_cli_set_msg(appctx, NULL, msg, CLI_ST_PRINT_FREE);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_cli_parse_option -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   payload -
+ *   appctx  -
+ *   private -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_cli_parse_option(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	char *msg = NULL;
+	bool  value = (uintptr_t)private;
+	int   retval = 0;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p", args, payload, appctx, private);
+
+	FLT_OT_ARGS_DUMP();
+
+	FLT_OT_PROXIES_LIST_START() {
+		_HA_ATOMIC_STORE(&(conf->tracer->flag_harderr), value);
+
+		(void)memprintf(&msg, "%s%s" FLT_OT_CLI_CMD " : filter set %s-errors", FLT_OT_CLI_MSG_CAT(msg), value ? "hard" : "soft");
+	} FLT_OT_PROXIES_LIST_END();
+
+	cmn_cli_set_msg(appctx, NULL, msg, CLI_ST_PRINT_FREE);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_cli_parse_logging -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   payload -
+ *   appctx  -
+ *   private -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_cli_parse_logging(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	char    *err = NULL, *msg = NULL;
+	uint8_t  value;
+	int      retval = 0;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p", args, payload, appctx, private);
+
+	FLT_OT_ARGS_DUMP();
+
+	if (FLT_OT_ARG_ISVALID(2)) {
+		if (strcasecmp(args[2], FLT_OT_CLI_LOGGING_OFF) == 0) {
+			value = FLT_OT_LOGGING_OFF;
+		}
+		else if (strcasecmp(args[2], FLT_OT_CLI_LOGGING_ON) == 0) {
+			value = FLT_OT_LOGGING_ON;
+		}
+		else if (strcasecmp(args[2], FLT_OT_CLI_LOGGING_NOLOGNORM) == 0) {
+			value = FLT_OT_LOGGING_ON | FLT_OT_LOGGING_NOLOGNORM;
+		}
+		else {
+			(void)memprintf(&err, "'%s' : invalid value, use <" FLT_OT_CLI_LOGGING_OFF " | " FLT_OT_CLI_LOGGING_ON " | " FLT_OT_CLI_LOGGING_NOLOGNORM ">", args[2]);
+
+			retval = 1;
+		}
+
+		if (retval == 0) {
+			FLT_OT_PROXIES_LIST_START() {
+				_HA_ATOMIC_STORE(&(conf->tracer->logging), value);
+
+				(void)memprintf(&msg, "%s%s" FLT_OT_CLI_CMD " : logging is %s", FLT_OT_CLI_MSG_CAT(msg), FLT_OT_CLI_LOGGING_STATE(value));
+			} FLT_OT_PROXIES_LIST_END();
+		}
+	} else {
+		FLT_OT_PROXIES_LIST_START() {
+			value = _HA_ATOMIC_LOAD(&(conf->tracer->logging));
+
+			(void)memprintf(&msg, "%s%s" FLT_OT_CLI_CMD " : logging is currently %s", FLT_OT_CLI_MSG_CAT(msg), FLT_OT_CLI_LOGGING_STATE(value));
+		} FLT_OT_PROXIES_LIST_END();
+	}
+
+	cmn_cli_set_msg(appctx, err, msg, CLI_ST_PRINT_FREE);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_cli_parse_rate -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   payload -
+ *   appctx  -
+ *   private -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_cli_parse_rate(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	char     *err = NULL, *msg = NULL;
+	uint32_t  value;
+	int       retval = 0;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p", args, payload, appctx, private);
+
+	FLT_OT_ARGS_DUMP();
+
+	if (FLT_OT_ARG_ISVALID(2)) {
+		value = FLT_OT_FLOAT_U32(flt_ot_strtod(args[2], 0.0, FLT_OT_RATE_LIMIT_MAX, &err), FLT_OT_RATE_LIMIT_MAX);
+		if (err == NULL) {
+			FLT_OT_PROXIES_LIST_START() {
+				_HA_ATOMIC_STORE(&(conf->tracer->rate_limit), value);
+
+				(void)memprintf(&msg, "%s%s" FLT_OT_CLI_CMD " : rate limit set to %.2f", FLT_OT_CLI_MSG_CAT(msg), FLT_OT_U32_FLOAT(value, FLT_OT_RATE_LIMIT_MAX));
+			} FLT_OT_PROXIES_LIST_END();
+		} else {
+			retval = 1;
+		}
+	} else {
+		FLT_OT_PROXIES_LIST_START() {
+			value = _HA_ATOMIC_LOAD(&(conf->tracer->rate_limit));
+
+			(void)memprintf(&msg, "%s%s" FLT_OT_CLI_CMD " : current rate limit is %.2f", FLT_OT_CLI_MSG_CAT(msg), FLT_OT_U32_FLOAT(value, FLT_OT_RATE_LIMIT_MAX));
+		} FLT_OT_PROXIES_LIST_END();
+	}
+
+	cmn_cli_set_msg(appctx, err, msg, CLI_ST_PRINT_FREE);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_cli_parse_status -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   payload -
+ *   appctx  -
+ *   private -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_cli_parse_status(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	const char *nl = "";
+	char       *msg = NULL;
+	int         retval = 0;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p", args, payload, appctx, private);
+
+	FLT_OT_ARGS_DUMP();
+	flt_ot_filters_dump();
+
+	(void)memprintf(&msg, " " FLT_OT_OPT_NAME " filter status\n" FLT_OT_STR_DASH_78);
+#ifdef DEBUG_OT
+	(void)memprintf(&msg, "%s\n   debug level: 0x%02hhx\n", msg, flt_ot_debug.level);
+#endif
+
+	FLT_OT_PROXIES_LIST_START() {
+		(void)memprintf(&msg, "%s\n%s   filter %s\n", msg, nl, conf->id);
+		(void)memprintf(&msg, "%s     configuration: %s\n", msg, conf->cfg_file);
+		(void)memprintf(&msg, "%s     disable count: %" PRIu64 " %" PRIu64 "\n\n", msg, conf->cnt.disabled[0], conf->cnt.disabled[1]);
+		(void)memprintf(&msg, "%s     tracer %s\n", msg, conf->tracer->id);
+		(void)memprintf(&msg, "%s       configuration: %s\n", msg, conf->tracer->config);
+		(void)memprintf(&msg, "%s       plugin:        %s\n", msg, conf->tracer->plugin);
+		(void)memprintf(&msg, "%s       rate limit:    %.2f %%\n", msg, FLT_OT_U32_FLOAT(conf->tracer->rate_limit, FLT_OT_RATE_LIMIT_MAX));
+		(void)memprintf(&msg, "%s       hard errors:   %s\n", msg, FLT_OT_STR_FLAG_YN(conf->tracer->flag_harderr));
+		(void)memprintf(&msg, "%s       disabled:      %s\n", msg, FLT_OT_STR_FLAG_YN(conf->tracer->flag_disabled));
+		(void)memprintf(&msg, "%s       logging:       %s\n", msg, FLT_OT_CLI_LOGGING_STATE(conf->tracer->logging));
+		(void)memprintf(&msg, "%s       analyzers:     %08x", msg, conf->tracer->analyzers);
+
+		nl = "\n";
+	} FLT_OT_PROXIES_LIST_END();
+
+	cmn_cli_set_msg(appctx, NULL, msg, CLI_ST_PRINT_FREE);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+static struct cli_kw_list cli_kws = { { }, {
+#ifdef DEBUG_OT
+	{ { FLT_OT_CLI_CMD, "debug", NULL }, FLT_OT_CLI_CMD " debug [level]   : set the OT filter debug level (default: get current debug level)", flt_ot_cli_parse_debug, NULL, NULL, NULL, 0 },
+#endif
+	{ { FLT_OT_CLI_CMD, "disable", NULL }, FLT_OT_CLI_CMD " disable         : disable the OT filter", flt_ot_cli_parse_disabled, NULL, NULL, (void *)1, 0 },
+	{ { FLT_OT_CLI_CMD, "enable", NULL }, FLT_OT_CLI_CMD " enable          : enable the OT filter", flt_ot_cli_parse_disabled, NULL, NULL, (void *)0, 0 },
+	{ { FLT_OT_CLI_CMD, "soft-errors", NULL }, FLT_OT_CLI_CMD " soft-errors     : turning off hard-errors mode", flt_ot_cli_parse_option, NULL, NULL, (void *)0, 0 },
+	{ { FLT_OT_CLI_CMD, "hard-errors", NULL }, FLT_OT_CLI_CMD " hard-errors     : enabling hard-errors mode", flt_ot_cli_parse_option, NULL, NULL, (void *)1, 0 },
+	{ { FLT_OT_CLI_CMD, "logging",  NULL }, FLT_OT_CLI_CMD " logging [state] : set logging state (default: get current logging state)", flt_ot_cli_parse_logging, NULL, NULL, NULL, 0 },
+	{ { FLT_OT_CLI_CMD, "rate", NULL }, FLT_OT_CLI_CMD " rate [value]    : set the rate limit (default: get current rate value)", flt_ot_cli_parse_rate, NULL, NULL, NULL, 0 },
+	{ { FLT_OT_CLI_CMD, "status", NULL }, FLT_OT_CLI_CMD " status          : show the OT filter status", flt_ot_cli_parse_status, NULL, NULL, NULL, 0 },
+	{ /* END */ }
+}};
+
+
+/***
+ * NAME
+ *   flt_ot_cli_init -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_cli_init(void)
+{
+	FLT_OT_FUNC("");
+
+	/* Register CLI keywords. */
+	cli_register_kw(&cli_kws);
+
+	FLT_OT_RETURN();
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/conf.c b/contrib/opentracing/src/conf.c
new file mode 100644
index 0000000..626d418
--- /dev/null
+++ b/contrib/opentracing/src/conf.c
@@ -0,0 +1,766 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+/***
+ * NAME
+ *   flt_ot_conf_hdr_init -
+ *
+ * ARGUMENTS
+ *   size    -
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static void *flt_ot_conf_hdr_init(size_t size, const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_hdr *retptr = NULL, *ptr;
+
+	FLT_OT_FUNC("%zu, \"%s\", %d, %p, %p:%p", size, id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	if (head != NULL)
+		list_for_each_entry(ptr, head, list)
+			if (strcmp(ptr->id, id) == 0) {
+				FLT_OT_ERR("'%s' : already defined", id);
+
+				FLT_OT_RETURN(retptr);
+			}
+
+	retptr = FLT_OT_CALLOC(1, size);
+	if (retptr != NULL) {
+		retptr->id_len = strlen(id);
+		if (retptr->id_len >= FLT_OT_ID_MAXLEN)
+			FLT_OT_ERR("'%s' : name too long", id);
+		else
+			retptr->id = FLT_OT_STRDUP(id);
+
+		if (retptr->id == NULL)
+			FLT_OT_FREE_CLEAR(retptr);
+	}
+
+	if (retptr != NULL) {
+		retptr->cfg_line = linenum;
+
+		if (head != NULL)
+			LIST_ADDQ(head, &(retptr->list));
+	} else {
+		FLT_OT_ERR("out of memory");
+	}
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_ph_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_ph *flt_ot_conf_ph_init(const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_ph *retptr;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p:%p", id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
+	if (retptr != NULL)
+		FLT_OT_DBG_CONF_PH("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_ph_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_ph_free(struct flt_ot_conf_ph **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_PH("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->id);
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_sample_expr_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_sample_expr *flt_ot_conf_sample_expr_init(const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_sample_expr *retptr;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p:%p", id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
+	if (retptr != NULL)
+		FLT_OT_DBG_CONF_SAMPLE_EXPR("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_sample_expr_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_sample_expr_free(struct flt_ot_conf_sample_expr **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_SAMPLE_EXPR("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->value);
+	release_sample_expr((*ptr)->expr);
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_sample_init -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_sample *flt_ot_conf_sample_init(char **args, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_sample *retptr;
+
+	FLT_OT_FUNC("%p, %d, %p, %p:%p", args, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), args[1], linenum, head, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	flt_ot_args_to_str(args, 2, &(retptr->value));
+	if (retptr->value == NULL) {
+		FLT_OT_FREE_CLEAR(retptr);
+
+		FLT_OT_RETURN(retptr);
+	}
+
+	retptr->num_exprs = flt_ot_args_count(args) - 2;
+	LIST_INIT(&(retptr->exprs));
+
+	FLT_OT_DBG_CONF_SAMPLE("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_sample_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_sample_free(struct flt_ot_conf_sample **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_SAMPLE("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->key);
+	FLT_OT_FREE((*ptr)->value);
+	FLT_OT_LIST_DESTROY(sample_expr, &((*ptr)->exprs));
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_str_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_str *flt_ot_conf_str_init(const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_str *retptr;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p:%p", id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
+	if (retptr != NULL)
+		FLT_OT_DBG_CONF_STR("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_str_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_str_free(struct flt_ot_conf_str **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_STR("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->str);
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_context_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_context *flt_ot_conf_context_init(const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_context *retptr;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p:%p", id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
+	if (retptr != NULL)
+		FLT_OT_DBG_CONF_CONTEXT("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_context_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_context_free(struct flt_ot_conf_context **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_CONTEXT("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->id);
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_span_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_span *flt_ot_conf_span_init(const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_span *retptr;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p:%p", id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	LIST_INIT(&(retptr->tags));
+	LIST_INIT(&(retptr->logs));
+	LIST_INIT(&(retptr->baggages));
+
+	FLT_OT_DBG_CONF_SPAN("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_span_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_span_free(struct flt_ot_conf_span **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_SPAN("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->id);
+	FLT_OT_FREE((*ptr)->ref_id);
+	FLT_OT_FREE((*ptr)->ctx_id);
+	FLT_OT_LIST_DESTROY(sample, &((*ptr)->tags));
+	FLT_OT_LIST_DESTROY(sample, &((*ptr)->logs));
+	FLT_OT_LIST_DESTROY(sample, &((*ptr)->baggages));
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_scope_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_scope *flt_ot_conf_scope_init(const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_scope *retptr = NULL;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p:%p", id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	LIST_INIT(&(retptr->acls));
+	LIST_INIT(&(retptr->contexts));
+	LIST_INIT(&(retptr->spans));
+	LIST_INIT(&(retptr->finish));
+
+	FLT_OT_DBG_CONF_SCOPE("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+/***
+ * NAME
+ *   flt_ot_conf_scope_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_scope_free(struct flt_ot_conf_scope **ptr)
+{
+	struct acl *acl, *aclback;
+
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_SCOPE("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->id);
+	list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) {
+		prune_acl(acl);
+		FLT_OT_LIST_DEL(&(acl->list));
+		FLT_OT_FREE(acl);
+	}
+	if ((*ptr)->cond != NULL) {
+		prune_acl_cond((*ptr)->cond);
+		FLT_OT_FREE((*ptr)->cond);
+	}
+	FLT_OT_LIST_DESTROY(context, &((*ptr)->contexts));
+	FLT_OT_LIST_DESTROY(span, &((*ptr)->spans));
+	FLT_OT_LIST_DESTROY(str, &((*ptr)->finish));
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_group_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_group *flt_ot_conf_group_init(const char *id, int linenum, struct list *head, char **err)
+{
+	struct flt_ot_conf_group *retptr;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p:%p", id, linenum, head, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, head, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	LIST_INIT(&(retptr->ph_scopes));
+
+	FLT_OT_DBG_CONF_GROUP("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_group_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_group_free(struct flt_ot_conf_group **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_GROUP("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->id);
+	FLT_OT_LIST_DESTROY(ph_scope, &((*ptr)->ph_scopes));
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_tracer_init -
+ *
+ * ARGUMENTS
+ *   id      -
+ *   linenum -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf_tracer *flt_ot_conf_tracer_init(const char *id, int linenum, char **err)
+{
+	struct flt_ot_conf_tracer *retptr;
+
+	FLT_OT_FUNC("\"%s\", %d, %p:%p", id, linenum, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_conf_hdr_init(sizeof(*retptr), id, linenum, NULL, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	retptr->rate_limit = FLT_OT_FLOAT_U32(FLT_OT_RATE_LIMIT_MAX, FLT_OT_RATE_LIMIT_MAX);
+	init_new_proxy(&(retptr->proxy_log));
+	LIST_INIT(&(retptr->acls));
+	LIST_INIT(&(retptr->ph_groups));
+	LIST_INIT(&(retptr->ph_scopes));
+
+	FLT_OT_DBG_CONF_TRACER("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_tracer_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_tracer_free(struct flt_ot_conf_tracer **ptr)
+{
+	struct acl    *acl, *aclback;
+	struct logsrv *logsrv, *logsrvback;
+
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF_TRACER("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->id);
+	FLT_OT_FREE((*ptr)->config);
+	FLT_OT_FREE((*ptr)->plugin);
+	FLT_OT_DBG(2, "- deleting acls list %s", flt_ot_list_debug(&((*ptr)->acls)));
+	list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) {
+		prune_acl(acl);
+		FLT_OT_LIST_DEL(&(acl->list));
+		FLT_OT_FREE(acl);
+	}
+	FLT_OT_DBG(2, "- deleting proxy_log.logsrvs list %s", flt_ot_list_debug(&((*ptr)->proxy_log.logsrvs)));
+	list_for_each_entry_safe(logsrv, logsrvback, &((*ptr)->proxy_log.logsrvs), list) {
+		LIST_DEL(&(logsrv->list));
+		FLT_OT_FREE(logsrv);
+	}
+	FLT_OT_LIST_DESTROY(ph_group, &((*ptr)->ph_groups));
+	FLT_OT_LIST_DESTROY(ph_scope, &((*ptr)->ph_scopes));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_init -
+ *
+ * ARGUMENTS
+ *   px -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_conf *flt_ot_conf_init(struct proxy *px)
+{
+	struct flt_ot_conf *retptr;
+
+	FLT_OT_FUNC("%p", px);
+
+	retptr = FLT_OT_CALLOC(1, sizeof(*retptr));
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	retptr->proxy = px;
+	LIST_INIT(&(retptr->groups));
+	LIST_INIT(&(retptr->scopes));
+
+	FLT_OT_DBG_CONF("- init ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_conf_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_conf_free(struct flt_ot_conf **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_CONF("- free ", *ptr);
+
+	FLT_OT_FREE((*ptr)->id);
+	FLT_OT_FREE((*ptr)->cfg_file);
+	flt_ot_conf_tracer_free(&((*ptr)->tracer));
+	FLT_OT_LIST_DESTROY(group, &((*ptr)->groups));
+	FLT_OT_LIST_DESTROY(scope, &((*ptr)->scopes));
+	FLT_OT_FREE_CLEAR(*ptr);
+
+	FLT_OT_RETURN();
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/event.c b/contrib/opentracing/src/event.c
new file mode 100644
index 0000000..90b5828
--- /dev/null
+++ b/contrib/opentracing/src/event.c
@@ -0,0 +1,332 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+#define FLT_OT_EVENT_DEF(a,b,c,d,e,f)   { AN_##b##_##a, SMP_OPT_DIR_##b, SMP_VAL_FE_##c, SMP_VAL_BE_##d, e, f },
+const struct flt_ot_event_data flt_ot_event_data[FLT_OT_EVENT_MAX] = { FLT_OT_EVENT_DEFINES };
+#undef FLT_OT_EVENT_DEF
+
+
+/***
+ * NAME
+ *   flt_ot_scope_run_span -
+ *
+ * ARGUMENTS
+ *   s         -
+ *   f         -
+ *   chn       -
+ *   dir       -
+ *   span      -
+ *   data      -
+ *   conf_span -
+ *   ts        -
+ *   err       -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+static int flt_ot_scope_run_span(struct stream *s, struct filter *f, struct channel *chn, uint dir, struct flt_ot_scope_span *span, struct flt_ot_scope_data *data, const struct flt_ot_conf_span *conf_span, const struct timespec *ts, char **err)
+{
+	struct flt_ot_conf *conf = FLT_OT_CONF(f);
+	int                 retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p, %p, %u, %p, %p, %p, %p, %p:%p", s, f, chn, dir, span, data, conf_span, ts, FLT_OT_DPTR_ARGS(err));
+
+	if (span == NULL)
+		FLT_OT_RETURN(retval);
+
+	if (span->span == NULL) {
+		span->span = ot_span_init(conf->tracer->tracer, span->id, ts, NULL, span->ref_type, FLT_OT_DEREF(span->ref_ctx, idx, -1), span->ref_span, data->tags, data->num_tags, err);
+		if (span->span == NULL)
+			retval = FLT_OT_RET_ERROR;
+	}
+	else if (data->num_tags > 0)
+		if (ot_span_tag(span->span, data->tags, data->num_tags) == -1)
+			retval = FLT_OT_RET_ERROR;
+
+	if ((span->span != NULL) && (data->baggage != NULL))
+		if (ot_span_set_baggage(span->span, data->baggage) == -1)
+			retval = FLT_OT_RET_ERROR;
+
+	if ((span->span != NULL) && (data->num_log_fields > 0))
+		if (ot_span_log(span->span, data->log_fields, data->num_log_fields) == -1)
+			retval = FLT_OT_RET_ERROR;
+
+	if ((span->span != NULL) && (conf_span->ctx_id != NULL)) {
+		struct otc_http_headers_writer  writer;
+		struct otc_text_map            *text_map = NULL;
+		struct otc_span_context        *span_ctx;
+
+		span_ctx = ot_inject_http_headers(conf->tracer->tracer, span->span, &writer, err);
+		if (span_ctx != NULL) {
+			int i = 0;
+
+			if (conf_span->ctx_flags & (FLT_OT_CTX_USE_VARS | FLT_OT_CTX_USE_HEADERS)) {
+				for (text_map = &(writer.text_map); i < text_map->count; i++) {
+					if (!(conf_span->ctx_flags & FLT_OT_CTX_USE_VARS))
+						/* Do nothing. */;
+					else if (flt_ot_var_register(FLT_OT_VARS_SCOPE, conf_span->ctx_id, text_map->key[i], err) == -1)
+						retval = FLT_OT_RET_ERROR;
+					else if (flt_ot_var_set(s, FLT_OT_VARS_SCOPE, conf_span->ctx_id, text_map->key[i], text_map->value[i], dir, err) == -1)
+						retval = FLT_OT_RET_ERROR;
+
+					if (!(conf_span->ctx_flags & FLT_OT_CTX_USE_HEADERS))
+						/* Do nothing. */;
+					else if (flt_ot_http_header_set(chn, conf_span->ctx_id, text_map->key[i], text_map->value[i], err) == -1)
+						retval = FLT_OT_RET_ERROR;
+				}
+			}
+
+			span_ctx->destroy(&span_ctx);
+			otc_text_map_destroy(&text_map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+		}
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_run -
+ *
+ * ARGUMENTS
+ *   s          -
+ *   f          -
+ *   chn        -
+ *   conf_scope -
+ *   ts         -
+ *   dir        -
+ *   err        -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+int flt_ot_scope_run(struct stream *s, struct filter *f, struct channel *chn, struct flt_ot_conf_scope *conf_scope, const struct timespec *ts, uint dir, char **err)
+{
+	struct flt_ot_conf         *conf = FLT_OT_CONF(f);
+	struct flt_ot_conf_context *conf_ctx;
+	struct flt_ot_conf_span    *conf_span;
+	struct flt_ot_conf_str     *finish;
+	struct timespec             ts_now;
+	int                         retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p, %p, %p, %p, %u, %p:%p", s, f, chn, conf_scope, ts, dir, FLT_OT_DPTR_ARGS(err));
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
+	FLT_OT_DBG(3, "run scope '%s' %d", conf_scope->id, conf_scope->event);
+	FLT_OT_DBG_CONF_SCOPE("run scope ", conf_scope);
+
+	if (ts == NULL) {
+		(void)clock_gettime(CLOCK_MONOTONIC, &ts_now);
+
+		ts = &ts_now;
+	}
+
+	if (conf_scope->cond != NULL) {
+		enum acl_test_res res;
+		int               rc;
+
+		res = acl_exec_cond(conf_scope->cond, s->be, s->sess, s, dir | SMP_OPT_FINAL);
+		rc  = acl_pass(res);
+		if (conf_scope->cond->pol == ACL_COND_UNLESS)
+			rc = !rc;
+
+		FLT_OT_DBG(3, "the ACL rule %s", rc ? "matches" : "does not match");
+
+		/*
+		 * If the rule does not match, the current scope is skipped.
+		 *
+		 * If it is a root span, further processing of the session is
+		 * disabled.  As soon as the first span is encountered which
+		 * is marked as root, further search is interrupted.
+		 */
+		if (!rc) {
+			list_for_each_entry(conf_span, &(conf_scope->spans), list)
+				if (conf_span->flag_root) {
+					FLT_OT_DBG(0, "session disabled");
+
+					FLT_OT_RT_CTX(f->ctx)->flag_disabled = 1;
+
+					_HA_ATOMIC_ADD(conf->cnt.disabled + 0, 1);
+
+					break;
+				}
+
+			FLT_OT_RETURN(retval);
+		}
+	}
+
+	list_for_each_entry(conf_ctx, &(conf_scope->contexts), list) {
+		struct otc_text_map *text_map;
+
+		FLT_OT_DBG(3, "run context '%s' -> '%s'", conf_scope->id, conf_ctx->id);
+		FLT_OT_DBG_CONF_CONTEXT("run context ", conf_ctx);
+
+		/*
+		 * The OpenTracing context is read from the HTTP header
+		 * or from HAProxy variables.
+		 */
+		if (conf_ctx->flags & FLT_OT_CTX_USE_HEADERS)
+			text_map = flt_ot_http_headers_get(chn, conf_ctx->id, conf_ctx->id_len, err);
+		else
+			text_map = flt_ot_vars_get(s, FLT_OT_VARS_SCOPE, conf_ctx->id, dir, err);
+
+		if (text_map != NULL) {
+			if (flt_ot_scope_context_init(f->ctx, conf->tracer->tracer, conf_ctx->id, conf_ctx->id_len, text_map, dir, err) == NULL)
+				retval = FLT_OT_RET_ERROR;
+
+			otc_text_map_destroy(&text_map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+		} else {
+			retval = FLT_OT_RET_ERROR;
+		}
+	}
+
+	list_for_each_entry(conf_span, &(conf_scope->spans), list) {
+		struct flt_ot_scope_data   data;
+		struct flt_ot_scope_span  *span;
+		struct flt_ot_conf_sample *sample;
+
+		FLT_OT_DBG(3, "run span '%s' -> '%s'", conf_scope->id, conf_span->id);
+		FLT_OT_DBG_CONF_SPAN("run span ", conf_span);
+
+		(void)memset(&data, 0, sizeof(data));
+
+		span = flt_ot_scope_span_init(f->ctx, conf_span->id, conf_span->id_len, conf_span->ref_type, conf_span->ref_id, conf_span->ref_id_len, dir, err);
+		if (span == NULL)
+			retval = FLT_OT_RET_ERROR;
+
+		list_for_each_entry(sample, &(conf_span->tags), list) {
+			FLT_OT_DBG(3, "adding tag '%s' -> '%s'", sample->key, sample->value);
+
+			if (flt_ot_sample_add(s, dir, sample, &data, FLT_OT_EVENT_SAMPLE_TAG, err) == FLT_OT_RET_ERROR)
+				retval = FLT_OT_RET_ERROR;
+		}
+
+		list_for_each_entry(sample, &(conf_span->logs), list) {
+			FLT_OT_DBG(3, "adding log '%s' -> '%s'", sample->key, sample->value);
+
+			if (flt_ot_sample_add(s, dir, sample, &data, FLT_OT_EVENT_SAMPLE_LOG, err) == FLT_OT_RET_ERROR)
+				retval = FLT_OT_RET_ERROR;
+		}
+
+		list_for_each_entry(sample, &(conf_span->baggages), list) {
+			FLT_OT_DBG(3, "adding baggage '%s' -> '%s'", sample->key, sample->value);
+
+			if (flt_ot_sample_add(s, dir, sample, &data, FLT_OT_EVENT_SAMPLE_BAGGAGE, err) == FLT_OT_RET_ERROR)
+				retval = FLT_OT_RET_ERROR;
+		}
+
+		if (retval != FLT_OT_RET_ERROR)
+			if (flt_ot_scope_run_span(s, f, chn, dir, span, &data, conf_span, ts, err) == FLT_OT_RET_ERROR)
+				retval = FLT_OT_RET_ERROR;
+
+		flt_ot_scope_data_free(&data);
+	}
+
+	list_for_each_entry(finish, &(conf_scope->finish), list)
+		if (flt_ot_scope_finish_mark(f->ctx, finish->str, finish->str_len) == -1)
+			retval = FLT_OT_RET_ERROR;
+
+	flt_ot_scope_finish_marked(f->ctx, ts);
+	flt_ot_scope_free_unused(f->ctx, chn);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_event_run -
+ *
+ * ARGUMENTS
+ *   s     -
+ *   f     -
+ *   chn   -
+ *   event -
+ *   err   -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+int flt_ot_event_run(struct stream *s, struct filter *f, struct channel *chn, int event, char **err)
+{
+	struct flt_ot_conf       *conf = FLT_OT_CONF(f);
+	struct flt_ot_conf_scope *conf_scope;
+	struct timespec           ts;
+	int                       retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p, %p, %d, %p:%p", s, f, chn, event, FLT_OT_DPTR_ARGS(err));
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
+	FLT_OT_DBG(3, "run event '%s' %d", flt_ot_event_data[event].name, event);
+
+#ifdef DEBUG_OT
+	_HA_ATOMIC_ADD(conf->cnt.event[event].htx + (htx_is_empty(htxbuf(&(chn->buf))) ? 1 : 0), 1);
+#endif
+
+	FLT_OT_RT_CTX(f->ctx)->analyzers |= flt_ot_event_data[event].an_bit;
+
+	/* All spans should be created/completed at the same time. */
+	(void)clock_gettime(CLOCK_MONOTONIC, &ts);
+
+	/*
+	 * It is possible that there are defined multiple scopes that use the
+	 * same event.  Therefore, there must not be a 'break' here, ie an
+	 * exit from the 'for' loop.
+	 */
+	list_for_each_entry(conf_scope, &(conf->scopes), list) {
+		if (conf_scope->event != event)
+			/* Do nothing. */;
+		else if (!conf_scope->flag_used)
+			FLT_OT_DBG(3, "scope '%s' %d not used", conf_scope->id, conf_scope->event);
+		else if (flt_ot_scope_run(s, f, chn, conf_scope, &ts, flt_ot_event_data[event].smp_opt_dir, err) == FLT_OT_RET_ERROR)
+			retval = FLT_OT_RET_ERROR;
+	}
+
+	flt_ot_vars_dump(s);
+	flt_ot_http_headers_dump(chn);
+
+	FLT_OT_DBG(3, "event = %d, chn = %p, s->req = %p, s->res = %p", event, chn, &(s->req), &(s->res));
+
+	FLT_OT_RETURN(retval);
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/filter.c b/contrib/opentracing/src/filter.c
new file mode 100644
index 0000000..6699d46
--- /dev/null
+++ b/contrib/opentracing/src/filter.c
@@ -0,0 +1,1153 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+/*
+ * OpenTracing filter id, used to identify OpenTracing filters.
+ * The name of this variable is consistent with the other filter names
+ * declared in include/haproxy/filters.h .
+ */
+const char *ot_flt_id = "the OpenTracing filter";
+
+
+/***
+ * NAME
+ *   flt_ot_is_disabled -
+ *
+ * ARGUMENTS
+ *   f     -
+ *   event -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+bool flt_ot_is_disabled(const struct filter *f FLT_OT_DBG_ARGS(, int event))
+{
+#ifdef DEBUG_OT
+	const struct flt_ot_conf *conf = FLT_OT_CONF(f);
+	const char               *msg;
+#endif
+	bool                      retval;
+
+	retval = FLT_OT_RT_CTX(f->ctx)->flag_disabled ? 1 : 0;
+
+#ifdef DEBUG_OT
+	msg    = retval ? " (disabled)" : "";
+
+	if (FLT_OT_IN_RANGE(event, 0, FLT_OT_EVENT_MAX - 1))
+		FLT_OT_DBG(2, "filter '%s', type: %s, event: '%s' %d%s", conf->id, flt_ot_type(f), flt_ot_event_data[event].name, event, msg);
+	else
+		FLT_OT_DBG(2, "filter '%s', type: %s%s", conf->id, flt_ot_type(f), msg);
+#endif
+
+	return retval;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_return_int -
+ *
+ * ARGUMENTS
+ *   f      -
+ *   err    -
+ *   retval -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_return_int(const struct filter *f, char **err, int retval)
+{
+	struct flt_ot_runtime_context *rt_ctx = f->ctx;
+
+	if ((retval == FLT_OT_RET_ERROR) || ((err != NULL) && (*err != NULL))) {
+		if (rt_ctx->flag_harderr) {
+			FLT_OT_DBG(1, "WARNING: filter hard-error (disabled)");
+
+			rt_ctx->flag_disabled = 1;
+
+			_HA_ATOMIC_ADD(FLT_OT_CONF(f)->cnt.disabled + 1, 1);
+		} else {
+			FLT_OT_DBG(1, "WARNING: filter soft-error");
+		}
+
+		retval = FLT_OT_RET_OK;
+	}
+
+	FLT_OT_ERR_FREE(*err);
+
+	return retval;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_return_void -
+ *
+ * ARGUMENTS
+ *   f   -
+ *   err -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_return_void(const struct filter *f, char **err)
+{
+	struct flt_ot_runtime_context *rt_ctx = f->ctx;
+
+	if ((err != NULL) && (*err != NULL)) {
+		if (rt_ctx->flag_harderr) {
+			FLT_OT_DBG(1, "WARNING: filter hard-error (disabled)");
+
+			rt_ctx->flag_disabled = 1;
+
+			_HA_ATOMIC_ADD(FLT_OT_CONF(f)->cnt.disabled + 1, 1);
+		} else {
+			FLT_OT_DBG(1, "WARNING: filter soft-error");
+		}
+	}
+
+	FLT_OT_ERR_FREE(*err);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_init - Initialize the filter.
+ *
+ * ARGUMENTS
+ *   p     -
+ *   fconf -
+ *
+ * DESCRIPTION
+ *   It initializes the filter for a proxy.  You may define this callback
+ *   if you need to complete your filter configuration.
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, any other value otherwise.
+ */
+static int flt_ot_init(struct proxy *p, struct flt_conf *fconf)
+{
+	struct flt_ot_conf *conf = FLT_OT_DEREF(fconf, conf, NULL);
+	char               *err = NULL;
+	int                 retval = FLT_OT_RET_ERROR;
+
+	FLT_OT_FUNC("%p, %p", p, fconf);
+
+	if (conf == NULL)
+		FLT_OT_RETURN(retval);
+
+	flt_ot_cli_init();
+
+	/*
+	 * Initialize the OpenTracing library.
+	 * Enable HTX streams filtering.
+	 */
+	retval = ot_init(&(conf->tracer->tracer), conf->tracer->config, conf->tracer->plugin, &err);
+	if (retval != FLT_OT_RET_ERROR)
+		fconf->flags |= FLT_CFG_FL_HTX;
+	else if (err != NULL) {
+		FLT_OT_ALERT("%s", err);
+
+		FLT_OT_ERR_FREE(err);
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_deinit - Free resources allocated by the filter.
+ *
+ * ARGUMENTS
+ *   p     -
+ *   fconf -
+ *
+ * DESCRIPTION
+ *   It cleans up what the parsing function and the init callback have done.
+ *   This callback is useful to release memory allocated for the filter
+ *   configuration.
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_deinit(struct proxy *p, struct flt_conf *fconf)
+{
+	struct flt_ot_conf **conf = (fconf == NULL) ? NULL : (typeof(conf))&(fconf->conf);
+#ifdef DEBUG_OT
+	int                  i;
+#endif
+
+	FLT_OT_FUNC("%p, %p", p, fconf);
+
+	if (conf == NULL)
+		FLT_OT_RETURN();
+
+	ot_debug();
+	ot_close(&((*conf)->tracer->tracer));
+
+#ifdef DEBUG_OT
+	FLT_OT_DBG(0, "--- used events ----------");
+	for (i = 0; i < FLT_OT_TABLESIZE((*conf)->cnt.event); i++)
+		if ((*conf)->cnt.event[i].flag_used)
+			FLT_OT_DBG(0, "  %02d: %" PRIu64 " / %" PRIu64, i, (*conf)->cnt.event[i].htx[0], (*conf)->cnt.event[i].htx[1]);
+#endif
+
+	flt_ot_conf_free(conf);
+
+	FLT_OT_MEMINFO();
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_check - Check configuration of the filter for the specified proxy.
+ *
+ * ARGUMENTS
+ *   p     -
+ *   fconf -
+ *
+ * DESCRIPTION
+ *   Optionally, by implementing the flt_ot_check() callback, you add a
+ *   step to check the internal configuration of your filter after the
+ *   parsing phase, when the HAProxy configuration is fully defined.
+ *
+ * RETURN VALUE
+ *   Returns the number of encountered errors.
+ */
+static int flt_ot_check(struct proxy *p, struct flt_conf *fconf)
+{
+	struct proxy             *px;
+	struct flt_ot_conf       *conf = FLT_OT_DEREF(fconf, conf, NULL);
+	struct flt_ot_conf_group *conf_group;
+	struct flt_ot_conf_scope *conf_scope;
+	struct flt_ot_conf_ph    *ph_group, *ph_scope;
+	int                       retval = 0, scope_unused_cnt = 0, span_root_cnt = 0;
+
+	FLT_OT_FUNC("%p, %p", p, fconf);
+
+	if (conf == NULL)
+		FLT_OT_RETURN(++retval);
+
+	/*
+	 * If only the proxy specified with the <p> parameter is checked, then
+	 * no duplicate filters can be found that are not defined in the same
+	 * configuration sections.
+	 */
+	for (px = proxies_list; px != NULL; px = px->next) {
+		struct flt_conf *fconf_tmp;
+
+		FLT_OT_DBG(2, "proxy '%s'", px->id);
+
+		/*
+		 * The names of all OT filters (filter ID) should be checked,
+		 * they must be unique.
+		 */
+		list_for_each_entry(fconf_tmp, &(px->filter_configs), list)
+			if ((fconf_tmp != fconf) && (fconf_tmp->id == ot_flt_id)) {
+				struct flt_ot_conf *conf_tmp = fconf_tmp->conf;
+
+				FLT_OT_DBG(2, "  OT filter '%s'", conf_tmp->id);
+
+				if (strcmp(conf_tmp->id, conf->id) == 0) {
+					FLT_OT_ALERT("''%s' : duplicated filter ID'", conf_tmp->id);
+
+					retval++;
+				}
+			}
+	}
+
+	if (FLT_OT_DEREF(conf->tracer, id, NULL) == NULL) {
+		FLT_OT_ALERT("''%s' : no tracer found'", conf->id);
+
+		retval++;
+	}
+
+	/*
+	 * Checking that all defined 'ot-group' sections have correctly declared
+	 * 'ot-scope' sections (ie whether the declared 'ot-scope' sections have
+	 * corresponding definitions).
+	 */
+	list_for_each_entry(conf_group, &(conf->groups), list)
+		list_for_each_entry(ph_scope, &(conf_group->ph_scopes), list) {
+			bool flag_found = 0;
+
+			list_for_each_entry(conf_scope, &(conf->scopes), list)
+				if (strcmp(ph_scope->id, conf_scope->id) == 0) {
+					ph_scope->ptr         = conf_scope;
+					conf_scope->flag_used = 1;
+					flag_found            = 1;
+
+					break;
+				}
+
+			if (!flag_found) {
+				FLT_OT_ALERT("'" FLT_OT_PARSE_SECTION_GROUP_ID " '%s' : try to use undefined " FLT_OT_PARSE_SECTION_SCOPE_ID " '%s''", conf_group->id, ph_scope->id);
+
+				retval++;
+			}
+		}
+
+	if (conf->tracer != NULL) {
+		/*
+		 * Checking that all declared 'groups' keywords have correctly
+		 * defined 'ot-group' sections.
+		 */
+		list_for_each_entry(ph_group, &(conf->tracer->ph_groups), list) {
+			bool flag_found = 0;
+
+			list_for_each_entry(conf_group, &(conf->groups), list)
+				if (strcmp(ph_group->id, conf_group->id) == 0) {
+					ph_group->ptr         = conf_group;
+					conf_group->flag_used = 1;
+					flag_found            = 1;
+
+					break;
+				}
+
+			if (!flag_found) {
+				FLT_OT_ALERT("'" FLT_OT_PARSE_SECTION_TRACER_ID " '%s' : try to use undefined " FLT_OT_PARSE_SECTION_GROUP_ID " '%s''", conf->tracer->id, ph_group->id);
+
+				retval++;
+			}
+		}
+
+		/*
+		 * Checking that all declared 'scopes' keywords have correctly
+		 * defined 'ot-scope' sections.
+		 */
+		list_for_each_entry(ph_scope, &(conf->tracer->ph_scopes), list) {
+			bool flag_found = 0;
+
+			list_for_each_entry(conf_scope, &(conf->scopes), list)
+				if (strcmp(ph_scope->id, conf_scope->id) == 0) {
+					ph_scope->ptr         = conf_scope;
+					conf_scope->flag_used = 1;
+					flag_found            = 1;
+
+					break;
+				}
+
+			if (!flag_found) {
+				FLT_OT_ALERT("'" FLT_OT_PARSE_SECTION_TRACER_ID " '%s' : try to use undefined " FLT_OT_PARSE_SECTION_SCOPE_ID " '%s''", conf->tracer->id, ph_scope->id);
+
+				retval++;
+			}
+		}
+	}
+
+	FLT_OT_DBG(3, "--- filter '%s' configuration ----------", conf->id);
+	FLT_OT_DBG(3, "- defined spans ----------");
+
+	list_for_each_entry(conf_scope, &(conf->scopes), list) {
+		if (conf_scope->flag_used) {
+			struct flt_ot_conf_span *conf_span;
+
+			/*
+			 * In principle, only one span should be labeled
+			 * as a root span.
+			 */
+			list_for_each_entry(conf_span, &(conf_scope->spans), list) {
+				FLT_OT_DBG_CONF_SPAN("   ", conf_span);
+
+				span_root_cnt += conf_span->flag_root ? 1 : 0;
+			}
+
+#ifdef DEBUG_OT
+			conf->cnt.event[conf_scope->event].flag_used = 1;
+#endif
+
+			/* Set the flags of the analyzers used. */
+			conf->tracer->analyzers |= flt_ot_event_data[conf_scope->event].an_bit;
+		} else {
+			FLT_OT_ALERT("''%s' : unused " FLT_OT_PARSE_SECTION_SCOPE_ID " '%s''", conf->id, conf_scope->id);
+
+			scope_unused_cnt++;
+		}
+	}
+
+	/*
+	 * Unused scopes or a number of root spans other than one do not
+	 * necessarily have to be errors, but it is good to print it when
+	 * starting HAProxy.
+	 */
+	if (scope_unused_cnt > 0)
+		FLT_OT_ALERT("''%s' : %d scope(s) not in use'", conf->id, scope_unused_cnt);
+
+	if (LIST_ISEMPTY(&(conf->scopes)))
+		/* Do nothing. */;
+	else if (span_root_cnt == 0)
+		FLT_OT_ALERT("''%s' : no span is marked as the root span'", conf->id);
+	else if (span_root_cnt > 1)
+		FLT_OT_ALERT("''%s' : multiple spans are marked as the root span'", conf->id);
+
+	FLT_OT_DBG_LIST(conf, group, "", "defined", _group,
+	                FLT_OT_DBG_CONF_GROUP("   ", _group);
+	                FLT_OT_DBG_LIST(_group, ph_scope, "   ", "used", _scope, FLT_OT_DBG_CONF_PH("      ", _scope)));
+	FLT_OT_DBG_LIST(conf, scope, "", "defined", _scope, FLT_OT_DBG_CONF_SCOPE("   ", _scope));
+
+	if (conf->tracer != NULL) {
+		FLT_OT_DBG(3, "   --- tracer '%s' configuration ----------", conf->tracer->id);
+		FLT_OT_DBG_LIST(conf->tracer, ph_group, "   ", "used", _group, FLT_OT_DBG_CONF_PH("      ", _group));
+		FLT_OT_DBG_LIST(conf->tracer, ph_scope, "   ", "used", _scope, FLT_OT_DBG_CONF_PH("      ", _scope));
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_init_per_thread -
+ *
+ * ARGUMENTS
+ *   p     -
+ *   fconf -
+ *
+ * DESCRIPTION
+ *   It initializes the filter for each thread.  It works the same way than
+ *   flt_ot_init() but in the context of a thread.  This callback is called
+ *   after the thread creation.
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, any other value otherwise.
+ */
+static int flt_ot_init_per_thread(struct proxy *p, struct flt_conf *fconf)
+{
+	int retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p", p, fconf);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_deinit_per_thread -
+ *
+ * ARGUMENTS
+ *   p     -
+ *   fconf -
+ *
+ * DESCRIPTION
+ *   It cleans up what the init_per_thread callback have done.  It is called
+ *   in the context of a thread, before exiting it.
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_deinit_per_thread(struct proxy *p, struct flt_conf *fconf)
+{
+	FLT_OT_FUNC("%p, %p", p, fconf);
+
+	FLT_OT_RETURN();
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   flt_ot_attach - Called when a filter instance is created and attach to a stream.
+ *
+ * ARGUMENTS
+ *   s -
+ *   f -
+ *
+ * DESCRIPTION
+ *   It is called after a filter instance creation, when it is attached to a
+ *   stream.  This happens when the stream is started for filters defined on
+ *   the stream's frontend and when the backend is set for filters declared
+ *   on the stream's backend.  It is possible to ignore the filter, if needed,
+ *   by returning 0.  This could be useful to have conditional filtering.
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 to ignore the filter,
+ *   any other value otherwise.
+ */
+static int flt_ot_attach(struct stream *s, struct filter *f)
+{
+	const struct flt_ot_conf *conf = FLT_OT_CONF(f);
+	char                     *err = NULL;
+
+	FLT_OT_FUNC("%p, %p", s, f);
+
+	if (conf->tracer->flag_disabled) {
+		FLT_OT_DBG(2, "filter '%s', type: %s (disabled)", conf->id, flt_ot_type(f));
+
+		FLT_OT_RETURN(FLT_OT_RET_IGNORE);
+	}
+	else if (conf->tracer->rate_limit < FLT_OT_FLOAT_U32(FLT_OT_RATE_LIMIT_MAX, FLT_OT_RATE_LIMIT_MAX)) {
+		uint32_t rnd = ha_random32();
+
+		if (conf->tracer->rate_limit <= rnd) {
+			FLT_OT_DBG(2, "filter '%s', type: %s (ignored: %u <= %u)", conf->id, flt_ot_type(f), conf->tracer->rate_limit, rnd);
+
+			FLT_OT_RETURN(FLT_OT_RET_IGNORE);
+		}
+	}
+
+	FLT_OT_DBG(2, "filter '%s', type: %s (run)", conf->id, flt_ot_type(f));
+
+	f->ctx = flt_ot_runtime_context_init(s, f, &err);
+	FLT_OT_ERR_FREE(err);
+	if (f->ctx == NULL) {
+		FLT_OT_LOG(LOG_EMERG, "failed to create context");
+
+		FLT_OT_RETURN(FLT_OT_RET_IGNORE);
+	}
+
+	/*
+	 * AN_REQ_WAIT_HTTP and AN_RES_WAIT_HTTP analyzers can only be used
+	 * in the .channel_post_analyze callback function.
+	 */
+	f->pre_analyzers  |= conf->tracer->analyzers & ((AN_REQ_ALL & ~AN_REQ_WAIT_HTTP & ~AN_REQ_HTTP_TARPIT) | (AN_RES_ALL & ~AN_RES_WAIT_HTTP));
+	f->post_analyzers |= conf->tracer->analyzers & (AN_REQ_WAIT_HTTP | AN_RES_WAIT_HTTP);
+
+	FLT_OT_LOG(LOG_INFO, "%08x %08x", f->pre_analyzers, f->post_analyzers);
+
+	flt_ot_vars_dump(s);
+	flt_ot_http_headers_dump(&(s->req));
+
+	FLT_OT_RETURN(FLT_OT_RET_OK);
+}
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_stream_start - Called when a stream is created.
+ *
+ * ARGUMENTS
+ *   s -
+ *   f -
+ *
+ * DESCRIPTION
+ *   It is called when a stream is started.  This callback can fail by
+ *   returning a negative value.  It will be considered as a critical error
+ *   by HAProxy which disabled the listener for a short time.
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, any other value otherwise.
+ */
+static int flt_ot_stream_start(struct stream *s, struct filter *f)
+{
+	char *err = NULL;
+	int   retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p", s, f);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN(retval);
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_stream_set_backend - Called when a backend is set for a stream.
+ *
+ * ARGUMENTS
+ *   s  -
+ *   f  -
+ *   be -
+ *
+ * DESCRIPTION
+ *   It is called when a backend is set for a stream.  This callbacks will be
+ *   called for all filters attached to a stream (frontend and backend).  Note
+ *   this callback is not called if the frontend and the backend are the same.
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, any other value otherwise.
+ */
+static int flt_ot_stream_set_backend(struct stream *s, struct filter *f, struct proxy *be)
+{
+	char *err = NULL;
+	int   retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p, %p", s, f, be);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN(retval);
+
+	FLT_OT_DBG(3, "backend: %s", be->id);
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_stream_stop - Called when a stream is destroyed.
+ *
+ * ARGUMENTS
+ *   s -
+ *   f -
+ *
+ * DESCRIPTION
+ *   It is called when a stream is stopped.  This callback always succeed.
+ *   Anyway, it is too late to return an error.
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_stream_stop(struct stream *s, struct filter *f)
+{
+	char *err = NULL;
+
+	FLT_OT_FUNC("%p, %p", s, f);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN();
+
+	flt_ot_return_void(f, &err);
+
+	FLT_OT_RETURN();
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   flt_ot_detach - Called when a filter instance is detach from a stream, just before its destruction.
+ *
+ * ARGUMENTS
+ *   s -
+ *   f -
+ *
+ * DESCRIPTION
+ *   It is called when a filter instance is detached from a stream, before its
+ *   destruction.  This happens when the stream is stopped for filters defined
+ *   on the stream's frontend and when the analyze ends for filters defined on
+ *   the stream's backend.
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_detach(struct stream *s, struct filter *f)
+{
+	FLT_OT_FUNC("%p, %p", s, f);
+
+	FLT_OT_DBG(2, "filter '%s', type: %s", FLT_OT_CONF(f)->id, flt_ot_type(f));
+
+	flt_ot_runtime_context_free(f);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_check_timeouts - Called when a stream is woken up because of an expired timer.
+ *
+ * ARGUMENTS
+ *   s -
+ *   f -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_check_timeouts(struct stream *s, struct filter *f)
+{
+	char *err = NULL;
+
+	FLT_OT_FUNC("%p, %p", s, f);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN();
+
+	s->pending_events |= TASK_WOKEN_MSG;
+
+	flt_ot_return_void(f, &err);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_channel_start_analyze - Called when analyze starts for a given channel.
+ *
+ * ARGUMENTS
+ *   s   -
+ *   f   -
+ *   chn -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+static int flt_ot_channel_start_analyze(struct stream *s, struct filter *f, struct channel *chn)
+{
+	char *err = NULL;
+	int   retval;
+
+	FLT_OT_FUNC("%p, %p, %p", s, f, chn);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OT_EVENT_RES_SERVER_SESS_START : FLT_OT_EVENT_REQ_CLIENT_SESS_START)))
+		FLT_OT_RETURN(FLT_OT_RET_OK);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
+
+	if (chn->flags & CF_ISRESP) {
+		/* The response channel. */
+		chn->analysers |= f->pre_analyzers & AN_RES_ALL;
+
+		/* The event 'on-server-session-start'. */
+		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_RES_SERVER_SESS_START, &err);
+		if (retval == FLT_OT_RET_WAIT) {
+			channel_dont_read(chn);
+			channel_dont_close(chn);
+		}
+	} else {
+		/* The request channel. */
+		chn->analysers |= f->pre_analyzers & AN_REQ_ALL;
+
+		/* The event 'on-client-session-start'. */
+		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_REQ_CLIENT_SESS_START, &err);
+	}
+
+//	register_data_filter(s, chn, f);
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_channel_pre_analyze - Called before a processing happens on a given channel.
+ *
+ * ARGUMENTS
+ *   s      -
+ *   f      -
+ *   chn    - the channel on which the analyzing is done
+ *   an_bit - the analyzer id
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+static int flt_ot_channel_pre_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
+{
+	char *err = NULL;
+	int   i, event = -1, retval;
+
+	FLT_OT_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit);
+
+	for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_event_data); i++)
+		if (flt_ot_event_data[i].an_bit == an_bit) {
+			event = i;
+
+			break;
+		}
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, event)))
+		FLT_OT_RETURN(FLT_OT_RET_OK);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), analyzer: %s", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), flt_ot_analyzer(an_bit));
+
+	retval = flt_ot_event_run(s, f, chn, event, &err);
+
+	if ((retval == FLT_OT_RET_WAIT) && (chn->flags & CF_ISRESP)) {
+		channel_dont_read(chn);
+		channel_dont_close(chn);
+	}
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_channel_post_analyze - Called after a processing happens on a given channel.
+ *
+ * ARGUMENTS
+ *   s      -
+ *   f      -
+ *   chn    -
+ *   an_bit -
+ *
+ * DESCRIPTION
+ *   This function, for its part, is not resumable.  It is called when a
+ *   filterable analyzer finishes its processing.  So it called once for
+ *   the same analyzer.
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+static int flt_ot_channel_post_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit)
+{
+	char *err = NULL;
+	int   i, event = -1, retval;
+
+	FLT_OT_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit);
+
+	for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_event_data); i++)
+		if (flt_ot_event_data[i].an_bit == an_bit) {
+			event = i;
+
+			break;
+		}
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, event)))
+		FLT_OT_RETURN(FLT_OT_RET_OK);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), analyzer: %s", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), flt_ot_analyzer(an_bit));
+
+	retval = flt_ot_event_run(s, f, chn, event, &err);
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_channel_end_analyze - Called when analyze ends for a given channel.
+ *
+ * ARGUMENTS
+ *   s   -
+ *   f   -
+ *   chn -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+static int flt_ot_channel_end_analyze(struct stream *s, struct filter *f, struct channel *chn)
+{
+	char *err = NULL;
+	int   rc, retval;
+
+	FLT_OT_FUNC("%p, %p, %p", s, f, chn);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, (chn->flags & CF_ISRESP) ? FLT_OT_EVENT_RES_SERVER_SESS_END : FLT_OT_EVENT_REQ_CLIENT_SESS_END)))
+		FLT_OT_RETURN(FLT_OT_RET_OK);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
+
+	if (chn->flags & CF_ISRESP) {
+		/* The response channel, event 'on-server-session-end'. */
+		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_RES_SERVER_SESS_END, &err);
+	} else {
+		/* The request channel, event 'on-client-session-end'. */
+		retval = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_REQ_CLIENT_SESS_END, &err);
+
+		/*
+		 * In case an event using server response is defined and not
+		 * executed, event 'on-server-unavailable' is called here.
+		 */
+		if ((FLT_OT_CONF(f)->tracer->analyzers & AN_RES_ALL) && !(FLT_OT_RT_CTX(f->ctx)->analyzers & AN_RES_ALL)) {
+			rc = flt_ot_event_run(s, f, chn, FLT_OT_EVENT_REQ_SERVER_UNAVAILABLE, &err);
+			if ((retval == FLT_OT_RET_OK) && (rc != FLT_OT_RET_OK))
+				retval = rc;
+		}
+	}
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_http_headers -
+ *
+ * ARGUMENTS
+ *   s   -
+ *   f   -
+ *   msg -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+static int flt_ot_http_headers(struct stream *s, struct filter *f, struct http_msg *msg)
+{
+	char          *err = NULL;
+	struct htx    *htx = htxbuf(&(msg->chn->buf));
+	struct htx_sl *sl = http_get_stline(htx);
+	int            retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p, %p", s, f, msg);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN(retval);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), %.*s %.*s %.*s", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), HTX_SL_P1_LEN(sl), HTX_SL_P1_PTR(sl), HTX_SL_P2_LEN(sl), HTX_SL_P2_PTR(sl), HTX_SL_P3_LEN(sl), HTX_SL_P3_PTR(sl));
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_http_payload -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   f      -
+ *   msg    -
+ *   offset -
+ *   len    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, any other value otherwise.
+ */
+static int flt_ot_http_payload(struct stream *s, struct filter *f, struct http_msg *msg, uint offset, uint len)
+{
+	char *err = NULL;
+	int   retval = len;
+
+	FLT_OT_FUNC("%p, %p, %p, %u, %u", s, f, msg, offset, len);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN(len);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), offset, len, retval);
+
+	if (retval != len)
+		task_wakeup(s->task, TASK_WOKEN_MSG);
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_http_end -
+ *
+ * ARGUMENTS
+ *   s   -
+ *   f   -
+ *   msg -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+static int flt_ot_http_end(struct stream *s, struct filter *f, struct http_msg *msg)
+{
+	char *err = NULL;
+	int   retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %p, %p", s, f, msg);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN(retval);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_http_reset -
+ *
+ * ARGUMENTS
+ *   s   -
+ *   f   -
+ *   msg -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_http_reset(struct stream *s, struct filter *f, struct http_msg *msg)
+{
+	char *err = NULL;
+
+	FLT_OT_FUNC("%p, %p, %p", s, f, msg);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s)", flt_ot_chn_label(msg->chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s));
+
+	flt_ot_return_void(f, &err);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_http_reply -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   f      -
+ *   status -
+ *   msg    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_http_reply(struct stream *s, struct filter *f, short status, const struct buffer *msg)
+{
+	char *err = NULL;
+
+	FLT_OT_FUNC("%p, %p, %hd, %p", s, f, status, msg);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG(3, "channel: -, mode: %s (%s)", flt_ot_pr_mode(s), flt_ot_stream_pos(s));
+
+	flt_ot_return_void(f, &err);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_tcp_payload -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   f      -
+ *   chn    -
+ *   offset -
+ *   len    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, any other value otherwise.
+ */
+static int flt_ot_tcp_payload(struct stream *s, struct filter *f, struct channel *chn, uint offset, uint len)
+{
+	char *err = NULL;
+	int   retval = len;
+
+	FLT_OT_FUNC("%p, %p, %p, %u, %u", s, f, chn, offset, len);
+
+	if (flt_ot_is_disabled(f FLT_OT_DBG_ARGS(, -1)))
+		FLT_OT_RETURN(len);
+
+	FLT_OT_DBG(3, "channel: %s, mode: %s (%s), offset: %u, len: %u, forward: %d", flt_ot_chn_label(chn), flt_ot_pr_mode(s), flt_ot_stream_pos(s), offset, len, retval);
+
+	if (s->flags & SF_HTX) {
+	} else {
+	}
+
+	if (retval != len)
+		task_wakeup(s->task, TASK_WOKEN_MSG);
+
+	FLT_OT_RETURN(flt_ot_return_int(f, &err, retval));
+}
+
+#endif /* DEBUG_OT */
+
+
+struct flt_ops flt_ot_ops = {
+	/* Callbacks to manage the filter lifecycle. */
+	.init                  = flt_ot_init,
+	.deinit                = flt_ot_deinit,
+	.check                 = flt_ot_check,
+	.init_per_thread       = FLT_OT_DBG_IFDEF(flt_ot_init_per_thread, NULL),
+	.deinit_per_thread     = FLT_OT_DBG_IFDEF(flt_ot_deinit_per_thread, NULL),
+
+	/* Stream callbacks. */
+	.attach                = flt_ot_attach,
+	.stream_start          = FLT_OT_DBG_IFDEF(flt_ot_stream_start, NULL),
+	.stream_set_backend    = FLT_OT_DBG_IFDEF(flt_ot_stream_set_backend, NULL),
+	.stream_stop           = FLT_OT_DBG_IFDEF(flt_ot_stream_stop, NULL),
+	.detach                = flt_ot_detach,
+	.check_timeouts        = flt_ot_check_timeouts,
+
+	/* Channel callbacks. */
+	.channel_start_analyze = flt_ot_channel_start_analyze,
+	.channel_pre_analyze   = flt_ot_channel_pre_analyze,
+	.channel_post_analyze  = flt_ot_channel_post_analyze,
+	.channel_end_analyze   = flt_ot_channel_end_analyze,
+
+	/* HTTP callbacks. */
+	.http_headers          = FLT_OT_DBG_IFDEF(flt_ot_http_headers, NULL),
+	.http_payload          = FLT_OT_DBG_IFDEF(flt_ot_http_payload, NULL),
+	.http_end              = FLT_OT_DBG_IFDEF(flt_ot_http_end, NULL),
+	.http_reset            = FLT_OT_DBG_IFDEF(flt_ot_http_reset, NULL),
+	.http_reply            = FLT_OT_DBG_IFDEF(flt_ot_http_reply, NULL),
+
+	/* TCP callbacks. */
+	.tcp_payload           = FLT_OT_DBG_IFDEF(flt_ot_tcp_payload, NULL)
+};
+
+
+REGISTER_BUILD_OPTS("Built with OpenTracing support.");
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/group.c b/contrib/opentracing/src/group.c
new file mode 100644
index 0000000..f9fdecc
--- /dev/null
+++ b/contrib/opentracing/src/group.c
@@ -0,0 +1,354 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+#define FLT_OT_GROUP_DEF(a,b,c)   { a, b, c },
+const struct flt_ot_group_data flt_ot_group_data[] = { FLT_OT_GROUP_DEFINES };
+#undef FLT_OT_GROUP_DEF
+
+
+/***
+ * NAME
+ *   flt_ot_group_action -
+ *
+ * ARGUMENTS
+ *   rule -
+ *   px   -
+ *   sess -
+ *   s    -
+ *   opts -
+ *
+ * DESCRIPTION
+ *   This is the action_ptr callback of a rule associated to the
+ *   FLT_OT_ACTION_GROUP action.
+ *
+ * RETURN VALUE
+ *   The function returns ACT_RET_CONT if processing is finished (with error or
+ *   not), otherwise, it returns ACT_RET_YIELD if the action is in progress.
+ */
+static enum act_return flt_ot_group_action(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int opts)
+{
+	const struct filter                 *filter;
+	const struct flt_conf               *fconf;
+	const struct flt_ot_conf            *conf;
+	const struct flt_ot_conf_group      *conf_group;
+	const struct flt_ot_runtime_context *rt_ctx = NULL;
+	const struct flt_ot_conf_ph         *ph_scope;
+	char                                *err = NULL;
+	int                                  i, rc;
+
+	FLT_OT_FUNC("%p, %p, %p, %p, %d", rule, px, sess, s, opts);
+
+	FLT_OT_DBG(3, "from: %d, arg.act %p:{ %p %p %p %p }", rule->from, &(rule->arg.act), rule->arg.act.p[0], rule->arg.act.p[1], rule->arg.act.p[2], rule->arg.act.p[3]);
+
+	fconf      = rule->arg.act.p[FLT_OT_ARG_FLT_CONF];
+	conf       = rule->arg.act.p[FLT_OT_ARG_CONF];
+	conf_group = ((const struct flt_ot_conf_ph *)(rule->arg.act.p[FLT_OT_ARG_GROUP]))->ptr;
+
+	if ((fconf == NULL) || (conf == NULL) || (conf_group == NULL)) {
+		FLT_OT_LOG(LOG_ERR, FLT_OT_ACTION_GROUP ": internal error, invalid group action");
+
+		FLT_OT_RETURN(ACT_RET_CONT);
+	}
+
+	if (conf->tracer->flag_disabled) {
+		FLT_OT_DBG(1, "filter '%s' disabled, group action '%s' ignored", conf->id, conf_group->id);
+
+		FLT_OT_RETURN(ACT_RET_CONT);
+	}
+
+	/* Find the OpenTracing filter instance from the current stream. */
+	list_for_each_entry(filter, &(s->strm_flt.filters), list)
+		if (filter->config == fconf) {
+			rt_ctx = filter->ctx;
+
+			break;
+		}
+
+	if (rt_ctx == NULL) {
+		FLT_OT_DBG(1, "cannot find filter, probably not attached to the stream");
+
+		FLT_OT_RETURN(ACT_RET_CONT);
+	}
+	else if (flt_ot_is_disabled(filter FLT_OT_DBG_ARGS(, -1))) {
+		FLT_OT_RETURN(ACT_RET_CONT);
+	}
+	else {
+		FLT_OT_DBG(3, "run group '%s'", conf_group->id);
+		FLT_OT_DBG_CONF_GROUP("run group ", conf_group);
+	}
+
+	/*
+	 * Check the value of rule->from; in case it is incorrect,
+	 * report an error.
+	 */
+	for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_group_data); i++)
+		if (flt_ot_group_data[i].act_from == rule->from)
+			break;
+
+	if (i >= FLT_OT_TABLESIZE(flt_ot_group_data)) {
+		FLT_OT_LOG(LOG_ERR, FLT_OT_ACTION_GROUP ": internal error, invalid rule->from=%d", rule->from);
+
+		FLT_OT_RETURN(ACT_RET_CONT);
+	}
+
+	list_for_each_entry(ph_scope, &(conf_group->ph_scopes), list) {
+		rc = flt_ot_scope_run(s, rt_ctx->filter, &(s->res), ph_scope->ptr, NULL, SMP_OPT_DIR_RES, &err);
+		if ((rc == FLT_OT_RET_ERROR) && (opts & ACT_OPT_FINAL)) {
+			/* XXX */
+		}
+	}
+
+	FLT_OT_RETURN(ACT_RET_CONT);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_group_check -
+ *
+ * ARGUMENTS
+ *   rule -
+ *   px   -
+ *   err  -
+ *
+ * DESCRIPTION
+ *   This is the check_ptr callback of a rule associated to the
+ *   FLT_OT_ACTION_GROUP action.
+ *
+ * RETURN VALUE
+ *   The function returns 1 in success case, otherwise,
+ *   it returns 0 and err is filled.
+ */
+static int flt_ot_group_check(struct act_rule *rule, struct proxy *px, char **err)
+{
+	struct flt_conf       *fconf_tmp, *fconf = NULL;
+	struct flt_ot_conf    *conf;
+	struct flt_ot_conf_ph *ph_group;
+	const char            *filter_id;
+	const char            *group_id;
+	bool                   flag_found = 0;
+	int                    i;
+
+	FLT_OT_FUNC("%p, %p, %p:%p", rule, px, FLT_OT_DPTR_ARGS(err));
+
+	filter_id = rule->arg.act.p[FLT_OT_ARG_FILTER_ID];
+	group_id  = rule->arg.act.p[FLT_OT_ARG_GROUP_ID];
+
+	FLT_OT_DBG(2, "checking filter_id='%s', group_id='%s'", filter_id, group_id);
+
+	/*
+	 * Check the value of rule->from; in case it is incorrect,
+	 * report an error.
+	 */
+	for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_group_data); i++)
+		if (flt_ot_group_data[i].act_from == rule->from)
+			break;
+
+	if (i >= FLT_OT_TABLESIZE(flt_ot_group_data)) {
+		FLT_OT_ERR("internal error, unexpected rule->from=%d, please report this bug!", rule->from);
+
+		FLT_OT_RETURN(0);
+	}
+
+	/*
+	 * Try to find the OpenTracing filter by checking all filters
+	 * for the proxy <px>.
+	 */
+	list_for_each_entry(fconf_tmp, &(px->filter_configs), list) {
+		conf = fconf_tmp->conf;
+
+		if (fconf_tmp->id != ot_flt_id) {
+			/* This is not an OpenTracing filter. */
+			continue;
+		}
+		else if (strcmp(conf->id, filter_id) == 0) {
+			/* This is the good filter ID. */
+			fconf = fconf_tmp;
+
+			break;
+		}
+	}
+
+	if (fconf == NULL) {
+		FLT_OT_ERR("unable to find the OpenTracing filter '%s' used by the " FLT_OT_ACTION_GROUP " '%s'", filter_id, group_id);
+
+		FLT_OT_RETURN(0);
+	}
+
+	/*
+	 * Attempt to find if the group is defined in the OpenTracing filter
+	 * configuration.
+	 */
+	list_for_each_entry(ph_group, &(conf->tracer->ph_groups), list)
+		if (strcmp(ph_group->id, group_id) == 0) {
+			flag_found = 1;
+
+			break;
+		}
+
+	if (!flag_found) {
+		FLT_OT_ERR("unable to find group '%s' in the OpenTracing filter '%s' configuration", group_id, filter_id);
+
+		FLT_OT_RETURN(0);
+	}
+
+	FLT_OT_FREE_CLEAR(rule->arg.act.p[FLT_OT_ARG_FILTER_ID]);
+	FLT_OT_FREE_CLEAR(rule->arg.act.p[FLT_OT_ARG_GROUP_ID]);
+
+	rule->arg.act.p[FLT_OT_ARG_FLT_CONF] = fconf;
+	rule->arg.act.p[FLT_OT_ARG_CONF]     = conf;
+	rule->arg.act.p[FLT_OT_ARG_GROUP]    = ph_group;
+
+	FLT_OT_RETURN(1);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_group_release -
+ *
+ * ARGUMENTS
+ *   rule -
+ *
+ * DESCRIPTION
+ *   This is the release_ptr callback of a rule associated to the
+ *   FLT_OT_ACTION_GROUP action.
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_group_release(struct act_rule *rule)
+{
+	FLT_OT_FUNC("%p", rule);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_group_parse -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   cur_arg -
+ *   px      -
+ *   rule    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ACT_RET_PRS_ERR if an error occurs, ACT_RET_PRS_OK otherwise.
+ */
+static enum act_parse_ret flt_ot_group_parse(const char **args, int *cur_arg, struct proxy *px, struct act_rule *rule, char **err)
+{
+	FLT_OT_FUNC("%p, %p, %p, %p, %p:%p", args, cur_arg, px, rule, FLT_OT_DPTR_ARGS(err));
+
+	if (!FLT_OT_ARG_ISVALID(*cur_arg) ||
+	    !FLT_OT_ARG_ISVALID(*cur_arg + 1) ||
+	    (FLT_OT_ARG_ISVALID(*cur_arg + 2) &&
+	     (strcmp(args[*cur_arg + 2], FLT_OT_CONDITION_IF) != 0) &&
+	     (strcmp(args[*cur_arg + 2], FLT_OT_CONDITION_UNLESS) != 0))) {
+		FLT_OT_ERR("expects: <filter-id> <group-id> [{ if | unless } ...]");
+
+		FLT_OT_RETURN(ACT_RET_PRS_ERR);
+	}
+
+	/* Copy the OpenTracing filter id. */
+	rule->arg.act.p[FLT_OT_ARG_FILTER_ID] = FLT_OT_STRDUP(args[*cur_arg]);
+	if (rule->arg.act.p[FLT_OT_ARG_FILTER_ID] == NULL) {
+		FLT_OT_ERR("%s : out of memory", args[*cur_arg]);
+
+		FLT_OT_RETURN(ACT_RET_PRS_ERR);
+	}
+
+	/* Copy the OpenTracing group id. */
+	rule->arg.act.p[FLT_OT_ARG_GROUP_ID] = FLT_OT_STRDUP(args[*cur_arg + 1]);
+	if (rule->arg.act.p[FLT_OT_ARG_GROUP_ID] == NULL) {
+		FLT_OT_ERR("%s : out of memory", args[*cur_arg + 1]);
+
+		FLT_OT_FREE_CLEAR(rule->arg.act.p[FLT_OT_ARG_FILTER_ID]);
+
+		FLT_OT_RETURN(ACT_RET_PRS_ERR);
+	}
+
+	rule->action      = ACT_CUSTOM;
+	rule->action_ptr  = flt_ot_group_action;
+	rule->check_ptr   = flt_ot_group_check;
+	rule->release_ptr = flt_ot_group_release;
+
+	*cur_arg += 2;
+
+	FLT_OT_RETURN(ACT_RET_PRS_OK);
+}
+
+
+static struct action_kw_list tcp_req_action_kws = { ILH, {
+		{ FLT_OT_ACTION_GROUP, flt_ot_group_parse },
+		{ /* END */ },
+	}
+};
+
+INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_action_kws);
+
+static struct action_kw_list tcp_res_action_kws = { ILH, {
+		{ FLT_OT_ACTION_GROUP, flt_ot_group_parse },
+		{ /* END */ },
+	}
+};
+
+INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_action_kws);
+
+static struct action_kw_list http_req_action_kws = { ILH, {
+		{ FLT_OT_ACTION_GROUP, flt_ot_group_parse },
+		{ /* END */ },
+	}
+};
+
+INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_action_kws);
+
+static struct action_kw_list http_res_action_kws = { ILH, {
+		{ FLT_OT_ACTION_GROUP, flt_ot_group_parse },
+		{ /* END */ },
+	}
+};
+
+INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_action_kws);
+
+static struct action_kw_list http_after_res_actions_kws = { ILH, {
+		{ FLT_OT_ACTION_GROUP, flt_ot_group_parse },
+		{ /* END */ },
+	}
+};
+
+INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_actions_kws);
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/http.c b/contrib/opentracing/src/http.c
new file mode 100644
index 0000000..72b31b7
--- /dev/null
+++ b/contrib/opentracing/src/http.c
@@ -0,0 +1,291 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_http_headers_dump -
+ *
+ * ARGUMENTS
+ *   chn -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_http_headers_dump(const struct channel *chn)
+{
+	const struct htx *htx;
+	int32_t           pos;
+
+	FLT_OT_FUNC("%p", chn);
+
+	if (chn == NULL)
+		FLT_OT_RETURN();
+
+	htx = htxbuf(&(chn->buf));
+
+	if (htx_is_empty(htx))
+		FLT_OT_RETURN();
+
+	for (pos = htx_get_first(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_HDR) {
+			struct ist n = htx_get_blk_name(htx, blk);
+			struct ist v = htx_get_blk_value(htx, blk);
+
+			FLT_OT_DBG(2, "'%.*s: %.*s'", (int)n.len, n.ptr, (int)v.len, v.ptr);
+		}
+		else if (type == HTX_BLK_EOH)
+			break;
+	}
+
+	FLT_OT_RETURN();
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   flt_ot_http_headers_get -
+ *
+ * ARGUMENTS
+ *   chn    -
+ *   prefix -
+ *   len    -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   This function is very similar to function http_action_set_header(), from
+ *   the HAProxy source.
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_text_map *flt_ot_http_headers_get(struct channel *chn, const char *prefix, size_t len, char **err)
+{
+	const struct htx    *htx;
+	size_t               prefix_len = (!FLT_OT_STR_ISVALID(prefix) || (len == 0)) ? 0 : (len + 1);
+	int32_t              pos;
+	struct otc_text_map *retptr = NULL;
+
+	FLT_OT_FUNC("%p, \"%s\", %zu, %p:%p", chn, prefix, len, FLT_OT_DPTR_ARGS(err));
+
+	if (chn == NULL)
+		FLT_OT_RETURN(retptr);
+
+	htx = htxbuf(&(chn->buf));
+
+	for (pos = htx_get_first(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_HDR) {
+			struct ist v, n = htx_get_blk_name(htx, blk);
+
+			if ((prefix_len == 0) || ((n.len >= prefix_len) && (strncasecmp(n.ptr, prefix, len) == 0))) {
+				if (retptr == NULL) {
+					retptr = otc_text_map_new(NULL, 8);
+					if (retptr == NULL) {
+						FLT_OT_ERR("failed to create HTTP header data");
+
+						break;
+					}
+				}
+
+				v = htx_get_blk_value(htx, blk);
+
+				/*
+				 * Here, an HTTP header (which is actually part
+				 * of the span context is added to the text_map.
+				 *
+				 * Before adding, the prefix is removed from the
+				 * HTTP header name.
+				 */
+				if (otc_text_map_add(retptr, n.ptr + prefix_len, n.len - prefix_len, v.ptr, v.len, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1) {
+					FLT_OT_ERR("failed to add HTTP header data");
+
+					otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+
+					break;
+				}
+			}
+		}
+		else if (type == HTX_BLK_EOH)
+			break;
+	}
+
+	ot_text_map_show(retptr);
+
+	if ((retptr != NULL) && (retptr->count == 0)) {
+		FLT_OT_DBG(2, "WARNING: no HTTP headers found");
+
+		otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+	}
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_http_header_set -
+ *
+ * ARGUMENTS
+ *   chn    -
+ *   prefix -
+ *   name   -
+ *   value  -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   This function is very similar to function http_action_set_header(), from
+ *   the HAProxy source.
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_http_header_set(struct channel *chn, const char *prefix, const char *name, const char *value, char **err)
+{
+	struct http_hdr_ctx  ctx = { .blk = NULL };
+	struct ist           ist_name;
+	struct buffer       *buffer = NULL;
+	struct htx          *htx;
+	int                  retval = -1;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %p:%p", chn, prefix, name, value, FLT_OT_DPTR_ARGS(err));
+
+	if ((chn == NULL) || (!FLT_OT_STR_ISVALID(prefix) && !FLT_OT_STR_ISVALID(name)))
+		FLT_OT_RETURN(retval);
+
+	htx = htxbuf(&(chn->buf));
+
+	/*
+	 * Very rare (about 1% of cases), htx is empty.
+	 * In order to avoid segmentation fault, we exit this function.
+	 */
+	if (htx_is_empty(htx)) {
+		FLT_OT_ERR("HTX is empty");
+
+		FLT_OT_RETURN(retval);
+	}
+
+	if (!FLT_OT_STR_ISVALID(prefix)) {
+		ist_name.ptr = (char *)name;
+		ist_name.len = strlen(name);
+	}
+	else if (!FLT_OT_STR_ISVALID(name)) {
+		ist_name.ptr = (char *)prefix;
+		ist_name.len = strlen(prefix);
+	}
+	else {
+		buffer = flt_ot_trash_alloc(0, err);
+		if (buffer == NULL)
+			FLT_OT_RETURN(retval);
+
+		(void)chunk_printf(buffer, "%s-%s", prefix, name);
+
+		ist_name.ptr = buffer->area;
+		ist_name.len = buffer->data;
+	}
+
+	/* Remove all occurrences of the header. */
+	while (http_find_header(htx, ist(""), &ctx, 1) == 1) {
+		struct ist n = htx_get_blk_name(htx, ctx.blk);
+#ifdef DEBUG_OT
+		struct ist v = htx_get_blk_value(htx, ctx.blk);
+#endif
+
+		/*
+		 * If the <name> parameter is not set, then remove all headers
+		 * that start with the contents of the <prefix> parameter.
+		 */
+		if (!FLT_OT_STR_ISVALID(name))
+			n.len = ist_name.len;
+
+		if (isteqi(n, ist_name))
+			if (http_remove_header(htx, &ctx) == 1)
+				FLT_OT_DBG(3, "HTTP header '%.*s: %.*s' removed", (int)n.len, n.ptr, (int)v.len, v.ptr);
+	}
+
+	/*
+	 * If the value pointer has a value of NULL, the HTTP header is not set
+	 * after deletion.
+	 */
+	if (value == NULL) {
+		/* Do nothing. */
+	}
+	else if (http_add_header(htx, ist_name, ist(value)) == 1) {
+		retval = 0;
+
+		FLT_OT_DBG(3, "HTTP header '%s: %s' added", ist_name.ptr, value);
+	}
+	else {
+		FLT_OT_ERR("failed to set HTTP header '%s: %s'", ist_name.ptr, value);
+	}
+
+	flt_ot_trash_free(&buffer);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_http_headers_remove -
+ *
+ * ARGUMENTS
+ *   chn    -
+ *   prefix -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_http_headers_remove(struct channel *chn, const char *prefix, char **err)
+{
+	int retval;
+
+	FLT_OT_FUNC("%p, \"%s\", %p:%p", chn, prefix, FLT_OT_DPTR_ARGS(err));
+
+	retval = flt_ot_http_header_set(chn, prefix, NULL, NULL, err);
+
+	FLT_OT_RETURN(retval);
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/opentracing.c b/contrib/opentracing/src/opentracing.c
new file mode 100644
index 0000000..b79b90a
--- /dev/null
+++ b/contrib/opentracing/src/opentracing.c
@@ -0,0 +1,1035 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+static struct pool_head *pool_head_ot_span_context = NULL;
+
+#ifdef USE_POOL_OT_SPAN_CONTEXT
+REGISTER_POOL(&pool_head_ot_span_context, "ot_span_context", MAX(sizeof(struct otc_span), sizeof(struct otc_span_context)));
+#endif
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   ot_text_map_show -
+ *
+ * ARGUMENTS
+ *   text_map -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void ot_text_map_show(const struct otc_text_map *text_map)
+{
+	FLT_OT_FUNC("%p", text_map);
+
+	if (text_map == NULL)
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_TEXT_MAP(text_map);
+
+	if ((text_map->key != NULL) && (text_map->value != NULL) && (text_map->count > 0)) {
+		size_t i;
+
+		for (i = 0; i < text_map->count; i++)
+			FLT_OT_DBG(3, "  \"%s\" -> \"%s\"", text_map->key[i], text_map->value[i]);
+	}
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   ot_debug -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void ot_debug(void)
+{
+	char buffer[BUFSIZ];
+
+	FLT_OT_FUNC("");
+
+	otc_statistics(buffer, sizeof(buffer));
+	FLT_OT_DBG(0, "%s", buffer);
+
+	FLT_OT_RETURN();
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   ot_mem_malloc -
+ *
+ * ARGUMENTS
+ *   func -
+ *   line -
+ *   size -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static void *ot_mem_malloc(FLT_OT_DBG_ARGS(const char *func, int line, ) size_t size)
+{
+	return flt_ot_pool_alloc(pool_head_ot_span_context, size, 1, NULL);
+}
+
+
+/***
+ * NAME
+ *   ot_mem_free -
+ *
+ * ARGUMENTS
+ *   func -
+ *   line -
+ *   ptr  -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void ot_mem_free(FLT_OT_DBG_ARGS(const char *func, int line, ) void *ptr)
+{
+	flt_ot_pool_free(pool_head_ot_span_context, &ptr);
+}
+
+
+/***
+ * NAME
+ *   ot_init -
+ *
+ * ARGUMENTS
+ *   tracer -
+ *   config -
+ *   plugin -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_init(struct otc_tracer **tracer, const char *config, const char *plugin, char **err)
+{
+	char cwd[PATH_MAX], path[PATH_MAX], errbuf[BUFSIZ] = "";
+	int  rc, retval = -1;
+
+	FLT_OT_FUNC("%p:%p \"%s\", \"%s\", %p:%p", FLT_OT_DPTR_ARGS(tracer), config, plugin, FLT_OT_DPTR_ARGS(err));
+
+	flt_ot_pools_info();
+#ifdef USE_POOL_OT_SPAN_CONTEXT
+	FLT_OT_DBG(2, "sizeof_pool(ot_span_context) = %u", pool_head_ot_span_context->size);
+#endif
+
+	if (getcwd(cwd, sizeof(cwd)) == NULL) {
+		FLT_OT_ERR("failed to get current working directory");
+
+		FLT_OT_RETURN(retval);
+	}
+	rc = snprintf(path, sizeof(path), "%s/%s", cwd, plugin);
+	if ((rc == -1) || (rc >= sizeof(path))) {
+		FLT_OT_ERR("failed to construct the OpenTracing plugin path");
+
+		FLT_OT_RETURN(retval);
+	}
+
+	*tracer = otc_tracer_init(path, config, NULL, errbuf, sizeof(errbuf));
+	if (*tracer == NULL) {
+		FLT_OT_ERR("%s", (*errbuf == '\0') ? "failed to initialize tracing library" : errbuf);
+	} else {
+		otc_ext_init(ot_mem_malloc, ot_mem_free);
+
+		retval = 0;
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   ot_close -
+ *
+ * ARGUMENTS
+ *   tracer -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void ot_close(struct otc_tracer **tracer)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(tracer));
+
+	if ((tracer == NULL) || (*tracer == NULL))
+		FLT_OT_RETURN();
+
+	(*tracer)->close(*tracer);
+
+	*tracer = NULL;
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   ot_span_init -
+ *
+ * ARGUMENTS
+ *   tracer         -
+ *   operation_name -
+ *   ts_steady      -
+ *   ts_system      -
+ *   ref_type       -
+ *   ref_ctx_idx    -
+ *   ref_span       -
+ *   tags           -
+ *   num_tags       -
+ *   err            -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span *ot_span_init(struct otc_tracer *tracer, const char *operation_name, const struct timespec *ts_steady, const struct timespec *ts_system, int ref_type, int ref_ctx_idx, const struct otc_span *ref_span, const struct otc_tag *tags, int num_tags, char **err)
+{
+	struct otc_start_span_options  options;
+	struct otc_span_context        context = { .idx = ref_ctx_idx, .span = ref_span };
+	struct otc_span_reference      references = { ref_type, &context };
+	struct otc_span               *retptr = NULL;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p, %d, %d, %p, %p, %d, %p:%p", tracer, operation_name, ts_steady, ts_system, ref_type, ref_ctx_idx, ref_span, tags, num_tags, FLT_OT_DPTR_ARGS(err));
+
+	if (operation_name == NULL)
+		FLT_OT_RETURN(retptr);
+	else if (tracer == NULL)
+		FLT_OT_RETURN(retptr);
+
+	(void)memset(&options, 0, sizeof(options));
+
+	if (ts_steady != NULL)
+		(void)memcpy(&(options.start_time_steady.value), ts_steady, sizeof(options.start_time_steady.value));
+
+	if (ts_system != NULL)
+		(void)memcpy(&(options.start_time_system.value), ts_system, sizeof(options.start_time_system.value));
+
+	if (FLT_OT_IN_RANGE(ref_type, otc_span_reference_child_of, otc_span_reference_follows_from)) {
+		options.references     = &references;
+		options.num_references = 1;
+	}
+
+	options.tags     = tags;
+	options.num_tags = num_tags;
+
+	retptr = tracer->start_span_with_options(tracer, operation_name, &options);
+	if (retptr == NULL)
+		FLT_OT_ERR("failed to init new span");
+	else
+		FLT_OT_DBG(2, "span %p:%zd initialized", retptr, retptr->idx);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_span_init_va -
+ *
+ * ARGUMENTS
+ *   tracer         -
+ *   operation_name -
+ *   ts_steady      -
+ *   ts_system      -
+ *   ref_type       -
+ *   ref_ctx_idx    -
+ *   ref_span       -
+ *   err            -
+ *   tag_key        -
+ *   tag_value      -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span *ot_span_init_va(struct otc_tracer *tracer, const char *operation_name, const struct timespec *ts_steady, const struct timespec *ts_system, int ref_type, int ref_ctx_idx, const struct otc_span *ref_span, char **err, const char *tag_key, const char *tag_value, ...)
+{
+	struct otc_tag   tags[FLT_OT_MAXTAGS];
+	int              num_tags = 0;
+	struct otc_span *retptr;
+
+	FLT_OT_FUNC("%p, \"%s\", %p, %p, %d, %d, %p, %p:%p, \"%s\", \"%s\", ...", tracer, operation_name, ts_steady, ts_system, ref_type, ref_ctx_idx, ref_span, FLT_OT_DPTR_ARGS(err), tag_key, tag_value);
+
+	if (tag_key != NULL) {
+		va_list ap;
+
+		va_start(ap, tag_value);
+		for (num_tags = 0; (num_tags < FLT_OT_TABLESIZE(tags)) && (tag_key != NULL) && (tag_value != NULL); num_tags++) {
+			tags[num_tags].key = (char *)tag_key;
+			FLT_OT_VSET(&(tags[num_tags].value), string, tag_value);
+
+			tag_key = va_arg(ap, typeof(tag_key));
+			if (tag_key != NULL)
+				tag_value = va_arg(ap, typeof(tag_value));
+		}
+		va_end(ap);
+	}
+
+	retptr = ot_span_init(tracer, operation_name, ts_steady, ts_system, ref_type, ref_ctx_idx, ref_span, tags, num_tags, err);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_span_tag -
+ *
+ * ARGUMENTS
+ *   span     -
+ *   tags     -
+ *   num_tags -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_span_tag(struct otc_span *span, const struct otc_tag *tags, int num_tags)
+{
+	int retval = -1;
+
+	FLT_OT_FUNC("%p, %p, %d", span, tags, num_tags);
+
+	if ((span == NULL) || (tags == NULL))
+		FLT_OT_RETURN(retval);
+
+	for (retval = 0; retval < num_tags; retval++)
+		span->set_tag(span, tags[retval].key, &(tags[retval].value));
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   ot_span_tag_va -
+ *
+ * ARGUMENTS
+ *   span  -
+ *   key   -
+ *   type  -
+ *   value -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_span_tag_va(struct otc_span *span, const char *key, int type, ...)
+{
+	va_list          ap;
+	struct otc_value ot_value;
+	int              retval = -1;
+
+	FLT_OT_FUNC("%p, \"%s\", %d, ...", span, key, type);
+
+	if ((span == NULL) || (key == NULL))
+		FLT_OT_RETURN(retval);
+
+	va_start(ap, type);
+	for (retval = 0; (key != NULL) && FLT_OT_IN_RANGE(type, otc_value_bool, otc_value_null); retval++) {
+		ot_value.type = type;
+		if (type == otc_value_bool)
+			ot_value.value.bool_value = va_arg(ap, typeof(ot_value.value.bool_value));
+		else if (type == otc_value_double)
+			ot_value.value.double_value = va_arg(ap, typeof(ot_value.value.double_value));
+		else if (type == otc_value_int64)
+			ot_value.value.int64_value = va_arg(ap, typeof(ot_value.value.int64_value));
+		else if (type == otc_value_uint64)
+			ot_value.value.uint64_value = va_arg(ap, typeof(ot_value.value.uint64_value));
+		else if (type == otc_value_string)
+			ot_value.value.string_value = va_arg(ap, typeof(ot_value.value.string_value));
+		else if (type == otc_value_null)
+			ot_value.value.string_value = va_arg(ap, typeof(ot_value.value.string_value));
+		span->set_tag(span, key, &ot_value);
+
+		key = va_arg(ap, typeof(key));
+		if (key != NULL)
+			type = va_arg(ap, typeof(type));
+	}
+	va_end(ap);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   ot_span_log -
+ *
+ * ARGUMENTS
+ *   span       -
+ *   log_fields -
+ *   num_fields -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_span_log(struct otc_span *span, const struct otc_log_field *log_fields, int num_fields)
+{
+	int retval = -1;
+
+	FLT_OT_FUNC("%p, %p, %d", span, log_fields, num_fields);
+
+	if ((span == NULL) || (log_fields == NULL))
+		FLT_OT_RETURN(retval);
+
+	retval = MIN(OTC_MAXLOGFIELDS, num_fields);
+
+	span->log_fields(span, log_fields, retval);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   ot_span_log_va -
+ *
+ * ARGUMENTS
+ *   span  -
+ *   key   -
+ *   value -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_span_log_va(struct otc_span *span, const char *key, const char *value, ...)
+{
+	va_list              ap;
+	struct otc_log_field log_field[OTC_MAXLOGFIELDS];
+	int                  retval = -1;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value);
+
+	if ((span == NULL) || (key == NULL) || (value == NULL))
+		FLT_OT_RETURN(retval);
+
+	va_start(ap, value);
+	for (retval = 0; (retval < FLT_OT_TABLESIZE(log_field)) && (key != NULL); retval++) {
+		log_field[retval].key                      = key;
+		log_field[retval].value.type               = otc_value_string;
+		log_field[retval].value.value.string_value = value;
+
+		key = va_arg(ap, typeof(key));
+		if (key != NULL)
+			value = va_arg(ap, typeof(value));
+	}
+	va_end(ap);
+
+	span->log_fields(span, log_field, retval);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   ot_span_log_fmt -
+ *
+ * ARGUMENTS
+ *   span   -
+ *   key    -
+ *   format -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_span_log_fmt(struct otc_span *span, const char *key, const char *format, ...)
+{
+	va_list ap;
+	char    value[BUFSIZ];
+	int     n;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, format);
+
+	if ((span == NULL) || (key == NULL) || (format == NULL))
+		FLT_OT_RETURN(-1);
+
+	va_start(ap, format);
+	n = vsnprintf(value, sizeof(value), format, ap);
+	if (!FLT_OT_IN_RANGE(n, 0, sizeof(value) - 1)) {
+		FLT_OT_DBG(2, "WARNING: log buffer too small (%d > %zu)", n, sizeof(value));
+
+		FLT_OT_STR_ELLIPSIS(value, sizeof(value));
+	}
+	va_end(ap);
+
+	FLT_OT_RETURN(ot_span_log_va(span, key, value, NULL));
+}
+
+
+/***
+ * NAME
+ *   ot_span_set_baggage -
+ *
+ * ARGUMENTS
+ *   span    -
+ *   baggage -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_span_set_baggage(struct otc_span *span, const struct otc_text_map *baggage)
+{
+	size_t i;
+	int    retval = -1;
+
+	FLT_OT_FUNC("%p, %p", span, baggage);
+
+	if ((span == NULL) || (baggage == NULL))
+		FLT_OT_RETURN(retval);
+
+	if ((baggage->key == NULL) || (baggage->value == NULL))
+		FLT_OT_RETURN(retval);
+
+	for (retval = i = 0; i < baggage->count; i++) {
+		FLT_OT_DBG(3, "set baggage: \"%s\" -> \"%s\"", baggage->key[i], baggage->value[i]);
+
+		if ((baggage->key[i] != NULL) && (baggage->value[i] != NULL)) {
+			span->set_baggage_item(span, baggage->key[i], baggage->value[i]);
+
+			retval++;
+		}
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   ot_span_set_baggage_va -
+ *
+ * ARGUMENTS
+ *   span  -
+ *   key   -
+ *   value -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int ot_span_set_baggage_va(struct otc_span *span, const char *key, const char *value, ...)
+{
+	va_list ap;
+	int     retval = -1;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value);
+
+	if ((span == NULL) || (key == NULL) || (value == NULL))
+		FLT_OT_RETURN(retval);
+
+	va_start(ap, value);
+	for (retval = 0; (key != NULL); retval++) {
+		FLT_OT_DBG(3, "set baggage: \"%s\" -> \"%s\"", key, value);
+
+		span->set_baggage_item(span, key, value);
+
+		key = va_arg(ap, typeof(key));
+		if (key != NULL)
+			value = va_arg(ap, typeof(value));
+	}
+	va_end(ap);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   ot_span_baggage_va -
+ *
+ * ARGUMENTS
+ *   span -
+ *   key  -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_text_map *ot_span_baggage_va(const struct otc_span *span, const char *key, ...)
+{
+	va_list              ap;
+	struct otc_text_map *retptr = NULL;
+	int                  i, n;
+
+	FLT_OT_FUNC("%p, \"%s\", ...", span, key);
+
+	if ((span == NULL) || (key == NULL))
+		FLT_OT_RETURN(retptr);
+
+	va_start(ap, key);
+	for (n = 1; va_arg(ap, typeof(key)) != NULL; n++);
+	va_end(ap);
+
+	retptr = otc_text_map_new(NULL, n);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	va_start(ap, key);
+	for (i = 0; (i < n) && (key != NULL); i++) {
+		char *value = (char *)span->baggage_item(span, key);
+
+		if (value != NULL) {
+			(void)otc_text_map_add(retptr, key, 0, value, 0, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE);
+
+			FLT_OT_DBG(3, "get baggage[%d]: \"%s\" -> \"%s\"", i, retptr->key[i], retptr->value[i]);
+		} else {
+			FLT_OT_DBG(3, "get baggage[%d]: \"%s\" -> invalid key", i, key);
+		}
+
+		key = va_arg(ap, typeof(key));
+	}
+	va_end(ap);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_inject_text_map -
+ *
+ * ARGUMENTS
+ *   tracer  -
+ *   span    -
+ *   carrier -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span_context *ot_inject_text_map(struct otc_tracer *tracer, const struct otc_span *span, struct otc_text_map_writer *carrier)
+{
+	struct otc_span_context *retptr = NULL;
+	int                      rc;
+
+	FLT_OT_FUNC("%p, %p, %p", tracer, span, carrier);
+
+	if ((span == NULL) || (carrier == NULL))
+		FLT_OT_RETURN(retptr);
+	else if (tracer == NULL)
+		FLT_OT_RETURN(retptr);
+
+	retptr = span->span_context((struct otc_span *)span);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	(void)memset(carrier, 0, sizeof(*carrier));
+
+	rc = tracer->inject_text_map(tracer, carrier, retptr);
+	if (rc != otc_propagation_error_code_success) {
+		FLT_OT_FREE_CLEAR(retptr);
+	} else {
+#ifdef DEBUG_OT
+		FLT_OT_DBG_TEXT_CARRIER(carrier, set);
+		ot_text_map_show(&(carrier->text_map));
+		FLT_OT_DBG_SPAN_CONTEXT(retptr);
+#endif
+	}
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_inject_http_headers -
+ *
+ * ARGUMENTS
+ *   tracer  -
+ *   span    -
+ *   carrier -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span_context *ot_inject_http_headers(struct otc_tracer *tracer, const struct otc_span *span, struct otc_http_headers_writer *carrier, char **err)
+{
+	struct otc_span_context *retptr = NULL;
+	int                      rc;
+
+	FLT_OT_FUNC("%p, %p, %p, %p:%p", tracer, span, carrier, FLT_OT_DPTR_ARGS(err));
+
+	if ((span == NULL) || (carrier == NULL))
+		FLT_OT_RETURN(retptr);
+	else if (tracer == NULL)
+		FLT_OT_RETURN(retptr);
+
+	retptr = span->span_context((struct otc_span *)span);
+	if (retptr == NULL) {
+		FLT_OT_ERR("failed to create span context");
+
+		FLT_OT_RETURN(retptr);
+	}
+
+	(void)memset(carrier, 0, sizeof(*carrier));
+
+	rc = tracer->inject_http_headers(tracer, carrier, retptr);
+	if (rc != otc_propagation_error_code_success) {
+		FLT_OT_ERR("failed to inject HTTP headers data");
+
+		FLT_OT_FREE_CLEAR(retptr);
+	} else {
+#ifdef DEBUG_OT
+		FLT_OT_DBG_TEXT_CARRIER(carrier, set);
+		ot_text_map_show(&(carrier->text_map));
+		FLT_OT_DBG_SPAN_CONTEXT(retptr);
+#endif
+	}
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_inject_binary -
+ *
+ * ARGUMENTS
+ *   tracer  -
+ *   span    -
+ *   carrier -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span_context *ot_inject_binary(struct otc_tracer *tracer, const struct otc_span *span, struct otc_custom_carrier_writer *carrier)
+{
+	struct otc_span_context *retptr = NULL;
+	int                      rc;
+
+	FLT_OT_FUNC("%p, %p, %p", tracer, span, carrier);
+
+	if ((span == NULL) || (carrier == NULL))
+		FLT_OT_RETURN(retptr);
+	else if (tracer == NULL)
+		FLT_OT_RETURN(retptr);
+
+	retptr = span->span_context((struct otc_span *)span);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	(void)memset(carrier, 0, sizeof(*carrier));
+
+	rc = tracer->inject_binary(tracer, carrier, retptr);
+	if (rc != otc_propagation_error_code_success) {
+		FLT_OT_FREE_CLEAR(retptr);
+	} else {
+#ifdef DEBUG_OT
+		struct otc_jaeger_trace_context *ctx = carrier->binary_data.data;
+
+		FLT_OT_DBG_CUSTOM_CARRIER(carrier, inject);
+		FLT_OT_DBG(3, "trace context: %016" PRIx64 "%016" PRIx64 ":%016" PRIx64 ":%016" PRIx64 ":%02hhx <%s> <%s>",
+		           ctx->trace_id[0], ctx->trace_id[1], ctx->span_id, ctx->parent_span_id, ctx->flags,
+		           flt_ot_str_hex(ctx->baggage, carrier->binary_data.size - sizeof(*ctx)),
+		           flt_ot_str_ctrl(ctx->baggage, carrier->binary_data.size - sizeof(*ctx)));
+		FLT_OT_DBG_SPAN_CONTEXT(retptr);
+#endif
+	}
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_extract_text_map -
+ *
+ * ARGUMENTS
+ *   tracer   -
+ *   carrier  -
+ *   text_map -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span_context *ot_extract_text_map(struct otc_tracer *tracer, struct otc_text_map_reader *carrier, const struct otc_text_map *text_map)
+{
+	struct otc_span_context *retptr = NULL;
+	int                      rc;
+
+	FLT_OT_FUNC("%p, %p, %p", tracer, carrier, text_map);
+
+	if (carrier == NULL)
+		FLT_OT_RETURN(retptr);
+	else if (tracer == NULL)
+		FLT_OT_RETURN(retptr);
+
+	if (text_map != NULL) {
+		(void)memset(carrier, 0, sizeof(*carrier));
+		(void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map));
+
+		FLT_OT_DBG_TEXT_CARRIER(carrier, foreach_key);
+	}
+
+	rc = tracer->extract_text_map(tracer, carrier, &retptr);
+	if (rc != otc_propagation_error_code_success)
+		FLT_OT_FREE_CLEAR(retptr);
+	else if (retptr != NULL)
+		FLT_OT_DBG_SPAN_CONTEXT(retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_extract_http_headers -
+ *
+ * ARGUMENTS
+ *   tracer   -
+ *   carrier  -
+ *   text_map -
+ *   err      -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span_context *ot_extract_http_headers(struct otc_tracer *tracer, struct otc_http_headers_reader *carrier, const struct otc_text_map *text_map, char **err)
+{
+	struct otc_span_context *retptr = NULL;
+	int                      rc;
+
+	FLT_OT_FUNC("%p, %p, %p, %p:%p", tracer, carrier, text_map, FLT_OT_DPTR_ARGS(err));
+
+	if (carrier == NULL)
+		FLT_OT_RETURN(retptr);
+	else if (tracer == NULL)
+		FLT_OT_RETURN(retptr);
+
+	if (text_map != NULL) {
+		(void)memset(carrier, 0, sizeof(*carrier));
+		(void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map));
+
+		FLT_OT_DBG_TEXT_CARRIER(carrier, foreach_key);
+	}
+
+	rc = tracer->extract_http_headers(tracer, carrier, &retptr);
+	if (rc != otc_propagation_error_code_success) {
+		FLT_OT_ERR("failed to extract HTTP headers data");
+
+		FLT_OT_FREE_CLEAR(retptr);
+	}
+	else if (retptr != NULL)
+		FLT_OT_DBG_SPAN_CONTEXT(retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_extract_binary -
+ *
+ * ARGUMENTS
+ *   tracer      -
+ *   carrier     -
+ *   binary_data -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_span_context *ot_extract_binary(struct otc_tracer *tracer, struct otc_custom_carrier_reader *carrier, const struct otc_binary_data *binary_data)
+{
+	struct otc_span_context *retptr = NULL;
+	int                      rc;
+
+	FLT_OT_FUNC("%p, %p, %p", tracer, carrier, binary_data);
+
+	if (carrier == NULL)
+		FLT_OT_RETURN(retptr);
+	else if (tracer == NULL)
+		FLT_OT_RETURN(retptr);
+
+	if ((FLT_OT_DEREF(binary_data, data, NULL) != NULL) && (binary_data->size > 0)) {
+		(void)memset(carrier, 0, sizeof(*carrier));
+		(void)memcpy(&(carrier->binary_data), binary_data, sizeof(carrier->binary_data));
+
+		FLT_OT_DBG_CUSTOM_CARRIER(carrier, extract);
+	}
+
+	rc = tracer->extract_binary(tracer, carrier, &retptr);
+	if (rc != otc_propagation_error_code_success)
+		FLT_OT_FREE_CLEAR(retptr);
+	else if (retptr != NULL)
+		FLT_OT_DBG_SPAN_CONTEXT(retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   ot_span_finish -
+ *
+ * ARGUMENTS
+ *   span      -
+ *   ts_finish -
+ *   log_ts    -
+ *   log_key   -
+ *   log_value -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void ot_span_finish(struct otc_span **span, const struct timespec *ts_finish, const struct timespec *log_ts, const char *log_key, const char *log_value, ...)
+{
+	struct otc_finish_span_options options;
+	struct otc_log_field           log_field[OTC_MAXLOGFIELDS];
+	struct otc_log_record          log_records = { .fields = log_field, .num_fields = 0 };
+#ifdef DEBUG_OT
+	typeof((*span)->idx)           idx = FLT_OT_DDEREF(span, idx, 0);
+#endif
+
+	FLT_OT_FUNC("%p:%p, %p, %p, \"%s\", \"%s\", ...", FLT_OT_DPTR_ARGS(span), ts_finish, log_ts, log_key, log_value);
+
+	if ((span == NULL) || (*span == NULL))
+		FLT_OT_RETURN();
+
+	(void)memset(&options, 0, sizeof(options));
+
+	if (ts_finish != NULL)
+		(void)memcpy(&(options.finish_time.value), ts_finish, sizeof(options.finish_time.value));
+
+	if (log_key != NULL) {
+		va_list ap;
+		int     i;
+
+		if (log_ts != NULL)
+			(void)memcpy(&(log_records.timestamp.value), log_ts, sizeof(log_records.timestamp.value));
+
+		va_start(ap, log_value);
+		for (i = 0; (i < FLT_OT_TABLESIZE(log_field)) && (log_key != NULL); i++) {
+			log_field[i].key                      = log_key;
+			log_field[i].value.type               = otc_value_string;
+			log_field[i].value.value.string_value = log_value;
+
+			log_key = va_arg(ap, typeof(log_key));
+			if (log_key != NULL)
+				log_value = va_arg(ap, typeof(log_value));
+		}
+		va_end(ap);
+
+		log_records.num_fields  = i;
+		options.log_records     = &log_records;
+		options.num_log_records = 1;
+	}
+
+	/*
+	 * Caution: memory allocated for the span is released
+	 *          in the function finish_with_options().
+	 */
+	(*span)->finish_with_options(*span, &options);
+
+	FLT_OT_DBG(2, "span %p:%zu finished", *span, idx);
+
+	*span = NULL;
+
+	FLT_OT_RETURN();
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/parser.c b/contrib/opentracing/src/parser.c
new file mode 100644
index 0000000..b53d58d
--- /dev/null
+++ b/contrib/opentracing/src/parser.c
@@ -0,0 +1,1216 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+#ifdef DEBUG_OT
+struct flt_ot_debug               flt_ot_debug;
+THREAD_LOCAL int                  dbg_indent_level = 0;
+#endif
+
+#ifdef OTC_DBG_MEM
+static struct otc_dbg_mem_data    dbg_mem_data[1000000];
+static struct otc_dbg_mem         dbg_mem;
+#endif
+
+static struct flt_ot_conf        *flt_ot_current_config = NULL;
+static struct flt_ot_conf_tracer *flt_ot_current_tracer = NULL;
+static struct flt_ot_conf_group  *flt_ot_current_group = NULL;
+static struct flt_ot_conf_scope  *flt_ot_current_scope = NULL;
+static struct flt_ot_conf_span   *flt_ot_current_span = NULL;
+
+
+/***
+ * NAME
+ *   flt_ot_parse_strdup -
+ *
+ * ARGUMENTS
+ *   ptr     -
+ *   str     -
+ *   err     -
+ *   err_msg -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_strdup(char **ptr, const char *str, char **err, const char *err_msg)
+{
+	int retval = ERR_NONE;
+
+	FLT_OT_FUNC("%p:%p, %p, %p:%p, \"%s\"", FLT_OT_DPTR_ARGS(ptr), str, FLT_OT_DPTR_ARGS(err), err_msg);
+
+	*ptr = FLT_OT_STRDUP(str);
+	if (*ptr == NULL) {
+		FLT_OT_PARSE_ERR(err, "'%s' : out of memory", err_msg);
+
+		retval |= ERR_ABORT | ERR_ALERT;
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_keyword -
+ *
+ * ARGUMENTS
+ *   ptr     -
+ *   args    -
+ *   cur_arg -
+ *   pos     -
+ *   err     -
+ *   err_msg -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_keyword(char **ptr, char **args, int cur_arg, int pos, char **err, const char *err_msg)
+{
+	int retval = ERR_NONE;
+
+	FLT_OT_FUNC("%p:%p, %p, %d, %d, %p:%p, \"%s\"", FLT_OT_DPTR_ARGS(ptr), args, cur_arg, pos, FLT_OT_DPTR_ARGS(err), err_msg);
+
+	if (*ptr != NULL) {
+		if (cur_arg == pos)
+			FLT_OT_PARSE_ERR(err, FLT_OT_FMT_TYPE "%s already set", err_msg);
+		else
+			FLT_OT_PARSE_ERR(err, "'%s' : %s already set", args[cur_arg], err_msg);
+	}
+	else if (!FLT_OT_ARG_ISVALID(pos + 1)) {
+		if (cur_arg == pos)
+			FLT_OT_PARSE_ERR(err, FLT_OT_FMT_TYPE "no %s set", err_msg);
+		else
+			FLT_OT_PARSE_ERR(err, "'%s' : no %s set", args[cur_arg], err_msg);
+	}
+	else {
+		retval = flt_ot_parse_strdup(ptr, args[pos + 1], err, args[cur_arg]);
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_invalid_char -
+ *
+ * ARGUMENTS
+ *   name -
+ *   type -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static const char *flt_ot_parse_invalid_char(const char *name, int type)
+{
+	const char *retptr = NULL;
+
+	FLT_OT_FUNC("\"%s\", %d", name, type);
+
+	if (!FLT_OT_STR_ISVALID(name))
+		FLT_OT_RETURN(retptr);
+
+	if (type == 1) {
+		retptr = invalid_char(name);
+	}
+	else if (type == 2) {
+		retptr = invalid_domainchar(name);
+	}
+	else if (type == 3) {
+		retptr = invalid_prefix_char(name);
+	}
+	else if (type == 4) {
+		retptr = name;
+
+		/*
+		 * Allowed characters are letters, numbers and '_', the first
+		 * character in the string must not be a number.
+		 */
+		if (!isdigit(*retptr))
+			for (++retptr; (*retptr == '_') || isalnum(*retptr); retptr++);
+
+		if (*retptr == '\0')
+			retptr = NULL;
+	}
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_check -
+ *
+ * ARGUMENTS
+ *   file            -
+ *   linenum         -
+ *   args            -
+ *   id              -
+ *   parse_data      -
+ *   parse_data_size -
+ *   pdata           -
+ *   err             -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_check(const char *file, int linenum, char **args, const void *id, const struct flt_ot_parse_data *parse_data, size_t parse_data_size, const struct flt_ot_parse_data **pdata, char **err)
+{
+	int i, retval = ERR_NONE;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p, %p, %zu, %p:%p, %p:%p", file, linenum, args, id, parse_data, parse_data_size, FLT_OT_DPTR_ARGS(pdata), FLT_OT_DPTR_ARGS(err));
+
+	FLT_OT_ARGS_DUMP();
+
+	*pdata = NULL;
+
+	for (i = 0; (*pdata == NULL) && (i < parse_data_size); i++)
+		if (strcmp(parse_data[i].name, args[0]) == 0)
+			*pdata = parse_data + i;
+
+	if (*pdata == NULL)
+		FLT_OT_PARSE_ERR(err, "'%s' : unknown keyword", args[0]);
+
+	if ((retval & ERR_CODE) || (id == NULL))
+		/* Do nothing. */;
+	else if ((id != flt_ot_current_tracer) && (flt_ot_current_config->tracer == NULL))
+		FLT_OT_PARSE_ERR(err, "tracer not defined");
+
+	/*
+	 * Checking that fewer arguments are specified in the configuration
+	 * line than is required.
+	 */
+	if (!(retval & ERR_CODE))
+		for (i = 1; i < (*pdata)->args_min; i++)
+			if (!FLT_OT_ARG_ISVALID(i))
+				FLT_OT_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
+
+	/*
+	 * Checking that more arguments are specified in the configuration
+	 * line than the maximum allowed.
+	 */
+	if (!(retval & ERR_CODE) && ((*pdata)->args_max > 0)) {
+		for ( ; (i <= (*pdata)->args_max) && FLT_OT_ARG_ISVALID(i); i++);
+
+		if (i > (*pdata)->args_max)
+			FLT_OT_PARSE_ERR(err, "'%s' : too many arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
+	}
+
+	/* Checking that the first argument has only allowed characters. */
+	if (!(retval & ERR_CODE) && ((*pdata)->check_name > 0)) {
+		const char *ic;
+
+		ic = flt_ot_parse_invalid_char(args[1], (*pdata)->check_name);
+		if (ic != NULL)
+			FLT_OT_PARSE_ERR(err, "%s '%s' : invalid character '%c'", args[0], args[1], *ic);
+	}
+
+	/* Checking that the data group name is defined. */
+	if (!(retval & ERR_CODE) && (*pdata)->flag_check_id && (id == NULL))
+		FLT_OT_PARSE_ERR(err, "'%s' : %s ID not set (use '%s%s')", args[0], parse_data[1].name, parse_data[1].name, parse_data[1].usage);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_sample_expr -
+ *
+ * ARGUMENTS
+ *   file    -
+ *   linenum -
+ *   args    -
+ *   idx     -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_sample_expr(const char *file, int linenum, char **args, int *idx, struct list *head, char **err)
+{
+	struct flt_ot_conf_sample_expr *expr;
+	int                             retval = ERR_NONE;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p, %p, %p:%p", file, linenum, args, idx, head, FLT_OT_DPTR_ARGS(err));
+
+	expr = flt_ot_conf_sample_expr_init(args[*idx], linenum, head, err);
+	if (expr != NULL) {
+		expr->expr = sample_parse_expr(args, idx, file, linenum, err, &(flt_ot_current_config->proxy->conf.args), NULL);
+		if (expr->expr != NULL)
+			FLT_OT_DBG(3, "sample expression '%s' added", expr->value);
+		else
+			retval |= ERR_ABORT | ERR_ALERT;
+	} else {
+			retval |= ERR_ABORT | ERR_ALERT;
+	}
+
+	if (retval & ERR_CODE)
+		flt_ot_conf_sample_expr_free(&expr);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_sample -
+ *
+ * ARGUMENTS
+ *   file    -
+ *   linenum -
+ *   args    -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_sample(const char *file, int linenum, char **args, struct list *head, char **err)
+{
+	struct flt_ot_conf_sample *sample;
+	int                        idx = 2, retval = ERR_NONE;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
+
+	sample = flt_ot_conf_sample_init(args, linenum, head, err);
+	if (sample == NULL)
+		FLT_OT_PARSE_ERR(err, "'%s' : out of memory", args[0]);
+
+	if (!(retval & ERR_CODE)) {
+		flt_ot_current_config->proxy->conf.args.ctx  = ARGC_OT;
+		flt_ot_current_config->proxy->conf.args.file = file;
+		flt_ot_current_config->proxy->conf.args.line = linenum;
+
+		while (!(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(idx))
+			retval = flt_ot_parse_cfg_sample_expr(file, linenum, args, &idx, &(sample->exprs), err);
+
+		flt_ot_current_config->proxy->conf.args.file = NULL;
+		flt_ot_current_config->proxy->conf.args.line = 0;
+	}
+
+	if (retval & ERR_CODE)
+		flt_ot_conf_sample_free(&sample);
+	else
+		FLT_OT_DBG(3, "sample '%s' -> '%s' added", sample->key, sample->value);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_str -
+ *
+ * ARGUMENTS
+ *   file    -
+ *   linenum -
+ *   args    -
+ *   head    -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, struct list *head, char **err)
+{
+	struct flt_ot_conf_str *str = NULL;
+	int                     i, retval = ERR_NONE;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
+
+	for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
+		if (flt_ot_conf_str_init(args[i], linenum, head, err) == NULL)
+			retval |= ERR_ABORT | ERR_ALERT;
+
+	if (retval & ERR_CODE)
+		flt_ot_conf_str_free(&str);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_file -
+ *
+ * ARGUMENTS
+ *   ptr     -
+ *   file    -
+ *   linenum -
+ *   args    -
+ *   err     -
+ *   err_msg -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_file(char **ptr, const char *file, int linenum, char **args, char **err, const char *err_msg)
+{
+	int retval = ERR_NONE;
+
+	FLT_OT_FUNC("%p:%p, \"%s\", %d, %p, %p:%p, \"%s\"", FLT_OT_DPTR_ARGS(ptr), file, linenum, args, FLT_OT_DPTR_ARGS(err), err_msg);
+
+	if (!FLT_OT_ARG_ISVALID(1))
+		FLT_OT_PARSE_ERR(err, "'%s' : no %s specified", flt_ot_current_tracer->id, err_msg);
+	else if (alertif_too_many_args(1, file, linenum, args, &retval))
+		retval |= ERR_ABORT | ERR_ALERT;
+	else if (access(args[1], R_OK) == -1)
+		FLT_OT_PARSE_ERR(err, "'%s' : %s", args[1], strerror(errno));
+	else
+		retval = flt_ot_parse_keyword(ptr, args, 0, 0, err, err_msg);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_check_scope -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns TRUE in case the configuration is not in the currently defined
+ *   scope, FALSE otherwise.
+ */
+static bool flt_ot_parse_check_scope(void)
+{
+	bool retval = 0;
+
+	if ((cfg_scope != NULL) && (flt_ot_current_config->id != NULL) && (strcmp(flt_ot_current_config->id, cfg_scope) != 0)) {
+		FLT_OT_DBG(1, "cfg_scope: '%s', id: '%s'", cfg_scope, flt_ot_current_config->id);
+
+		retval = 1;
+	}
+
+	return retval;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_tracer -
+ *
+ * ARGUMENTS
+ *   file    -
+ *   linenum -
+ *   args    -
+ *   kw_mod  -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_tracer(const char *file, int linenum, char **args, int kw_mod)
+{
+#define FLT_OT_PARSE_TRACER_DEF(a,b,c,d,e,f,g)   { FLT_OT_PARSE_TRACER_##a, b, c, d, e, f, g },
+	static const struct flt_ot_parse_data  parse_data[] = { FLT_OT_PARSE_TRACER_DEFINES };
+#undef FLT_OT_PARSE_TRACER_DEF
+	const struct flt_ot_parse_data        *pdata = NULL;
+	char                                  *err = NULL, *err_log = NULL;
+	int                                    i, retval = ERR_NONE;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
+
+	if (flt_ot_parse_check_scope())
+		FLT_OT_RETURN(retval);
+
+	retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_tracer, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
+	if (retval & ERR_CODE) {
+		FLT_OT_PARSE_IFERR_ALERT();
+
+		FLT_OT_RETURN(retval);
+	}
+
+	if (pdata->keyword == FLT_OT_PARSE_TRACER_ID) {
+		if (flt_ot_current_config->tracer != NULL) {
+			FLT_OT_PARSE_ERR(&err, "'%s' : tracer can be defined only once", args[1]);
+		} else {
+			flt_ot_current_tracer = flt_ot_conf_tracer_init(args[1], linenum, &err);
+			if (flt_ot_current_tracer == NULL)
+				retval |= ERR_ABORT | ERR_ALERT;
+		}
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_LOG) {
+		if (parse_logsrv(args, &(flt_ot_current_tracer->proxy_log.logsrvs), kw_mod == KWM_NO, &err_log) == 0) {
+			FLT_OT_PARSE_ERR(&err, "'%s %s ...' : %s", args[0], args[1], err_log);
+			FLT_OT_FREE_CLEAR(err_log);
+
+			retval |= ERR_ABORT | ERR_ALERT;
+		} else {
+			flt_ot_current_tracer->logging |= FLT_OT_LOGGING_ON;
+		}
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_CONFIG) {
+		retval = flt_ot_parse_cfg_file(&(flt_ot_current_tracer->config), file, linenum, args, &err, "configuration file");
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_PLUGIN) {
+		retval = flt_ot_parse_cfg_file(&(flt_ot_current_tracer->plugin), file, linenum, args, &err, "plugin library");
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_GROUPS) {
+		for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
+			if (flt_ot_conf_ph_init(args[i], linenum, &(flt_ot_current_tracer->ph_groups), &err) == NULL)
+				retval |= ERR_ABORT | ERR_ALERT;
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_SCOPES) {
+		for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
+			if (flt_ot_conf_ph_init(args[i], linenum, &(flt_ot_current_tracer->ph_scopes), &err) == NULL)
+				retval |= ERR_ABORT | ERR_ALERT;
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_ACL) {
+		if (strcasecmp(args[1], "or") == 0)
+			FLT_OT_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);
+		else if (parse_acl((const char **)args + 1, &(flt_ot_current_tracer->acls), &err, &(flt_ot_current_config->proxy->conf.args), file, linenum) == NULL)
+			retval |= ERR_ABORT | ERR_ALERT;
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_RATE_LIMIT) {
+		flt_ot_current_tracer->rate_limit = FLT_OT_FLOAT_U32(flt_ot_strtod(args[1], 0.0, FLT_OT_RATE_LIMIT_MAX, &err), FLT_OT_RATE_LIMIT_MAX);
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_OPTION) {
+		if (strcmp(args[1], FLT_OT_PARSE_OPTION_DISABLED) == 0) {
+			flt_ot_current_tracer->flag_disabled = (kw_mod == KWM_NO) ? 0 : 1;
+		}
+		else if (strcmp(args[1], FLT_OT_PARSE_OPTION_HARDERR) == 0) {
+			flt_ot_current_tracer->flag_harderr = (kw_mod == KWM_NO) ? 0 : 1;
+		}
+		else if (strcmp(args[1], FLT_OT_PARSE_OPTION_NOLOGNORM) == 0) {
+			if (kw_mod == KWM_NO)
+				flt_ot_current_tracer->logging &= ~FLT_OT_LOGGING_NOLOGNORM;
+			else
+				flt_ot_current_tracer->logging |= FLT_OT_LOGGING_NOLOGNORM;
+		}
+		else
+			FLT_OT_PARSE_ERR(&err, "'%s' : unknown option '%s'", args[0], args[1]);
+	}
+#ifdef DEBUG_OT
+	else if (pdata->keyword == FLT_OT_PARSE_TRACER_DEBUG_LEVEL) {
+		flt_ot_debug.level = flt_ot_strtoll(args[1], 0, 255, &err);
+	}
+#else
+	else {
+		FLT_OT_PARSE_WARNING("'%s' : keyword ignored", file, linenum, args[0]);
+	}
+#endif
+
+	FLT_OT_PARSE_IFERR_ALERT();
+
+	if ((retval & ERR_CODE) && (flt_ot_current_tracer != NULL))
+		flt_ot_conf_tracer_free(&flt_ot_current_tracer);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_post_parse_cfg_tracer -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_post_parse_cfg_tracer(void)
+{
+	int retval = ERR_NONE;
+
+	FLT_OT_FUNC("");
+
+	if (flt_ot_current_tracer == NULL)
+		FLT_OT_RETURN(retval);
+
+	flt_ot_current_config->tracer = flt_ot_current_tracer;
+
+	if (flt_ot_current_tracer->id == NULL)
+		FLT_OT_RETURN(retval);
+
+	if (flt_ot_current_tracer->config == NULL)
+		FLT_OT_POST_PARSE_ALERT("tracer '%s' has no configuration file specified", flt_ot_current_tracer->cfg_line, flt_ot_current_tracer->id);
+
+	if (flt_ot_current_tracer->plugin == NULL)
+		FLT_OT_POST_PARSE_ALERT("tracer '%s' has no plugin library specified", flt_ot_current_tracer->cfg_line, flt_ot_current_tracer->id);
+
+	flt_ot_current_tracer = NULL;
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_group -
+ *
+ * ARGUMENTS
+ *   file    -
+ *   linenum -
+ *   args    -
+ *   kw_mod  -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_group(const char *file, int linenum, char **args, int kw_mod)
+{
+#define FLT_OT_PARSE_GROUP_DEF(a,b,c,d,e,f,g)   { FLT_OT_PARSE_GROUP_##a, b, c, d, e, f, g },
+	static const struct flt_ot_parse_data  parse_data[] = { FLT_OT_PARSE_GROUP_DEFINES };
+#undef FLT_OT_PARSE_GROUP_DEF
+	const struct flt_ot_parse_data        *pdata = NULL;
+	char                                  *err = NULL;
+	int                                    i, retval = ERR_NONE;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
+
+	if (flt_ot_parse_check_scope())
+		FLT_OT_RETURN(retval);
+
+	retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_group, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
+	if (retval & ERR_CODE) {
+		FLT_OT_PARSE_IFERR_ALERT();
+
+		FLT_OT_RETURN(retval);
+	}
+
+	if (pdata->keyword == FLT_OT_PARSE_GROUP_ID) {
+		flt_ot_current_group = flt_ot_conf_group_init(args[1], linenum, &(flt_ot_current_config->groups), &err);
+		if (flt_ot_current_config == NULL)
+			retval |= ERR_ABORT | ERR_ALERT;
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_GROUP_SCOPES) {
+		for (i = 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(i); i++)
+			if (flt_ot_conf_ph_init(args[i], linenum, &(flt_ot_current_group->ph_scopes), &err) == NULL)
+				retval |= ERR_ABORT | ERR_ALERT;
+	}
+
+	FLT_OT_PARSE_IFERR_ALERT();
+
+	if ((retval & ERR_CODE) && (flt_ot_current_group != NULL))
+		flt_ot_conf_group_free(&flt_ot_current_group);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_post_parse_cfg_group -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_post_parse_cfg_group(void)
+{
+	int retval = ERR_NONE;
+
+	FLT_OT_FUNC("");
+
+	if (flt_ot_current_group == NULL)
+		FLT_OT_RETURN(retval);
+
+	/* Check that the group has at least one scope defined. */
+	if (LIST_ISEMPTY(&(flt_ot_current_group->ph_scopes)))
+		FLT_OT_POST_PARSE_ALERT("group '%s' has no defined scope(s)", flt_ot_current_group->cfg_line, flt_ot_current_group->id);
+
+	flt_ot_current_group = NULL;
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_scope_ctx -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   cur_arg -
+ *   err     -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_scope_ctx(char **args, int cur_arg, char **err)
+{
+	uint8_t flags = 0;
+	int     retval = ERR_NONE;
+
+	FLT_OT_FUNC("%p, %d, %p:%p", args, cur_arg, FLT_OT_DPTR_ARGS(err));
+
+	if (strcmp(args[cur_arg], FLT_OT_PARSE_CTX_USE_HEADERS) == 0)
+		flags = FLT_OT_CTX_USE_HEADERS;
+	else if (strcmp(args[cur_arg], FLT_OT_PARSE_CTX_USE_VARS) == 0)
+		flags = FLT_OT_CTX_USE_VARS;
+	else
+		FLT_OT_PARSE_ERR(err, "'%s' : invalid context storage type", args[0]);
+
+	if (flags == 0)
+		/* Do nothing. */;
+	else if (flt_ot_current_span->ctx_flags & flags)
+		FLT_OT_PARSE_ERR(err, "'%s' : %s already used", args[0], args[cur_arg]);
+	else
+		flt_ot_current_span->ctx_flags |= flags;
+
+	FLT_OT_DBG(2, "ctx_flags: 0x%02hhx (0x%02hhx)", flt_ot_current_span->ctx_flags, flags);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_acl -
+ *
+ * ARGUMENTS
+ *   file    -
+ *   linenum -
+ *   px      -
+ *   args    -
+ *   err     -
+ *   head    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static struct acl_cond *flt_ot_parse_acl(const char *file, int linenum, struct proxy *px, const char **args, char **err, struct list *head, ...)
+{
+	va_list          ap;
+	int              n = 0;
+	struct acl_cond *retptr = NULL;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p, %p, ...", file, linenum, px, args, FLT_OT_DPTR_ARGS(err), head);
+
+	for (va_start(ap, head); (retptr == NULL) && (head != NULL); head = va_arg(ap, typeof(head)), n++) {
+		retptr = build_acl_cond(file, linenum, head, px, args, (n == 0) ? err : NULL);
+		if (retptr != NULL)
+			FLT_OT_DBG(2, "ACL build done, using list %p %d", head, n);
+	}
+	va_end(ap);
+
+	if ((retptr != NULL) && (err != NULL))
+		FLT_OT_FREE_CLEAR(*err);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg_scope -
+ *
+ * ARGUMENTS
+ *   file    -
+ *   linenum -
+ *   args    -
+ *   kw_mod  -
+ *
+ * DESCRIPTION
+ *   Function used to load the scope block configuration.
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg_scope(const char *file, int linenum, char **args, int kw_mod)
+{
+#define FLT_OT_PARSE_SCOPE_DEF(a,b,c,d,e,f,g)   { FLT_OT_PARSE_SCOPE_##a, b, c, d, e, f, g },
+	static const struct flt_ot_parse_data  parse_data[] = { FLT_OT_PARSE_SCOPE_DEFINES };
+#undef FLT_OT_PARSE_SCOPE_DEF
+	const struct flt_ot_parse_data        *pdata = NULL;
+	char                                  *err = NULL;
+	int                                    i, retval = ERR_NONE;
+
+	FLT_OT_FUNC("\"%s\", %d, %p, 0x%08x", file, linenum, args, kw_mod);
+
+	if (flt_ot_parse_check_scope())
+		FLT_OT_RETURN(retval);
+
+	retval = flt_ot_parse_cfg_check(file, linenum, args, flt_ot_current_span, parse_data, FLT_OT_TABLESIZE(parse_data), &pdata, &err);
+	if (retval & ERR_CODE) {
+		FLT_OT_PARSE_IFERR_ALERT();
+
+		FLT_OT_RETURN(retval);
+	}
+
+	if (pdata->keyword == FLT_OT_PARSE_SCOPE_ID) {
+		/* Initialization of a new scope. */
+		flt_ot_current_scope = flt_ot_conf_scope_init(args[1], linenum, &(flt_ot_current_config->scopes), &err);
+		if (flt_ot_current_scope == NULL)
+			retval |= ERR_ABORT | ERR_ALERT;
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_SPAN) {
+		/*
+		 * Checking if this is the beginning of the definition of
+		 * a new span.
+		 */
+		if (flt_ot_current_span != NULL) {
+			FLT_OT_DBG(3, "span '%s' (done)", flt_ot_current_span->id);
+
+			flt_ot_current_span = NULL;
+		}
+
+		/* Initialization of a new span. */
+		flt_ot_current_span = flt_ot_conf_span_init(args[1], linenum, &(flt_ot_current_scope->spans), &err);
+
+		/*
+		 * In case the span has a defined reference,
+		 * the correctness of the arguments is checked here.
+		 */
+		if (flt_ot_current_span == NULL) {
+			retval |= ERR_ABORT | ERR_ALERT;
+		}
+		else if (FLT_OT_ARG_ISVALID(2)) {
+			for (i = 2; (i < pdata->args_max) && FLT_OT_ARG_ISVALID(i); i++)
+				if (strcmp(args[i], FLT_OT_PARSE_SPAN_ROOT) == 0) {
+					if (flt_ot_current_span->flag_root)
+						FLT_OT_PARSE_ERR(&err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
+					else
+						flt_ot_current_span->flag_root = 1;
+				}
+				else if ((strcmp(args[i], FLT_OT_PARSE_SPAN_REF_CHILD) == 0) || (strcmp(args[i], FLT_OT_PARSE_SPAN_REF_FOLLOWS) == 0)) {
+					if (!FLT_OT_ARG_ISVALID(i + 1)) {
+						FLT_OT_PARSE_ERR(&err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+					}
+					else if (strcmp(args[i++], FLT_OT_PARSE_SPAN_REF_CHILD) == 0) {
+						flt_ot_current_span->ref_type   = otc_span_reference_child_of;
+						flt_ot_current_span->ref_id_len = strlen(args[i]);
+
+						retval = flt_ot_parse_strdup(&(flt_ot_current_span->ref_id), args[i], &err, args[1]);
+					}
+					else {
+						flt_ot_current_span->ref_type   = otc_span_reference_follows_from;
+						flt_ot_current_span->ref_id_len = strlen(args[i]);
+
+						retval = flt_ot_parse_strdup(&(flt_ot_current_span->ref_id), args[i], &err, args[1]);
+					}
+				}
+				else {
+					FLT_OT_PARSE_ERR(&err, "'%s' : invalid argument (use '%s%s')", args[i], pdata->name, pdata->usage);
+				}
+		}
+		else {
+			/*
+			 * This is not a faulty configuration, only such a case
+			 * will be logged.
+			 */
+			FLT_OT_DBG(3, "new span '%s' without reference", flt_ot_current_span->id);
+		}
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_TAG) {
+		retval = flt_ot_parse_cfg_sample(file, linenum, args, &(flt_ot_current_span->tags), &err);
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_LOG) {
+		retval = flt_ot_parse_cfg_sample(file, linenum, args, &(flt_ot_current_span->logs), &err);
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_BAGGAGE) {
+		retval = flt_ot_parse_cfg_sample(file, linenum, args, &(flt_ot_current_span->baggages), &err);
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_INJECT) {
+		/*
+		 * Automatic context name generation can be specified here
+		 * if the contents of the FLT_OT_PARSE_CTX_AUTONAME macro
+		 * are used as the name.  In that case, if the context is
+		 * after a particular event, it gets its name; otherwise
+		 * it gets the name of the current span.
+		 */
+		if (flt_ot_current_span->ctx_id != NULL)
+			FLT_OT_PARSE_ERR(&err, "'%s' : only one context per span is allowed", args[1]);
+		else if (strcmp(args[1], FLT_OT_PARSE_CTX_AUTONAME) != 0)
+			retval = flt_ot_parse_strdup(&(flt_ot_current_span->ctx_id), args[1], &err, args[0]);
+		else if (flt_ot_current_scope->event != FLT_OT_EVENT_REQ_NONE)
+			retval = flt_ot_parse_strdup(&(flt_ot_current_span->ctx_id), flt_ot_event_data[flt_ot_current_scope->event].name, &err, args[0]);
+		else
+			retval = flt_ot_parse_strdup(&(flt_ot_current_span->ctx_id), flt_ot_current_span->id, &err, args[0]);
+
+		if (flt_ot_current_span->ctx_id != NULL) {
+			flt_ot_current_span->ctx_id_len = strlen(flt_ot_current_span->ctx_id);
+
+			/*
+			 * Here is checked the context storage type; which, if
+			 * not explicitly specified, is set to HTTP headers.
+			 *
+			 * It is possible to use both types of context storage
+			 * at the same time.
+			 */
+			if (FLT_OT_ARG_ISVALID(2)) {
+				retval = flt_ot_parse_cfg_scope_ctx(args, 2, &err);
+				if (!(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(3))
+					retval = flt_ot_parse_cfg_scope_ctx(args, 3, &err);
+			} else {
+				flt_ot_current_span->ctx_flags = FLT_OT_CTX_USE_HEADERS;
+			}
+		}
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_EXTRACT) {
+		struct flt_ot_conf_context *conf_ctx;
+
+		/*
+		 * Here is checked the context storage type; which, if
+		 * not explicitly specified, is set to HTTP headers.
+		 */
+		conf_ctx = flt_ot_conf_context_init(args[1], linenum, &(flt_ot_current_scope->contexts), &err);
+		if (conf_ctx == NULL)
+			retval |= ERR_ABORT | ERR_ALERT;
+		else if (!FLT_OT_ARG_ISVALID(2))
+			conf_ctx->flags = FLT_OT_CTX_USE_HEADERS;
+		else if (strcmp(args[2], FLT_OT_PARSE_CTX_USE_HEADERS) == 0)
+			conf_ctx->flags = FLT_OT_CTX_USE_HEADERS;
+		else if (strcmp(args[2], FLT_OT_PARSE_CTX_USE_VARS) == 0)
+			conf_ctx->flags = FLT_OT_CTX_USE_VARS;
+		else
+			FLT_OT_PARSE_ERR(&err, "'%s' : invalid context storage type", args[2]);
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_FINISH) {
+		retval = flt_ot_parse_cfg_str(file, linenum, args, &(flt_ot_current_scope->finish), &err);
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_ACL) {
+		if (strcasecmp(args[1], "or") == 0)
+			FLT_OT_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);
+		else if (parse_acl((const char **)args + 1, &(flt_ot_current_scope->acls), &err, &(flt_ot_current_config->proxy->conf.args), file, linenum) == NULL)
+			retval |= ERR_ABORT | ERR_ALERT;
+	}
+	else if (pdata->keyword == FLT_OT_PARSE_SCOPE_EVENT) {
+		/* Scope can only have one event defined. */
+		if (flt_ot_current_scope->event != FLT_OT_EVENT_REQ_NONE) {
+			FLT_OT_PARSE_ERR(&err, "'%s' : event already set", flt_ot_current_scope->id);
+		} else {
+			/* Check the event name. */
+			for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_event_data); i++)
+				if (strcmp(flt_ot_event_data[i].name, args[1]) == 0) {
+					flt_ot_current_scope->event = i;
+
+					break;
+				}
+
+			/*
+			 * The event can have some condition defined and this
+			 * is checked here.
+			 */
+			if (flt_ot_current_scope->event == FLT_OT_EVENT_REQ_NONE) {
+				FLT_OT_PARSE_ERR(&err, "'%s' : unknown event", args[1]);
+			}
+			else if (!FLT_OT_ARG_ISVALID(2)) {
+				/* Do nothing. */
+			}
+			else if ((strcmp(args[2], FLT_OT_CONDITION_IF) == 0) || (strcmp(args[2], FLT_OT_CONDITION_UNLESS) == 0)) {
+				/*
+				 * We will first try to build ACL condition using
+				 * local settings and then if that fails, using
+				 * global settings (from tracer block).  If it
+				 * also fails, then try to use ACL defined in
+				 * the HAProxy configuration.
+				 */
+				flt_ot_current_scope->cond = flt_ot_parse_acl(file, linenum, flt_ot_current_config->proxy, (const char **)args + 2, &err, &(flt_ot_current_scope->acls), &(flt_ot_current_config->tracer->acls), &(flt_ot_current_config->proxy->acl), NULL);
+				if (flt_ot_current_scope->cond == NULL)
+					retval |= ERR_ABORT | ERR_ALERT;
+			}
+			else {
+				FLT_OT_PARSE_ERR(&err, "'%s' : expects either 'if' or 'unless' followed by a condition but found '%s'", args[1], args[2]);
+			}
+
+			if (!(retval & ERR_CODE))
+				FLT_OT_DBG(3, "event '%s'", args[1]);
+		}
+	}
+
+	FLT_OT_PARSE_IFERR_ALERT();
+
+	if ((retval & ERR_CODE) && (flt_ot_current_scope != NULL)) {
+		flt_ot_conf_scope_free(&flt_ot_current_scope);
+
+		flt_ot_current_span = NULL;
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_post_parse_cfg_scope -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   In this function the correctness of the complete scope block is examined.
+ *   This does not mean that all elements are checked here, but only those for
+ *   which it has not been possible to establish their complete correctness in
+ *   the function flt_ot_parse_cfg_scope().
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_post_parse_cfg_scope(void)
+{
+	struct flt_ot_conf_span *conf_span;
+	int                      retval = ERR_NONE;
+
+	FLT_OT_FUNC("");
+
+	if (flt_ot_current_scope == NULL)
+		FLT_OT_RETURN(retval);
+
+	/* If span context inject is used, check that this is possible. */
+	list_for_each_entry(conf_span, &(flt_ot_current_scope->spans), list)
+		if ((conf_span->ctx_id != NULL) && (conf_span->ctx_flags & FLT_OT_CTX_USE_HEADERS))
+			if (!flt_ot_event_data[flt_ot_current_scope->event].flag_http_inject)
+				FLT_OT_POST_PARSE_ALERT("inject '%s' : cannot use on this event", conf_span->cfg_line, conf_span->ctx_id);
+
+	if (retval & ERR_CODE)
+		flt_ot_conf_scope_free(&flt_ot_current_scope);
+
+	flt_ot_current_scope = NULL;
+	flt_ot_current_span  = NULL;
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse_cfg -
+ *
+ * ARGUMENTS
+ *   conf     -
+ *   flt_name -
+ *   err      -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse_cfg(struct flt_ot_conf *conf, const char *flt_name, char **err)
+{
+	struct list backup_sections;
+	int         retval = ERR_ABORT | ERR_ALERT;
+
+	FLT_OT_FUNC("%p, \"%s\", %p:%p", conf, flt_name, FLT_OT_DPTR_ARGS(err));
+
+	flt_ot_current_config = conf;
+
+	/* Backup sections. */
+	LIST_INIT(&backup_sections);
+	cfg_backup_sections(&backup_sections);
+
+	/* Register new OT sections and parse the OT filter configuration file. */
+	if (!cfg_register_section(FLT_OT_PARSE_SECTION_TRACER_ID, flt_ot_parse_cfg_tracer, flt_ot_post_parse_cfg_tracer))
+		/* Do nothing. */;
+	else if (!cfg_register_section(FLT_OT_PARSE_SECTION_GROUP_ID, flt_ot_parse_cfg_group, flt_ot_post_parse_cfg_group))
+		/* Do nothing. */;
+	else if (!cfg_register_section(FLT_OT_PARSE_SECTION_SCOPE_ID, flt_ot_parse_cfg_scope, flt_ot_post_parse_cfg_scope))
+		/* Do nothing. */;
+	else if (access(conf->cfg_file, R_OK) == -1)
+		FLT_OT_PARSE_ERR(err, "'%s' : %s", conf->cfg_file, strerror(errno));
+	else
+		retval = readcfgfile(conf->cfg_file);
+
+	/* Unregister OT sections and restore previous sections. */
+	cfg_unregister_sections();
+	cfg_restore_sections(&backup_sections);
+
+	flt_ot_current_config = NULL;
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_parse -
+ *
+ * ARGUMENTS
+ *   args    -
+ *   cur_arg -
+ *   px      -
+ *   fconf   -
+ *   err     -
+ *   private -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_ot_parse(char **args, int *cur_arg, struct proxy *px, struct flt_conf *fconf, char **err, void *private)
+{
+	struct flt_ot_conf *conf = NULL;
+	int                 pos, retval = ERR_NONE;
+
+#ifdef DEBUG_OT
+	FLT_OT_RUN_ONCE(
+#  ifndef DEBUG_OT_SYSTIME
+		(void)memcpy(&(flt_ot_debug.start), &now, sizeof(flt_ot_debug.start));
+#  endif
+
+		flt_ot_debug.level = FLT_OT_DEBUG_LEVEL;
+	);
+#endif
+
+	FLT_OT_FUNC("%p, %p, %p, %p, %p:%p, %p", args, cur_arg, px, fconf, FLT_OT_DPTR_ARGS(err), private);
+
+#ifdef OTC_DBG_MEM
+	FLT_OT_RUN_ONCE(
+		if (otc_dbg_mem_init(&dbg_mem, dbg_mem_data, FLT_OT_TABLESIZE(dbg_mem_data), 0xff) == -1) {
+			FLT_OT_PARSE_ERR(err, "cannot initialize memory debugger");
+
+			FLT_OT_RETURN(retval);
+		}
+	);
+#endif
+
+	FLT_OT_ARGS_DUMP();
+
+	conf = flt_ot_conf_init(px);
+	if (conf == NULL) {
+		FLT_OT_PARSE_ERR(err, "'%s' : out of memory", args[*cur_arg]);
+
+		FLT_OT_RETURN(retval);
+	}
+
+	for (pos = *cur_arg + 1; !(retval & ERR_CODE) && FLT_OT_ARG_ISVALID(pos); pos++) {
+		FLT_OT_DBG(3, "args[%d:2] : { '%s' '%s' }", pos, args[pos], args[pos + 1]);
+
+		if (strcmp(args[pos], FLT_OT_OPT_FILTER_ID) == 0) {
+			retval = flt_ot_parse_keyword(&(conf->id), args, *cur_arg, pos, err, "name");
+			pos++;
+		}
+		else if (strcmp(args[pos], FLT_OT_OPT_CONFIG) == 0) {
+			retval = flt_ot_parse_keyword(&(conf->cfg_file), args, *cur_arg, pos, err, "configuration file");
+			if (!(retval & ERR_CODE))
+				retval = flt_ot_parse_cfg(conf, args[*cur_arg], err);
+			pos++;
+		}
+		else {
+			FLT_OT_PARSE_ERR(err, "'%s' : unknown keyword '%s'", args[*cur_arg], args[pos]);
+		}
+	}
+
+	/* If the OpenTracing filter ID is not set, use default name. */
+	if (!(retval & ERR_CODE) && (conf->id == NULL)) {
+		ha_warning("parsing : " FLT_OT_FMT_TYPE FLT_OT_FMT_NAME "'no filter id set, using default id '%s'\n", FLT_OT_OPT_FILTER_ID_DEFAULT);
+
+		retval = flt_ot_parse_strdup(&(conf->id), FLT_OT_OPT_FILTER_ID_DEFAULT, err, args[*cur_arg]);
+	}
+
+	if (!(retval & ERR_CODE) && (conf->cfg_file == NULL))
+		FLT_OT_PARSE_ERR(err, "'%s' : no configuration file specified", args[*cur_arg]);
+
+	if (retval & ERR_CODE) {
+		flt_ot_conf_free(&conf);
+	} else {
+		fconf->id   = ot_flt_id;
+		fconf->ops  = &flt_ot_ops;
+		fconf->conf = conf;
+
+		*cur_arg = pos;
+
+		FLT_OT_DBG(3, "filter set: id '%s', config '%s'", conf->id, conf->cfg_file);
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/* Declare the filter parser for FLT_OT_OPT_NAME keyword. */
+static struct flt_kw_list flt_kws = { FLT_OT_SCOPE, { }, {
+		{ FLT_OT_OPT_NAME, flt_ot_parse, NULL },
+		{ NULL, NULL, NULL },
+	}
+};
+
+INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws);
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/pool.c b/contrib/opentracing/src/pool.c
new file mode 100644
index 0000000..2f4b534
--- /dev/null
+++ b/contrib/opentracing/src/pool.c
@@ -0,0 +1,223 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+/***
+ * NAME
+ *   flt_ot_pool_alloc -
+ *
+ * ARGUMENTS
+ *   pool       -
+ *   size       -
+ *   flag_clear -
+ *   err        -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+void *flt_ot_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err)
+{
+	void *retptr;
+
+	FLT_OT_FUNC("%p, %zu, %hhu, %p:%p", pool, size, flag_clear, FLT_OT_DPTR_ARGS(err));
+
+	if (pool != NULL) {
+		retptr = pool_alloc_dirty(pool);
+		if (retptr != NULL)
+			FLT_OT_DBG(2, "POOL_ALLOC: %s:%d(%p %zu)", __func__, __LINE__, retptr, FLT_OT_DEREF(pool, size, size));
+	} else {
+		retptr = FLT_OT_MALLOC(size);
+	}
+
+	if (retptr == NULL)
+		FLT_OT_ERR("out of memory");
+	else if (flag_clear)
+		(void)memset(retptr, 0, size);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_pool_strndup -
+ *
+ * ARGUMENTS
+ *   pool -
+ *   s    -
+ *   size -
+ *   err  -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+void *flt_ot_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err)
+{
+	void *retptr;
+
+	FLT_OT_FUNC("%p, \"%.*s\", %zu, %p:%p", pool, (int)size, s, size, FLT_OT_DPTR_ARGS(err));
+
+	if (pool != NULL) {
+		retptr = pool_alloc_dirty(pool);
+		if (retptr != NULL) {
+			(void)memcpy(retptr, s, MIN(pool->size - 1, size));
+
+			((uint8_t *)retptr)[MIN(pool->size - 1, size)] = '\0';
+		}
+	} else {
+		retptr = FLT_OT_STRNDUP(s, size);
+	}
+
+	if (retptr != NULL)
+		FLT_OT_DBG(2, "POOL_STRNDUP: %s:%d(%p %zu)", __func__, __LINE__, retptr, FLT_OT_DEREF(pool, size, size));
+	else
+		FLT_OT_ERR("out of memory");
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_pool_free -
+ *
+ * ARGUMENTS
+ *   pool -
+ *   ptr  -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_pool_free(struct pool_head *pool, void **ptr)
+{
+	FLT_OT_FUNC("%p, %p:%p", pool, FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG(2, "POOL_FREE: %s:%d(%p %u)", __func__, __LINE__, *ptr, FLT_OT_DEREF(pool, size, 0));
+
+	if (pool != NULL)
+		pool_free(pool, *ptr);
+	else
+		FLT_OT_FREE(*ptr);
+
+	*ptr = NULL;
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_trash_alloc -
+ *
+ * ARGUMENTS
+ *   flag_clear -
+ *   err        -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+struct buffer *flt_ot_trash_alloc(bool flag_clear, char **err)
+{
+	struct buffer *retptr;
+
+	FLT_OT_FUNC("%hhu, %p:%p", flag_clear, FLT_OT_DPTR_ARGS(err));
+
+#ifdef USE_TRASH_CHUNK
+	retptr = alloc_trash_chunk();
+	if (retptr != NULL)
+		FLT_OT_DBG(2, "TRASH_ALLOC: %s:%d(%p %zu)", __func__, __LINE__, retptr, retptr->size);
+#else
+	retptr = FLT_OT_MALLOC(sizeof(*retptr));
+	if (retptr != NULL) {
+		chunk_init(retptr, FLT_OT_MALLOC(global.tune.bufsize), global.tune.bufsize);
+		if (retptr->area == NULL)
+			FLT_OT_FREE_CLEAR(retptr);
+		else
+			*(retptr->area) = '\0';
+	}
+#endif
+
+	if (retptr == NULL)
+		FLT_OT_ERR("out of memory");
+	else if (flag_clear)
+		(void)memset(retptr->area, 0, retptr->size);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_trash_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_trash_free(struct buffer **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG(2, "TRASH_FREE: %s:%d(%p %zu)", __func__, __LINE__, *ptr, (*ptr)->size);
+
+#ifdef USE_TRASH_CHUNK
+	free_trash_chunk(*ptr);
+#else
+	FLT_OT_FREE((*ptr)->area);
+	FLT_OT_FREE(*ptr);
+#endif
+
+	*ptr = NULL;
+
+	FLT_OT_RETURN();
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/scope.c b/contrib/opentracing/src/scope.c
new file mode 100644
index 0000000..462c865
--- /dev/null
+++ b/contrib/opentracing/src/scope.c
@@ -0,0 +1,631 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+static struct pool_head *pool_head_ot_scope_span = NULL;
+static struct pool_head *pool_head_ot_scope_context = NULL;
+static struct pool_head *pool_head_ot_runtime_context = NULL;
+
+#ifdef USE_POOL_OT_SCOPE_SPAN
+REGISTER_POOL(&pool_head_ot_scope_span, "ot_scope_span", sizeof(struct flt_ot_scope_span));
+#endif
+#ifdef USE_POOL_OT_SCOPE_CONTEXT
+REGISTER_POOL(&pool_head_ot_scope_context, "ot_scope_context", sizeof(struct flt_ot_scope_context));
+#endif
+#ifdef USE_POOL_OT_RUNTIME_CONTEXT
+REGISTER_POOL(&pool_head_ot_runtime_context, "ot_runtime_context", sizeof(struct flt_ot_runtime_context));
+#endif
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_pools_info -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_pools_info(void)
+{
+	/*
+	 * In case we have some error in the configuration file,
+	 * it is possible that this pool was not initialized.
+	 */
+#ifdef USE_POOL_BUFFER
+	FLT_OT_DBG(2, "sizeof_pool(buffer) = %u", FLT_OT_DEREF(pool_head_buffer, size, 0));
+#endif
+#ifdef USE_TRASH_CHUNK
+	FLT_OT_DBG(2, "sizeof_pool(trash) = %u", FLT_OT_DEREF(pool_head_trash, size, 0));
+#endif
+
+#ifdef USE_POOL_OT_SCOPE_SPAN
+	FLT_OT_DBG(2, "sizeof_pool(ot_scope_span) = %u", pool_head_ot_scope_span->size);
+#endif
+#ifdef USE_POOL_OT_SCOPE_CONTEXT
+	FLT_OT_DBG(2, "sizeof_pool(ot_scope_context) = %u", pool_head_ot_scope_context->size);
+#endif
+#ifdef USE_POOL_OT_RUNTIME_CONTEXT
+	FLT_OT_DBG(2, "sizeof_pool(ot_runtime_context) = %u", pool_head_ot_runtime_context->size);
+#endif
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   flt_ot_runtime_context_init -
+ *
+ * ARGUMENTS
+ *   s   -
+ *   f   -
+ *   err -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_runtime_context *flt_ot_runtime_context_init(struct stream *s, struct filter *f, char **err)
+{
+	const struct flt_ot_conf      *conf = FLT_OT_CONF(f);
+	struct flt_ot_runtime_context *retptr = NULL;
+
+	FLT_OT_FUNC("%p, %p, %p:%p", s, f, FLT_OT_DPTR_ARGS(err));
+
+	retptr = flt_ot_pool_alloc(pool_head_ot_runtime_context, sizeof(*retptr), 1, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	retptr->stream        = s;
+	retptr->filter        = f;
+	retptr->uuid.u64[0]   = ha_random64();
+	retptr->uuid.u64[1]   = ha_random64();
+	retptr->flag_harderr  = conf->tracer->flag_harderr;
+	retptr->flag_disabled = conf->tracer->flag_disabled;
+	retptr->logging       = conf->tracer->logging;
+	LIST_INIT(&(retptr->spans));
+	LIST_INIT(&(retptr->contexts));
+
+	(void)snprintf(retptr->uuid.s, sizeof(retptr->uuid.s), "%08x-%04hx-%04hx-%04hx-%012" PRIx64,
+	               retptr->uuid.time_low,
+	               retptr->uuid.time_mid,
+	               (retptr->uuid.time_hi_and_version & UINT16_C(0xfff)) | UINT16_C(0x4000),
+	               retptr->uuid.clock_seq | UINT16_C(0x8000),
+	               (uint64_t)retptr->uuid.node);
+
+	if (flt_ot_var_register(FTL_OT_VAR_UUID, err) != -1)
+		(void)flt_ot_var_set(s, FTL_OT_VAR_UUID, retptr->uuid.s, SMP_OPT_DIR_REQ, err);
+
+	FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_runtime_context_free -
+ *
+ * ARGUMENTS
+ *   f -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_runtime_context_free(struct filter *f)
+{
+	struct flt_ot_runtime_context *rt_ctx = f->ctx;
+
+	FLT_OT_FUNC("%p", f);
+
+	if (rt_ctx == NULL)
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
+
+	if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
+		struct timespec           ts;
+		struct flt_ot_scope_span *span, *span_back;
+
+		/* All spans should be completed at the same time. */
+		(void)clock_gettime(CLOCK_MONOTONIC, &ts);
+
+		list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list) {
+			ot_span_finish(&(span->span), &ts, NULL, NULL, NULL);
+			flt_ot_scope_span_free(&span);
+		}
+	}
+
+	if (!LIST_ISEMPTY(&(rt_ctx->contexts))) {
+		struct flt_ot_scope_context *ctx, *ctx_back;
+
+		list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list)
+			flt_ot_scope_context_free(&ctx);
+	}
+
+	flt_ot_pool_free(pool_head_ot_runtime_context, &(f->ctx));
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_span_init -
+ *
+ * ARGUMENTS
+ *   rt_ctx     -
+ *   id         -
+ *   id_len     -
+ *   ref_type   -
+ *   ref_id     -
+ *   ref_id_len -
+ *   dir        -
+ *   err        -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_scope_span *flt_ot_scope_span_init(struct flt_ot_runtime_context *rt_ctx, const char *id, size_t id_len, otc_span_reference_type_t ref_type, const char *ref_id, size_t ref_id_len, uint dir, char **err)
+{
+	struct otc_span             *ref_span = NULL;
+	struct otc_span_context     *ref_ctx = NULL;
+	struct flt_ot_scope_span    *span, *retptr = NULL;
+	struct flt_ot_scope_context *ctx;
+
+	FLT_OT_FUNC("%p, \"%s\", %zu, %d, \"%s\", %zu, %u, %p:%p", rt_ctx, id, id_len, ref_type, ref_id, ref_id_len, dir, FLT_OT_DPTR_ARGS(err));
+
+	if ((rt_ctx == NULL) || (id == NULL))
+		FLT_OT_RETURN(retptr);
+
+	list_for_each_entry(span, &(rt_ctx->spans), list)
+		if ((span->id_len == id_len) && (memcmp(span->id, id, id_len) == 0)) {
+			FLT_OT_DBG(2, "found span %p", span);
+
+			FLT_OT_RETURN(span);
+		}
+
+	if (ref_id != NULL) {
+		list_for_each_entry(span, &(rt_ctx->spans), list)
+			if ((span->id_len == ref_id_len) && (memcmp(span->id, ref_id, ref_id_len) == 0)) {
+				ref_span = span->span;
+
+				break;
+			}
+
+		if (ref_span != NULL) {
+			FLT_OT_DBG(2, "found referenced span %p", span);
+		} else {
+			list_for_each_entry(ctx, &(rt_ctx->contexts), list)
+				if ((ctx->id_len == ref_id_len) && (memcmp(ctx->id, ref_id, ref_id_len) == 0)) {
+					ref_ctx = ctx->context;
+
+					break;
+				}
+
+			if (ref_ctx != NULL) {
+				FLT_OT_DBG(2, "found referenced context %p", ctx);
+			} else {
+				FLT_OT_ERR("cannot find referenced span/context '%s'", ref_id);
+
+				FLT_OT_RETURN(retptr);
+			}
+		}
+	}
+
+	retptr = flt_ot_pool_alloc(pool_head_ot_scope_span, sizeof(*retptr), 1, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	retptr->id          = id;
+	retptr->id_len      = id_len;
+	retptr->smp_opt_dir = dir;
+	retptr->ref_type    = ref_type;
+	retptr->ref_span    = ref_span;
+	retptr->ref_ctx     = ref_ctx;
+	LIST_ADD(&(rt_ctx->spans), &(retptr->list));
+
+	FLT_OT_DBG_SCOPE_SPAN("new span ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_span_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_scope_span_free(struct flt_ot_scope_span **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_SCOPE_SPAN("", *ptr);
+
+	/* If the span is still active, do nothing. */
+	if ((*ptr)->span != NULL) {
+		FLT_OT_DBG(2, "cannot finish active span");
+
+		FLT_OT_RETURN();
+	}
+
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	flt_ot_pool_free(pool_head_ot_scope_span, (void **)ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_context_init -
+ *
+ * ARGUMENTS
+ *   rt_ctx   -
+ *   tracer   -
+ *   id       -
+ *   id_len   -
+ *   text_map -
+ *   dir      -
+ *   err      -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct flt_ot_scope_context *flt_ot_scope_context_init(struct flt_ot_runtime_context *rt_ctx, struct otc_tracer *tracer, const char *id, size_t id_len, const struct otc_text_map *text_map, uint dir, char **err)
+{
+	struct otc_http_headers_reader  reader;
+	struct otc_span_context        *span_ctx;
+	struct flt_ot_scope_context    *retptr = NULL;
+
+	FLT_OT_FUNC("%p, %p, \"%s\", %zu, %p, %u, %p:%p", rt_ctx, tracer, id, id_len, text_map, dir, FLT_OT_DPTR_ARGS(err));
+
+	if ((rt_ctx == NULL) || (tracer == NULL) || (id == NULL) || (text_map == NULL))
+		FLT_OT_RETURN(retptr);
+
+	list_for_each_entry(retptr, &(rt_ctx->contexts), list)
+		if ((retptr->id_len == id_len) && (memcmp(retptr->id, id, id_len) == 0)) {
+			FLT_OT_DBG(2, "found context %p", retptr);
+
+			FLT_OT_RETURN(retptr);
+		}
+
+	retptr = flt_ot_pool_alloc(pool_head_ot_scope_context, sizeof(*retptr), 1, err);
+	if (retptr == NULL)
+		FLT_OT_RETURN(retptr);
+
+	span_ctx = ot_extract_http_headers(tracer, &reader, text_map, err);
+	if (span_ctx == NULL) {
+		flt_ot_scope_context_free(&retptr);
+
+		FLT_OT_RETURN(retptr);
+	}
+
+	retptr->id          = id;
+	retptr->id_len      = id_len;
+	retptr->smp_opt_dir = dir;
+	retptr->context     = span_ctx;
+	LIST_ADD(&(rt_ctx->contexts), &(retptr->list));
+
+	FLT_OT_DBG_SCOPE_CONTEXT("new context ", retptr);
+
+	FLT_OT_RETURN(retptr);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_context_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_scope_context_free(struct flt_ot_scope_context **ptr)
+{
+	FLT_OT_FUNC("%p:%p", FLT_OT_DPTR_ARGS(ptr));
+
+	if ((ptr == NULL) || (*ptr == NULL))
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_SCOPE_CONTEXT("", *ptr);
+
+	if ((*ptr)->context != NULL)
+		(*ptr)->context->destroy(&((*ptr)->context));
+
+	FLT_OT_LIST_DEL(&((*ptr)->list));
+	flt_ot_pool_free(pool_head_ot_scope_context, (void **)ptr);
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_data_free -
+ *
+ * ARGUMENTS
+ *   ptr -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_scope_data_free(struct flt_ot_scope_data *ptr)
+{
+	int i;
+
+	FLT_OT_FUNC("%p", ptr);
+
+	if (ptr == NULL)
+		FLT_OT_RETURN();
+
+	FLT_OT_DBG_SCOPE_DATA("", ptr);
+
+	for (i = 0; i < ptr->num_tags; i++)
+		if (ptr->tags[i].value.type == otc_value_string)
+			FLT_OT_FREE_VOID(ptr->tags[i].value.value.string_value);
+	otc_text_map_destroy(&(ptr->baggage), OTC_TEXT_MAP_FREE_VALUE);
+	for (i = 0; i < ptr->num_log_fields; i++)
+		if (ptr->log_fields[i].value.type == otc_value_string)
+			FLT_OT_FREE_VOID(ptr->log_fields[i].value.value.string_value);
+
+	(void)memset(ptr, 0, sizeof(*ptr));
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_finish_mark -
+ *
+ * ARGUMENTS
+ *   rt_ctx -
+ *   id     -
+ *   id_len -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_scope_finish_mark(const struct flt_ot_runtime_context *rt_ctx, const char *id, size_t id_len)
+{
+	struct flt_ot_scope_span    *span;
+	struct flt_ot_scope_context *ctx;
+	int                          span_cnt = 0, ctx_cnt = 0, retval;
+
+	FLT_OT_FUNC("%p, \"%s\", %zu", rt_ctx, id, id_len);
+
+	if (FLT_OT_STR_CMP(FLT_OT_SCOPE_SPAN_FINISH_ALL, id, id_len)) {
+		list_for_each_entry(span, &(rt_ctx->spans), list) {
+			span->flag_finish = 1;
+			span_cnt++;
+		}
+
+		list_for_each_entry(ctx, &(rt_ctx->contexts), list) {
+			ctx->flag_finish = 1;
+			ctx_cnt++;
+		}
+
+		FLT_OT_DBG(2, "marked %d span(s), %d context(s)", span_cnt, ctx_cnt);
+	}
+	else if (FLT_OT_STR_CMP(FLT_OT_SCOPE_SPAN_FINISH_REQ, id, id_len)) {
+		list_for_each_entry(span, &(rt_ctx->spans), list)
+			if (span->smp_opt_dir == SMP_OPT_DIR_REQ) {
+				span->flag_finish = 1;
+				span_cnt++;
+			}
+
+		list_for_each_entry(ctx, &(rt_ctx->contexts), list)
+			if (ctx->smp_opt_dir == SMP_OPT_DIR_REQ) {
+				ctx->flag_finish = 1;
+				span_cnt++;
+			}
+
+		FLT_OT_DBG(2, "marked REQuest channel %d span(s), %d context(s)", span_cnt, ctx_cnt);
+	}
+	else if (FLT_OT_STR_CMP(FLT_OT_SCOPE_SPAN_FINISH_RES, id, id_len)) {
+		list_for_each_entry(span, &(rt_ctx->spans), list)
+			if (span->smp_opt_dir == SMP_OPT_DIR_RES) {
+				span->flag_finish = 1;
+				span_cnt++;
+			}
+
+		list_for_each_entry(ctx, &(rt_ctx->contexts), list)
+			if (ctx->smp_opt_dir == SMP_OPT_DIR_RES) {
+				ctx->flag_finish = 1;
+				ctx_cnt++;
+			}
+
+		FLT_OT_DBG(2, "marked RESponse channel %d span(s), %d context(s)", span_cnt, ctx_cnt);
+	}
+	else {
+		list_for_each_entry(span, &(rt_ctx->spans), list)
+			if ((span->id_len == id_len) && (memcmp(span->id, id, id_len) == 0)) {
+				span->flag_finish = 1;
+				span_cnt++;
+
+				break;
+			}
+
+		list_for_each_entry(ctx, &(rt_ctx->contexts), list)
+			if ((ctx->id_len == id_len) && (memcmp(ctx->id, id, id_len) == 0)) {
+				ctx->flag_finish = 1;
+				ctx_cnt++;
+
+				break;
+			}
+
+		if (span_cnt > 0)
+			FLT_OT_DBG(2, "marked span '%s'", id);
+		if (ctx_cnt > 0)
+			FLT_OT_DBG(2, "marked context '%s'", id);
+		if ((span_cnt + ctx_cnt) == 0)
+			FLT_OT_DBG(2, "cannot find span/context '%s'", id);
+	}
+
+	retval = span_cnt + ctx_cnt;
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_finish_marked -
+ *
+ * ARGUMENTS
+ *   rt_ctx    -
+ *   ts_finish -
+ *
+ * DESCRIPTION
+ *   Finish marked spans.
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_scope_finish_marked(const struct flt_ot_runtime_context *rt_ctx, const struct timespec *ts_finish)
+{
+	struct flt_ot_scope_span    *span;
+	struct flt_ot_scope_context *ctx;
+
+	FLT_OT_FUNC("%p, %p", rt_ctx, ts_finish);
+
+	list_for_each_entry(span, &(rt_ctx->spans), list)
+		if (span->flag_finish) {
+			FLT_OT_DBG_SCOPE_SPAN("finishing span ", span);
+
+			ot_span_finish(&(span->span), ts_finish, NULL, NULL, NULL);
+
+			span->flag_finish = 0;
+		}
+
+	list_for_each_entry(ctx, &(rt_ctx->contexts), list)
+		if (ctx->flag_finish) {
+			FLT_OT_DBG_SCOPE_CONTEXT("finishing context ", ctx);
+
+			if (ctx->context != NULL)
+				ctx->context->destroy(&(ctx->context));
+
+			ctx->flag_finish = 0;
+		}
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_scope_free_unused -
+ *
+ * ARGUMENTS
+ *   rt_ctx -
+ *   chn    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_scope_free_unused(struct flt_ot_runtime_context *rt_ctx, struct channel *chn)
+{
+	FLT_OT_FUNC("%p", rt_ctx);
+
+	if (rt_ctx == NULL)
+		FLT_OT_RETURN();
+
+	if (!LIST_ISEMPTY(&(rt_ctx->spans))) {
+		struct flt_ot_scope_span *span, *span_back;
+
+		list_for_each_entry_safe(span, span_back, &(rt_ctx->spans), list)
+			if (span->span == NULL)
+				flt_ot_scope_span_free(&span);
+	}
+
+	if (!LIST_ISEMPTY(&(rt_ctx->contexts))) {
+		struct flt_ot_scope_context *ctx, *ctx_back;
+
+		list_for_each_entry_safe(ctx, ctx_back, &(rt_ctx->contexts), list)
+			if (ctx->context == NULL) {
+				/*
+				 * All headers and variables associated with
+				 * the context in question should be deleted.
+				 */
+				(void)flt_ot_http_headers_remove(chn, ctx->id, NULL);
+				(void)flt_ot_vars_unset(rt_ctx->stream, FLT_OT_VARS_SCOPE, ctx->id, ctx->smp_opt_dir, NULL);
+
+				flt_ot_scope_context_free(&ctx);
+			}
+	}
+
+	FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", rt_ctx);
+
+	FLT_OT_RETURN();
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/util.c b/contrib/opentracing/src/util.c
new file mode 100644
index 0000000..3adc5a3
--- /dev/null
+++ b/contrib/opentracing/src/util.c
@@ -0,0 +1,793 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_args_dump -
+ *
+ * ARGUMENTS
+ *   args -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_args_dump(char **args)
+{
+	int i, n;
+
+	for (n = 1; FLT_OT_ARG_ISVALID(n); n++);
+
+	(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), dbg_indent_level, FLT_OT_DBG_INDENT, n, args[0]);
+
+	for (i = 1; FLT_OT_ARG_ISVALID(i); i++)
+		(void)fprintf(stderr, "'%s' ", args[i]);
+
+	(void)fprintf(stderr, "}\n");
+}
+
+
+/***
+ * NAME
+ *   flt_ot_filters_dump -
+ *
+ * ARGUMENTS
+ *   This function takes no arguments.
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_filters_dump(void)
+{
+	struct flt_conf *fconf;
+	struct proxy    *px;
+
+	FLT_OT_FUNC("");
+
+	for (px = proxies_list; px != NULL; px = px->next) {
+		FLT_OT_DBG(2, "proxy '%s'", px->id);
+
+		list_for_each_entry(fconf, &(px->filter_configs), list)
+			if (fconf->id == ot_flt_id) {
+				struct flt_ot_conf *conf = fconf->conf;
+
+				FLT_OT_DBG(2, "  OT filter '%s'", conf->id);
+			}
+	}
+
+	FLT_OT_RETURN();
+}
+
+
+/***
+ * NAME
+ *   flt_ot_chn_label -
+ *
+ * ARGUMENTS
+ *   chn -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_chn_label(const struct channel *chn)
+{
+	return (chn->flags & CF_ISRESP) ? "RESponse" : "REQuest";
+}
+
+
+/***
+ * NAME
+ *   flt_ot_pr_mode -
+ *
+ * ARGUMENTS
+ *   s -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_pr_mode(const struct stream *s)
+{
+	struct proxy *px = (s->flags & SF_BE_ASSIGNED) ? s->be : strm_fe(s);
+
+	return (px->mode == PR_MODE_HTTP) ? "HTTP" : "TCP";
+}
+
+
+/***
+ * NAME
+ *   flt_ot_stream_pos -
+ *
+ * ARGUMENTS
+ *   s -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_stream_pos(const struct stream *s)
+{
+	return (s->flags & SF_BE_ASSIGNED) ? "backend" : "frontend";
+}
+
+
+/***
+ * NAME
+ *   flt_ot_type -
+ *
+ * ARGUMENTS
+ *   f -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_type(const struct filter *f)
+{
+	return (f->flags & FLT_FL_IS_BACKEND_FILTER) ? "backend" : "frontend";
+}
+
+
+/***
+ * NAME
+ *   flt_ot_analyzer -
+ *
+ * ARGUMENTS
+ *   an_bit -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_analyzer(uint an_bit)
+{
+#define FLT_OT_AN_DEF(a)   { a, #a },
+	static const struct {
+		uint        an_bit;
+		const char *str;
+	} flt_ot_an[] = { FLT_OT_AN_DEFINES };
+#undef FLT_OT_AN_DEF
+	const char *retptr = "invalid an_bit";
+	int         i;
+
+	for (i = 0; i < FLT_OT_TABLESIZE(flt_ot_an); i++)
+		if (flt_ot_an[i].an_bit == an_bit) {
+			retptr = flt_ot_an[i].str;
+
+			break;
+		}
+
+	return retptr;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_str_hex -
+ *
+ * ARGUMENTS
+ *   data -
+ *   size -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_str_hex(const void *data, size_t size)
+{
+	static THREAD_LOCAL char  retbuf[BUFSIZ];
+	const uint8_t            *ptr = data;
+	size_t                    i;
+
+	if (data == NULL)
+		return "(null)";
+	else if (size == 0)
+		return "()";
+
+	for (i = 0, size <<= 1; (i < (sizeof(retbuf) - 2)) && (i < size); ptr++) {
+		retbuf[i++] = FLT_OT_NIBBLE_TO_HEX(*ptr >> 4);
+		retbuf[i++] = FLT_OT_NIBBLE_TO_HEX(*ptr & 0x0f);
+	}
+
+	retbuf[i] = '\0';
+
+	return retbuf;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_str_ctrl -
+ *
+ * ARGUMENTS
+ *   data -
+ *   size -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_str_ctrl(const void *data, size_t size)
+{
+	static THREAD_LOCAL char  retbuf[BUFSIZ];
+	const uint8_t            *ptr = data;
+	size_t                    i, n = 0;
+
+	if (data == NULL)
+		return "(null)";
+	else if (size == 0)
+		return "()";
+
+	for (i = 0; (n < (sizeof(retbuf) - 1)) && (i < size); i++)
+		retbuf[n++] = ((ptr[i] >= 0x20) && (ptr[i] <= 0x7e)) ? ptr[i] : '.';
+
+	retbuf[n] = '\0';
+
+	return retbuf;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_list_debug -
+ *
+ * ARGUMENTS
+ *   head -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+const char *flt_ot_list_debug(const struct list *head)
+{
+	FLT_OT_BUFFER_THR(retbuf, 4, 64, retptr);
+
+	if ((head == NULL) || LIST_ISEMPTY(head)) {
+		(void)strncpy(retptr, (head == NULL) ? "{ null list }" : "{ empty list }", sizeof(retbuf[0]));
+	}
+	else if (head->p == head->n) {
+		(void)snprintf(retptr, sizeof(retbuf[0]), "{ %p * 1 }", head->p);
+	}
+	else {
+		const struct list *ptr;
+		size_t             count = 0;
+
+		for (ptr = head->n; ptr != head; ptr = ptr->n, count++);
+
+		(void)snprintf(retptr, sizeof(retbuf[0]), "{ %p %p %zu }", head->p, head->n, count);
+	}
+
+	return (retptr);
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   flt_ot_chunk_add -
+ *
+ * ARGUMENTS
+ *   chk -
+ *   src -
+ *   n   -
+ *   err -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+ssize_t flt_ot_chunk_add(struct buffer *chk, const void *src, size_t n, char **err)
+{
+	FLT_OT_FUNC("%p, %p, %zu, %p:%p", chk, src, n, FLT_OT_DPTR_ARGS(err));
+
+	if ((chk == NULL) || (src == NULL))
+		FLT_OT_RETURN(-1);
+
+	if (chk->area == NULL)
+		chunk_init(chk, FLT_OT_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
+
+	if (chk->area == NULL) {
+		FLT_OT_ERR("out of memory");
+
+		FLT_OT_RETURN(-1);
+	}
+	else if (n > (chk->size - chk->data)) {
+		FLT_OT_ERR("chunk size too small");
+
+		FLT_OT_RETURN(-1);
+	}
+
+	(void)memcpy(chk->area + chk->data, src, n);
+	chk->data += n;
+
+	FLT_OT_RETURN(chk->data);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_args_count -
+ *
+ * ARGUMENTS
+ *   args -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_args_count(char **args)
+{
+	int retval = 0;
+
+	if (args != NULL)
+		for ( ; FLT_OT_ARG_ISVALID(retval); retval++);
+
+	return retval;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_args_to_str -
+ *
+ * ARGUMENTS
+ *   args -
+ *   idx  -
+ *   str  -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_args_to_str(char **args, int idx, char **str)
+{
+	int i;
+
+	if ((args == NULL) || (*args == NULL))
+		return;
+
+	for (i = idx; FLT_OT_ARG_ISVALID(i); i++)
+		(void)memprintf(str, "%s%s%s", (*str == NULL) ? "" : *str, (i == idx) ? "" : " ", args[i]);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_strtod -
+ *
+ * ARGUMENTS
+ *   nptr      -
+ *   limit_min -
+ *   limit_max -
+ *   err       -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+double flt_ot_strtod(const char *nptr, double limit_min, double limit_max, char **err)
+{
+	char   *endptr = NULL;
+	double  retval;
+
+	errno = 0;
+
+	retval = strtod(nptr, &endptr);
+	if ((errno != 0) || FLT_OT_STR_ISVALID(endptr))
+		FLT_OT_ERR("'%s' : invalid value", nptr);
+	else if (!FLT_OT_IN_RANGE(retval, limit_min, limit_max))
+		FLT_OT_ERR("'%s' : value out of range [%.2f, %.2f]", nptr, limit_min, limit_max);
+
+	return retval;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_strtoll -
+ *
+ * ARGUMENTS
+ *   nptr      -
+ *   limit_min -
+ *   limit_max -
+ *   err       -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int64_t flt_ot_strtoll(const char *nptr, int64_t limit_min, int64_t limit_max, char **err)
+{
+	char    *endptr = NULL;
+	int64_t  retval;
+
+	errno = 0;
+
+	retval = strtoll(nptr, &endptr, 0);
+	if ((errno != 0) || FLT_OT_STR_ISVALID(endptr))
+		FLT_OT_ERR("'%s' : invalid value", nptr);
+	else if (!FLT_OT_IN_RANGE(retval, limit_min, limit_max))
+		FLT_OT_ERR("'%s' : value out of range [%" PRId64 ", %" PRId64 "]", nptr, limit_min, limit_max);
+
+	return retval;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_sample_to_str -
+ *
+ * ARGUMENTS
+ *   data  -
+ *   value -
+ *   size  -
+ *   err   -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err)
+{
+	int retval = -1;
+
+	FLT_OT_FUNC("%p, %p, %zu, %p:%p", data, value, size, FLT_OT_DPTR_ARGS(err));
+
+	if ((data == NULL) || (value == NULL) || (size == 0))
+		FLT_OT_RETURN(retval);
+
+	*value = '\0';
+
+	if (data->type == SMP_T_ANY) {
+		FLT_OT_ERR("invalid sample data type %d", data->type);
+	}
+	else if (data->type == SMP_T_BOOL) {
+		value[0] = data->u.sint ? '1' : '0';
+		value[1] = '\0';
+
+		retval = 1;
+	}
+	else if (data->type == SMP_T_SINT) {
+		retval = snprintf(value, size, "%lld", data->u.sint);
+	}
+	else if (data->type == SMP_T_ADDR) {
+		/* This type is never used to qualify a sample. */
+	}
+	else if (data->type == SMP_T_IPV4) {
+		if (INET_ADDRSTRLEN > size)
+			FLT_OT_ERR("sample data size too large");
+		else if (inet_ntop(AF_INET, &(data->u.ipv4), value, INET_ADDRSTRLEN) == NULL)
+			FLT_OT_ERR("invalid IPv4 address");
+		else
+			retval = strlen(value);
+	}
+	else if (data->type == SMP_T_IPV6) {
+		if (INET6_ADDRSTRLEN > size)
+			FLT_OT_ERR("sample data size too large");
+		else if (inet_ntop(AF_INET6, &(data->u.ipv6), value, INET6_ADDRSTRLEN) == NULL)
+			FLT_OT_ERR("invalid IPv6 address");
+		else
+			retval = strlen(value);
+	}
+	else if (data->type == SMP_T_STR) {
+		if (data->u.str.data >= size) {
+			FLT_OT_ERR("sample data size too large");
+		}
+		else if (data->u.str.data > 0) {
+			retval = data->u.str.data;
+
+			(void)strncat(value, data->u.str.area, retval);
+		}
+		else {
+			/*
+			 * There is no content to add but we will still return
+			 * the correct status.
+			 */
+			retval = 0;
+		}
+	}
+	else if (data->type == SMP_T_BIN) {
+		FLT_OT_ERR("invalid sample data type %d", data->type);
+	}
+	else if (data->type != SMP_T_METH) {
+		FLT_OT_ERR("invalid sample data type %d", data->type);
+	}
+	else if (data->u.meth.meth == HTTP_METH_OPTIONS) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_OPTIONS);
+
+		(void)memcpy(value, HTTP_METH_STR_OPTIONS, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_GET) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_GET);
+
+		(void)memcpy(value, HTTP_METH_STR_GET, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_HEAD) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_HEAD);
+
+		(void)memcpy(value, HTTP_METH_STR_HEAD, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_POST) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_POST);
+
+		(void)memcpy(value, HTTP_METH_STR_POST, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_PUT) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_PUT);
+
+		(void)memcpy(value, HTTP_METH_STR_PUT, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_DELETE) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_DELETE);
+
+		(void)memcpy(value, HTTP_METH_STR_DELETE, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_TRACE) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_TRACE);
+
+		(void)memcpy(value, HTTP_METH_STR_TRACE, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_CONNECT) {
+		retval = FLT_OT_STR_SIZE(HTTP_METH_STR_CONNECT);
+
+		(void)memcpy(value, HTTP_METH_STR_CONNECT, retval + 1);
+	}
+	else if (data->u.meth.meth == HTTP_METH_OTHER) {
+		if (data->u.meth.str.data >= size) {
+			FLT_OT_ERR("sample data size too large");
+		} else {
+			retval = data->u.meth.str.data;
+
+			(void)strncat(value, data->u.meth.str.area, retval);
+		}
+	}
+	else {
+		FLT_OT_ERR("invalid HTTP method");
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_sample_to_value -
+ *
+ * ARGUMENTS
+ *   key   -
+ *   data  -
+ *   value -
+ *   err   -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_sample_to_value(const char *key, const struct sample_data *data, struct otc_value *value, char **err)
+{
+	int retval = -1;
+
+	FLT_OT_FUNC("\"%s\", %p, %p, %p:%p", key, data, value, FLT_OT_DPTR_ARGS(err));
+
+	if ((data == NULL) || (value == NULL))
+		FLT_OT_RETURN(retval);
+
+	if (data->type == SMP_T_BOOL) {
+		value->type             = otc_value_bool;
+		value->value.bool_value = data->u.sint ? 1 : 0;
+
+		retval = sizeof(value->value.bool_value);
+	}
+	else if (data->type == SMP_T_SINT) {
+		value->type              = otc_value_int64;
+		value->value.int64_value = data->u.sint;
+
+		retval = sizeof(value->value.int64_value);
+	}
+	else {
+		value->type               = otc_value_string;
+		value->value.string_value = FLT_OT_MALLOC(global.tune.bufsize);
+
+		if (value->value.string_value == NULL)
+			FLT_OT_ERR("out of memory");
+		else
+			retval = flt_ot_sample_to_str(data, (char *)value->value.string_value, global.tune.bufsize, err);
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_sample_add -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   dir    -
+ *   sample -
+ *   data   -
+ *   type   -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   Returns a negative value if an error occurs, 0 if it needs to wait,
+ *   any other value otherwise.
+ */
+int flt_ot_sample_add(struct stream *s, uint dir, struct flt_ot_conf_sample *sample, struct flt_ot_scope_data *data, int type, char **err)
+{
+	const struct flt_ot_conf_sample_expr *expr;
+	struct sample                         smp;
+	struct otc_value                      value;
+	struct buffer                         buffer;
+	int                                   idx = 0, rc, retval = FLT_OT_RET_OK;
+
+	FLT_OT_FUNC("%p, %u, %p, %p, %d, %p:%p", s, dir, data, sample, type, FLT_OT_DPTR_ARGS(err));
+
+	FLT_OT_DBG_CONF_SAMPLE("sample ", sample);
+
+	(void)memset(&buffer, 0, sizeof(buffer));
+
+	list_for_each_entry(expr, &(sample->exprs), list) {
+		FLT_OT_DBG_CONF_SAMPLE_EXPR("sample expression ", expr);
+
+		(void)memset(&smp, 0, sizeof(smp));
+
+		/*
+		 * If we have only one expression to process, then the data
+		 * type that is the result of the expression is converted to
+		 * an equivalent data type (if possible) that is written to
+		 * the tracer.
+		 *
+		 * If conversion is not possible, or if we have multiple
+		 * expressions to process, then the result is converted to
+		 * a string and as such sent to the tracer.
+		 */
+		if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) != NULL) {
+			FLT_OT_DBG(3, "data type %d: '%s'", smp.data.type, expr->value);
+		} else {
+			FLT_OT_DBG(2, "WARNING: failed to fetch '%s' value", expr->value);
+
+			/*
+			 * In case the fetch failed, we will set the result
+			 * (sample) to an empty static string.
+			 */
+			(void)memset(&(smp.data), 0, sizeof(smp.data));
+			smp.data.type       = SMP_T_STR;
+			smp.data.u.str.area = "";
+		}
+
+		if ((sample->num_exprs == 1) && (type == FLT_OT_EVENT_SAMPLE_TAG)) {
+			if (flt_ot_sample_to_value(sample->key, &(smp.data), &value, err) == -1)
+				retval = FLT_OT_RET_ERROR;
+		} else {
+			if (buffer.area == NULL) {
+				chunk_init(&buffer, FLT_OT_CALLOC(1, global.tune.bufsize), global.tune.bufsize);
+				if (buffer.area == NULL) {
+					FLT_OT_ERR("out of memory");
+
+					retval = FLT_OT_RET_ERROR;
+
+					break;
+				}
+			}
+
+			rc = flt_ot_sample_to_str(&(smp.data), buffer.area + buffer.data, buffer.size - buffer.data, err);
+			if (rc == -1) {
+				retval = FLT_OT_RET_ERROR;
+			} else {
+				buffer.data += rc;
+
+				if (sample->num_exprs == ++idx) {
+					value.type               = otc_value_string;
+					value.value.string_value = buffer.area;
+				}
+			}
+		}
+	}
+
+	if (retval == FLT_OT_RET_ERROR) {
+		/* Do nothing. */
+	}
+	else if (type == FLT_OT_EVENT_SAMPLE_TAG) {
+		struct otc_tag *tag = data->tags + data->num_tags++;
+
+		tag->key = sample->key;
+		(void)memcpy(&(tag->value), &value, sizeof(tag->value));
+	}
+	else if (type == FLT_OT_EVENT_SAMPLE_LOG) {
+		struct otc_log_field *log_field = data->log_fields + data->num_log_fields++;
+
+		log_field->key = sample->key;
+		(void)memcpy(&(log_field->value), &value, sizeof(log_field->value));
+	}
+	else {
+		if (data->baggage == NULL)
+			data->baggage = otc_text_map_new(NULL, FLT_OT_MAXBAGGAGES);
+
+		if (data->baggage == NULL) {
+			FLT_OT_ERR("out of memory");
+
+			retval = FLT_OT_RET_ERROR;
+		}
+		else if (otc_text_map_add(data->baggage, sample->key, 0, value.value.string_value, 0, 0) == -1) {
+			FLT_OT_ERR("out of memory");
+
+			retval = FLT_OT_RET_ERROR;
+		}
+		else
+			FLT_OT_DBG(3, "baggage[%zu]: '%s' -> '%s'", data->baggage->count - 1, data->baggage->key[data->baggage->count - 1], data->baggage->value[data->baggage->count - 1]);
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/src/vars.c b/contrib/opentracing/src/vars.c
new file mode 100644
index 0000000..0db5514
--- /dev/null
+++ b/contrib/opentracing/src/vars.c
@@ -0,0 +1,578 @@
+/***
+ * Copyright 2020 HAProxy Technologies
+ *
+ * This file is part of the HAProxy OpenTracing filter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "include.h"
+
+
+#ifdef DEBUG_OT
+
+/***
+ * NAME
+ *   flt_ot_vars_scope_dump -
+ *
+ * ARGUMENTS
+ *   vars  -
+ *   scope -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+static void flt_ot_vars_scope_dump(struct vars *vars, const char *scope)
+{
+	const struct var *var;
+
+	if (vars == NULL)
+		return;
+
+	HA_RWLOCK_RDLOCK(VARS_LOCK, &(vars->rwlock));
+	list_for_each_entry(var, &(vars->head), l)
+		FLT_OT_DBG(2, "'%s.%s' -> '%.*s'", scope, var->name, (int)var->data.u.str.data, var->data.u.str.area);
+	HA_RWLOCK_RDUNLOCK(VARS_LOCK, &(vars->rwlock));
+}
+
+
+/***
+ * NAME
+ *   flt_ot_vars_dump -
+ *
+ * ARGUMENTS
+ *   s -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   This function does not return a value.
+ */
+void flt_ot_vars_dump(struct stream *s)
+{
+	FLT_OT_FUNC("%p", s);
+
+	/*
+	 * It would be nice if we could use the get_vars() function from HAProxy
+	 * source here to get the value of the 'vars' pointer, but it is defined
+	 * as 'static inline', so unfortunately none of this is possible.
+	 */
+	flt_ot_vars_scope_dump(&(global.vars), "PROC");
+	flt_ot_vars_scope_dump(&(s->sess->vars), "SESS");
+	flt_ot_vars_scope_dump(&(s->vars_txn), "TXN");
+	flt_ot_vars_scope_dump(&(s->vars_reqres), "REQ/RES");
+
+	FLT_OT_RETURN();
+}
+
+#endif /* DEBUG_OT */
+
+
+/***
+ * NAME
+ *   flt_ot_get_vars -
+ *
+ * ARGUMENTS
+ *   s     -
+ *   scope -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static inline struct vars *flt_ot_get_vars(struct stream *s, const char *scope)
+{
+	struct vars *retptr = NULL;
+
+	if (strcasecmp(scope, "proc") == 0)
+		retptr = &(global.vars);
+	else if (strcasecmp(scope, "sess") == 0)
+		retptr = (&(s->sess->vars));
+	else if (strcasecmp(scope, "txn") == 0)
+		retptr = (&(s->vars_txn));
+	else if ((strcasecmp(scope, "req") == 0) || (strcasecmp(scope, "res") == 0))
+		retptr = (&(s->vars_reqres));
+
+	return retptr;
+}
+
+
+/***
+ * NAME
+ *   flt_ot_normalize_name -
+ *
+ * ARGUMENTS
+ *   var_name -
+ *   size     -
+ *   len      -
+ *   name     -
+ *   err      -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_normalize_name(char *var_name, size_t size, int *len, const char *name, char **err)
+{
+	int retval = 0;
+
+	FLT_OT_FUNC("%p, %zu, %p, \"%s\", %p:%p", var_name, size, len, name, FLT_OT_DPTR_ARGS(err));
+
+	if (!FLT_OT_STR_ISVALID(name))
+		FLT_OT_RETURN(retval);
+
+	/*
+	 * In case the name of the variable consists of several elements,
+	 * the character '.' is added between them.
+	 */
+	if ((*len == 0) || (var_name[*len - 1] == '.'))
+		/* Do nothing. */;
+	else if (*len < (size - 1))
+		var_name[(*len)++] = '.';
+	else
+		retval = -1;
+
+	/*
+	 * HAProxy does not allow the use of variable names containing '-'
+	 * or ' '.  This of course applies to HTTP header names as well.
+	 * Also, here the capital letters are converted to lowercase.
+	 */
+	while (retval != -1)
+		if (*len >= (size - 1)) {
+			FLT_OT_ERR("failed to normalize variable name, buffer too small");
+
+			retval = -1;
+		} else {
+			uint8_t ch = name[retval];
+
+			if (ch == '\0')
+				break;
+			else if (ch == '-')
+				ch = FLT_OT_VAR_CHAR_DASH;
+			else if (ch == ' ')
+				ch = FLT_OT_VAR_CHAR_SPACE;
+			else if (isupper(ch))
+				ch = ist_lc[ch];
+
+			var_name[(*len)++] = ch;
+			retval++;
+		}
+
+	var_name[*len] = '\0';
+
+	FLT_OT_DBG(3, "var_name: \"%s\" %d/%d", var_name, retval, *len);
+
+	if (retval == -1)
+		*len = retval;
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_var_name -
+ *
+ * ARGUMENTS
+ *   scope    -
+ *   prefix   -
+ *   name     -
+ *   var_name -
+ *   size     -
+ *   err      -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+static int flt_ot_var_name(const char *scope, const char *prefix, const char *name, char *var_name, size_t size, char **err)
+{
+	int retval = 0;
+
+	FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %p, %zu, %p:%p", scope, prefix, name, var_name, size, FLT_OT_DPTR_ARGS(err));
+
+	if (flt_ot_normalize_name(var_name, size, &retval, scope, err) >= 0)
+		if (flt_ot_normalize_name(var_name, size, &retval, prefix, err) >= 0)
+			(void)flt_ot_normalize_name(var_name, size, &retval, name, err);
+
+	if (retval == -1)
+		FLT_OT_ERR("failed to construct variable name '%s.%s.%s'", scope, prefix, name);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_var_register -
+ *
+ * ARGUMENTS
+ *   scope  -
+ *   prefix -
+ *   name   -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_var_register(const char *scope, const char *prefix, const char *name, char **err)
+{
+	struct arg arg;
+	char       var_name[BUFSIZ];
+	int        retval;
+
+	FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %p:%p", scope, prefix, name, FLT_OT_DPTR_ARGS(err));
+
+	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
+	if (retval == -1)
+		FLT_OT_RETURN(retval);
+
+	/* Set <size> to 0 to not release var_name memory in vars_check_arg(). */
+	(void)memset(&arg, 0, sizeof(arg));
+	arg.type          = ARGT_STR;
+	arg.data.str.area = var_name;
+	arg.data.str.data = retval;
+
+	if (vars_check_arg(&arg, err) == 0) {
+		FLT_OT_ERR_APPEND("failed to register variable '%s': %s", var_name, *err);
+
+		retval = -1;
+	} else {
+		FLT_OT_DBG(2, "variable '%s' registered", arg.data.var.name);
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_var_set -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   scope  -
+ *   prefix -
+ *   name   -
+ *   value  -
+ *   opt    -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_var_set(struct stream *s, const char *scope, const char *prefix, const char *name, const char *value, uint opt, char **err)
+{
+	struct sample smp;
+	char          var_name[BUFSIZ];
+	int           retval;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, value, opt, FLT_OT_DPTR_ARGS(err));
+
+	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
+	if (retval == -1)
+		FLT_OT_RETURN(retval);
+
+	(void)memset(&smp, 0, sizeof(smp));
+	(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
+	smp.data.type       = SMP_T_STR;
+	smp.data.u.str.area = (char *)value;
+	smp.data.u.str.data = strlen(value);
+
+	vars_set_by_name_ifexist(var_name, retval, &smp);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_var_unset -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   scope  -
+ *   prefix -
+ *   name   -
+ *   opt    -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_var_unset(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err)
+{
+	struct sample smp;
+	char          var_name[BUFSIZ];
+	int           retval;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, opt, FLT_OT_DPTR_ARGS(err));
+
+	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
+	if (retval == -1)
+		FLT_OT_RETURN(retval);
+
+	(void)memset(&smp, 0, sizeof(smp));
+	(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
+
+	vars_unset_by_name_ifexist(var_name, retval, &smp);
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_vars_unset -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   scope  -
+ *   prefix -
+ *   opt    -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
+{
+	struct sample  smp;
+	struct vars   *vars;
+	struct var    *var, *var_back;
+	char           var_prefix[BUFSIZ], var_name[BUFSIZ];
+	uint           size;
+	int            var_prefix_len, var_name_len, retval = -1;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, opt, FLT_OT_DPTR_ARGS(err));
+
+	vars = flt_ot_get_vars(s, scope);
+	if (vars == NULL)
+		FLT_OT_RETURN(retval);
+
+	var_prefix_len = flt_ot_var_name(NULL, prefix, NULL, var_prefix, sizeof(var_prefix), err);
+	if (var_prefix_len == -1)
+		FLT_OT_RETURN(retval);
+
+	retval = 0;
+
+	HA_RWLOCK_WRLOCK(VARS_LOCK, &(vars->rwlock));
+	list_for_each_entry_safe(var, var_back, &(vars->head), l) {
+		FLT_OT_DBG(3, "variable cmp '%s' '%s' %d", var_prefix, var->name, var_prefix_len);
+
+		if (strncmp(var_prefix, var->name, var_prefix_len) == 0) {
+			var_name_len = snprintf(var_name, sizeof(var_name), "%s.%s", scope, var->name);
+			if ((var_name_len == -1) || (var_name_len >= sizeof(var_name))) {
+				FLT_OT_DBG(2, "'%s.%s' variable name too long", scope, var->name);
+
+				break;
+			}
+
+			FLT_OT_DBG(2, "- '%s' -> '%.*s'", var_name, (int)var->data.u.str.data, var->data.u.str.area);
+
+			(void)memset(&smp, 0, sizeof(smp));
+			(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
+
+			size = var_clear(var);
+			var_accounting_diff(vars, smp.sess, smp.strm, -size);
+
+			retval++;
+		}
+	}
+	HA_RWLOCK_WRUNLOCK(VARS_LOCK, &(vars->rwlock));
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_var_get -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   scope  -
+ *   prefix -
+ *   name   -
+ *   value  -
+ *   opt    -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+int flt_ot_var_get(struct stream *s, const char *scope, const char *prefix, const char *name, char **value, uint opt, char **err)
+{
+	struct sample smp;
+	char          var_name[BUFSIZ], var_value[BUFSIZ];
+	int           retval;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %p:%p, %u, %p:%p", s, scope, prefix, name, FLT_OT_DPTR_ARGS(value), opt, FLT_OT_DPTR_ARGS(err));
+
+	retval = flt_ot_var_name(scope, prefix, name, var_name, sizeof(var_name), err);
+	if (retval == -1)
+		FLT_OT_RETURN(retval);
+
+	(void)memset(&smp, 0, sizeof(smp));
+	(void)smp_set_owner(&smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
+
+	if (vars_get_by_name(var_name, retval, &smp)) {
+		retval = flt_ot_sample_to_str(&(smp.data), var_value, sizeof(var_value), err);
+		if (retval != -1)
+			FLT_OT_DBG(3, "data type %d: '%s' = '%s'", smp.data.type, var_name, var_value);
+	} else {
+		FLT_OT_ERR("failed to get variable '%s'", var_name);
+
+		retval = -1;
+	}
+
+	FLT_OT_RETURN(retval);
+}
+
+
+/***
+ * NAME
+ *   flt_ot_vars_get -
+ *
+ * ARGUMENTS
+ *   s      -
+ *   scope  -
+ *   prefix -
+ *   opt    -
+ *   err    -
+ *
+ * DESCRIPTION
+ *   -
+ *
+ * RETURN VALUE
+ *   -
+ */
+struct otc_text_map *flt_ot_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err)
+{
+	struct vars         *vars;
+	const struct var    *var;
+	char                 var_name[BUFSIZ], ot_var_name[BUFSIZ];
+	int                  rc, i;
+	struct otc_text_map *retptr = NULL;
+
+	FLT_OT_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, opt, FLT_OT_DPTR_ARGS(err));
+
+	vars = flt_ot_get_vars(s, scope);
+	if (vars == NULL)
+		FLT_OT_RETURN(retptr);
+
+	rc = flt_ot_var_name(NULL, prefix, NULL, var_name, sizeof(var_name), err);
+	if (rc == -1)
+		FLT_OT_RETURN(retptr);
+
+	HA_RWLOCK_RDLOCK(VARS_LOCK, &(vars->rwlock));
+	list_for_each_entry(var, &(vars->head), l) {
+		FLT_OT_DBG(3, "variable cmp '%s' '%s' %d", var_name, var->name, rc);
+
+		if (strncmp(var_name, var->name, rc) == 0) {
+			FLT_OT_DBG(2, "'%s.%s' -> '%.*s'", scope, var->name, (int)var->data.u.str.data, var->data.u.str.area);
+
+			if (retptr == NULL) {
+				retptr = otc_text_map_new(NULL, 8);
+				if (retptr == NULL) {
+					FLT_OT_ERR("failed to create data");
+
+					break;
+				}
+			}
+
+			/*
+			 * Eh, because the use of some characters is not allowed
+			 * in the variable name, the conversion of the replaced
+			 * characters to the original is performed here.
+			 */
+			for (i = 0; ; )
+				if (i >= (FLT_OT_TABLESIZE(ot_var_name) - 1)) {
+					FLT_OT_ERR("failed to reverse variable name, buffer too small");
+
+					otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+
+					break;
+				} else {
+					char ch = var->name[rc + i + 1];
+
+					if (ch == '\0')
+						break;
+					else if (ch == FLT_OT_VAR_CHAR_DASH)
+						ch = '-';
+					else if (ch == FLT_OT_VAR_CHAR_SPACE)
+						ch = ' ';
+
+					ot_var_name[i++] = ch;
+				}
+			ot_var_name[i] = '\0';
+
+			if (retptr == NULL) {
+				break;
+			}
+			else if (otc_text_map_add(retptr, ot_var_name, i, var->data.u.str.area, var->data.u.str.data, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1) {
+				FLT_OT_ERR("failed to add map data");
+
+				otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+
+				break;
+			}
+		}
+	}
+	HA_RWLOCK_RDUNLOCK(VARS_LOCK, &(vars->rwlock));
+
+	ot_text_map_show(retptr);
+
+	if ((retptr != NULL) && (retptr->count == 0)) {
+		FLT_OT_DBG(2, "WARNING: no variables found");
+
+		otc_text_map_destroy(&retptr, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
+	}
+
+	FLT_OT_RETURN(retptr);
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */