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/Makefile b/Makefile
index feb8010..95d52ca 100644
--- a/Makefile
+++ b/Makefile
@@ -53,6 +53,7 @@
 #   USE_SYSTEMD          : enable sd_notify() support.
 #   USE_OBSOLETE_LINKER  : use when the linker fails to emit __start_init/__stop_init
 #   USE_THREAD_DUMP      : use the more advanced thread state dump system. Automatic.
+#   USE_OT               : enable the OpenTracing filter
 #
 # Options can be forced by specifying "USE_xxx=1" or can be disabled by using
 # "USE_xxx=" (empty string). The list of enabled and disabled options for a
@@ -102,6 +103,10 @@
 #   LUA_INC        : force the include path to lua
 #   LUA_LIB_NAME   : force the lib name (or automatically evaluated, by order of
 #                                        priority : lua5.3, lua53, lua).
+#   OT_DEBUG       : compile the OpenTracing filter in debug mode
+#   OT_INC         : force the include path to libopentracing-c-wrapper
+#   OT_LIB         : force the lib path to libopentracing-c-wrapper
+#   OT_RUNPATH     : add RUNPATH for libopentracing-c-wrapper to haproxy executable
 #   IGNOREGIT      : ignore GIT commit versions if set.
 #   VERSION        : force haproxy version reporting.
 #   SUBVERS        : add a sub-version (eg: platform, model, ...).
@@ -296,7 +301,7 @@
            USE_GETADDRINFO USE_OPENSSL USE_LUA USE_FUTEX USE_ACCEPT4          \
            USE_CLOSEFROM USE_ZLIB USE_SLZ USE_CPU_AFFINITY USE_TFO USE_NS     \
            USE_DL USE_RT USE_DEVICEATLAS USE_51DEGREES USE_WURFL USE_SYSTEMD  \
-           USE_OBSOLETE_LINKER USE_PRCTL USE_THREAD_DUMP USE_EVPORTS
+           USE_OBSOLETE_LINKER USE_PRCTL USE_THREAD_DUMP USE_EVPORTS USE_OT
 
 #### Target system options
 # Depending on the target platform, some options are set, as well as some
@@ -759,6 +764,10 @@
 OPTIONS_OBJS  += src/namespace.o
 endif
 
+ifneq ($(USE_OT),)
+include contrib/opentracing/Makefile
+endif
+
 #### Global link options
 # These options are added at the end of the "ld" command line. Use LDFLAGS to
 # add options at the beginning of the "ld" command line if needed.
diff --git a/contrib/opentracing/AUTHORS b/contrib/opentracing/AUTHORS
new file mode 100644
index 0000000..92b2831
--- /dev/null
+++ b/contrib/opentracing/AUTHORS
@@ -0,0 +1 @@
+Miroslav Zagorac <mzagorac@haproxy.com>
diff --git a/contrib/opentracing/MAINTAINERS b/contrib/opentracing/MAINTAINERS
new file mode 100644
index 0000000..92b2831
--- /dev/null
+++ b/contrib/opentracing/MAINTAINERS
@@ -0,0 +1 @@
+Miroslav Zagorac <mzagorac@haproxy.com>
diff --git a/contrib/opentracing/Makefile b/contrib/opentracing/Makefile
new file mode 100644
index 0000000..065f6f4
--- /dev/null
+++ b/contrib/opentracing/Makefile
@@ -0,0 +1,61 @@
+# USE_OT     : enable the OpenTracing filter
+# OT_DEBUG   : compile the OpenTracing filter in debug mode
+# OT_INC     : force the include path to libopentracing-c-wrapper
+# OT_LIB     : force the lib path to libopentracing-c-wrapper
+# OT_RUNPATH : add libopentracing-c-wrapper RUNPATH to haproxy executable
+
+OT_DEFINE    =
+OT_CFLAGS    =
+OT_LDFLAGS   =
+OT_DEBUG_EXT =
+OTC_WRAPPER  = opentracing-c-wrapper
+
+ifneq ($(OT_DEBUG),)
+OT_DEBUG_EXT = _dbg
+OT_DEFINE    = -DDEBUG_OT
+endif
+
+ifeq ($(OT_INC),)
+OT_CFLAGS = $(shell pkg-config --silence-errors --cflags $(OTC_WRAPPER)$(OT_DEBUG_EXT))
+else
+ifneq ($(wildcard $(OT_INC)/$(OTC_WRAPPER)/.*),)
+OT_CFLAGS = -I$(OT_INC) $(if $(OT_DEBUG),-DOTC_DBG_MEM)
+endif
+endif
+
+ifeq ($(OT_CFLAGS),)
+$(error OpenTracing C wrapper : can't find headers)
+endif
+
+ifeq ($(OT_LIB),)
+OT_LDFLAGS = $(shell pkg-config --silence-errors --libs $(OTC_WRAPPER)$(OT_DEBUG_EXT))
+else
+ifneq ($(wildcard $(OT_LIB)/lib$(OTC_WRAPPER).*),)
+OT_LDFLAGS = -L$(OT_LIB) -l$(OTC_WRAPPER)$(OT_DEBUG_EXT)
+ifneq ($(OT_RUNPATH),)
+OT_LDFLAGS += -Wl,--rpath,$(OT_LIB)
+endif
+endif
+endif
+
+ifeq ($(OT_LDFLAGS),)
+$(error OpenTracing C wrapper : can't find library)
+endif
+
+OPTIONS_OBJS += \
+	contrib/opentracing/src/cli.o         \
+	contrib/opentracing/src/conf.o        \
+	contrib/opentracing/src/event.o       \
+	contrib/opentracing/src/filter.o      \
+	contrib/opentracing/src/group.o       \
+	contrib/opentracing/src/http.o        \
+	contrib/opentracing/src/opentracing.o \
+	contrib/opentracing/src/parser.o      \
+	contrib/opentracing/src/pool.o        \
+	contrib/opentracing/src/scope.o       \
+	contrib/opentracing/src/util.o        \
+	contrib/opentracing/src/vars.o
+
+OPTIONS_CFLAGS  += $(OT_CFLAGS) -Icontrib/opentracing/include
+OPTIONS_LDFLAGS += $(OT_LDFLAGS)
+OPTIONS_CFLAGS  += $(OT_DEFINE)
diff --git a/contrib/opentracing/README b/contrib/opentracing/README
new file mode 100644
index 0000000..a08f471
--- /dev/null
+++ b/contrib/opentracing/README
@@ -0,0 +1,794 @@
+                   -----------------------------------------
+                      The HAProxy OpenTracing filter (OT)
+                                  Version 1.0
+                          ( Last update: 2020-12-10 )
+                   -----------------------------------------
+                           Author : Miroslav Zagorac
+                     Contact : mzagorac at haproxy dot com
+
+
+SUMMARY
+--------
+
+  0.    Terms
+  1.    Introduction
+  2.    Build instructions
+  3.    Basic concepts in OpenTracing
+  4.    OT configuration
+  4.1.    OT scope
+  4.2.    "ot-tracer" section
+  4.3.    "ot-scope" section
+  4.4.    "ot-group" section
+  5.    Examples
+  5.1     Benchmarking results
+  6.    OT CLI
+  7.    Known bugs and limitations
+
+
+0. Terms
+---------
+
+* OT: The HAProxy OpenTracing filter
+
+  OT is the HAProxy filter that allows you to send data to distributed
+  tracing systems via the OpenTracing API.
+
+
+1. Introduction
+----------------
+
+Nowadays there is a growing need to divide a process into microservices and
+there is a problem of monitoring the work of the same process.  One way to
+solve this problem is to use distributed tracing service in a central location,
+the so-called tracer.
+
+OT is a feature introduced in HAProxy 2.4.  This filter enables communication
+via the OpenTracing API with OpenTracing compatible servers (tracers).
+Currently, tracers that support this API include Datadog, Jaeger, LightStep
+and Zipkin.
+
+The OT filter was primarily tested with the Jaeger tracer, while configurations
+for both Datadog and Zipkin tracers were also set in the test directory.
+
+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% (provided that no more than 10% of requests are
+sent to the tracer, which is determined by the keyword 'rate-limit').
+
+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.
+
+
+2. Build instructions
+----------------------
+
+OT is the HAProxy filter and as such is compiled together with HAProxy.
+
+To communicate with some OpenTracing compatible tracer, the OT filter uses the
+OpenTracing C Wrapper library (which again uses the OpenTracing CPP library).
+This means that we must have both libraries installed on the system on which
+we want to compile or use HAProxy.
+
+Instructions for compiling and installing both required libraries can be
+found at https://github.com/haproxytech/opentracing-c-wrapper .
+
+Also, to use the OT filter when running HAProxy we need to have an OpenTracing
+plugin for the tracer we want to use.  We will return to this later, in
+section 5.
+
+The OT filter can be more easily compiled using the pkg-config tool, if we
+have the OpenTracing C Wrapper library installed so that it contains pkg-config
+files (which have the .pc extension).  If the pkg-config tool cannot be used,
+then the path to the directory where the include files and libraries are
+located can be explicitly specified.
+
+Below are examples of the two ways to compile HAProxy with the OT filter, the
+first using the pkg-congfig tool and the second explicitly specifying the path
+to the OpenTracing C Wrapper include and library.
+
+Note: prompt '%' indicates that the command is executed under a unprivileged
+      user, while prompt '#' indicates that the command is executed under the
+      root user.
+
+Example of compiling HAProxy using the pkg-congfig tool (assuming the
+OpenTracing C Wrapper library is installed in the /opt directory):
+
+  % PKG_CONFIG_PATH=/opt/lib/pkgconfig make USE_OT=1 TARGET=linux-glibc
+
+The OT filter can also be compiled in debug mode as follows:
+
+  % PKG_CONFIG_PATH=/opt/lib/pkgconfig make USE_OT=1 OT_DEBUG=1 TARGET=linux-glibc
+
+HAProxy compilation example explicitly specifying path to the OpenTracing C
+Wrapper include and library:
+
+  % make USE_OT=1 OT_INC=/opt/include OT_LIB=/opt/lib TARGET=linux-glibc
+
+In case we want to use debug mode, then it looks like this:
+
+  % make USE_OT=1 OT_DEBUG=1 OT_INC=/opt/include OT_LIB=/opt/lib TARGET=linux-glibc
+
+If the library we want to use is not installed on a unix system, then a locally
+installed library can be used (say, which is compiled and installed in the user
+home directory).  In this case instead of /opt/include and /opt/lib the
+equivalent paths to the local installation should be specified.  Of course,
+in that case the pkg-config tool can also be used if we have a complete
+installation (with .pc files).
+
+last but not least, if the pkg-config tool is not used when compiling, then
+HAProxy executable may not be able to find the OpenTracing C Wrapper library
+at startup.  This can be solved in several ways, for example using the
+LD_LIBRARY_PATH environment variable which should be set to the path where the
+library is located before starting the HAProxy.
+
+  % LD_LIBRARY_PATH=/opt/lib /path-to/haproxy ...
+
+Another way is to add RUNPATH to HAProxy executable that contains the path to
+the library in question.
+
+  % make USE_OT=1 OT_RUNPATH=1 OT_INC=/opt/include OT_LIB=/opt/lib TARGET=linux-glibc
+
+After HAProxy is compiled, we can check if the OT filter is enabled:
+
+  % ./haproxy -vv | grep opentracing
+  --- command output ----------
+          [  OT] opentracing
+  --- command output ----------
+
+
+3. Basic concepts in OpenTracing
+---------------------------------
+
+Basic concepts of OpenTracing can be read on the OpenTracing documentation
+website https://opentracing.io/docs/overview/.
+
+Here we will list only the most important elements of distributed tracing and
+these are 'trace', 'span' and 'span context'.  Trace is a description of the
+complete transaction we want to record in the tracing system.  A span is an
+operation that represents a unit of work that is recorded in a tracing system.
+Span context is a group of information related to a particular span that is
+passed on to the system (from service to service).  Using this context, we can
+add new spans to already open trace (or supplement data in already open spans).
+
+An individual span may contain one or more tags, logs and baggage items.
+The tag is a key-value element that is valid for the entire span.  Log is a
+key-value element that allows you to write some data at a certain time, it
+can be used for debugging.  A baggage item is a key-value data pair that can
+be used for the duration of an entire trace, from the moment it is added to
+the span.
+
+
+4. OT configuration
+--------------------
+
+In order for the OT filter to be used, it must be included in the HAProxy
+configuration, in the proxy section (frontend / listen / backend):
+
+   frontend ot-test
+     ...
+     filter opentracing [id <id>] config <file>
+     ...
+
+If no filter id is specified, 'ot-filter' is used as default.  The 'config'
+parameter must be specified and it contains the path of the file used to
+configure the OT filter.
+
+
+4.1 OT scope
+-------------
+
+If the filter id is defined for the OT filter, then the OT scope with
+the same name should be defined in the configuration file.  In the same
+configuration file we can have several defined OT scopes.
+
+Each OT scope must have a defined (only one) "ot-tracer" section that is
+used to configure the operation of the OT filter and define the used groups
+and scopes.
+
+OT scope starts with the id of the filter specified in square brackets and
+ends with the end of the file or when a new OT scope is defined.
+
+For example, this defines two OT scopes in the same configuration file:
+  [my-first-ot-filter]
+    ot-tracer tracer1
+    ...
+    ot-group group1
+    ...
+    ot-scope scope1
+    ...
+
+  [my-second-ot-filter]
+    ...
+
+
+4.2. "ot-tracer" section
+-------------------------
+
+Only one "ot-tracer" section must be defined for each OT scope.
+
+There are several keywords that must be defined for the OT filter to work.
+These are 'config' which defines the configuration file for the OpenTracing
+API, and 'plugin' which defines the OpenTracing plugin used.
+
+Through optional keywords can be defined ACLs, logging, rate limit, and groups
+and scopes that define the tracing model.
+
+
+ot-tracer <name>
+  A new OT with the name <name> is created.
+
+  Arguments :
+    name - the name of the tracer section
+
+
+  The following keywords are supported in this section:
+    - mandatory keywords:
+      - config
+      - plugin
+
+    - optional keywords:
+      - acl
+      - debug-level
+      - groups
+      - [no] log
+      - [no] option disabled
+      - [no] option dontlog-normal
+      - [no] option hard-errors
+      - rate-limit
+      - scopes
+
+
+acl <aclname> <criterion> [flags] [operator] <value> ...
+  Declare or complete an access list.
+
+  To configure and use the ACL, see section 7 of the HAProxy Configuration
+  Manual.
+
+
+config <file>
+  'config' is one of the two mandatory keywords associated with the OT tracer
+  configuration.  This keyword sets the path of the configuration file for the
+  OpenTracing tracer plugin.  To set the contents of this configuration file,
+  it is best to look at the documentation related to the OpenTracing tracer we
+  want to use.
+
+  Arguments :
+    file - the path of the configuration file
+
+
+debug-level <value>
+  This keyword sets the value of the debug level related to the display of
+  debug messages in the OT filter.  The 'debug-level' value is binary, ie
+  a single value bit enables or disables the display of the corresponding
+  debug message that uses that bit.  The default value is set via the
+  FLT_OT_DEBUG_LEVEL macro in the include/config.h file.  Debug level value
+  is used only if the OT filter is compiled with the debug mode enabled,
+  otherwise it is ignored.
+
+  Arguments :
+    value - binary value ranging from 0 to 255 (8 bits)
+
+
+groups <name> ...
+  A list of "ot-group" groups used for the currently defined tracer is declared.
+  Several groups can be specified in one line.
+
+  Arguments :
+    name - the name of the OT group
+
+
+log global
+log <addr> [len <len>] [format <fmt>] <facility> [<level> [<minlevel>]]
+no log
+  Enable per-instance logging of events and traffic.
+
+  To configure and use the logging system, see section 4.2 of the HAProxy
+  Configuration Manual.
+
+
+option disabled
+no option disabled
+  Keyword which turns the operation of the OT filter on or off.  By default
+  the filter is on.
+
+
+option dontlog-normal
+no option dontlog-normal
+  Enable or disable logging of normal, successful processing.  By default,
+  this option is disabled.  For this option to be considered, logging must
+  be turned on.
+
+  See also: 'log' keyword description.
+
+
+option hard-errors
+no option hard-errors
+  During the operation of the filter, some errors may occur, caused by
+  incorrect configuration of the tracer or some error related to the operation
+  of HAProxy.  By default, such an error will not interrupt the filter
+  operation for the stream in which the error occurred.  If the 'hard-error'
+  option is enabled, the operation error prohibits all further processing of
+  events and groups in the stream in which the error occurred.
+
+
+plugin <file>
+  'plugin' is one of the two mandatory keywords associated with the OT tracer
+  configuration.  This keyword sets the path of the OpenTracing tracer plugin.
+
+  Arguments :
+    file - the name of the plugin used
+
+
+rate-limit <value>
+  This option allows limiting the use of the OT filter, ie it can be influenced
+  whether the OT filter is activated for a stream or not.  Determining whether
+  or not a filter is activated depends on the value of this option that is
+  compared to a randomly selected value when attaching the filter to the stream.
+  By default, the value of this option is set to 100.0, ie the OT filter is
+  activated for each stream.
+
+  Arguments :
+    value - floating point value ranging from 0.0 to 100.0
+
+
+scopes <name> ...
+  This keyword declares a list of "ot-scope" definitions used for the currently
+  defined tracer.  Multiple scopes can be specified in the same line.
+
+  Arguments :
+    name - the name of the OT scope
+
+
+4.3. "ot-scope" section
+------------------------
+
+Stream processing begins with filter attachment, then continues with the
+processing of a number of defined events and groups, and ends with filter
+detachment.  The "ot-scope" section is used to define actions related to
+individual events.  However, this section may be part of a group, so the
+event does not have to be part of the definition.
+
+
+ot-scope <name>
+  Creates a new OT scope definition named <name>.
+
+  Arguments :
+    name - the name of the OT scope
+
+
+  The following keywords are supported in this section:
+    - acl
+    - baggage
+    - event
+    - extract
+    - finish
+    - inject
+    - log
+    - span
+    - tag
+
+
+acl <aclname> <criterion> [flags] [operator] <value> ...
+  Declare or complete an access list.
+
+  To configure and use the ACL, see section 7 of the HAProxy Configuration
+  Manual.
+
+
+baggage <name> <sample> ...
+  Baggage items allow the propagation of data between spans, ie allow the
+  assignment of metadata that is propagated to future children spans.
+  This data is formatted in the style of key-value pairs and is part of
+  the context that can be transferred between processes that are part of
+  a server architecture.
+
+  This kewyord allows setting the baggage for the currently active span.  The
+  data type is always a string, ie any sample type is converted to a string.
+  The exception is a binary value that is not supported by the OT filter.
+
+  See the 'tag' keyword description for the data type conversion table.
+
+  Arguments :
+    name   - key part of a data pair
+    sample - sample expression (value part of a data pair), at least one
+             sample must be present
+
+
+event <name> [{ if | unless } <condition>]
+  Set the event that triggers the 'ot-scope' to which it is assigned.
+  Optionally, it can be followed by an ACL-based condition, in which case it
+  will only be evaluated if the condition is true.
+
+  ACL-based conditions are executed in the context of a stream that processes
+  the client and server connections.  To configure and use the ACL, see
+  section 7 of the HAProxy Configuration Manual.
+
+  Arguments :
+    name      - the event name
+    condition - a standard ACL-based condition
+
+  Supported events are (the table gives the names of the events in the OT
+  filter and the corresponding equivalent in the SPOE filter):
+
+    -------------------------------------|------------------------------
+      the OT filter                      |  the SPOE filter
+    -------------------------------------|------------------------------
+      on-client-session-start            |  on-client-session
+      on-frontend-tcp-request            |  on-frontend-tcp-request
+      on-http-wait-request               |  -
+      on-http-body-request               |  -
+      on-frontend-http-request           |  on-frontend-http-request
+      on-switching-rules-request         |  -
+      on-backend-tcp-request             |  on-backend-tcp-request
+      on-backend-http-request            |  on-backend-http-request
+      on-process-server-rules-request    |  -
+      on-http-process-request            |  -
+      on-tcp-rdp-cookie-request          |  -
+      on-process-sticking-rules-request  |  -
+      on-client-session-end              |  -
+      on-server-unavailable              |  -
+    -------------------------------------|------------------------------
+      on-server-session-start            |  on-server-session
+      on-tcp-response                    |  on-tcp-response
+      on-http-wait-response              |  -
+      on-process-store-rules-response    |  -
+      on-http-response                   |  on-http-response
+      on-server-session-end              |  -
+    -------------------------------------|------------------------------
+
+
+extract <name-prefix> [use-vars | use-headers]
+  For a more detailed description of the propagation process of the span
+  context, see the description of the keyword 'inject'.  Only the process
+  of extracting data from the carrier is described here.
+
+  Arguments :
+    name-prefix - data name prefix (ie key element prefix)
+    use-vars    - data is extracted from HAProxy variables
+    use-headers - data is extracted from the HTTP header
+
+
+  Below is an example of using HAProxy variables to transfer span context data:
+
+  --- test/ctx/ot.cfg --------------------------------------------------------
+      ...
+      ot-scope client_session_start_2
+          extract "ot_ctx_1" use-vars
+          span "Client session" child-of "ot_ctx_1"
+      ...
+  ----------------------------------------------------------------------------
+
+
+finish <name> ...
+  Closing a particular span or span context.  Instead of the name of the span,
+  there are several specially predefined names with which we can finish certain
+  groups of spans.  So it can be used as the name '*req*' for all open spans
+  related to the request channel, '*res*' for all open spans related to the
+  response channel and '*' for all open spans regardless of which channel they
+  are related to.  Several spans and/or span contexts can be specified in one
+  line.
+
+  Arguments :
+    name - the name of the span or context context
+
+
+inject <name-prefix> [use-vars] [use-headers]
+  In OpenTracing, the transfer of data related to the tracing process between
+  microservices that are part of a larger service is done through the
+  propagation of the span context.  The basic operations that allow us to
+  access and transfer this data are 'inject' and 'extract'.
+
+  'inject' allows us to extract span context so that the obtained data can
+  be forwarded to another process (microservice) via the selected carrier.
+  'inject' in the name actually means inject data into carrier.  Carrier is
+  an interface here (ie a data structure) that allows us to transfer tracing
+  state from one process to another.
+
+  Data transfer can take place via one of two selected storage methods, the
+  first is by adding data to the HTTP header and the second is by using HAProxy
+  variables.  Only data transfer via HTTP header can be used to transfer data
+  to another process (ie microservice).  All data is organized in the form of
+  key-value data pairs.
+
+  No matter which data transfer method you use, we need to specify a prefix
+  for the key element.  All alphanumerics (lowercase only) and underline
+  character can be used to construct the data name prefix.  Uppercase letters
+  can actually be used, but they will be converted to lowercase when creating
+  the prefix.
+
+  Arguments :
+    name-prefix - data name prefix (ie key element prefix)
+    use-vars    - HAProxy variables are used to store and transfer data
+    use-headers - HTTP headers are used to store and transfer data
+
+
+  Below is an example of using HTTP headers and variables, and how this is
+  reflected in the internal data of the HAProxy process.
+
+  --- test/ctx/ot.cfg --------------------------------------------------------
+      ...
+      ot-scope client_session_start_1
+          span "HAProxy session" root
+              inject "ot_ctx_1" use-headers use-vars
+      ...
+  ----------------------------------------------------------------------------
+
+    - generated HAProxy variable (key -> value):
+      txn.ot_ctx_1.uberDtraceDid -> 8f1a05a3518d2283:8f1a05a3518d2283:0:1
+
+    - generated HTTP header (key: value):
+      ot_ctx_1-uber-trace-id: 8f1a05a3518d2283:8f1a05a3518d2283:0:1
+
+  Because HAProxy does not allow the '-' character in the variable name (which
+  is automatically generated by the OpenTracing API and on which we have no
+  influence), it is converted to the letter 'D'.  We can see that there is no
+  such conversion in the name of the HTTP header because the '-' sign is allowed
+  there.  Due to this conversion, initially all uppercase letters are converted
+  to lowercase because otherwise we would not be able to distinguish whether
+  the disputed sign '-' is used or not.
+
+  Thus created HTTP headers and variables are deleted when executing the
+  'finish' keyword or when detaching the stream from the filter.
+
+
+log <name> <sample> ...
+  This kewyord allows setting the log for the currently active span.  The
+  data type is always a string, ie any sample type is converted to a string.
+  The exception is a binary value that is not supported by the OT filter.
+
+  See the 'tag' keyword description for the data type conversion table.
+
+  Arguments :
+    name   - key part of a data pair
+    sample - sample expression (value part of a data pair), at least one
+             sample must be present
+
+
+span <name> [<reference>]
+  Creating a new span (or referencing an already opened one).  If a new span
+  is created, it can be a child of the referenced span, follow from the
+  referenced span, or be root 'span'.  In case we did not specify a reference
+  to the previously created span, the new span will become the root span.
+  We need to pay attention to the fact that in one trace there can be only
+  one root span.  In case we have specified a non-existent span as a reference,
+  a new span will not be created.
+
+  Arguments :
+    name      - the name of the span being created or referenced (operation
+                name)
+    reference - span or span context to which the created span is referenced
+
+
+tag <name> <sample> ...
+  This kewyord allows setting a tag for the currently active span.  The first
+  argument is the name of the tag (tag ID) and the second its value.  A value
+  can consist of one or more data.  If the value is only one data, then the
+  type of that data depends on the type of the HAProxy sample.  If the value
+  contains more data, then the data type is string.  The data conversion table
+  is below:
+
+   HAProxy sample data type | the OpenTracing data type
+  --------------------------+---------------------------
+            NULL            |        NULL
+            BOOL            |        BOOL
+            INT32           |        INT64
+            UINT32          |        UINT64
+            INT64           |        INT64
+            UINT64          |        UINT64
+            IPV4            |        STRING
+            IPV6            |        STRING
+            STRING          |        STRING
+            BINARY          |        UNSUPPORTED
+  --------------------------+---------------------------
+
+  Arguments :
+    name   - key part of a data pair
+    sample - sample expression (value part of a data pair), at least one
+             sample must be present
+
+
+4.4. "ot-group" section
+------------------------
+
+This section allows us to define a group of OT scopes, that is not activated
+via an event but is triggered from TCP or HTTP rules.  More precisely, these
+are the following rules: 'tcp-request', 'tcp-response', 'http-request',
+'http-response' and 'http-after-response'.  These rules can be defined in the
+HAProxy configuration file.
+
+
+ot-group <name>
+  Creates a new OT group definition named <name>.
+
+  Arguments :
+    name - the name of the OT group
+
+
+  The following keywords are supported in this section:
+    - scopes
+
+
+scopes <name> ...
+  'ot-scope' sections that are part of the specified group are defined.  If
+  the mentioned 'ot-scope' sections are used only in some OT group, they do
+  not have to have defined events.  Several 'ot-scope' sections can be
+  specified in one line.
+
+  Arguments :
+    name - the name of the 'ot-scope' section
+
+
+5. Examples
+------------
+
+Several examples of the OT filter configuration can be found in the test
+directory.  A brief description of the prepared configurations follows:
+
+cmp   - the configuration very similar to that of the spoa-opentracing project.
+        It was made to compare the speed of the OT filter with the
+        implementation of distributed tracing via spoa-opentracing application.
+
+sa    - the configuration in which all possible events are used.
+
+ctx   - the configuration is very similar to the previous one, with the only
+        difference that the spans are opened using the span context as a span
+        reference.
+
+fe be - a slightly more complicated example of the OT filter configuration
+        that uses two cascaded HAProxy services.  The span context between
+        HAProxy processes is transmitted via the HTTP header.
+
+empty - the empty configuration in which the OT filter is initialized but
+        no event is triggered.  It is not very usable, except to check the
+        behavior of the OT filter in the case of a similar configuration.
+
+
+In order to be able to collect data (and view results via the web interface)
+we need to install some of the supported tracers.  We will use the Jaeger
+tracer as an example.  Installation instructions can be found on the website
+https://www.jaegertracing.io/download/.  For the impatient, here we will list
+how the image to test the operation of the tracer system can be installed
+without much reading of the documentation.
+
+  # docker pull jaegertracing/all-in-one:latest
+  # docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
+    -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 \
+    -p 16686:16686 -p 14268:14268 -p 9411:9411 jaegertracing/all-in-one:latest
+
+The last command will also initialize and run the Jaeger container.  If we
+want to use that container later, it can be started and stopped in the classic
+way, using the 'docker container start/stop' commands.
+
+
+In order to be able to use any of the configurations from the test directory,
+we must also have a tracer plugin in that directory (all examples use the
+Jaeger tracer plugin).  The simplest way is to download the tracer plugin
+using the already prepared shell script get-opentracing-plugins.sh.
+The script accepts one argument, the directory in which the download is made.
+If run without an argument, the script downloads all plugins to the current
+directory.
+
+  % ./get-opentracing-plugins.sh
+
+After that, we can run one of the pre-configured configurations using the
+provided script run-xxx.sh (where xxx is the name of the configuration being
+tested).  For example:
+
+  % ./run-sa.sh
+
+The script will create a new log file each time it is run (because part of the
+log file name is the start time of the script).
+
+Eh, someone will surely notice that all test configurations use the Jaeger
+tracing plugin that cannot be downloaded using the get-opentracing-plugins.sh
+script.  Unfortunately, the latest precompiled version that can be downloaded
+is 0.4.2, for newer ones only the source code can be found.  Version 0.4.2 has
+a bug that can cause the operation of the OT filter to get stuck, so it is
+better not to use this version.  Here is the procedure by which we can compile
+a newer version of the plugin (in our example it is 0.5.0).
+
+Important note: the GCC version must be at least 4.9 or later.
+
+  % wget https://github.com/jaegertracing/jaeger-client-cpp/archive/v0.5.0.tar.gz
+  % tar xf v0.5.0.tar.gz
+  % cd jaeger-client-cpp-0.5.0
+  % mkdir build
+  % cd build
+  % cmake -DCMAKE_INSTALL_PREFIX=/opt -DJAEGERTRACING_PLUGIN=ON -DHUNTER_CONFIGURATION_TYPES=Release -DHUNTER_BUILD_SHARED_LIBS=OFF ..
+  % make
+
+After the plugin is compiled, it will be in the current directory.  The name
+of the plugin is libjaegertracing_plugin.so.
+
+
+5.1. Benchmarking results
+--------------------------
+
+To check the operation of the OT filter, several different test configurations
+have been made which are located in the test directory.  The test results of
+the same configurations (with the names README-speed-xxx, where xxx is the name
+of the configuration being tested) are also in the directory of the same name.
+
+All tests were performed on the same debian 9.13 system, CPU i7-4770, 32 GB RAM.
+For the purpose of testing, the thttpd web server on port 8000 was used.
+Testing was done with the wrk utility running via run-xxx.sh scripts; that is,
+via the test-speed.sh script that is run as follows:
+
+  % ./test-speed.sh all
+
+The above mentioned thttpd web server is run from that script and it should be
+noted that we need to have the same installed on the system (or change the path
+to the thttpd server in that script if it is installed elsewhere).
+
+Each test is performed several times over a period of 5 minutes per individual
+test.  The only difference when running the tests for the same configuration
+was in changing the 'rate-limit' parameter (and the 'option disabled' option),
+which is set to the following values: 100.0, 50.0, 10.0, 2.5 and 0.0 percent.
+Then a test is performed with the OT filter active but disabled for request
+processing ('option disabled' is included in the ot.cfg configuration).  In
+the last test, the OT filter is not used at all, ie it is not active and does
+not affect the operation of HAProxy in any way.
+
+
+6. OT CLI
+----------
+
+Via the HAProxy CLI interface we can find out the current status of the OT
+filter and change several of its settings.
+
+All supported CLI commands can be found in the following way, using the
+socat utility with the assumption that the HAProxy CLI socket path is set
+to /tmp/haproxy.sock (of course, instead of socat, nc or other utility can
+be used with a change in arguments when running the same):
+
+  % echo "help" | socat - UNIX-CONNECT:/tmp/haproxy.sock | grep flt-ot
+  --- command output ----------
+  flt-ot debug [level]   : set the OT filter debug level (default: get current debug level)
+  flt-ot disable         : disable the OT filter
+  flt-ot enable          : enable the OT filter
+  flt-ot soft-errors     : turning off hard-errors mode
+  flt-ot hard-errors     : enabling hard-errors mode
+  flt-ot logging [state] : set logging state (default: get current logging state)
+  flt-ot rate [value]    : set the rate limit (default: get current rate value)
+  flt-ot status          : show the OT filter status
+  --- command output ----------
+
+'flt-ot debug' can only be used in case the OT filter is compiled with the
+debug mode enabled.
+
+
+7. Known bugs and limitations
+------------------------------
+
+The name of the span context definition can contain only letters, numbers and
+characters '_' and '-'.  Also, all uppercase letters in the name are converted
+to lowercase.  The character '-' is converted internally to the 'D' character,
+and since a HAProxy variable is generated from that name, this should be taken
+into account if we want to use it somewhere in the HAProxy configuration.
+The above mentioned span context is used in the 'inject' and 'extract' keywords.
+
+Let's look a little at the example test/fe-be (configurations are in the
+test/fe and test/be directories, 'fe' is here the abbreviation for frontend
+and 'be' for backend).  In case we have the 'rate-limit' set to a value less
+than 100.0, then distributed tracing will not be started with each new HTTP
+request.  It also means that the span context will not be delivered (via the
+HTTP header) to the backend HAProxy process.  The 'rate-limit' on the backend
+HAProxy must be set to 100.0, but because the frontend HAProxy does not send
+a span context every time, all such cases will cause an error to be reported
+on the backend server.  Therefore, the 'hard-errors' option must be set on the
+backend server, so that processing on that stream is stopped as soon as the
+first error occurs.  Such cases will slow down the backend server's response
+a bit (in the example in question it is about 3%).
diff --git a/contrib/opentracing/README-func b/contrib/opentracing/README-func
new file mode 100644
index 0000000..273c7f9
--- /dev/null
+++ b/contrib/opentracing/README-func
@@ -0,0 +1,298 @@
+Here I will write down some specifics of certain parts of the source, these are
+just some of my thoughts and clues and they are probably not too important for
+a wider audience.
+
+src/parser.c
+------------------------------------------------------------------------------
+The first thing to run when starting the HAProxy is the flt_ot_parse() function
+which actually parses the filter configuration.
+
+In case of correct configuration, the function returns ERR_NONE (or 0), while
+in case of incorrect configuration it returns the combination of ERR_* flags
+(ERR_NONE here does not belong to that bit combination because its value is 0).
+
+One of the parameters of the function is <char **err> in which an error message
+can be returned, if it exists.  In that case the return value of the function
+should have some of the ERR_* flags set.
+
+Let's look at an example of the following filter configuration what the function
+call sequence looks like.
+
+Filter configuration line:
+   filter opentracing [id <id>] config <file>
+
+Function call sequence:
+   flt_ot_parse(<err>) {
+      /* Initialization of the filter configuration data. */
+      flt_ot_conf_init() {
+      }
+
+      /* Setting the filter name. */
+      flt_ot_parse_keyword(<err>) {
+         flt_ot_parse_strdup(<err>) {
+         }
+      }
+
+      /* Setting the filter configuration file name. */
+      flt_ot_parse_keyword(<err>) {
+         flt_ot_parse_strdup(<err>) {
+         }
+      }
+
+      /* Checking the configuration of the filter. */
+      flt_ot_parse_cfg(<err>) {
+         flt_ot_parse_cfg_tracer() {
+         }
+         ...
+         flt_ot_post_parse_cfg_tracer() {
+         }
+         flt_ot_parse_cfg_group() {
+         }
+         ...
+         flt_ot_post_parse_cfg_group() {
+         }
+         flt_ot_parse_cfg_scope() {
+         }
+         ...
+         flt_ot_post_parse_cfg_scope() {
+         }
+      }
+   }
+
+Checking the filter configuration is actually much more complicated, only the
+name of the main function flt_ot_parse_cfg() that does it is listed here.
+
+All functions that use the <err> parameter should set the error status using
+that pointer.  All other functions (actually these are all functions called
+by the flt_ot_parse_cfg() function) should set the error message using the
+ha_warning()/ha_alert() HAProxy functions.  Of course, the return value (the
+mentioned combination of ERR_* bits) is set in all these functions and it
+indicates whether the filter configuration is correct or not.
+
+
+src/group.c
+------------------------------------------------------------------------------
+The OT filter allows the use of groups within which one or more 'ot-scope'
+declarations can be found.  These groups can be used using several HAProxy
+rules, more precisely 'http-request', 'http-response', 'tcp-request',
+'tcp-response' and 'http-after-response' rules.
+
+Configuration example for the specified rules:
+   <rule> ot-group <filter-id> <group-name> [ { if | unless } <condition> ]
+
+Parsing each of these rules is performed by the flt_ot_group_parse() function.
+After parsing the configuration, its verification is performed via the
+flt_ot_group_check() function.  One parsing function and one configuration
+check function are called for each defined rule.
+
+   flt_ot_group_parse(<err>) {
+   }
+   ...
+   flt_ot_group_check() {
+   }
+   ...
+
+
+When deinitializing the module, the function flt_ot_group_release() is called
+(which is actually an release_ptr callback function from one of the above
+rules).  One callback function is called for each defined rule.
+
+   flt_ot_group_release() {
+   }
+   ...
+
+
+src/filter.c
+------------------------------------------------------------------------------
+After parsing and checking the configuration, the flt_ot_check() function is
+called which associates the 'ot-group' and 'ot-scope' definitions with their
+declarations.  This procedure concludes the configuration of the OT filter and
+after that its initialization is possible.
+
+   flt_ops.check = flt_ot_check;
+   flt_ot_check() {
+   }
+
+
+The initialization of the OT filter is done via the flt_ot_init() callback
+function.  In this function the OpenTracing API library is also initialized.
+It is also possible to initialize for each thread individually, but nothing
+is being done here for now.
+
+   flt_ops.init = flt_ot_init;
+   flt_ot_init() {
+      flt_ot_cli_init() {
+      }
+      /* Initialization of the OpenTracing API. */
+      ot_init(<err>) {
+      }
+   }
+
+   flt_ops.init_per_thread = flt_ot_init_per_thread;
+   flt_ot_init_per_thread() {
+   }
+   ...
+
+
+After the filter instance is created and attached to the stream, the
+flt_ot_attach() function is called.  In this function a new OT runtime
+context is created, and flags are set that define which analyzers are used.
+
+   flt_ops.attach = flt_ot_attach;
+   flt_ot_attach() {
+      /* In case OT is disabled, nothing is done on this stream further. */
+      flt_ot_runtime_context_init(<err>) {
+         flt_ot_pool_alloc() {
+         }
+         /* Initializing and setting the variable 'sess.ot.uuid'. */
+         if (flt_ot_var_register(<err>) != -1) {
+            flt_ot_var_set(<err>) {
+            }
+         }
+      }
+   }
+
+
+When a stream is started, this function is called.  At the moment, nothing
+is being done in it.
+
+   flt_ops.stream_start = flt_ot_stream_start;
+   flt_ot_stream_start() {
+   }
+
+
+Channel analyzers are called when executing individual filter events.
+For each of the four analyzer functions, the events associated with them
+are listed.
+
+ Events:
+   -  1 'on-client-session-start'
+   - 15 'on-server-session-start'
+------------------------------------------------------------------------
+   flt_ops.channel_start_analyze = flt_ot_channel_start_analyze;
+   flt_ot_channel_start_analyze() {
+      flt_ot_event_run() {
+         /* Run event. */
+         flt_ot_scope_run() {
+            /* Processing of all ot-scopes defined for the current event. */
+         }
+      }
+   }
+
+
+ Events:
+   -  2 'on-frontend-tcp-request'
+   -  4 'on-http-body-request'
+   -  5 'on-frontend-http-request'
+   -  6 'on-switching-rules-request'
+   -  7 'on-backend-tcp-request'
+   -  8 'on-backend-http-request'
+   -  9 'on-process-server-rules-request'
+   - 10 'on-http-process-request'
+   - 11 'on-tcp-rdp-cookie-request'
+   - 12 'on-process-sticking-rules-request
+   - 16 'on-tcp-response'
+   - 18 'on-process-store-rules-response'
+   - 19 'on-http-response'
+------------------------------------------------------------------------
+   flt_ops.channel_pre_analyze = flt_ot_channel_pre_analyze;
+   flt_ot_channel_pre_analyze() {
+      flt_ot_event_run() {
+         /* Run event. */
+         flt_ot_scope_run() {
+            /* Processing of all ot-scopes defined for the current event. */
+         }
+      }
+   }
+
+
+ Events:
+   -  3 'on-http-wait-request'
+   - 17 'on-http-wait-response'
+------------------------------------------------------------------------
+   flt_ops.channel_post_analyze = flt_ot_channel_post_analyze;
+   flt_ot_channel_post_analyze() {
+      flt_ot_event_run() {
+         /* Run event. */
+         flt_ot_scope_run() {
+            /* Processing of all ot-scopes defined for the current event. */
+         }
+      }
+   }
+
+
+ Events:
+   - 13 'on-client-session-end'
+   - 14 'on-server-unavailable'
+   - 20 'on-server-session-end'
+------------------------------------------------------------------------
+   flt_ops.channel_end_analyze = flt_ot_channel_end_analyze;
+   flt_ot_channel_end_analyze() {
+      flt_ot_event_run() {
+         /* Run event. */
+         flt_ot_scope_run() {
+            /* Processing of all ot-scopes defined for the current event. */
+         }
+      }
+
+      /* In case the backend server does not work, event 'on-server-unavailable'
+         is called here before event 'on-client-session-end'. */
+      if ('on-server-unavailable') {
+         flt_ot_event_run() {
+            /* Run event. */
+            flt_ot_scope_run() {
+               /* Processing of all ot-scopes defined for the current event. */
+            }
+         }
+      }
+   }
+
+
+After the stream has stopped, this function is called.  At the moment, nothing
+is being done in it.
+
+   flt_ops.stream_stop = flt_ot_stream_stop;
+   flt_ot_stream_stop() {
+   }
+
+
+Then, before the instance filter is detached from the stream, the following
+function is called.  It deallocates the runtime context of the OT filter.
+
+   flt_ops.detach = flt_ot_detach;
+   flt_ot_detach() {
+      flt_ot_runtime_context_free() {
+         flt_ot_pool_free() {
+         }
+      }
+   }
+
+
+Module deinitialization begins with deinitialization of individual threads
+(as many threads as configured for the HAProxy process).  Because nothing
+special is connected to the process threads, nothing is done in this function.
+
+   flt_ops.deinit_per_thread = flt_ot_deinit_per_thread;
+   flt_ot_deinit_per_thread() {
+   }
+   ...
+
+
+For this function see the above description related to the src/group.c file.
+
+   flt_ot_group_release() {
+   }
+   ...
+
+
+Module deinitialization ends with the flt_ot_deinit() function, in which all
+memory occupied by module operation (and OpenTracing API operation, of course)
+is freed.
+
+   flt_ops.deinit = flt_ot_deinit;
+   flt_ot_deinit() {
+      ot_close() {
+      }
+      flt_ot_conf_free() {
+      }
+   }
diff --git a/contrib/opentracing/README-pool b/contrib/opentracing/README-pool
new file mode 100644
index 0000000..09a6c15
--- /dev/null
+++ b/contrib/opentracing/README-pool
@@ -0,0 +1,25 @@
+Used pools:
+
+-------------------------------+-----------------------------+-----------------------------
+          head / name          |            size             |           define
+-------------------------------+-----------------------------+-----------------------------
+ pool_head_ buffer             | global.tune.bufsize = 16384 | USE_POOL_BUFFER
+ pool_head_ trash              |                  32 + 16384 | USE_TRASH_CHUNK
+-------------------------------+-----------------------------+-----------------------------
+ pool_head_ ot_scope_span      |                          96 | USE_POOL_OT_SCOPE_SPAN
+ pool_head_ ot_scope_context   |                          64 | USE_POOL_OT_SCOPE_CONTEXT
+ pool_head_ ot_runtime_context |                         128 | USE_POOL_OT_RUNTIME_CONTEXT
+ pool_head_ ot_span_context    |                          96 | USE_POOL_OT_SPAN_CONTEXT
+-------------------------------+-----------------------------+-----------------------------
+
+By defining individual definitions in file include/config.h, it is possible to
+switch individual pools on / off.  If a particular pool is not used, memory is
+used in a 'normal' way instead, using malloc()/free() functions.
+
+This is made only from the aspect of debuging the program, i.e. comparing the
+speed of operation using different methods of working with memory.
+
+In general, it would be better to use memory pools, due to less fragmentation
+of memory space after long operation of the program.  The speed of operation
+is similar to when using standard allocation functions (when testing it was
+shown that pool use was fast by about 1%).
diff --git a/contrib/opentracing/include/cli.h b/contrib/opentracing/include/cli.h
new file mode 100644
index 0000000..80ed6e8
--- /dev/null
+++ b/contrib/opentracing/include/cli.h
@@ -0,0 +1,50 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_CLI_H_
+#define _OPENTRACING_CLI_H_
+
+#define FLT_OT_CLI_CMD                 "flt-ot"
+
+#define FLT_OT_CLI_LOGGING_OFF         "off"
+#define FLT_OT_CLI_LOGGING_ON          "on"
+#define FLT_OT_CLI_LOGGING_NOLOGNORM   "dontlog-normal"
+#define FLT_OT_CLI_LOGGING_STATE(a)    ((a) & FLT_OT_LOGGING_ON) ? (((a) & FLT_OT_LOGGING_NOLOGNORM) ? "enabled, " FLT_OT_CLI_LOGGING_NOLOGNORM : "enabled") : "disabled"
+
+#define FLT_OT_CLI_MSG_CAT(a)          ((a) == NULL) ? "" : (a), ((a) == NULL) ? "" : "\n"
+
+enum FLT_OT_LOGGING_enum {
+	FLT_OT_LOGGING_OFF       = 0,
+	FLT_OT_LOGGING_ON        = 1 << 0,
+	FLT_OT_LOGGING_NOLOGNORM = 1 << 1,
+};
+
+
+void flt_ot_cli_init(void);
+
+#endif /* _OPENTRACING_CLI_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/conf.h b/contrib/opentracing/include/conf.h
new file mode 100644
index 0000000..97df12e
--- /dev/null
+++ b/contrib/opentracing/include/conf.h
@@ -0,0 +1,227 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_CONF_H_
+#define _OPENTRACING_CONF_H_
+
+#define FLT_OT_CONF(f)              ((struct flt_ot_conf *)FLT_CONF(f))
+#define FLT_OT_CONF_HDR_FMT         "%p:{ { '%.*s' %zu %d } "
+#define FLT_OT_CONF_HDR_ARGS(a,b)   (a), (int)(a)->b##_len, (a)->b, (a)->b##_len, (a)->cfg_line
+#define FLT_OT_STR_HDR_ARGS(a,b)    (a)->b, (a)->b##_len
+
+#define FLT_OT_DBG_CONF_SAMPLE_EXPR(f,a) \
+	FLT_OT_DBG(3, "%s%p:{ '%s' %p }", (f), (a), (a)->value, (a)->expr)
+
+#define FLT_OT_DBG_CONF_SAMPLE(f,a)               \
+	FLT_OT_DBG(3, "%s%p:{ '%s' '%s' %s %d }", \
+	           (f), (a), (a)->key, (a)->value, flt_ot_list_debug(&((a)->exprs)), (a)->num_exprs)
+
+#define FLT_OT_DBG_CONF_STR(f,a) \
+	FLT_OT_DBG(3, f FLT_OT_CONF_HDR_FMT "}", FLT_OT_CONF_HDR_ARGS(a, str))
+
+#define FLT_OT_DBG_CONF_CONTEXT(f,a) \
+	FLT_OT_DBG(3, f FLT_OT_CONF_HDR_FMT "0x%02hhx }", FLT_OT_CONF_HDR_ARGS(a, id), (a)->flags)
+
+#define FLT_OT_DBG_CONF_SPAN(f,a)                                                                                   \
+	FLT_OT_DBG(3, f FLT_OT_CONF_HDR_FMT "'%s' %zu %d '%s' %zu %hhu 0x%02hhx %s %s %s }",                        \
+	           FLT_OT_CONF_HDR_ARGS(a, id), FLT_OT_STR_HDR_ARGS(a, ref_id), (a)->ref_type,                      \
+	           FLT_OT_STR_HDR_ARGS(a, ctx_id), (a)->flag_root, (a)->ctx_flags, flt_ot_list_debug(&((a)->tags)), \
+	           flt_ot_list_debug(&((a)->logs)), flt_ot_list_debug(&((a)->baggages)))
+
+#define FLT_OT_DBG_CONF_SCOPE(f,a)                                                                           \
+	FLT_OT_DBG(3, f FLT_OT_CONF_HDR_FMT "%hhu %d %s %p %s %s %s }",                                      \
+	           FLT_OT_CONF_HDR_ARGS(a, id), (a)->flag_used, (a)->event, flt_ot_list_debug(&((a)->acls)), \
+	           (a)->cond, flt_ot_list_debug(&((a)->contexts)), flt_ot_list_debug(&((a)->spans)),         \
+	           flt_ot_list_debug(&((a)->finish)))
+
+#define FLT_OT_DBG_CONF_GROUP(f,a)                       \
+	FLT_OT_DBG(3, f FLT_OT_CONF_HDR_FMT "%hhu %s }", \
+	           FLT_OT_CONF_HDR_ARGS(a, id), (a)->flag_used, flt_ot_list_debug(&((a)->ph_scopes)))
+
+#define FLT_OT_DBG_CONF_PH(f,a) \
+	FLT_OT_DBG(3, f FLT_OT_CONF_HDR_FMT "%p }", FLT_OT_CONF_HDR_ARGS(a, id), (a)->ptr)
+
+#define FLT_OT_DBG_CONF_TRACER(f,a)                                                                                                   \
+	FLT_OT_DBG(3, f FLT_OT_CONF_HDR_FMT "'%s' '%s' %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %s %s %s }",                             \
+	           FLT_OT_CONF_HDR_ARGS(a, id), (a)->config, (a)->plugin, (a)->tracer, (a)->rate_limit, (a)->flag_harderr,            \
+	           (a)->flag_disabled, (a)->logging, &((a)->proxy_log), flt_ot_list_debug(&((a)->proxy_log.logsrvs)), (a)->analyzers, \
+	           flt_ot_list_debug(&((a)->acls)), flt_ot_list_debug(&((a)->ph_groups)), flt_ot_list_debug(&((a)->ph_scopes)))
+
+#define FLT_OT_DBG_CONF(f,a)                                                  \
+	FLT_OT_DBG(3, "%s%p:{ %p '%s' '%s' %p %s %s }",                       \
+	           (f), (a), (a)->proxy, (a)->id, (a)->cfg_file, (a)->tracer, \
+	           flt_ot_list_debug(&((a)->groups)), flt_ot_list_debug(&((a)->scopes)))
+
+#define FLT_OT_STR_HDR(a)        \
+	struct {                 \
+		char   *a;       \
+		size_t  a##_len; \
+	}
+
+#define FLT_OT_CONF_HDR(a)            \
+	struct {                      \
+		FLT_OT_STR_HDR(a);    \
+		int         cfg_line; \
+		struct list list;     \
+	}
+
+
+struct flt_ot_conf_hdr {
+	FLT_OT_CONF_HDR(id);
+};
+
+/* flt_ot_conf_sample->exprs */
+struct flt_ot_conf_sample_expr {
+	FLT_OT_CONF_HDR(value);   /* The sample value. */
+	struct sample_expr *expr; /* The sample expression. */
+};
+
+/*
+ * flt_ot_conf_span->tags
+ * flt_ot_conf_span->logs
+ * flt_ot_conf_span->baggages
+ */
+struct flt_ot_conf_sample {
+	FLT_OT_CONF_HDR(key);   /* The sample name. */
+	char        *value;     /* The sample content. */
+	struct list  exprs;     /* Used to chain sample expressions. */
+	int          num_exprs; /* Number of defined expressions. */
+};
+
+/* flt_ot_conf_scope->finish */
+struct flt_ot_conf_str {
+	FLT_OT_CONF_HDR(str); /* String content/length. */
+};
+
+/* flt_ot_conf_scope->contexts */
+struct flt_ot_conf_context {
+	FLT_OT_CONF_HDR(id); /* The name of the context. */
+	uint8_t flags;       /* The type of storage from which the span context is extracted.  */
+};
+
+/* flt_ot_conf_scope->spans */
+struct flt_ot_conf_span {
+	FLT_OT_CONF_HDR(id);    /* The name of the span. */
+	FLT_OT_STR_HDR(ref_id); /* The reference name, if used. */
+	int         ref_type;   /* The reference type. */
+	FLT_OT_STR_HDR(ctx_id); /* The span context name, if used. */
+	uint8_t     ctx_flags;  /* The type of storage used for the span context. */
+	bool        flag_root;  /* Whether this is a root span. */
+	struct list tags;       /* The set of key:value tags. */
+	struct list logs;       /* The set of key:value logs. */
+	struct list baggages;   /* The set of key:value baggage items. */
+};
+
+struct flt_ot_conf_scope {
+	FLT_OT_CONF_HDR(id);        /* The scope name. */
+	bool             flag_used; /* The indication that the scope is being used. */
+	int              event;     /* FLT_OT_EVENT_* */
+	struct list      acls;      /* ACLs declared on this scope. */
+	struct acl_cond *cond;      /* ACL condition to meet. */
+	struct list      contexts;  /* Declared contexts. */
+	struct list      spans;     /* Declared spans. */
+	struct list      finish;    /* The list of spans to be finished. */
+};
+
+struct flt_ot_conf_group {
+	FLT_OT_CONF_HDR(id);   /* The group name. */
+	bool        flag_used; /* The indication that the group is being used. */
+	struct list ph_scopes; /* List of all used scopes. */
+};
+
+struct flt_ot_conf_ph {
+	FLT_OT_CONF_HDR(id); /* The scope/group name. */
+	void *ptr;           /* Pointer to real placeholder structure. */
+};
+#define flt_ot_conf_ph_group      flt_ot_conf_ph
+#define flt_ot_conf_ph_scope      flt_ot_conf_ph
+
+struct flt_ot_conf_tracer {
+	FLT_OT_CONF_HDR(id);              /* The tracer name. */
+	char              *config;        /* The OpenTracing configuration file name. */
+	char              *plugin;        /* The OpenTracing plugin library file name. */
+	struct otc_tracer *tracer;        /* The OpenTracing tracer handle. */
+	uint32_t           rate_limit;    /* [0 2^32-1] <-> [0.0 100.0] */
+	bool               flag_harderr;  /* [0 1] */
+	bool               flag_disabled; /* [0 1] */
+	uint8_t            logging;       /* [0 1 3] */
+	struct proxy       proxy_log;     /* The log server list. */
+	uint               analyzers;     /* Defined channel analyzers. */
+	struct list        acls;          /* ACLs declared on this tracer. */
+	struct list        ph_groups;     /* List of all used groups. */
+	struct list        ph_scopes;     /* List of all used scopes. */
+};
+
+struct flt_ot_counters {
+#ifdef DEBUG_OT
+	struct {
+		bool     flag_used; /* Whether this event is used. */
+		uint64_t htx[2];    /* htx_is_empty() function result counter. */
+	} event[FLT_OT_EVENT_MAX];
+#endif
+
+	uint64_t disabled[2];       /* How many times stream processing is disabled. */
+};
+
+/* The OpenTracing filter configuration. */
+struct flt_ot_conf {
+	struct proxy              *proxy;    /* Proxy owning the filter. */
+	char                      *id;       /* The OpenTracing filter id. */
+	char                      *cfg_file; /* The OpenTracing filter configuration file name. */
+	struct flt_ot_conf_tracer *tracer;   /* There can only be one tracer. */
+	struct list                groups;   /* List of all available groups. */
+	struct list                scopes;   /* List of all available scopes. */
+	struct flt_ot_counters     cnt;      /* Various counters related to filter operation. */
+};
+
+
+#define flt_ot_conf_ph_group_free   flt_ot_conf_ph_free
+#define flt_ot_conf_ph_scope_free   flt_ot_conf_ph_free
+
+struct flt_ot_conf_ph          *flt_ot_conf_ph_init(const char *id, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_ph_free(struct flt_ot_conf_ph **ptr);
+struct flt_ot_conf_sample_expr *flt_ot_conf_sample_expr_init(const char *id, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_sample_expr_free(struct flt_ot_conf_sample_expr **ptr);
+struct flt_ot_conf_sample      *flt_ot_conf_sample_init(char **args, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_sample_free(struct flt_ot_conf_sample **ptr);
+struct flt_ot_conf_str         *flt_ot_conf_str_init(const char *id, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_str_free(struct flt_ot_conf_str **ptr);
+struct flt_ot_conf_context     *flt_ot_conf_context_init(const char *id, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_context_free(struct flt_ot_conf_context **ptr);
+struct flt_ot_conf_span        *flt_ot_conf_span_init(const char *id, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_span_free(struct flt_ot_conf_span **ptr);
+struct flt_ot_conf_scope       *flt_ot_conf_scope_init(const char *id, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_scope_free(struct flt_ot_conf_scope **ptr);
+struct flt_ot_conf_group       *flt_ot_conf_group_init(const char *id, int linenum, struct list *head, char **err);
+void                            flt_ot_conf_group_free(struct flt_ot_conf_group **ptr);
+struct flt_ot_conf_tracer      *flt_ot_conf_tracer_init(const char *id, int linenum, char **err);
+void                            flt_ot_conf_tracer_free(struct flt_ot_conf_tracer **ptr);
+struct flt_ot_conf             *flt_ot_conf_init(struct proxy *px);
+void                            flt_ot_conf_free(struct flt_ot_conf **ptr);
+
+#endif /* _OPENTRACING_CONF_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/config.h b/contrib/opentracing/include/config.h
new file mode 100644
index 0000000..3b26365
--- /dev/null
+++ b/contrib/opentracing/include/config.h
@@ -0,0 +1,46 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_CONFIG_H_
+#define _OPENTRACING_CONFIG_H_
+
+#undef  DEBUG_OT_SYSTIME
+#define USE_POOL_BUFFER
+#define USE_POOL_OT_SPAN_CONTEXT
+#define USE_POOL_OT_SCOPE_SPAN
+#define USE_POOL_OT_SCOPE_CONTEXT
+#define USE_POOL_OT_RUNTIME_CONTEXT
+#define USE_TRASH_CHUNK
+
+#define FLT_OT_ID_MAXLEN        64
+#define FLT_OT_MAXTAGS          8
+#define FLT_OT_MAXBAGGAGES      8
+#define FLT_OT_RATE_LIMIT_MAX   100.0
+#define FLT_OT_DEBUG_LEVEL      0b00001111
+
+#endif /* _OPENTRACING_CONFIG_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/debug.h b/contrib/opentracing/include/debug.h
new file mode 100644
index 0000000..7becdf7
--- /dev/null
+++ b/contrib/opentracing/include/debug.h
@@ -0,0 +1,96 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_DEBUG_H_
+#define _OPENTRACING_DEBUG_H_
+
+#ifdef DEBUG_FULL
+#  define DEBUG_OT
+#endif
+
+#ifdef DEBUG_OT
+#  ifdef DEBUG_OT_SYSTIME
+#     define FLT_OT_DBG_FMT(f)      "[% 2d] %ld.%06ld [" FLT_OT_SCOPE "]: " f, tid, now.tv_sec, now.tv_usec
+#  else
+#     define FLT_OT_DBG_FMT(f)      "[% 2d] %11.6f [" FLT_OT_SCOPE "]: " f, tid, FLT_OT_TV_UDIFF(&(flt_ot_debug.start), &now) / 1e6
+#  endif
+#  define FLT_OT_DBG_INDENT         "                                                                                "
+#  define FLT_OT_DBG(l,f, ...)                                                             \
+	do {                                                                               \
+		if (!(l) || (flt_ot_debug.level & (1 << (l))))                             \
+			(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*s" f "\n"),               \
+			              dbg_indent_level, FLT_OT_DBG_INDENT, ##__VA_ARGS__); \
+	} while (0)
+#  define FLT_OT_FUNC(f, ...)       do { FLT_OT_DBG(1, "%s(" f ") {", __func__, ##__VA_ARGS__); dbg_indent_level += 3; } while (0)
+#  define FLT_OT_RETURN(a)          do { dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
+#  define FLT_OT_DBG_IFDEF(a,b)     a
+#  define FLT_OT_DBG_ARGS(a, ...)   a, ##__VA_ARGS__
+
+struct flt_ot_debug {
+#ifndef DEBUG_OT_SYSTIME
+	struct timeval start;
+#endif
+	uint8_t        level;
+};
+
+
+extern THREAD_LOCAL int    dbg_indent_level;
+extern struct flt_ot_debug flt_ot_debug;
+
+#else
+
+#  define FLT_OT_DBG(...)           while (0)
+#  define FLT_OT_FUNC(...)          while (0)
+#  define FLT_OT_RETURN(a)          return a
+#  define FLT_OT_DBG_IFDEF(a,b)     b
+#  define FLT_OT_DBG_ARGS(...)
+#endif /* DEBUG_OT */
+
+/*
+ *  ON  | NOLOGNORM |
+ * -----+-----------+-------------
+ *   0  |     0     |  no log
+ *   0  |     1     |  no log
+ *   1  |     0     |  log all
+ *   1  |     1     |  log errors
+ * -----+-----------+-------------
+ */
+#define FLT_OT_LOG(l,f, ...)                                                                                                    \
+	do {                                                                                                                    \
+		if (!(conf->tracer->logging & FLT_OT_LOGGING_ON))                                                               \
+			FLT_OT_DBG(3, "NOLOG[%d]: [" FLT_OT_SCOPE "]: [%s] " f, (l), conf->id, ##__VA_ARGS__);                  \
+		else if ((conf->tracer->logging & FLT_OT_LOGGING_NOLOGNORM) && ((l) > LOG_ERR))                                 \
+			FLT_OT_DBG(2, "NOLOG[%d]: [" FLT_OT_SCOPE "]: [%s] " f, (l), conf->id, ##__VA_ARGS__);                  \
+		else {                                                                                                          \
+			send_log(&(conf->tracer->proxy_log), (l), "[" FLT_OT_SCOPE "]: [%s] " f "\n", conf->id, ##__VA_ARGS__); \
+                                                                                                                                \
+			FLT_OT_DBG(1, "LOG[%d]: %s", (l), logline);                                                             \
+		}                                                                                                               \
+	} while (0)
+
+#endif /* _OPENTRACING_DEBUG_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/define.h b/contrib/opentracing/include/define.h
new file mode 100644
index 0000000..8769161
--- /dev/null
+++ b/contrib/opentracing/include/define.h
@@ -0,0 +1,104 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_DEFINE_H_
+#define _OPENTRACING_DEFINE_H_
+
+#define FLT_OT_DEREF(a,m,v)        (((a) != NULL) ? (a)->m : (v))
+#define FLT_OT_DDEREF(a,m,v)       ((((a) != NULL) && (*(a) != NULL)) ? (*(a))->m : (v))
+#define FLT_OT_TABLESIZE(a)        (sizeof(a) / sizeof((a)[0]))
+#define FLT_OT_IN_RANGE(v,a,b)     (((v) >= (a)) && ((v) <= (b)))
+#define FLT_OT_DPTR_ARGS(a)        (a), ((a) == NULL) ? NULL : *(a)
+#define FLT_OT_ARG_ISVALID(n)      ((args[n] != NULL) && *args[n])
+#define FLT_OT_TV_UDIFF(a,b)       (((b)->tv_sec - (a)->tv_sec) * 1000000 + (b)->tv_usec - (a)->tv_usec)
+#define FLT_OT_U32_FLOAT(a,b)      ((a) * (double)(b) / UINT32_MAX)
+#define FLT_OT_FLOAT_U32(a,b)      ((uint32_t)((a) / (double)(b) * UINT32_MAX + 0.5))
+
+#define FLT_OT_STR_DASH_72         "------------------------------------------------------------------------"
+#define FLT_OT_STR_DASH_78         FLT_OT_STR_DASH_72 "------"
+#define FLT_OT_STR_FLAG_YN(a)      (a) ? "yes" : "no"
+
+#define FLT_OT_STR_SIZE(a)         (sizeof(a) - 1)
+#define FLT_OT_STR_ADDRSIZE(a)     (a), FLT_OT_STR_SIZE(a)
+#define FLT_OT_STR_ISVALID(a)      (((a) != NULL) && (*(a) != '\0'))
+#define FLT_OT_STR_CMP(S,s,l)      (((l) == FLT_OT_STR_SIZE(S)) && (memcmp((s), FLT_OT_STR_ADDRSIZE(S)) == 0))
+#define FLT_OT_STR_ELLIPSIS(a,n)   do { if ((a) != NULL) { if ((n) > 0) (a)[(n) - 1] = '\0'; if ((n) > 3) (a)[(n) - 2] = (a)[(n) - 3] = (a)[(n) - 4] = '.'; } } while (0)
+#define FLT_OT_NIBBLE_TO_HEX(a)    ((a) + (((a) < 10) ? '0' : ('a' - 10)))
+
+#define FLT_OT_FREE(a)             do { if ((a) != NULL) OTC_DBG_FREE(a); } while (0)
+#define FLT_OT_FREE_VOID(a)        do { if ((a) != NULL) OTC_DBG_FREE((void *)(a)); } while (0)
+#define FLT_OT_FREE_CLEAR(a)       do { if ((a) != NULL) { OTC_DBG_FREE(a); (a) = NULL; } } while (0)
+#define FLT_OT_STRDUP(s)           OTC_DBG_STRDUP(s)
+#define FLT_OT_STRNDUP(s,n)        OTC_DBG_STRNDUP((s), (n))
+#define FLT_OT_CALLOC(n,e)         OTC_DBG_CALLOC((n), (e))
+#define FLT_OT_MALLOC(s)           OTC_DBG_MALLOC((s))
+#define FLT_OT_MEMINFO()           OTC_DBG_MEMINFO()
+
+#define FLT_OT_RUN_ONCE(f)         do { static bool __f = 1; if (__f) { __f = 0; f; } } while (0)
+
+#define FLT_OT_LIST_ISVALID(a)     (((a) != NULL) && ((a)->n != NULL) && ((a)->p != NULL))
+#define FLT_OT_LIST_DEL(a)         do { if (FLT_OT_LIST_ISVALID(a)) LIST_DEL(a); } while (0)
+#define FLT_OT_LIST_DESTROY(t,h)                                                  \
+	do {                                                                      \
+		struct flt_ot_conf_##t *_ptr, *_back;                             \
+                                                                                  \
+		if (!FLT_OT_LIST_ISVALID(h) || LIST_ISEMPTY(h))                   \
+			break;                                                    \
+                                                                                  \
+		FLT_OT_DBG(2, "- deleting " #t " list %s", flt_ot_list_debug(h)); \
+                                                                                  \
+		list_for_each_entry_safe(_ptr, _back, (h), list)                  \
+			flt_ot_conf_##t##_free(&_ptr);                            \
+	} while (0)
+
+#define FLT_OT_BUFFER_THR(b,m,n,p)                \
+	static THREAD_LOCAL char    b[m][n];      \
+	static THREAD_LOCAL size_t  __idx = 0;    \
+	char                       *p = b[__idx]; \
+	__idx = (__idx + 1) % (m)
+
+#define FLT_OT_ERR(f, ...)                                      \
+	do {                                                    \
+		if ((err != NULL) && (*err == NULL))            \
+			(void)memprintf(err, f, ##__VA_ARGS__); \
+	} while (0)
+#define FLT_OT_ERR_APPEND(f, ...)                               \
+	do {                                                    \
+		if (err != NULL)                                \
+			(void)memprintf(err, f, ##__VA_ARGS__); \
+	} while (0)
+#define FLT_OT_ERR_FREE(p)                                                  \
+	do {                                                                \
+		if ((p) == NULL)                                            \
+			break;                                              \
+                                                                            \
+		FLT_OT_DBG(0, "%s:%d: ERROR: %s", __func__, __LINE__, (p)); \
+		FLT_OT_FREE_CLEAR(p);                                       \
+	} while (0)
+
+#endif /* _OPENTRACING_DEFINE_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/event.h b/contrib/opentracing/include/event.h
new file mode 100644
index 0000000..8d59163
--- /dev/null
+++ b/contrib/opentracing/include/event.h
@@ -0,0 +1,120 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_EVENT_H_
+#define _OPENTRACING_EVENT_H_
+
+/*
+ * This must be defined in order for macro FLT_OT_EVENT_DEFINES
+ * and structure flt_ot_event_data to have the correct contents.
+ */
+#define AN_REQ_NONE                 0
+#define AN_REQ_CLIENT_SESS_START    0
+#define AN_REQ_SERVER_UNAVAILABLE   0
+#define AN_REQ_CLIENT_SESS_END      0
+#define AN_RES_SERVER_SESS_START    0
+#define AN_RES_SERVER_SESS_END      0
+#define SMP_VAL_FE_                 0
+#define SMP_VAL_BE_                 0
+
+/*
+ * Event names are selected to be somewhat compatible with the SPOE filter,
+ * from which the following names are taken:
+ *   - on-client-session -> on-client-session-start
+ *   - on-frontend-tcp-request
+ *   - on-frontend-http-request
+ *   - on-backend-tcp-request
+ *   - on-backend-http-request
+ *   - on-server-session -> on-server-session-start
+ *   - on-tcp-response
+ *   - on-http-response
+ *
+ * FLT_OT_EVENT_NONE is used as an index for 'ot-scope' sections that do not
+ * have an event defined.  The 'ot-scope' sections thus defined can be used
+ * within the 'ot-group' section.
+ *
+ * A description of the macro arguments can be found in the structure
+ * flt_ot_event_data definition
+ */
+#define FLT_OT_EVENT_DEFINES                                                                                \
+	FLT_OT_EVENT_DEF(              NONE, REQ,        ,        , 0, "")                                  \
+	FLT_OT_EVENT_DEF( CLIENT_SESS_START, REQ, CON_ACC,        , 1, "on-client-session-start")           \
+	FLT_OT_EVENT_DEF(        INSPECT_FE, REQ, REQ_CNT,        , 1, "on-frontend-tcp-request")           \
+	FLT_OT_EVENT_DEF(         WAIT_HTTP, REQ,        ,        , 1, "on-http-wait-request")              \
+	FLT_OT_EVENT_DEF(         HTTP_BODY, REQ,        ,        , 1, "on-http-body-request")              \
+	FLT_OT_EVENT_DEF(   HTTP_PROCESS_FE, REQ, HRQ_HDR,        , 1, "on-frontend-http-request")          \
+	FLT_OT_EVENT_DEF(   SWITCHING_RULES, REQ,        ,        , 1, "on-switching-rules-request")        \
+	FLT_OT_EVENT_DEF(        INSPECT_BE, REQ, REQ_CNT, REQ_CNT, 1, "on-backend-tcp-request")            \
+	FLT_OT_EVENT_DEF(   HTTP_PROCESS_BE, REQ, HRQ_HDR, HRQ_HDR, 1, "on-backend-http-request")           \
+/*	FLT_OT_EVENT_DEF(       HTTP_TARPIT, REQ,        ,        , 1, "on-http-tarpit-request") */         \
+	FLT_OT_EVENT_DEF(         SRV_RULES, REQ,        ,        , 1, "on-process-server-rules-request")   \
+	FLT_OT_EVENT_DEF(        HTTP_INNER, REQ,        ,        , 1, "on-http-process-request")           \
+	FLT_OT_EVENT_DEF(   PRST_RDP_COOKIE, REQ,        ,        , 1, "on-tcp-rdp-cookie-request")         \
+	FLT_OT_EVENT_DEF(    STICKING_RULES, REQ,        ,        , 1, "on-process-sticking-rules-request") \
+	FLT_OT_EVENT_DEF(   CLIENT_SESS_END, REQ,        ,        , 0, "on-client-session-end")             \
+	FLT_OT_EVENT_DEF(SERVER_UNAVAILABLE, REQ,        ,        , 0, "on-server-unavailable")             \
+                                                                                                            \
+	FLT_OT_EVENT_DEF( SERVER_SESS_START, RES,        , SRV_CON, 0, "on-server-session-start")           \
+	FLT_OT_EVENT_DEF(           INSPECT, RES, RES_CNT, RES_CNT, 0, "on-tcp-response")                   \
+	FLT_OT_EVENT_DEF(         WAIT_HTTP, RES,        ,        , 1, "on-http-wait-response")             \
+	FLT_OT_EVENT_DEF(       STORE_RULES, RES,        ,        , 1, "on-process-store-rules-response")   \
+	FLT_OT_EVENT_DEF(   HTTP_PROCESS_BE, RES, HRS_HDR, HRS_HDR, 1, "on-http-response")                  \
+	FLT_OT_EVENT_DEF(   SERVER_SESS_END, RES,        ,        , 0, "on-server-session-end")
+
+enum FLT_OT_EVENT_enum {
+#define FLT_OT_EVENT_DEF(a,b,c,d,e,f)   FLT_OT_EVENT_##b##_##a,
+	FLT_OT_EVENT_DEFINES
+	FLT_OT_EVENT_MAX
+#undef FLT_OT_EVENT_DEF
+};
+
+enum FLT_OT_EVENT_SAMPLE_enum {
+	FLT_OT_EVENT_SAMPLE_TAG = 0,
+	FLT_OT_EVENT_SAMPLE_LOG,
+	FLT_OT_EVENT_SAMPLE_BAGGAGE,
+};
+
+struct flt_ot_event_data {
+	uint        an_bit;           /* Used channel analyser. */
+	uint        smp_opt_dir;      /* Fetch direction (request/response). */
+	uint        smp_val_fe;       /* Valid FE fetch location. */
+	uint        smp_val_be;       /* Valid BE fetch location. */
+	bool        flag_http_inject; /* Span context injection allowed. */
+	const char *name;             /* Filter event name. */
+};
+
+struct flt_ot_conf_scope;
+
+
+extern const struct flt_ot_event_data flt_ot_event_data[FLT_OT_EVENT_MAX];
+
+
+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);
+int flt_ot_event_run(struct stream *s, struct filter *f, struct channel *chn, int event, char **err);
+
+#endif /* _OPENTRACING_EVENT_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/filter.h b/contrib/opentracing/include/filter.h
new file mode 100644
index 0000000..0b36354
--- /dev/null
+++ b/contrib/opentracing/include/filter.h
@@ -0,0 +1,68 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_FILTER_H_
+#define _OPENTRACING_FILTER_H_
+
+#define FLT_OT_FMT_NAME           "'" FLT_OT_OPT_NAME "' : "
+#define FLT_OT_FMT_TYPE           "'filter' : "
+#define FTL_OT_VAR_UUID           "sess", "ot", "uuid"
+#define FLT_OT_ALERT(f, ...)      ha_alert(FLT_OT_FMT_TYPE FLT_OT_FMT_NAME f "\n", ##__VA_ARGS__)
+
+#define FLT_OT_CONDITION_IF       "if"
+#define FLT_OT_CONDITION_UNLESS   "unless"
+
+enum FLT_OT_RET_enum {
+	FLT_OT_RET_ERROR  = -1,
+	FLT_OT_RET_WAIT   = 0,
+	FLT_OT_RET_IGNORE = 0,
+	FLT_OT_RET_OK     = 1,
+};
+
+#define FLT_OT_DBG_LIST(d,m,p,t,v,f)                                 \
+	do {                                                         \
+		if (LIST_ISEMPTY(&((d)->m##s))) {                    \
+			FLT_OT_DBG(3, p "- no " #m "s " t);          \
+		} else {                                             \
+			const struct flt_ot_conf_##m *v;             \
+                                                                     \
+			FLT_OT_DBG(3, p "- " t " " #m "s: %s",       \
+			           flt_ot_list_debug(&((d)->m##s))); \
+			list_for_each_entry(v, &((d)->m##s), list)   \
+				do { f; } while (0);                 \
+		}                                                    \
+	} while (0)
+
+
+extern const char     *ot_flt_id;
+extern struct flt_ops  flt_ot_ops;
+
+
+bool flt_ot_is_disabled(const struct filter *f FLT_OT_DBG_ARGS(, int event));
+
+#endif /* _OPENTRACING_FILTER_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/group.h b/contrib/opentracing/include/group.h
new file mode 100644
index 0000000..a9bfcc6
--- /dev/null
+++ b/contrib/opentracing/include/group.h
@@ -0,0 +1,61 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_GROUP_H_
+#define _OPENTRACING_GROUP_H_
+
+#define FLT_OT_ACTION_GROUP   "ot-group"
+
+enum FLT_OT_ARG_enum {
+	FLT_OT_ARG_FILTER_ID = 0,
+	FLT_OT_ARG_GROUP_ID,
+
+	FLT_OT_ARG_FLT_CONF = 0,
+	FLT_OT_ARG_CONF,
+	FLT_OT_ARG_GROUP,
+};
+
+/*
+ * A description of the macro arguments can be found in the structure
+ * flt_ot_group_data definition
+ */
+#define FLT_OT_GROUP_DEFINES                                                     \
+	FLT_OT_GROUP_DEF(ACT_F_TCP_REQ_CON, SMP_VAL_FE_CON_ACC, SMP_OPT_DIR_REQ) \
+	FLT_OT_GROUP_DEF(ACT_F_TCP_REQ_SES, SMP_VAL_FE_SES_ACC, SMP_OPT_DIR_REQ) \
+	FLT_OT_GROUP_DEF(ACT_F_TCP_REQ_CNT, SMP_VAL_FE_REQ_CNT, SMP_OPT_DIR_REQ) \
+	FLT_OT_GROUP_DEF(ACT_F_TCP_RES_CNT, SMP_VAL_BE_RES_CNT, SMP_OPT_DIR_RES) \
+	FLT_OT_GROUP_DEF(ACT_F_HTTP_REQ,    SMP_VAL_FE_HRQ_HDR, SMP_OPT_DIR_REQ) \
+	FLT_OT_GROUP_DEF(ACT_F_HTTP_RES,    SMP_VAL_BE_HRS_HDR, SMP_OPT_DIR_RES)
+
+struct flt_ot_group_data {
+	enum act_from act_from;    /* ACT_F_* */
+	uint          smp_val;     /* Valid FE/BE fetch location. */
+	uint          smp_opt_dir; /* Fetch direction (request/response). */
+};
+
+#endif /* _OPENTRACING_GROUP_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/http.h b/contrib/opentracing/include/http.h
new file mode 100644
index 0000000..c323cde
--- /dev/null
+++ b/contrib/opentracing/include/http.h
@@ -0,0 +1,41 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_HTTP_H_
+#define _OPENTRACING_HTTP_H_
+
+#ifndef DEBUG_OT
+#  define flt_ot_http_headers_dump(...)   while (0)
+#else
+void                 flt_ot_http_headers_dump(const struct channel *chn);
+#endif
+struct otc_text_map *flt_ot_http_headers_get(struct channel *chn, const char *prefix, size_t len, char **err);
+int                  flt_ot_http_header_set(struct channel *chn, const char *prefix, const char *name, const char *value, char **err);
+int                  flt_ot_http_headers_remove(struct channel *chn, const char *prefix, char **err);
+
+#endif /* _OPENTRACING_HTTP_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/include.h b/contrib/opentracing/include/include.h
new file mode 100644
index 0000000..3c0d110
--- /dev/null
+++ b/contrib/opentracing/include/include.h
@@ -0,0 +1,63 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_INCLUDE_H_
+#define _OPENTRACING_INCLUDE_H_
+
+#include <errno.h>
+#include <stdbool.h>
+
+#include <haproxy/api.h>
+#include <haproxy/cfgparse.h>
+#include <haproxy/acl.h>
+#include <haproxy/cli.h>
+#include <haproxy/filters.h>
+#include <haproxy/http_htx.h>
+#include <haproxy/http_rules.h>
+#include <haproxy/log.h>
+#include <haproxy/sample.h>
+#include <haproxy/tcp_rules.h>
+#include <haproxy/vars.h>
+
+#include "config.h"
+#include "debug.h"
+#include "define.h"
+#include "cli.h"
+#include "event.h"
+#include "conf.h"
+#include "filter.h"
+#include "group.h"
+#include "http.h"
+#include "opentracing.h"
+#include "parser.h"
+#include "pool.h"
+#include "scope.h"
+#include "util.h"
+#include "vars.h"
+
+#endif /* _OPENTRACING_INCLUDE_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/opentracing.h b/contrib/opentracing/include/opentracing.h
new file mode 100644
index 0000000..2dbf532
--- /dev/null
+++ b/contrib/opentracing/include/opentracing.h
@@ -0,0 +1,85 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_OT_H_
+#define _OPENTRACING_OT_H_
+
+#include <opentracing-c-wrapper/include.h>
+
+
+#define FLT_OT_VSET(p,t,v) \
+	do { (p)->type = otc_value_##t; (p)->value.t##_value = (v); } while (0)
+
+#define FLT_OT_DBG_TEXT_MAP(a)                     \
+	FLT_OT_DBG(3, "%p:{ %p %p %zu/%zu %hhu }", \
+	           (a), (a)->key, (a)->value, (a)->count, (a)->size, (a)->is_dynamic)
+
+#define FLT_OT_DBG_TEXT_CARRIER(a,f)                                                 \
+	FLT_OT_DBG(3, "%p:{ { %p %p %zu/%zu %hhu } %p }",                            \
+	           (a), (a)->text_map.key, (a)->text_map.value, (a)->text_map.count, \
+	           (a)->text_map.size, (a)->text_map.is_dynamic, (a)->f)
+
+#define FLT_OT_DBG_CUSTOM_CARRIER(a,f)                                \
+	FLT_OT_DBG(3, "%p:{ { %p %zu %hhu } %p }",                    \
+	           (a), (a)->binary_data.data, (a)->binary_data.size, \
+	           (a)->binary_data.is_dynamic, (a)->f)
+
+#define FLT_OT_DBG_SPAN_CONTEXT(a) \
+	FLT_OT_DBG(3, "%p:{ %" PRId64 " %p %p }", (a), (a)->idx, (a)->span, (a)->destroy)
+
+
+#ifndef DEBUG_OT
+#  define ot_debug()              while (0)
+#  define ot_text_map_show(...)   while (0)
+#else
+void                     ot_text_map_show(const struct otc_text_map *text_map);
+void                     ot_debug(void);
+#endif
+int                      ot_init(struct otc_tracer **tracer, const char *config, const char *plugin, char **err);
+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);
+int                      ot_span_tag(struct otc_span *span, const struct otc_tag *tags, int num_tags);
+int                      ot_span_log(struct otc_span *span, const struct otc_log_field *log_fields, int num_fields);
+int                      ot_span_set_baggage(struct otc_span *span, const struct otc_text_map *baggage);
+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 *ot_extract_http_headers(struct otc_tracer *tracer, struct otc_http_headers_reader *carrier, const struct otc_text_map *text_map, char **err);
+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, ...);
+void                     ot_close(struct otc_tracer **tracer);
+
+/* Unused code. */
+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, ...);
+int                      ot_span_tag_va(struct otc_span *span, const char *key, int type, ...);
+int                      ot_span_log_va(struct otc_span *span, const char *key, const char *value, ...);
+int                      ot_span_log_fmt(struct otc_span *span, const char *key, const char *format, ...) __attribute__ ((format(printf, 3, 4)));
+int                      ot_span_set_baggage_va(struct otc_span *span, const char *key, const char *value, ...);
+struct otc_text_map     *ot_span_baggage_va(const struct otc_span *span, const char *key, ...);
+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 *ot_inject_binary(struct otc_tracer *tracer, const struct otc_span *span, struct otc_custom_carrier_writer *carrier);
+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 *ot_extract_binary(struct otc_tracer *tracer, struct otc_custom_carrier_reader *carrier, const struct otc_binary_data *binary_data);
+
+#endif /* _OPENTRACING_OT_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/parser.h b/contrib/opentracing/include/parser.h
new file mode 100644
index 0000000..5e7b298
--- /dev/null
+++ b/contrib/opentracing/include/parser.h
@@ -0,0 +1,148 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_PARSER_H_
+#define _OPENTRACING_PARSER_H_
+
+#define FLT_OT_SCOPE                        "OT"
+
+/*
+ * filter FLT_OT_OPT_NAME FLT_OT_OPT_FILTER_ID <FLT_OT_OPT_FILTER_ID_DEFAULT> FLT_OT_OPT_CONFIG <file>
+ */
+#define FLT_OT_OPT_NAME                     "opentracing"
+#define FLT_OT_OPT_FILTER_ID                "id"
+#define FLT_OT_OPT_FILTER_ID_DEFAULT        "ot-filter"
+#define FLT_OT_OPT_CONFIG                   "config"
+
+#define FLT_OT_PARSE_SECTION_TRACER_ID      "ot-tracer"
+#define FLT_OT_PARSE_SECTION_GROUP_ID       "ot-group"
+#define FLT_OT_PARSE_SECTION_SCOPE_ID       "ot-scope"
+
+#define FLT_OT_PARSE_SPAN_ROOT              "root"
+#define FLT_OT_PARSE_SPAN_REF_CHILD         "child-of"
+#define FLT_OT_PARSE_SPAN_REF_FOLLOWS       "follows-from"
+#define FLT_OT_PARSE_CTX_AUTONAME           "-"
+#define FLT_OT_PARSE_CTX_USE_HEADERS        "use-headers"
+#define FLT_OT_PARSE_CTX_USE_VARS           "use-vars"
+#define FLT_OT_PARSE_OPTION_HARDERR         "hard-errors"
+#define FLT_OT_PARSE_OPTION_DISABLED        "disabled"
+#define FLT_OT_PARSE_OPTION_NOLOGNORM       "dontlog-normal"
+
+/*
+ * A description of the macro arguments can be found in the structure
+ * flt_ot_parse_data definition
+ */
+#define FLT_OT_PARSE_TRACER_DEFINES                                                                                                                          \
+	FLT_OT_PARSE_TRACER_DEF(         ID, 0, 1, 2, 2, "ot-tracer",   " <name>")                                                                           \
+	FLT_OT_PARSE_TRACER_DEF(        ACL, 0, 1, 3, 0, "acl",         " <name> <criterion> [flags] [operator] <value> ...")                                \
+	FLT_OT_PARSE_TRACER_DEF(        LOG, 0, 1, 2, 0, "log",         " { global | <addr> [len <len>] [format <fmt>] <facility> [<level> [<minlevel>]] }") \
+	FLT_OT_PARSE_TRACER_DEF(     CONFIG, 0, 0, 2, 2, "config",      " <file>")                                                                           \
+	FLT_OT_PARSE_TRACER_DEF(     PLUGIN, 0, 0, 2, 2, "plugin",      " <file>")                                                                           \
+	FLT_OT_PARSE_TRACER_DEF(     GROUPS, 0, 0, 2, 0, "groups",      " <name> ...")                                                                       \
+	FLT_OT_PARSE_TRACER_DEF(     SCOPES, 0, 0, 2, 0, "scopes",      " <name> ...")                                                                       \
+	FLT_OT_PARSE_TRACER_DEF( RATE_LIMIT, 0, 0, 2, 2, "rate-limit",  " <value>")                                                                          \
+	FLT_OT_PARSE_TRACER_DEF(     OPTION, 0, 0, 2, 2, "option",      " { disabled | dontlog-normal | hard-errors }")                                      \
+	FLT_OT_PARSE_TRACER_DEF(DEBUG_LEVEL, 0, 0, 2, 2, "debug-level", " <value>")
+
+#define FLT_OT_PARSE_GROUP_DEFINES                                        \
+	FLT_OT_PARSE_GROUP_DEF(    ID, 0, 1, 2, 2, "ot-group", " <name>") \
+	FLT_OT_PARSE_GROUP_DEF(SCOPES, 0, 0, 2, 0, "scopes",   " <name> ...")
+
+#define FLT_OT_PARSE_SCOPE_DEFINES                                                                                    \
+	FLT_OT_PARSE_SCOPE_DEF(     ID, 0, 1, 2, 2, "ot-scope", " <name>")                                            \
+	FLT_OT_PARSE_SCOPE_DEF(   SPAN, 0, 0, 2, 5, "span",     " <name> [<reference>] [root]")                       \
+	FLT_OT_PARSE_SCOPE_DEF(    TAG, 1, 0, 3, 0, "tag",      " <name> <sample> ...")                               \
+	FLT_OT_PARSE_SCOPE_DEF(    LOG, 1, 0, 3, 0, "log",      " <name> <sample> ...")                               \
+	FLT_OT_PARSE_SCOPE_DEF(BAGGAGE, 1, 4, 3, 0, "baggage",  " <name> <sample> ...")                               \
+	FLT_OT_PARSE_SCOPE_DEF( INJECT, 1, 3, 2, 4, "inject",   " <name-prefix> [use-vars] [use-headers]")            \
+	FLT_OT_PARSE_SCOPE_DEF(EXTRACT, 0, 3, 2, 3, "extract",  " <name-prefix> [use-vars | use-headers]")            \
+	FLT_OT_PARSE_SCOPE_DEF( FINISH, 0, 0, 2, 0, "finish",   " <name> ...")                                        \
+	FLT_OT_PARSE_SCOPE_DEF(    ACL, 0, 1, 3, 0, "acl",      " <name> <criterion> [flags] [operator] <value> ...") \
+	FLT_OT_PARSE_SCOPE_DEF(  EVENT, 0, 0, 2, 0, "event",    " <name> [{ if | unless } <condition>]")
+
+enum FLT_OT_PARSE_TRACER_enum {
+#define FLT_OT_PARSE_TRACER_DEF(a,b,c,d,e,f,g)   FLT_OT_PARSE_TRACER_##a,
+	FLT_OT_PARSE_TRACER_DEFINES
+#undef FLT_OT_PARSE_TRACER_DEF
+};
+
+enum FLT_OT_PARSE_GROUP_enum {
+#define FLT_OT_PARSE_GROUP_DEF(a,b,c,d,e,f,g)   FLT_OT_PARSE_GROUP_##a,
+	FLT_OT_PARSE_GROUP_DEFINES
+#undef FLT_OT_PARSE_GROUP_DEF
+};
+
+enum FLT_OT_PARSE_SCOPE_enum {
+#define FLT_OT_PARSE_SCOPE_DEF(a,b,c,d,e,f,g)   FLT_OT_PARSE_SCOPE_##a,
+	FLT_OT_PARSE_SCOPE_DEFINES
+#undef FLT_OT_PARSE_SCOPE_DEF
+};
+
+enum FLT_OT_CTX_USE_enum {
+	FLT_OT_CTX_USE_VARS    = 1 << 0,
+	FLT_OT_CTX_USE_HEADERS = 1 << 1,
+};
+
+struct flt_ot_parse_data {
+	int         keyword;       /* Keyword index. */
+	bool        flag_check_id; /* Whether the group ID must be defined for the keyword. */
+	int         check_name;    /* Checking allowed characters in the name. */
+	int         args_min;      /* The minimum number of arguments required. */
+	int         args_max;      /* The maximum number of arguments allowed. */
+	const char *name;          /* Keyword name. */
+	const char *usage;         /* Usage text to be printed in case of an error. */
+};
+
+#define FLT_OT_PARSE_WARNING(f, ...) \
+	ha_warning("parsing [%s:%d] : " FLT_OT_FMT_TYPE FLT_OT_FMT_NAME "'" f "'\n", ##__VA_ARGS__);
+#define FLT_OT_PARSE_ALERT(f, ...)                                                                         \
+	do {                                                                                               \
+		ha_alert("parsing [%s:%d] : " FLT_OT_FMT_TYPE FLT_OT_FMT_NAME "'" f "'\n", ##__VA_ARGS__); \
+                                                                                                           \
+		retval |= ERR_ABORT | ERR_ALERT;                                                           \
+	} while (0)
+#define FLT_OT_POST_PARSE_ALERT(f, ...) \
+	FLT_OT_PARSE_ALERT(f, flt_ot_current_config->cfg_file, ##__VA_ARGS__)
+
+#define FLT_OT_PARSE_ERR(e,f, ...)                              \
+	do {                                                    \
+		if (*(e) == NULL)                               \
+			(void)memprintf((e), f, ##__VA_ARGS__); \
+                                                                \
+		retval |= ERR_ABORT | ERR_ALERT;                \
+	} while (0)
+#define FLT_OT_PARSE_IFERR_ALERT()                            \
+	do {                                                  \
+		if (err == NULL)                              \
+			break;                                \
+                                                              \
+		FLT_OT_PARSE_ALERT("%s", file, linenum, err); \
+		FLT_OT_ERR_FREE(err);                         \
+	} while (0)
+
+#endif /* _OPENTRACING_PARSER_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/pool.h b/contrib/opentracing/include/pool.h
new file mode 100644
index 0000000..df72c84
--- /dev/null
+++ b/contrib/opentracing/include/pool.h
@@ -0,0 +1,39 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_POOL_H_
+#define _OPENTRACING_POOL_H_
+
+void          *flt_ot_pool_alloc(struct pool_head *pool, size_t size, bool flag_clear, char **err);
+void          *flt_ot_pool_strndup(struct pool_head *pool, const char *s, size_t size, char **err);
+void           flt_ot_pool_free(struct pool_head *pool, void **ptr);
+
+struct buffer *flt_ot_trash_alloc(bool flag_clear, char **err);
+void           flt_ot_trash_free(struct buffer **ptr);
+
+#endif /* _OPENTRACING_POOL_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/scope.h b/contrib/opentracing/include/scope.h
new file mode 100644
index 0000000..7b3f265
--- /dev/null
+++ b/contrib/opentracing/include/scope.h
@@ -0,0 +1,141 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_SCOPE_H_
+#define _OPENTRACING_SCOPE_H_
+
+#define FLT_OT_SCOPE_SPAN_FINISH_REQ   "*req*"
+#define FLT_OT_SCOPE_SPAN_FINISH_RES   "*res*"
+#define FLT_OT_SCOPE_SPAN_FINISH_ALL   "*"
+
+#define FLT_OT_RT_CTX(a)               ((struct flt_ot_runtime_context *)(a))
+
+#define FLT_OT_DBG_SCOPE_SPAN(f,a)                                         \
+	FLT_OT_DBG(3, "%s%p:{ '%s' %zu %u %hhu %p %d %p %p }",             \
+	           (f), (a), FLT_OT_STR_HDR_ARGS(a, id), (a)->smp_opt_dir, \
+	           (a)->flag_finish, (a)->span, (a)->ref_type, (a)->ref_span, (a)->ref_ctx)
+
+#define FLT_OT_DBG_SCOPE_CONTEXT(f,a)                                      \
+	FLT_OT_DBG(3, "%s%p:{ '%s' %zu %u %hhu %p }",                      \
+	           (f), (a), FLT_OT_STR_HDR_ARGS(a, id), (a)->smp_opt_dir, \
+	           (a)->flag_finish, (a)->context)
+
+#define FLT_OT_DBG_SCOPE_DATA(f,a)               \
+	FLT_OT_DBG(3, "%s%p:{ %p %d %p %p %d }", \
+	           (f), (a), (a)->tags, (a)->num_tags, (a)->baggage, (a)->log_fields, (a)->num_log_fields)
+
+#define FLT_OT_DBG_RUNTIME_CONTEXT(f,a)                                                                                    \
+	FLT_OT_DBG(3, "%s%p:{ %p %p { %016" PRIx64 " %016" PRIx64 " '%s' } %hhu %hhu 0x%02hhx 0x%08x %s %s }",             \
+	           (f), (a), (a)->stream, (a)->filter, (a)->uuid.u64[0], (a)->uuid.u64[1], (a)->uuid.s, (a)->flag_harderr, \
+	           (a)->flag_disabled, (a)->logging, (a)->analyzers, flt_ot_list_debug(&((a)->spans)),                     \
+	           flt_ot_list_debug(&((a)->contexts)))
+
+#define FLT_OT_CONST_STR_HDR(a)      \
+	struct {                     \
+		const char *a;       \
+		size_t      a##_len; \
+	}
+
+
+struct flt_ot_scope_data {
+	struct otc_tag        tags[FLT_OT_MAXTAGS];         /* Defined tags. */
+	int                   num_tags;                     /* The number of tags used. */
+	struct otc_text_map  *baggage;                      /* Defined baggage. */
+	struct otc_log_field  log_fields[OTC_MAXLOGFIELDS]; /* Defined logs. */
+	int                   num_log_fields;               /* The number of log fields used. */
+};
+
+/* flt_ot_runtime_context->spans */
+struct flt_ot_scope_span {
+	FLT_OT_CONST_STR_HDR(id);               /* The span operation name/len. */
+	uint                       smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
+	bool                       flag_finish; /* Whether the span is marked for completion. */
+	struct otc_span           *span;        /* The current span. */
+	otc_span_reference_type_t  ref_type;    /* Span reference type. */
+	struct otc_span           *ref_span;    /* Span to which the current span refers. */
+	struct otc_span_context   *ref_ctx;     /* Span context to which the current span refers. */
+	struct list                list;        /* Used to chain this structure. */
+};
+
+/* flt_ot_runtime_context->contexts */
+struct flt_ot_scope_context {
+	FLT_OT_CONST_STR_HDR(id);             /* The span context name/len. */
+	uint                     smp_opt_dir; /* SMP_OPT_DIR_RE(Q|S) */
+	bool                     flag_finish; /* Whether the span context is marked for completion. */
+	struct otc_span_context *context;     /* The current span context. */
+	struct list              list;        /* Used to chain this structure. */
+};
+
+struct flt_ot_uuid {
+	union {
+		uint64_t u64[2];
+		uint8_t  u8[16];
+		struct {
+			uint32_t time_low;
+			uint16_t time_mid;
+			uint16_t time_hi_and_version;
+			uint16_t clock_seq;
+			uint64_t node : 48;
+		} __attribute__((packed));
+	};
+	char s[40];
+};
+
+/* The runtime filter context attached to a stream. */
+struct flt_ot_runtime_context {
+	struct stream      *stream;        /* The stream to which the filter is attached. */
+	struct filter      *filter;        /* The OpenTracing filter. */
+	struct flt_ot_uuid  uuid;          /* Randomly generated UUID. */
+	bool                flag_harderr;  /* [0 1] */
+	bool                flag_disabled; /* [0 1] */
+	uint8_t             logging;       /* [0 1 3] */
+	uint                analyzers;     /* Executed channel analyzers. */
+	struct list         spans;         /* The scope spans. */
+	struct list         contexts;      /* The scope contexts. */
+};
+
+
+#ifndef DEBUG_OT
+#  define flt_ot_pools_info()   while (0)
+#else
+void                           flt_ot_pools_info(void);
+#endif
+struct flt_ot_runtime_context *flt_ot_runtime_context_init(struct stream *s, struct filter *f, char **err);
+void                           flt_ot_runtime_context_free(struct filter *f);
+
+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);
+void                           flt_ot_scope_span_free(struct flt_ot_scope_span **ptr);
+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);
+void                           flt_ot_scope_context_free(struct flt_ot_scope_context **ptr);
+void                           flt_ot_scope_data_free(struct flt_ot_scope_data *ptr);
+
+int                            flt_ot_scope_finish_mark(const struct flt_ot_runtime_context *rt_ctx, const char *id, size_t id_len);
+void                           flt_ot_scope_finish_marked(const struct flt_ot_runtime_context *rt_ctx, const struct timespec *ts_finish);
+void                           flt_ot_scope_free_unused(struct flt_ot_runtime_context *rt_ctx, struct channel *chn);
+
+#endif /* _OPENTRACING_SCOPE_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/util.h b/contrib/opentracing/include/util.h
new file mode 100644
index 0000000..776ddd2
--- /dev/null
+++ b/contrib/opentracing/include/util.h
@@ -0,0 +1,109 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_UTIL_H_
+#define _OPENTRACING_UTIL_H_
+
+#define HTTP_METH_STR_OPTIONS   "OPTIONS"
+#define HTTP_METH_STR_GET       "GET"
+#define HTTP_METH_STR_HEAD      "HEAD"
+#define HTTP_METH_STR_POST      "POST"
+#define HTTP_METH_STR_PUT       "PUT"
+#define HTTP_METH_STR_DELETE    "DELETE"
+#define HTTP_METH_STR_TRACE     "TRACE"
+#define HTTP_METH_STR_CONNECT   "CONNECT"
+
+/* Defined in include/haproxy/channel-t.h. */
+#define FLT_OT_AN_DEFINES                     \
+	FLT_OT_AN_DEF(AN_REQ_INSPECT_FE)      \
+	FLT_OT_AN_DEF(AN_REQ_WAIT_HTTP)       \
+	FLT_OT_AN_DEF(AN_REQ_HTTP_BODY)       \
+	FLT_OT_AN_DEF(AN_REQ_HTTP_PROCESS_FE) \
+	FLT_OT_AN_DEF(AN_REQ_SWITCHING_RULES) \
+	FLT_OT_AN_DEF(AN_REQ_INSPECT_BE)      \
+	FLT_OT_AN_DEF(AN_REQ_HTTP_PROCESS_BE) \
+	FLT_OT_AN_DEF(AN_REQ_HTTP_TARPIT)     \
+	FLT_OT_AN_DEF(AN_REQ_SRV_RULES)       \
+	FLT_OT_AN_DEF(AN_REQ_HTTP_INNER)      \
+	FLT_OT_AN_DEF(AN_REQ_PRST_RDP_COOKIE) \
+	FLT_OT_AN_DEF(AN_REQ_STICKING_RULES)  \
+	FLT_OT_AN_DEF(AN_REQ_HTTP_XFER_BODY)  \
+	FLT_OT_AN_DEF(AN_REQ_WAIT_CLI)        \
+	FLT_OT_AN_DEF(AN_RES_INSPECT)         \
+	FLT_OT_AN_DEF(AN_RES_WAIT_HTTP)       \
+	FLT_OT_AN_DEF(AN_RES_STORE_RULES)     \
+	FLT_OT_AN_DEF(AN_RES_HTTP_PROCESS_BE) \
+	FLT_OT_AN_DEF(AN_RES_HTTP_PROCESS_FE) \
+	FLT_OT_AN_DEF(AN_RES_HTTP_XFER_BODY)  \
+	FLT_OT_AN_DEF(AN_RES_WAIT_CLI)
+
+#define FLT_OT_PROXIES_LIST_START()                                             \
+	do {                                                                    \
+		struct flt_conf *fconf;                                         \
+		struct proxy    *px;                                            \
+                                                                                \
+		for (px = proxies_list; px != NULL; px = px->next)              \
+			list_for_each_entry(fconf, &(px->filter_configs), list) \
+				if (fconf->id == ot_flt_id) {                   \
+					struct flt_ot_conf *conf = fconf->conf;
+#define FLT_OT_PROXIES_LIST_END() \
+				} \
+	} while (0)
+
+#ifdef DEBUG_OT
+#  define FLT_OT_ARGS_DUMP()   do { if (flt_ot_debug.level & (1 << 2)) flt_ot_args_dump(args); } while (0)
+#else
+#  define FLT_OT_ARGS_DUMP()   while (0)
+#endif
+
+
+#ifndef DEBUG_OT
+#  define flt_ot_filters_dump()   while (0)
+#else
+void        flt_ot_args_dump(char **args);
+void        flt_ot_filters_dump(void);
+const char *flt_ot_chn_label(const struct channel *chn);
+const char *flt_ot_pr_mode(const struct stream *s);
+const char *flt_ot_stream_pos(const struct stream *s);
+const char *flt_ot_type(const struct filter *f);
+const char *flt_ot_analyzer(uint an_bit);
+const char *flt_ot_str_hex(const void *data, size_t size);
+const char *flt_ot_str_ctrl(const void *data, size_t size);
+const char *flt_ot_list_debug(const struct list *head);
+#endif
+
+ssize_t     flt_ot_chunk_add(struct buffer *chk, const void *src, size_t n, char **err);
+int         flt_ot_args_count(char **args);
+void        flt_ot_args_to_str(char **args, int idx, char **str);
+double      flt_ot_strtod(const char *nptr, double limit_min, double limit_max, char **err);
+int64_t     flt_ot_strtoll(const char *nptr, int64_t limit_min, int64_t limit_max, char **err);
+int         flt_ot_sample_to_str(const struct sample_data *data, char *value, size_t size, char **err);
+int         flt_ot_sample_to_value(const char *key, const struct sample_data *data, struct otc_value *value, char **err);
+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);
+
+#endif /* _OPENTRACING_UTIL_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
diff --git a/contrib/opentracing/include/vars.h b/contrib/opentracing/include/vars.h
new file mode 100644
index 0000000..5ce8879
--- /dev/null
+++ b/contrib/opentracing/include/vars.h
@@ -0,0 +1,49 @@
+/***
+ * 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.
+ */
+#ifndef _OPENTRACING_VARS_H_
+#define _OPENTRACING_VARS_H_
+
+#define FLT_OT_VARS_SCOPE       "txn"
+#define FLT_OT_VAR_CHAR_DASH    'D'
+#define FLT_OT_VAR_CHAR_SPACE   'S'
+
+
+#ifndef DEBUG_OT
+#  define flt_ot_vars_dump(...)   while (0)
+#else
+void                 flt_ot_vars_dump(struct stream *s);
+#endif
+int                  flt_ot_var_register(const char *scope, const char *prefix, const char *name, char **err);
+int                  flt_ot_var_set(struct stream *s, const char *scope, const char *prefix, const char *name, const char *value, uint opt, char **err);
+int                  flt_ot_var_unset(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err);
+int                  flt_ot_vars_unset(struct stream *s, const char *scope, const char *prefix, uint opt, char **err);
+int                  flt_ot_var_get(struct stream *s, const char *scope, const char *prefix, const char *name, char **value, uint opt, char **err);
+struct otc_text_map *flt_ot_vars_get(struct stream *s, const char *scope, const char *prefix, uint opt, char **err);
+
+#endif /* _OPENTRACING_VARS_H_ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ *
+ * vi: noexpandtab shiftwidth=8 tabstop=8
+ */
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
+ */
diff --git a/contrib/opentracing/test/README-speed-cmp b/contrib/opentracing/test/README-speed-cmp
new file mode 100644
index 0000000..9251faa
--- /dev/null
+++ b/contrib/opentracing/test/README-speed-cmp
@@ -0,0 +1,111 @@
+--- rate-limit 100.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   650.95us  431.15us  46.44ms   96.67%
+    Req/Sec     1.44k    51.39     2.57k    74.89%
+  Latency Distribution
+     50%  608.00us
+     75%  760.00us
+     90%    0.91ms
+     99%    1.31ms
+  3434836 requests in 5.00m, 0.89GB read
+Requests/sec:  11446.99
+Transfer/sec:      3.03MB
+----------------------------------------------------------------------
+
+--- rate-limit 50.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   398.00us  371.39us  22.56ms   97.23%
+    Req/Sec     2.32k    84.01     2.76k    74.84%
+  Latency Distribution
+     50%  350.00us
+     75%  467.00us
+     90%  593.00us
+     99%    1.03ms
+  5530848 requests in 5.00m, 1.43GB read
+Requests/sec:  18434.31
+Transfer/sec:      4.89MB
+----------------------------------------------------------------------
+
+--- rate-limit 10.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   316.75us  351.92us  23.00ms   98.57%
+    Req/Sec     2.87k    94.02     3.22k    79.30%
+  Latency Distribution
+     50%  273.00us
+     75%  342.00us
+     90%  424.00us
+     99%    0.94ms
+  6859293 requests in 5.00m, 1.78GB read
+Requests/sec:  22862.16
+Transfer/sec:      6.06MB
+----------------------------------------------------------------------
+
+--- rate-limit 2.5 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   307.90us  368.64us  26.08ms   98.71%
+    Req/Sec     2.96k   103.84     3.23k    83.76%
+  Latency Distribution
+     50%  264.00us
+     75%  327.00us
+     90%  402.00us
+     99%    0.97ms
+  7065667 requests in 5.00m, 1.83GB read
+Requests/sec:  23550.37
+Transfer/sec:      6.24MB
+----------------------------------------------------------------------
+
+--- rate-limit 0.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   304.60us  376.36us  30.26ms   98.74%
+    Req/Sec     2.99k   106.93     3.24k    83.08%
+  Latency Distribution
+     50%  262.00us
+     75%  323.00us
+     90%  396.00us
+     99%    0.95ms
+  7136261 requests in 5.00m, 1.85GB read
+Requests/sec:  23785.77
+Transfer/sec:      6.31MB
+----------------------------------------------------------------------
+
+--- rate-limit disabled --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   300.90us  342.35us  22.13ms   98.74%
+    Req/Sec     3.00k    95.67     3.33k    81.11%
+  Latency Distribution
+     50%  261.00us
+     75%  322.00us
+     90%  394.00us
+     99%  806.00us
+  7159525 requests in 5.00m, 1.85GB read
+Requests/sec:  23863.05
+Transfer/sec:      6.33MB
+----------------------------------------------------------------------
+
+--- rate-limit off --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   302.51us  371.99us  30.26ms   98.77%
+    Req/Sec     3.00k   104.43     3.73k    83.74%
+  Latency Distribution
+     50%  260.00us
+     75%  321.00us
+     90%  394.00us
+     99%    0.89ms
+  7170345 requests in 5.00m, 1.86GB read
+Requests/sec:  23898.19
+Transfer/sec:      6.34MB
+----------------------------------------------------------------------
diff --git a/contrib/opentracing/test/README-speed-ctx b/contrib/opentracing/test/README-speed-ctx
new file mode 100644
index 0000000..fa8fc2c
--- /dev/null
+++ b/contrib/opentracing/test/README-speed-ctx
@@ -0,0 +1,111 @@
+--- rate-limit 100.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency     2.49ms  799.87us  43.00ms   70.90%
+    Req/Sec   393.01     20.61   696.00     71.68%
+  Latency Distribution
+     50%    2.50ms
+     75%    3.00ms
+     90%    3.38ms
+     99%    4.23ms
+  939237 requests in 5.00m, 249.01MB read
+Requests/sec:   3130.01
+Transfer/sec:    849.75KB
+----------------------------------------------------------------------
+
+--- rate-limit 50.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency     1.27ms    0.97ms  40.77ms   56.91%
+    Req/Sec   778.22     70.30     1.36k    69.10%
+  Latency Distribution
+     50%    1.36ms
+     75%    1.80ms
+     90%    2.49ms
+     99%    3.51ms
+  1859055 requests in 5.00m, 492.88MB read
+Requests/sec:   6195.58
+Transfer/sec:      1.64MB
+----------------------------------------------------------------------
+
+--- rate-limit 10.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   442.00us  481.47us  31.61ms   90.27%
+    Req/Sec     2.25k   130.05     2.73k    72.83%
+  Latency Distribution
+     50%  287.00us
+     75%  526.00us
+     90%    0.92ms
+     99%    1.76ms
+  5380213 requests in 5.00m, 1.39GB read
+Requests/sec:  17930.27
+Transfer/sec:      4.75MB
+----------------------------------------------------------------------
+
+--- rate-limit 2.5 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   346.65us  414.65us  28.50ms   95.63%
+    Req/Sec     2.75k   159.74     3.23k    84.68%
+  Latency Distribution
+     50%  271.00us
+     75%  353.00us
+     90%  505.00us
+     99%    1.55ms
+  6560093 requests in 5.00m, 1.70GB read
+Requests/sec:  21864.43
+Transfer/sec:      5.80MB
+----------------------------------------------------------------------
+
+--- rate-limit 0.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   313.32us  402.25us  24.73ms   98.55%
+    Req/Sec     2.95k   145.03     3.21k    88.99%
+  Latency Distribution
+     50%  264.00us
+     75%  327.00us
+     90%  403.00us
+     99%    1.33ms
+  7050847 requests in 5.00m, 1.83GB read
+Requests/sec:  23501.14
+Transfer/sec:      6.23MB
+----------------------------------------------------------------------
+
+--- rate-limit disabled --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   310.19us  384.76us  22.18ms   98.66%
+    Req/Sec     2.96k   115.62     3.37k    84.30%
+  Latency Distribution
+     50%  265.00us
+     75%  327.00us
+     90%  402.00us
+     99%    1.10ms
+  7058682 requests in 5.00m, 1.83GB read
+Requests/sec:  23526.70
+Transfer/sec:      6.24MB
+----------------------------------------------------------------------
+
+--- rate-limit off --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   305.86us  367.56us  25.76ms   98.65%
+    Req/Sec     2.99k   116.93     3.43k    85.59%
+  Latency Distribution
+     50%  261.00us
+     75%  322.00us
+     90%  396.00us
+     99%    1.09ms
+  7137173 requests in 5.00m, 1.85GB read
+Requests/sec:  23788.84
+Transfer/sec:      6.31MB
+----------------------------------------------------------------------
diff --git a/contrib/opentracing/test/README-speed-fe-be b/contrib/opentracing/test/README-speed-fe-be
new file mode 100644
index 0000000..ab2b7af
--- /dev/null
+++ b/contrib/opentracing/test/README-speed-fe-be
@@ -0,0 +1,111 @@
+--- rate-limit 100.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency     0.89ms  466.84us  35.44ms   94.39%
+    Req/Sec     1.09k    39.30     1.32k    72.60%
+  Latency Distribution
+     50%  823.00us
+     75%    1.00ms
+     90%    1.20ms
+     99%    2.14ms
+  2594524 requests in 5.00m, 687.86MB read
+Requests/sec:   8645.83
+Transfer/sec:      2.29MB
+----------------------------------------------------------------------
+
+--- rate-limit 50.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   681.74us  463.28us  20.45ms   95.46%
+    Req/Sec     1.41k    54.00     1.60k    68.97%
+  Latency Distribution
+     50%  613.00us
+     75%  785.00us
+     90%    0.98ms
+     99%    2.06ms
+  3367473 requests in 5.00m, 0.87GB read
+Requests/sec:  11222.76
+Transfer/sec:      2.98MB
+----------------------------------------------------------------------
+
+--- rate-limit 10.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   558.32us  458.54us  29.40ms   97.73%
+    Req/Sec     1.72k    60.67     2.05k    73.10%
+  Latency Distribution
+     50%  494.00us
+     75%  610.00us
+     90%  743.00us
+     99%    2.08ms
+  4105420 requests in 5.00m, 1.06GB read
+Requests/sec:  13683.36
+Transfer/sec:      3.63MB
+----------------------------------------------------------------------
+
+--- rate-limit 2.5 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   542.66us  440.31us  22.63ms   97.88%
+    Req/Sec     1.76k    60.02     2.00k    72.27%
+  Latency Distribution
+     50%  481.00us
+     75%  588.00us
+     90%  710.00us
+     99%    2.05ms
+  4214525 requests in 5.00m, 1.09GB read
+Requests/sec:  14046.76
+Transfer/sec:      3.72MB
+----------------------------------------------------------------------
+
+--- rate-limit 0.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   529.06us  414.38us  30.09ms   97.97%
+    Req/Sec     1.80k    59.34     2.05k    74.47%
+  Latency Distribution
+     50%  473.00us
+     75%  576.00us
+     90%  692.00us
+     99%    1.79ms
+  4287428 requests in 5.00m, 1.11GB read
+Requests/sec:  14290.45
+Transfer/sec:      3.79MB
+----------------------------------------------------------------------
+
+--- rate-limit disabled --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   517.81us  463.10us  36.81ms   98.25%
+    Req/Sec     1.85k    62.39     2.21k    75.65%
+  Latency Distribution
+     50%  458.00us
+     75%  558.00us
+     90%  670.00us
+     99%    1.96ms
+  4416273 requests in 5.00m, 1.14GB read
+Requests/sec:  14719.43
+Transfer/sec:      3.90MB
+----------------------------------------------------------------------
+
+--- rate-limit off --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   511.67us  428.18us  27.68ms   98.15%
+    Req/Sec     1.86k    60.67     2.05k    75.44%
+  Latency Distribution
+     50%  455.00us
+     75%  554.00us
+     90%  666.00us
+     99%    1.81ms
+  4441271 requests in 5.00m, 1.15GB read
+Requests/sec:  14803.32
+Transfer/sec:      3.92MB
+----------------------------------------------------------------------
diff --git a/contrib/opentracing/test/README-speed-sa b/contrib/opentracing/test/README-speed-sa
new file mode 100644
index 0000000..ea8749d
--- /dev/null
+++ b/contrib/opentracing/test/README-speed-sa
@@ -0,0 +1,111 @@
+--- rate-limit 100.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency     1.24ms  522.78us  35.59ms   79.12%
+    Req/Sec   767.71     38.72     3.02k    72.19%
+  Latency Distribution
+     50%    1.20ms
+     75%    1.51ms
+     90%    1.78ms
+     99%    2.37ms
+  1834067 requests in 5.00m, 486.25MB read
+Requests/sec:   6111.57
+Transfer/sec:      1.62MB
+----------------------------------------------------------------------
+
+--- rate-limit 50.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   593.11us  476.81us  43.00ms   91.27%
+    Req/Sec     1.59k    81.15     2.07k    71.14%
+  Latency Distribution
+     50%  549.00us
+     75%  788.00us
+     90%    1.03ms
+     99%    1.62ms
+  3795987 requests in 5.00m, 0.98GB read
+Requests/sec:  12650.65
+Transfer/sec:      3.35MB
+----------------------------------------------------------------------
+
+--- rate-limit 10.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   326.02us  355.00us  29.23ms   98.05%
+    Req/Sec     2.80k    88.05     3.30k    75.36%
+  Latency Distribution
+     50%  277.00us
+     75%  356.00us
+     90%  456.00us
+     99%    0.97ms
+  6675563 requests in 5.00m, 1.73GB read
+Requests/sec:  22249.78
+Transfer/sec:      5.90MB
+----------------------------------------------------------------------
+
+--- rate-limit 2.5 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   311.77us  357.45us  24.11ms   98.62%
+    Req/Sec     2.91k    94.70     3.18k    78.52%
+  Latency Distribution
+     50%  268.00us
+     75%  334.00us
+     90%  413.00us
+     99%    0.94ms
+  6960933 requests in 5.00m, 1.80GB read
+Requests/sec:  23201.07
+Transfer/sec:      6.15MB
+----------------------------------------------------------------------
+
+--- rate-limit 0.0 --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   302.51us  330.50us  25.84ms   98.69%
+    Req/Sec     2.98k    91.46     3.40k    78.84%
+  Latency Distribution
+     50%  263.00us
+     75%  325.00us
+     90%  397.00us
+     99%  812.00us
+  7112084 requests in 5.00m, 1.84GB read
+Requests/sec:  23705.14
+Transfer/sec:      6.28MB
+----------------------------------------------------------------------
+
+--- rate-limit disabled --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   303.01us  353.98us  28.03ms   98.76%
+    Req/Sec     2.99k    93.97     3.34k    81.12%
+  Latency Distribution
+     50%  262.00us
+     75%  323.00us
+     90%  395.00us
+     99%  838.00us
+  7133837 requests in 5.00m, 1.85GB read
+Requests/sec:  23777.95
+Transfer/sec:      6.30MB
+----------------------------------------------------------------------
+
+--- rate-limit off --------------------------------------------------
+Running 5m test @ http://localhost:10080/index.html
+  8 threads and 8 connections
+  Thread Stats   Avg      Stdev     Max   +/- Stdev
+    Latency   302.61us  349.74us  25.48ms   98.75%
+    Req/Sec     2.99k    94.85     3.49k    80.75%
+  Latency Distribution
+     50%  262.00us
+     75%  323.00us
+     90%  395.00us
+     99%  822.00us
+  7132714 requests in 5.00m, 1.85GB read
+Requests/sec:  23773.35
+Transfer/sec:      6.30MB
+----------------------------------------------------------------------
diff --git a/contrib/opentracing/test/be/cfg-dd.json b/contrib/opentracing/test/be/cfg-dd.json
new file mode 100644
index 0000000..8b69b03
--- /dev/null
+++ b/contrib/opentracing/test/be/cfg-dd.json
@@ -0,0 +1,5 @@
+{
+    "service":    "BE",
+    "agent_host": "localhost",
+    "agent_port": 8126
+}
diff --git a/contrib/opentracing/test/be/cfg-jaeger.yml b/contrib/opentracing/test/be/cfg-jaeger.yml
new file mode 100644
index 0000000..0893166
--- /dev/null
+++ b/contrib/opentracing/test/be/cfg-jaeger.yml
@@ -0,0 +1,34 @@
+service_name:
+    BE
+
+###
+# When using configuration object to instantiate the tracer, the type of
+# sampling can be selected via sampler.type and sampler.param properties.
+# Jaeger libraries support the following samplers:
+#
+#  - Constant (sampler.type=const) sampler always makes the same decision for
+#    all traces.  It either samples all traces (sampler.param=1) or none of
+#    them (sampler.param=0).
+#
+#  - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling
+#    decision with the probability of sampling equal to the value of
+#    sampler.param property.  For example, with sampler.param=0.1 approximately
+#    1 in 10 traces will be sampled.
+#
+#  - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate
+#    limiter to ensure that traces are sampled with a certain constant rate.
+#    For example, when sampler.param=2.0 it will sample requests with the rate
+#    of 2 traces per second.
+#
+#  - Remote (sampler.type=remote, which is also the default) sampler consults
+#    Jaeger agent for the appropriate sampling strategy to use in the current
+#    service.  This allows controlling the sampling strategies in the services
+#    from a central configuration in Jaeger backend, or even dynamically.
+#
+sampler:
+    type: ratelimiting
+    param: 10.0
+
+reporter:
+    logSpans: true
+    localAgentHostPort: localhost:6831
diff --git a/contrib/opentracing/test/be/cfg-zipkin.json b/contrib/opentracing/test/be/cfg-zipkin.json
new file mode 100644
index 0000000..f0e30d5
--- /dev/null
+++ b/contrib/opentracing/test/be/cfg-zipkin.json
@@ -0,0 +1,4 @@
+{
+    "service_name":   "BE",
+    "collector_host": "localhost"
+}
diff --git a/contrib/opentracing/test/be/haproxy.cfg b/contrib/opentracing/test/be/haproxy.cfg
new file mode 100644
index 0000000..c225a2f
--- /dev/null
+++ b/contrib/opentracing/test/be/haproxy.cfg
@@ -0,0 +1,37 @@
+global
+#   nbthread 1
+    maxconn 5000
+    hard-stop-after 10s
+#   log localhost:514 local7 debug
+#   debug
+    stats socket /tmp/haproxy-be.sock mode 666 level admin
+
+defaults
+    log     global
+    mode    http
+    option  httplog
+    option  dontlognull
+    option  httpclose
+    retries 3
+    maxconn 4000
+    timeout connect 5000
+    timeout client  50000
+    timeout server  50000
+
+listen stats
+    mode http
+    bind *:8002
+    stats uri /
+    stats admin if TRUE
+    stats refresh 10s
+
+frontend ot-test-be-frontend
+    bind *:11080
+    mode http
+    default_backend servers-backend
+
+    filter opentracing id ot-test-be config be/ot.cfg
+
+backend servers-backend
+    mode http
+    server server-1 127.0.0.1:8000
diff --git a/contrib/opentracing/test/be/ot.cfg b/contrib/opentracing/test/be/ot.cfg
new file mode 100644
index 0000000..edd3f76
--- /dev/null
+++ b/contrib/opentracing/test/be/ot.cfg
@@ -0,0 +1,62 @@
+[ot-test-be]
+    ot-tracer ot-test-tracer
+        config be/cfg-jaeger.yml
+        plugin libjaeger_opentracing_plugin-0.5.0.so
+#       log localhost:514 local7 debug
+        option dontlog-normal
+        option hard-errors
+        no option disabled
+
+        scopes frontend_http_request
+        scopes backend_tcp_request
+        scopes backend_http_request
+        scopes client_session_end
+
+        scopes server_session_start
+        scopes tcp_response
+        scopes http_response
+        scopes server_session_end
+
+    ot-scope frontend_http_request
+        extract "ot-ctx" use-headers
+        span "HAProxy session" child-of "ot-ctx" root
+            baggage "haproxy_id" var(sess.ot.uuid)
+        span "Client session" child-of "HAProxy session"
+        span "Frontend HTTP request" child-of "Client session"
+            tag "http.method" method
+            tag "http.url" url
+            tag "http.version" str("HTTP/") req.ver
+        event on-frontend-http-request
+
+    ot-scope backend_tcp_request
+        span "Backend TCP request" follows-from "Frontend HTTP request"
+        finish "Frontend HTTP request"
+        event on-backend-tcp-request
+
+    ot-scope backend_http_request
+        span "Backend HTTP request" follows-from "Backend TCP request"
+        finish "Backend TCP request"
+        event on-backend-http-request
+
+    ot-scope client_session_end
+        finish "Client session"
+        event on-client-session-end
+
+    ot-scope server_session_start
+        span "Server session" child-of "HAProxy session"
+        finish "Backend HTTP request"
+        event on-server-session-start
+
+    ot-scope tcp_response
+        span "TCP response" child-of "Server session"
+        event on-tcp-response
+
+    ot-scope http_response
+        span "HTTP response" follows-from "TCP response"
+            tag "http.status_code" status
+        finish "TCP response"
+        event on-http-response
+
+    ot-scope server_session_end
+        finish *
+        event on-server-session-end
diff --git a/contrib/opentracing/test/cmp/cfg-dd.json b/contrib/opentracing/test/cmp/cfg-dd.json
new file mode 100644
index 0000000..a931f45
--- /dev/null
+++ b/contrib/opentracing/test/cmp/cfg-dd.json
@@ -0,0 +1,5 @@
+{
+    "service":    "CMP",
+    "agent_host": "localhost",
+    "agent_port": 8126
+}
diff --git a/contrib/opentracing/test/cmp/cfg-jaeger.yml b/contrib/opentracing/test/cmp/cfg-jaeger.yml
new file mode 100644
index 0000000..78efc2d
--- /dev/null
+++ b/contrib/opentracing/test/cmp/cfg-jaeger.yml
@@ -0,0 +1,34 @@
+service_name:
+    CMP
+
+###
+# When using configuration object to instantiate the tracer, the type of
+# sampling can be selected via sampler.type and sampler.param properties.
+# Jaeger libraries support the following samplers:
+#
+#  - Constant (sampler.type=const) sampler always makes the same decision for
+#    all traces.  It either samples all traces (sampler.param=1) or none of
+#    them (sampler.param=0).
+#
+#  - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling
+#    decision with the probability of sampling equal to the value of
+#    sampler.param property.  For example, with sampler.param=0.1 approximately
+#    1 in 10 traces will be sampled.
+#
+#  - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate
+#    limiter to ensure that traces are sampled with a certain constant rate.
+#    For example, when sampler.param=2.0 it will sample requests with the rate
+#    of 2 traces per second.
+#
+#  - Remote (sampler.type=remote, which is also the default) sampler consults
+#    Jaeger agent for the appropriate sampling strategy to use in the current
+#    service.  This allows controlling the sampling strategies in the services
+#    from a central configuration in Jaeger backend, or even dynamically.
+#
+sampler:
+    type: ratelimiting
+    param: 10.0
+
+reporter:
+    logSpans: true
+    localAgentHostPort: localhost:6831
diff --git a/contrib/opentracing/test/cmp/cfg-zipkin.json b/contrib/opentracing/test/cmp/cfg-zipkin.json
new file mode 100644
index 0000000..7e9d3dd
--- /dev/null
+++ b/contrib/opentracing/test/cmp/cfg-zipkin.json
@@ -0,0 +1,4 @@
+{
+    "service_name":   "CMP",
+    "collector_host": "localhost"
+}
diff --git a/contrib/opentracing/test/cmp/haproxy.cfg b/contrib/opentracing/test/cmp/haproxy.cfg
new file mode 100644
index 0000000..9d22725
--- /dev/null
+++ b/contrib/opentracing/test/cmp/haproxy.cfg
@@ -0,0 +1,36 @@
+global
+#   nbthread 1
+    maxconn 5000
+    hard-stop-after 10s
+    stats socket /tmp/haproxy.sock mode 666 level admin
+
+defaults
+    log     global
+    mode    http
+    option  httplog
+    option  dontlognull
+    option  httpclose
+    retries 3
+    maxconn 4000
+    timeout connect 5000
+    timeout client  50000
+    timeout server  50000
+
+listen stats
+    mode http
+    bind *:8001
+    stats uri /
+    stats admin if TRUE
+    stats refresh 10s
+
+frontend ot-test-cmp-frontend
+    bind *:10080
+    mode http
+    default_backend servers-backend
+
+    acl acl-http-status-ok status 100:399
+    filter opentracing id ot-test-cmp config cmp/ot.cfg
+
+backend servers-backend
+    mode http
+    server server-1 127.0.0.1:8000
diff --git a/contrib/opentracing/test/cmp/ot.cfg b/contrib/opentracing/test/cmp/ot.cfg
new file mode 100644
index 0000000..21b15dd
--- /dev/null
+++ b/contrib/opentracing/test/cmp/ot.cfg
@@ -0,0 +1,83 @@
+[ot-test-cmp]
+    ot-tracer ot-test-tracer
+        config cmp/cfg-jaeger.yml
+        plugin libjaeger_opentracing_plugin-0.5.0.so
+#       log localhost:514 local7 debug
+        option dontlog-normal
+        option hard-errors
+        no option disabled
+        rate-limit 100.0
+
+        scopes client_session_start
+        scopes frontend_tcp_request
+        scopes frontend_http_request
+        scopes backend_tcp_request
+        scopes backend_http_request
+        scopes server_unavailable
+
+        scopes server_session_start
+        scopes tcp_response
+        scopes http_response http_response-error server_session_end client_session_end
+
+    ot-scope client_session_start
+        span "HAProxy session" root
+            baggage "haproxy_id" var(sess.ot.uuid)
+        span "Client session" child-of "HAProxy session"
+        event on-client-session-start
+
+    ot-scope frontend_tcp_request
+        span "Frontend TCP request" child-of "Client session"
+        event on-frontend-tcp-request
+
+    ot-scope frontend_http_request
+        span "Frontend HTTP request" follows-from "Frontend TCP request"
+            tag "http.method" method
+            tag "http.url" url
+            tag "http.version" str("HTTP/") req.ver
+        finish "Frontend TCP request"
+        event on-frontend-http-request
+
+    ot-scope backend_tcp_request
+        span "Backend TCP request" follows-from "Frontend HTTP request"
+        finish "Frontend HTTP request"
+        event on-backend-tcp-request
+
+    ot-scope backend_http_request
+        span "Backend HTTP request" follows-from "Backend TCP request"
+        finish "Backend TCP request"
+        event on-backend-http-request
+
+    ot-scope server_unavailable
+        span "HAProxy session"
+            tag "error" bool(true)
+            log "status" str("503 Service Unavailable")
+        finish *
+        event on-server-unavailable
+
+    ot-scope server_session_start
+        span "Server session" child-of "HAProxy session"
+        finish "Backend HTTP request"
+        event on-server-session-start
+
+    ot-scope tcp_response
+        span "TCP response" child-of "Server session"
+        event on-tcp-response
+
+    ot-scope http_response
+        span "HTTP response" follows-from "TCP response"
+            tag "http.status_code" status
+        finish "TCP response"
+        event on-http-response
+
+    ot-scope http_response-error
+        span "HTTP response"
+            tag "error" bool(true)
+        event on-http-response if !acl-http-status-ok
+
+    ot-scope server_session_end
+        finish "HTTP response" "Server session"
+        event on-http-response
+
+    ot-scope client_session_end
+        finish "*"
+        event on-http-response
diff --git a/contrib/opentracing/test/ctx/cfg-dd.json b/contrib/opentracing/test/ctx/cfg-dd.json
new file mode 100644
index 0000000..f68d97a
--- /dev/null
+++ b/contrib/opentracing/test/ctx/cfg-dd.json
@@ -0,0 +1,5 @@
+{
+    "service":    "CTX",
+    "agent_host": "localhost",
+    "agent_port": 8126
+}
diff --git a/contrib/opentracing/test/ctx/cfg-jaeger.yml b/contrib/opentracing/test/ctx/cfg-jaeger.yml
new file mode 100644
index 0000000..659724a
--- /dev/null
+++ b/contrib/opentracing/test/ctx/cfg-jaeger.yml
@@ -0,0 +1,34 @@
+service_name:
+    CTX
+
+###
+# When using configuration object to instantiate the tracer, the type of
+# sampling can be selected via sampler.type and sampler.param properties.
+# Jaeger libraries support the following samplers:
+#
+#  - Constant (sampler.type=const) sampler always makes the same decision for
+#    all traces.  It either samples all traces (sampler.param=1) or none of
+#    them (sampler.param=0).
+#
+#  - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling
+#    decision with the probability of sampling equal to the value of
+#    sampler.param property.  For example, with sampler.param=0.1 approximately
+#    1 in 10 traces will be sampled.
+#
+#  - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate
+#    limiter to ensure that traces are sampled with a certain constant rate.
+#    For example, when sampler.param=2.0 it will sample requests with the rate
+#    of 2 traces per second.
+#
+#  - Remote (sampler.type=remote, which is also the default) sampler consults
+#    Jaeger agent for the appropriate sampling strategy to use in the current
+#    service.  This allows controlling the sampling strategies in the services
+#    from a central configuration in Jaeger backend, or even dynamically.
+#
+sampler:
+    type: ratelimiting
+    param: 10.0
+
+reporter:
+    logSpans: true
+    localAgentHostPort: localhost:6831
diff --git a/contrib/opentracing/test/ctx/cfg-zipkin.json b/contrib/opentracing/test/ctx/cfg-zipkin.json
new file mode 100644
index 0000000..3a3a257
--- /dev/null
+++ b/contrib/opentracing/test/ctx/cfg-zipkin.json
@@ -0,0 +1,4 @@
+{
+    "service_name":   "CTX",
+    "collector_host": "localhost"
+}
diff --git a/contrib/opentracing/test/ctx/haproxy.cfg b/contrib/opentracing/test/ctx/haproxy.cfg
new file mode 100644
index 0000000..d240a99
--- /dev/null
+++ b/contrib/opentracing/test/ctx/haproxy.cfg
@@ -0,0 +1,38 @@
+global
+#   nbthread 1
+    maxconn 5000
+    hard-stop-after 10s
+    stats socket /tmp/haproxy.sock mode 666 level admin
+
+defaults
+    log     global
+    mode    http
+    option  httplog
+    option  dontlognull
+    option  httpclose
+    retries 3
+    maxconn 4000
+    timeout connect 5000
+    timeout client  50000
+    timeout server  50000
+
+listen stats
+    mode http
+    bind *:8001
+    stats uri /
+    stats admin if TRUE
+    stats refresh 10s
+
+frontend ot-test-ctx-frontend
+    bind *:10080
+    mode http
+    default_backend servers-backend
+
+    acl acl-http-status-ok status 100:399
+    filter opentracing id ot-test-ctx config ctx/ot.cfg
+    http-response ot-group ot-test-ctx http_response_group if acl-http-status-ok
+    http-after-response ot-group ot-test-ctx http_after_response_group if !acl-http-status-ok
+
+backend servers-backend
+    mode http
+    server server-1 127.0.0.1:8000
diff --git a/contrib/opentracing/test/ctx/ot.cfg b/contrib/opentracing/test/ctx/ot.cfg
new file mode 100644
index 0000000..a06a4e0
--- /dev/null
+++ b/contrib/opentracing/test/ctx/ot.cfg
@@ -0,0 +1,197 @@
+[ot-test-ctx]
+    ot-tracer ot-test-tracer
+        log localhost:514 local7 debug
+        config ctx/cfg-jaeger.yml
+        plugin libjaeger_opentracing_plugin-0.5.0.so
+        option dontlog-normal
+        option hard-errors
+        no option disabled
+        rate-limit 100.0
+
+        groups http_response_group
+        groups http_after_response_group
+
+        scopes client_session_start_1
+        scopes client_session_start_2
+        scopes frontend_tcp_request
+        scopes http_wait_request
+        scopes http_body_request
+        scopes frontend_http_request
+        scopes switching_rules_request
+        scopes backend_tcp_request
+        scopes backend_http_request
+        scopes process_server_rules_request
+        scopes http_process_request
+        scopes tcp_rdp_cookie_request
+        scopes process_sticking_rules_request
+        scopes client_session_end
+        scopes server_unavailable
+
+        scopes server_session_start
+        scopes tcp_response
+        scopes http_wait_response
+        scopes process_store_rules_response
+        scopes http_response http_response-error
+        scopes server_session_end
+
+    ot-group http_response_group
+        scopes http_response_1
+        scopes http_response_2
+
+    ot-scope http_response_1
+        span "HTTP response"
+            log "hdr.content" res.hdr("content-type") str("; length: ") res.hdr("content-length") str(" bytes")
+
+    ot-scope http_response_2
+        span "HTTP response"
+            log "hdr.date" res.hdr("date") str(" / ") res.hdr("last-modified")
+
+    ot-group http_after_response_group
+        scopes http_after_response
+
+    ot-scope http_after_response
+        span "HAProxy response" child-of "HAProxy session"
+            tag "error" bool(true)
+            tag "http.status_code" status
+
+    ot-scope client_session_start_1
+        span "HAProxy session" root
+            inject "ot_ctx_1" use-headers use-vars
+            baggage "haproxy_id" var(sess.ot.uuid)
+        event on-client-session-start
+
+    ot-scope client_session_start_2
+        extract "ot_ctx_1" use-vars
+        span "Client session" child-of "ot_ctx_1"
+            inject "ot_ctx_2" use-headers use-vars
+        event on-client-session-start
+
+    ot-scope frontend_tcp_request
+        extract "ot_ctx_2" use-vars
+        span "Frontend TCP request" child-of "ot_ctx_2"
+            inject "ot_ctx_3" use-headers use-vars
+        event on-frontend-tcp-request
+
+    ot-scope http_wait_request
+        extract "ot_ctx_3" use-vars
+        span "HTTP wait request" follows-from "ot_ctx_3"
+            inject "ot_ctx_4" use-headers use-vars
+        finish "Frontend TCP request" "ot_ctx_3"
+        event on-http-wait-request
+
+    ot-scope http_body_request
+        extract "ot_ctx_4" use-vars
+        span "HTTP body request" follows-from "ot_ctx_4"
+            inject "ot_ctx_5" use-headers use-vars
+        finish "HTTP wait request" "ot_ctx_4"
+        event on-http-body-request
+
+    ot-scope frontend_http_request
+        extract "ot_ctx_5" use-vars
+        span "Frontend HTTP request" follows-from "ot_ctx_5"
+            tag "http.method" method
+            tag "http.url" url
+            tag "http.version" str("HTTP/") req.ver
+            inject "ot_ctx_6" use-headers use-vars
+        finish "HTTP body request" "ot_ctx_5"
+        event on-frontend-http-request
+
+    ot-scope switching_rules_request
+        extract "ot_ctx_6" use-vars
+        span "Switching rules request" follows-from "ot_ctx_6"
+            inject "ot_ctx_7" use-headers use-vars
+        finish "Frontend HTTP request" "ot_ctx_6"
+        event on-switching-rules-request
+
+    ot-scope backend_tcp_request
+        extract "ot_ctx_7" use-vars
+        span "Backend TCP request" follows-from "ot_ctx_7"
+            inject "ot_ctx_8" use-headers use-vars
+        finish "Switching rules request" "ot_ctx_7"
+        event on-backend-tcp-request
+
+    ot-scope backend_http_request
+        extract "ot_ctx_8" use-vars
+        span "Backend HTTP request" follows-from "ot_ctx_8"
+            inject "ot_ctx_9" use-headers use-vars
+        finish "Backend TCP request" "ot_ctx_8"
+        event on-backend-http-request
+
+    ot-scope process_server_rules_request
+        extract "ot_ctx_9" use-vars
+        span "Process server rules request" follows-from "ot_ctx_9"
+            inject "ot_ctx_10" use-headers use-vars
+        finish "Backend HTTP request" "ot_ctx_9"
+        event on-process-server-rules-request
+
+    ot-scope http_process_request
+        extract "ot_ctx_10" use-vars
+        span "HTTP process request" follows-from "ot_ctx_10"
+            inject "ot_ctx_11" use-headers use-vars
+        finish "Process server rules request" "ot_ctx_10"
+        event on-http-process-request
+
+    ot-scope tcp_rdp_cookie_request
+        extract "ot_ctx_11" use-vars
+        span "TCP RDP cookie request" follows-from "ot_ctx_11"
+            inject "ot_ctx_12" use-headers use-vars
+        finish "HTTP process request" "ot_ctx_11"
+        event on-tcp-rdp-cookie-request
+
+    ot-scope process_sticking_rules_request
+        extract "ot_ctx_12" use-vars
+        span "Process sticking rules request" follows-from "ot_ctx_12"
+            inject "ot_ctx_13" use-headers use-vars
+        finish "TCP RDP cookie request" "ot_ctx_12"
+        event on-process-sticking-rules-request
+
+    ot-scope client_session_end
+        finish "Client session" "ot_ctx_2"
+        event on-client-session-end
+
+    ot-scope server_unavailable
+        finish *
+        event on-server-unavailable
+
+    ot-scope server_session_start
+        span "Server session" child-of "ot_ctx_1"
+            inject "ot_ctx_14" use-vars
+        extract "ot_ctx_13" use-vars
+        finish "Process sticking rules request" "ot_ctx_13"
+        event on-server-session-start
+
+    ot-scope tcp_response
+        extract "ot_ctx_14" use-vars
+        span "TCP response" child-of "ot_ctx_14"
+            inject "ot_ctx_15" use-vars
+        event on-tcp-response
+
+    ot-scope http_wait_response
+        extract "ot_ctx_15" use-vars
+        span "HTTP wait response" follows-from "ot_ctx_15"
+            inject "ot_ctx_16" use-headers use-vars
+        finish "TCP response" "ot_ctx_15"
+        event on-http-wait-response
+
+    ot-scope process_store_rules_response
+        extract "ot_ctx_16" use-vars
+        span "Process store rules response" follows-from "ot_ctx_16"
+            inject "ot_ctx_17" use-headers use-vars
+        finish "HTTP wait response" "ot_ctx_16"
+        event on-process-store-rules-response
+
+    ot-scope http_response
+        extract "ot_ctx_17" use-vars
+        span "HTTP response" follows-from "ot_ctx_17"
+            tag "http.status_code" status
+        finish "Process store rules response" "ot_ctx_17"
+        event on-http-response
+
+    ot-scope http_response-error
+        span "HTTP response"
+            tag "error" bool(true)
+        event on-http-response if !acl-http-status-ok
+
+    ot-scope server_session_end
+        finish *
+        event on-server-session-end
diff --git a/contrib/opentracing/test/empty/cfg-dd.json b/contrib/opentracing/test/empty/cfg-dd.json
new file mode 100644
index 0000000..38b65f1
--- /dev/null
+++ b/contrib/opentracing/test/empty/cfg-dd.json
@@ -0,0 +1,5 @@
+{
+    "service":    "EMPTY",
+    "agent_host": "localhost",
+    "agent_port": 8126
+}
diff --git a/contrib/opentracing/test/empty/cfg-jaeger.yml b/contrib/opentracing/test/empty/cfg-jaeger.yml
new file mode 100644
index 0000000..08fadd8
--- /dev/null
+++ b/contrib/opentracing/test/empty/cfg-jaeger.yml
@@ -0,0 +1,34 @@
+service_name:
+    EMPTY
+
+###
+# When using configuration object to instantiate the tracer, the type of
+# sampling can be selected via sampler.type and sampler.param properties.
+# Jaeger libraries support the following samplers:
+#
+#  - Constant (sampler.type=const) sampler always makes the same decision for
+#    all traces.  It either samples all traces (sampler.param=1) or none of
+#    them (sampler.param=0).
+#
+#  - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling
+#    decision with the probability of sampling equal to the value of
+#    sampler.param property.  For example, with sampler.param=0.1 approximately
+#    1 in 10 traces will be sampled.
+#
+#  - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate
+#    limiter to ensure that traces are sampled with a certain constant rate.
+#    For example, when sampler.param=2.0 it will sample requests with the rate
+#    of 2 traces per second.
+#
+#  - Remote (sampler.type=remote, which is also the default) sampler consults
+#    Jaeger agent for the appropriate sampling strategy to use in the current
+#    service.  This allows controlling the sampling strategies in the services
+#    from a central configuration in Jaeger backend, or even dynamically.
+#
+sampler:
+    type: ratelimiting
+    param: 10.0
+
+reporter:
+    logSpans: true
+    localAgentHostPort: localhost:6831
diff --git a/contrib/opentracing/test/empty/cfg-zipkin.json b/contrib/opentracing/test/empty/cfg-zipkin.json
new file mode 100644
index 0000000..55fde9f
--- /dev/null
+++ b/contrib/opentracing/test/empty/cfg-zipkin.json
@@ -0,0 +1,4 @@
+{
+    "service_name":   "EMPTY",
+    "collector_host": "localhost"
+}
diff --git a/contrib/opentracing/test/empty/haproxy.cfg b/contrib/opentracing/test/empty/haproxy.cfg
new file mode 100644
index 0000000..9d40db9
--- /dev/null
+++ b/contrib/opentracing/test/empty/haproxy.cfg
@@ -0,0 +1,30 @@
+global
+    stats socket /tmp/haproxy.sock mode 666 level admin
+
+defaults
+    log    global
+    mode   http
+    option httplog
+    option dontlognull
+    option httpclose
+    timeout connect 5000
+    timeout client  50000
+    timeout server  50000
+
+listen stats
+    mode http
+    bind *:8001
+    stats uri /
+    stats admin if TRUE
+    stats refresh 10s
+
+frontend ot-test-empty
+    bind *:10080
+    mode http
+    default_backend servers-backend
+
+    filter opentracing id ot-test-empty config empty/ot.cfg
+
+backend servers-backend
+    mode http
+    server server-1 127.0.0.1:8000
diff --git a/contrib/opentracing/test/empty/ot.cfg b/contrib/opentracing/test/empty/ot.cfg
new file mode 100644
index 0000000..961c8bc
--- /dev/null
+++ b/contrib/opentracing/test/empty/ot.cfg
@@ -0,0 +1,3 @@
+ot-tracer ot-test-tracer
+    config empty/cfg-jaeger.yml
+    plugin libjaeger_opentracing_plugin-0.5.0.so
diff --git a/contrib/opentracing/test/fe/cfg-dd.json b/contrib/opentracing/test/fe/cfg-dd.json
new file mode 100644
index 0000000..84afe56
--- /dev/null
+++ b/contrib/opentracing/test/fe/cfg-dd.json
@@ -0,0 +1,5 @@
+{
+    "service":    "FE",
+    "agent_host": "localhost",
+    "agent_port": 8126
+}
diff --git a/contrib/opentracing/test/fe/cfg-jaeger.yml b/contrib/opentracing/test/fe/cfg-jaeger.yml
new file mode 100644
index 0000000..1365efa
--- /dev/null
+++ b/contrib/opentracing/test/fe/cfg-jaeger.yml
@@ -0,0 +1,34 @@
+service_name:
+    FE
+
+###
+# When using configuration object to instantiate the tracer, the type of
+# sampling can be selected via sampler.type and sampler.param properties.
+# Jaeger libraries support the following samplers:
+#
+#  - Constant (sampler.type=const) sampler always makes the same decision for
+#    all traces.  It either samples all traces (sampler.param=1) or none of
+#    them (sampler.param=0).
+#
+#  - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling
+#    decision with the probability of sampling equal to the value of
+#    sampler.param property.  For example, with sampler.param=0.1 approximately
+#    1 in 10 traces will be sampled.
+#
+#  - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate
+#    limiter to ensure that traces are sampled with a certain constant rate.
+#    For example, when sampler.param=2.0 it will sample requests with the rate
+#    of 2 traces per second.
+#
+#  - Remote (sampler.type=remote, which is also the default) sampler consults
+#    Jaeger agent for the appropriate sampling strategy to use in the current
+#    service.  This allows controlling the sampling strategies in the services
+#    from a central configuration in Jaeger backend, or even dynamically.
+#
+sampler:
+    type: ratelimiting
+    param: 10.0
+
+reporter:
+    logSpans: true
+    localAgentHostPort: localhost:6831
diff --git a/contrib/opentracing/test/fe/cfg-zipkin.json b/contrib/opentracing/test/fe/cfg-zipkin.json
new file mode 100644
index 0000000..1546b10
--- /dev/null
+++ b/contrib/opentracing/test/fe/cfg-zipkin.json
@@ -0,0 +1,4 @@
+{
+    "service_name":   "FE",
+    "collector_host": "localhost"
+}
diff --git a/contrib/opentracing/test/fe/haproxy.cfg b/contrib/opentracing/test/fe/haproxy.cfg
new file mode 100644
index 0000000..bfc0ec9
--- /dev/null
+++ b/contrib/opentracing/test/fe/haproxy.cfg
@@ -0,0 +1,37 @@
+global
+#   nbthread 1
+    maxconn 5000
+    hard-stop-after 10s
+#   log localhost:514 local7 debug
+#   debug
+    stats socket /tmp/haproxy-fe.sock mode 666 level admin
+
+defaults
+    log     global
+    mode    http
+    option  httplog
+    option  dontlognull
+    option  httpclose
+    retries 3
+    maxconn 4000
+    timeout connect 5000
+    timeout client  50000
+    timeout server  50000
+
+listen stats
+    mode http
+    bind *:8001
+    stats uri /
+    stats admin if TRUE
+    stats refresh 10s
+
+frontend ot-test-fe-frontend
+    bind *:10080
+    mode http
+    default_backend servers-backend
+
+    filter opentracing id ot-test-fe config fe/ot.cfg
+
+backend servers-backend
+    mode http
+    server server-1 127.0.0.1:11080
diff --git a/contrib/opentracing/test/fe/ot.cfg b/contrib/opentracing/test/fe/ot.cfg
new file mode 100644
index 0000000..11de828
--- /dev/null
+++ b/contrib/opentracing/test/fe/ot.cfg
@@ -0,0 +1,74 @@
+[ot-test-fe]
+    ot-tracer ot-test-tracer
+        config fe/cfg-jaeger.yml
+        plugin libjaeger_opentracing_plugin-0.5.0.so
+#       log localhost:514 local7 debug
+        option dontlog-normal
+        option hard-errors
+        no option disabled
+        rate-limit 100.0
+
+        scopes client_session_start
+        scopes frontend_tcp_request
+        scopes frontend_http_request
+        scopes backend_tcp_request
+        scopes backend_http_request
+        scopes client_session_end
+
+        scopes server_session_start
+        scopes tcp_response
+        scopes http_response
+        scopes server_session_end
+
+    ot-scope client_session_start
+        span "HAProxy session" root
+            baggage "haproxy_id" var(sess.ot.uuid)
+        span "Client session" child-of "HAProxy session"
+        event on-client-session-start
+
+    ot-scope frontend_tcp_request
+        span "Frontend TCP request" child-of "Client session"
+        event on-frontend-tcp-request
+
+    ot-scope frontend_http_request
+        span "Frontend HTTP request" follows-from "Frontend TCP request"
+            tag "http.method" method
+            tag "http.url" url
+            tag "http.version" str("HTTP/") req.ver
+        finish "Frontend TCP request"
+        event on-frontend-http-request
+
+    ot-scope backend_tcp_request
+        span "Backend TCP request" follows-from "Frontend HTTP request"
+        finish "Frontend HTTP request"
+        event on-backend-tcp-request
+
+    ot-scope backend_http_request
+        span "Backend HTTP request" follows-from "Backend TCP request"
+        finish "Backend TCP request"
+        span "HAProxy session"
+            inject "ot-ctx" use-headers
+        event on-backend-http-request
+
+    ot-scope client_session_end
+        finish "Client session"
+        event on-client-session-end
+
+    ot-scope server_session_start
+        span "Server session" child-of "HAProxy session"
+        finish "Backend HTTP request"
+        event on-server-session-start
+
+    ot-scope tcp_response
+        span "TCP response" child-of "Server session"
+        event on-tcp-response
+
+    ot-scope http_response
+        span "HTTP response" follows-from "TCP response"
+            tag "http.status_code" status
+        finish "TCP response"
+        event on-http-response
+
+    ot-scope server_session_end
+        finish *
+        event on-server-session-end
diff --git a/contrib/opentracing/test/func-stat.sh b/contrib/opentracing/test/func-stat.sh
new file mode 100755
index 0000000..cf5bd9e
--- /dev/null
+++ b/contrib/opentracing/test/func-stat.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+#
+test ${#} -lt 1 && exit 1
+
+awk '/ {$/ { sub(/\(.*/, "", $5); print $5 }' "${@}" | sort | uniq -c
diff --git a/contrib/opentracing/test/get-opentracing-plugins.sh b/contrib/opentracing/test/get-opentracing-plugins.sh
new file mode 100755
index 0000000..f2fe2d6
--- /dev/null
+++ b/contrib/opentracing/test/get-opentracing-plugins.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+_ARG_DIR="${1:-.}"
+
+
+get ()
+{
+	local _arg_tracer="${1}"
+	local _arg_version="${2}"
+	local _arg_url="${3}"
+	local _arg_file="${4}"
+	local _var_tmpfile="_tmpfile_"
+	local _var_plugin="lib${_arg_tracer}_opentracing_plugin-${_arg_version}.so"
+
+	test -e "${_var_plugin}" && return 0
+
+	wget "https://github.com/${_arg_url}/releases/download/v${_arg_version}/${_arg_file}" -O "${_var_tmpfile}" || {
+		rm "${_var_tmpfile}"
+		return 1
+	}
+
+	case "$(file ${_var_tmpfile})" in
+	  *shared\ object*)
+		mv "${_var_tmpfile}" "${_var_plugin}" ;;
+
+	  *gzip\ compressed\ data*)
+		gzip -cd "${_var_tmpfile}" > "${_var_plugin}"
+		rm "${_var_tmpfile}" ;;
+	esac
+}
+
+
+mkdir -p "${_ARG_DIR}" && cd "${_ARG_DIR}" || exit 1
+
+get dd 1.1.2 DataDog/dd-opentracing-cpp linux-amd64-libdd_opentracing_plugin.so.gz
+get dd 1.2.0 DataDog/dd-opentracing-cpp linux-amd64-libdd_opentracing_plugin.so.gz
+
+get jaeger 0.4.2 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so
+#et jaeger 0.5.0 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so
+#et jaeger 0.6.0 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so
+
+get lightstep 0.12.0 lightstep/lightstep-tracer-cpp linux-amd64-liblightstep_tracer_plugin.so.gz
+get lightstep 0.13.0 lightstep/lightstep-tracer-cpp linux-amd64-liblightstep_tracer_plugin.so.gz
+
+get zipkin 0.5.2 rnburn/zipkin-cpp-opentracing linux-amd64-libzipkin_opentracing_plugin.so.gz
diff --git a/contrib/opentracing/test/index.html b/contrib/opentracing/test/index.html
new file mode 100644
index 0000000..09ed6fa
--- /dev/null
+++ b/contrib/opentracing/test/index.html
@@ -0,0 +1 @@
+<html><body><p>Did I err?</p></body></html>
diff --git a/contrib/opentracing/test/run-cmp.sh b/contrib/opentracing/test/run-cmp.sh
new file mode 100755
index 0000000..77b48bd
--- /dev/null
+++ b/contrib/opentracing/test/run-cmp.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+_ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
+       _ARGS="-f cmp/haproxy.cfg"
+    _LOG_DIR="_logs"
+        _LOG="${_LOG_DIR}/_log-$(basename ${0} .sh)-$(date +%s)"
+
+
+test -x "${_ARG_HAPROXY}" || exit 1
+mkdir -p "${_LOG_DIR}"    || exit 2
+
+echo "executing: ${_ARG_HAPROXY} ${_ARGS} > ${_LOG}"
+"${_ARG_HAPROXY}" ${_ARGS} >"${_LOG}" 2>&1
diff --git a/contrib/opentracing/test/run-ctx.sh b/contrib/opentracing/test/run-ctx.sh
new file mode 100755
index 0000000..064fa7d
--- /dev/null
+++ b/contrib/opentracing/test/run-ctx.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+_ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
+       _ARGS="-f ctx/haproxy.cfg"
+    _LOG_DIR="_logs"
+        _LOG="${_LOG_DIR}/_log-$(basename ${0} .sh)-$(date +%s)"
+
+
+test -x "${_ARG_HAPROXY}" || exit 1
+mkdir -p "${_LOG_DIR}"    || exit 2
+
+echo "executing: ${_ARG_HAPROXY} ${_ARGS} > ${_LOG}"
+"${_ARG_HAPROXY}" ${_ARGS} >"${_LOG}" 2>&1
diff --git a/contrib/opentracing/test/run-fe-be.sh b/contrib/opentracing/test/run-fe-be.sh
new file mode 100755
index 0000000..7e70ad6
--- /dev/null
+++ b/contrib/opentracing/test/run-fe-be.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+_ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
+    _ARGS_FE="-f fe/haproxy.cfg"
+    _ARGS_BE="-f be/haproxy.cfg"
+       _TIME="$(date +%s)"
+    _LOG_DIR="_logs"
+     _LOG_FE="${_LOG_DIR}/_log-$(basename ${0} fe-be.sh)fe-${_TIME}"
+     _LOG_BE="${_LOG_DIR}/_log-$(basename ${0} fe-be.sh)be-${_TIME}"
+
+
+__exit ()
+{
+	test -z "${2}" && {
+		echo
+		echo "Script killed!"
+
+		echo "Waiting for jobs to complete..."
+		pkill --signal SIGUSR1 haproxy
+		wait
+	}
+
+	test -n "${1}" && {
+		echo
+		echo "${1}"
+		echo
+	}
+
+	exit ${2:-100}
+}
+
+
+trap __exit INT TERM
+
+test -x "${_ARG_HAPROXY}" || __exit "${_ARG_HAPROXY}: executable does not exist" 1
+mkdir -p "${_LOG_DIR}"    || __exit "${_ARG_HAPROXY}: cannot create log directory" 2
+
+echo "\n------------------------------------------------------------------------"
+echo "--- executing: ${_ARG_HAPROXY} ${_ARGS_BE} > ${_LOG_BE}"
+"${_ARG_HAPROXY}" ${_ARGS_BE} >"${_LOG_BE}" 2>&1 &
+
+echo "--- executing: ${_ARG_HAPROXY} ${_ARGS_FE} > ${_LOG_FE}"
+"${_ARG_HAPROXY}" ${_ARGS_FE} >"${_LOG_FE}" 2>&1 &
+echo "------------------------------------------------------------------------\n"
+
+echo "Press CTRL-C to quit..."
+wait
diff --git a/contrib/opentracing/test/run-sa.sh b/contrib/opentracing/test/run-sa.sh
new file mode 100755
index 0000000..e5682ea
--- /dev/null
+++ b/contrib/opentracing/test/run-sa.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+_ARG_HAPROXY="${1:-$(realpath -L ${PWD}/../../../haproxy)}"
+       _ARGS="-f sa/haproxy.cfg"
+    _LOG_DIR="_logs"
+        _LOG="${_LOG_DIR}/_log-$(basename ${0} .sh)-$(date +%s)"
+
+
+test -x "${_ARG_HAPROXY}" || exit 1
+mkdir -p "${_LOG_DIR}"    || exit 2
+
+echo "executing: ${_ARG_HAPROXY} ${_ARGS} > ${_LOG}"
+"${_ARG_HAPROXY}" ${_ARGS} >"${_LOG}" 2>&1
diff --git a/contrib/opentracing/test/sa/cfg-dd.json b/contrib/opentracing/test/sa/cfg-dd.json
new file mode 100644
index 0000000..0c476f7
--- /dev/null
+++ b/contrib/opentracing/test/sa/cfg-dd.json
@@ -0,0 +1,5 @@
+{
+    "service":    "SA",
+    "agent_host": "localhost",
+    "agent_port": 8126
+}
diff --git a/contrib/opentracing/test/sa/cfg-jaeger.yml b/contrib/opentracing/test/sa/cfg-jaeger.yml
new file mode 100644
index 0000000..e14f91e
--- /dev/null
+++ b/contrib/opentracing/test/sa/cfg-jaeger.yml
@@ -0,0 +1,34 @@
+service_name:
+    SA
+
+###
+# When using configuration object to instantiate the tracer, the type of
+# sampling can be selected via sampler.type and sampler.param properties.
+# Jaeger libraries support the following samplers:
+#
+#  - Constant (sampler.type=const) sampler always makes the same decision for
+#    all traces.  It either samples all traces (sampler.param=1) or none of
+#    them (sampler.param=0).
+#
+#  - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling
+#    decision with the probability of sampling equal to the value of
+#    sampler.param property.  For example, with sampler.param=0.1 approximately
+#    1 in 10 traces will be sampled.
+#
+#  - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate
+#    limiter to ensure that traces are sampled with a certain constant rate.
+#    For example, when sampler.param=2.0 it will sample requests with the rate
+#    of 2 traces per second.
+#
+#  - Remote (sampler.type=remote, which is also the default) sampler consults
+#    Jaeger agent for the appropriate sampling strategy to use in the current
+#    service.  This allows controlling the sampling strategies in the services
+#    from a central configuration in Jaeger backend, or even dynamically.
+#
+sampler:
+    type: ratelimiting
+    param: 10.0
+
+reporter:
+    logSpans: true
+    localAgentHostPort: localhost:6831
diff --git a/contrib/opentracing/test/sa/cfg-zipkin.json b/contrib/opentracing/test/sa/cfg-zipkin.json
new file mode 100644
index 0000000..9d155ba
--- /dev/null
+++ b/contrib/opentracing/test/sa/cfg-zipkin.json
@@ -0,0 +1,4 @@
+{
+    "service_name":   "SA",
+    "collector_host": "localhost"
+}
diff --git a/contrib/opentracing/test/sa/haproxy.cfg b/contrib/opentracing/test/sa/haproxy.cfg
new file mode 100644
index 0000000..988e3ab
--- /dev/null
+++ b/contrib/opentracing/test/sa/haproxy.cfg
@@ -0,0 +1,40 @@
+global
+#   nbthread 1
+    maxconn 5000
+    hard-stop-after 10s
+#   log localhost:514 local7 debug
+#   debug
+    stats socket /tmp/haproxy.sock mode 666 level admin
+
+defaults
+    log     global
+    mode    http
+    option  httplog
+    option  dontlognull
+    option  httpclose
+    retries 3
+    maxconn 4000
+    timeout connect 5000
+    timeout client  50000
+    timeout server  50000
+
+listen stats
+    mode http
+    bind *:8001
+    stats uri /
+    stats admin if TRUE
+    stats refresh 10s
+
+frontend ot-test-sa-frontend
+    bind *:10080
+    mode http
+    default_backend servers-backend
+
+    acl acl-http-status-ok status 100:399
+    filter opentracing id ot-test-sa config sa/ot.cfg
+    http-response ot-group ot-test-sa http_response_group if acl-http-status-ok
+    http-after-response ot-group ot-test-sa http_after_response_group if !acl-http-status-ok
+
+backend servers-backend
+    mode http
+    server server-1 127.0.0.1:8000
diff --git a/contrib/opentracing/test/sa/ot.cfg b/contrib/opentracing/test/sa/ot.cfg
new file mode 100644
index 0000000..ae7413b
--- /dev/null
+++ b/contrib/opentracing/test/sa/ot.cfg
@@ -0,0 +1,160 @@
+[ot-test-sa]
+    ot-tracer ot-test-tracer
+        log localhost:514 local7 debug
+        config sa/cfg-jaeger.yml
+        plugin libjaeger_opentracing_plugin-0.5.0.so
+        option dontlog-normal
+        option hard-errors
+        no option disabled
+        rate-limit 100.0
+
+        groups http_response_group
+        groups http_after_response_group
+
+        scopes client_session_start
+        scopes frontend_tcp_request
+        scopes http_wait_request
+        scopes http_body_request
+        scopes frontend_http_request
+        scopes switching_rules_request
+        scopes backend_tcp_request
+        scopes backend_http_request
+        scopes process_server_rules_request
+        scopes http_process_request
+        scopes tcp_rdp_cookie_request
+        scopes process_sticking_rules_request
+        scopes client_session_end
+        scopes server_unavailable
+
+        scopes server_session_start
+        scopes tcp_response
+        scopes http_wait_response
+        scopes process_store_rules_response
+        scopes http_response http_response-error
+        scopes server_session_end
+
+    ot-group http_response_group
+        scopes http_response_1
+        scopes http_response_2
+
+    ot-scope http_response_1
+        span "HTTP response"
+            log "hdr.content" res.hdr("content-type") str("; length: ") res.hdr("content-length") str(" bytes")
+
+    ot-scope http_response_2
+        span "HTTP response"
+            log "hdr.date" res.hdr("date") str(" / ") res.hdr("last-modified")
+
+    ot-group http_after_response_group
+        scopes http_after_response
+
+    ot-scope http_after_response
+        span "HAProxy response" child-of "HAProxy session"
+            tag "error" bool(true)
+            tag "http.status_code" status
+
+    ot-scope client_session_start
+        span "HAProxy session" root
+            baggage "haproxy_id" var(sess.ot.uuid)
+        span "Client session" child-of "HAProxy session"
+        acl acl-test-src-ip src 127.0.0.1
+        event on-client-session-start if acl-test-src-ip
+
+    ot-scope frontend_tcp_request
+        span "Frontend TCP request" child-of "Client session"
+        event on-frontend-tcp-request
+
+    ot-scope http_wait_request
+        span "HTTP wait request" follows-from "Frontend TCP request"
+        finish "Frontend TCP request"
+        event on-http-wait-request
+
+    ot-scope http_body_request
+        span "HTTP body request" follows-from "HTTP wait request"
+        finish "HTTP wait request"
+        event on-http-body-request
+
+    ot-scope frontend_http_request
+        span "Frontend HTTP request" follows-from "HTTP body request"
+            tag "http.method" method
+            tag "http.url" url
+            tag "http.version" str("HTTP/") req.ver
+        finish "HTTP body request"
+        event on-frontend-http-request
+
+    ot-scope switching_rules_request
+        span "Switching rules request" follows-from "Frontend HTTP request"
+        finish "Frontend HTTP request"
+        event on-switching-rules-request
+
+    ot-scope backend_tcp_request
+        span "Backend TCP request" follows-from "Switching rules request"
+        finish "Switching rules request"
+        event on-backend-tcp-request
+
+    ot-scope backend_http_request
+        span "Backend HTTP request" follows-from "Backend TCP request"
+        finish "Backend TCP request"
+        event on-backend-http-request
+
+    ot-scope process_server_rules_request
+        span "Process server rules request" follows-from "Backend HTTP request"
+        finish "Backend HTTP request"
+        event on-process-server-rules-request
+
+    ot-scope http_process_request
+        span "HTTP process request" follows-from "Process server rules request"
+        finish "Process server rules request"
+        event on-http-process-request
+
+    ot-scope tcp_rdp_cookie_request
+        span "TCP RDP cookie request" follows-from "HTTP process request"
+        finish "HTTP process request"
+        event on-tcp-rdp-cookie-request
+
+    ot-scope process_sticking_rules_request
+        span "Process sticking rules request" follows-from "TCP RDP cookie request"
+        finish "TCP RDP cookie request"
+        event on-process-sticking-rules-request
+
+    ot-scope client_session_end
+        finish "Client session"
+        event on-client-session-end
+
+    ot-scope server_unavailable
+        finish *
+        event on-server-unavailable
+
+    ot-scope server_session_start
+        span "Server session" child-of "HAProxy session"
+        finish "Process sticking rules request"
+        event on-server-session-start
+
+    ot-scope tcp_response
+        span "TCP response" child-of "Server session"
+        event on-tcp-response
+
+    ot-scope http_wait_response
+        span "HTTP wait response" follows-from "TCP response"
+        finish "TCP response"
+        event on-http-wait-response
+
+    ot-scope process_store_rules_response
+        span "Process store rules response" follows-from "HTTP wait response"
+        finish "HTTP wait response"
+        event on-process-store-rules-response
+
+    ot-scope http_response
+        span "HTTP response" follows-from "Process store rules response"
+            tag "http.status_code" status
+        finish "Process store rules response"
+        event on-http-response
+
+    ot-scope http_response-error
+        span "HTTP response"
+            tag "error" bool(true)
+        event on-http-response if !acl-http-status-ok
+
+    ot-scope server_session_end
+        finish *
+        event on-server-session-end
diff --git a/contrib/opentracing/test/test-speed.sh b/contrib/opentracing/test/test-speed.sh
new file mode 100755
index 0000000..ef2ccf0
--- /dev/null
+++ b/contrib/opentracing/test/test-speed.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+      _ARG_CFG="${1}"
+      _ARG_DIR="${2}"
+      _LOG_DIR="_logs"
+_HTTPD_PIDFILE="${_LOG_DIR}/thttpd.pid"
+
+
+httpd_run ()
+{
+
+	test -e "${_HTTPD_PIDFILE}" && return
+
+	thttpd -p 8000 -d . -nos -nov -l /dev/null -i "${_HTTPD_PIDFILE}"
+}
+
+httpd_stop ()
+{
+	test -e "${_HTTPD_PIDFILE}" || return
+
+	kill -TERM "$(cat ${_HTTPD_PIDFILE})"
+	rm "${_HTTPD_PIDFILE}"
+}
+
+haproxy_run ()
+{
+	_arg_ratio="${1}"
+	_var_sed_ot=
+	_var_sed_haproxy=
+
+	if test "${_arg_ratio}" = "disabled"; then
+		_var_sed_ot="s/no \(option disabled\)/\1/"
+	elif test "${_arg_ratio}" = "off"; then
+		_var_sed_haproxy="s/^\(.* filter opentracing .*\)/#\1/g; s/^\(.* ot-group .*\)/#\1/g"
+	else
+		_var_sed_ot="s/\(rate-limit\) 100.0/\1 ${_arg_ratio}/"
+	fi
+
+	sed "${_var_sed_haproxy}" "${_ARG_DIR}/haproxy.cfg.in" > "${_ARG_DIR}/haproxy.cfg"
+	sed "${_var_sed_ot}"      "${_ARG_DIR}/ot.cfg.in" > "${_ARG_DIR}/ot.cfg"
+
+	if test "${_ARG_DIR}" = "fe"; then
+		if test "${_arg_ratio}" = "disabled" -o "${_arg_ratio}" = "off"; then
+			sed "${_var_sed_haproxy}" "be/haproxy.cfg.in" > "be/haproxy.cfg"
+			sed "${_var_sed_ot}"      "be/ot.cfg.in" > "be/ot.cfg"
+		fi
+	fi
+
+	./run-${_ARG_CFG}.sh &
+	sleep 5
+}
+
+wrk_run ()
+{
+	_arg_ratio="${1}"
+
+	echo "--- rate-limit ${_arg_ratio} --------------------------------------------------"
+	wrk -c8 -d300 -t8 --latency http://localhost:10080/index.html
+	echo "----------------------------------------------------------------------"
+	echo
+
+	sleep 10
+}
+
+
+mkdir -p "${_LOG_DIR}" || exit 1
+
+if test "${_ARG_CFG}" = "all"; then
+	${0} fe-be fe > "${_LOG_DIR}/README-speed-fe-be"
+	${0} sa sa    > "${_LOG_DIR}/README-speed-sa"
+	${0} cmp cmp  > "${_LOG_DIR}/README-speed-cmp"
+	${0} ctx ctx  > "${_LOG_DIR}/README-speed-ctx"
+	exit 0
+fi
+
+test -n "${_ARG_CFG}" -a -f "run-${_ARG_CFG}.sh" || exit 2
+test -n "${_ARG_DIR}" -a -d "${_ARG_DIR}"        || exit 3
+
+test -e "${_ARG_DIR}/haproxy.cfg.in" || cp -af "${_ARG_DIR}/haproxy.cfg" "${_ARG_DIR}/haproxy.cfg.in"
+test -e "${_ARG_DIR}/ot.cfg.in"      || cp -af "${_ARG_DIR}/ot.cfg" "${_ARG_DIR}/ot.cfg.in"
+if test "${_ARG_DIR}" = "fe"; then
+	test -e "be/haproxy.cfg.in" || cp -af "be/haproxy.cfg" "be/haproxy.cfg.in"
+	test -e "be/ot.cfg.in"      || cp -af "be/ot.cfg" "be/ot.cfg.in"
+fi
+
+httpd_run
+
+for _var_ratio in 100.0 50.0 10.0 2.5 0.0 disabled off; do
+	haproxy_run "${_var_ratio}"
+	wrk_run "${_var_ratio}"
+
+	pkill --signal SIGUSR1 haproxy
+	wait
+done
+
+httpd_stop