Compare commits

..

No commits in common. "master" and "v3.4-dev11" have entirely different histories.

338 changed files with 16483 additions and 7811 deletions

18
.cirrus.yml Normal file
View file

@ -0,0 +1,18 @@
FreeBSD_task:
freebsd_instance:
matrix:
image_family: freebsd-14-3
only_if: $CIRRUS_BRANCH =~ 'master|next'
install_script:
- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2
script:
- sudo sysctl kern.corefile=/tmp/%N.%P.core
- sudo sysctl kern.sugid_coredump=1
- scripts/build-vtest.sh
- gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
- ./haproxy -vv
- ldd haproxy
test_script:
- env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel
on_failure:
debug_script: (for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done && ls /tmp/haproxy.*.core && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core)

2
.github/matrix.py vendored
View file

@ -172,6 +172,7 @@ def main(ref_name):
"FLAGS": [ "FLAGS": [
'DEBUG="-DDEBUG_LIST"', 'DEBUG="-DDEBUG_LIST"',
"USE_ZLIB=1", "USE_ZLIB=1",
"USE_OT=1",
"OT_INC=${HOME}/opt-ot/include", "OT_INC=${HOME}/opt-ot/include",
"OT_LIB=${HOME}/opt-ot/lib", "OT_LIB=${HOME}/opt-ot/lib",
"OT_RUNPATH=1", "OT_RUNPATH=1",
@ -205,6 +206,7 @@ def main(ref_name):
'ARCH_FLAGS="-g -fsanitize=address"', 'ARCH_FLAGS="-g -fsanitize=address"',
'OPT_CFLAGS="-O1"', 'OPT_CFLAGS="-O1"',
"USE_ZLIB=1", "USE_ZLIB=1",
"USE_OT=1",
"DEBUG=-DDEBUG_STRICT=2", "DEBUG=-DDEBUG_STRICT=2",
"OT_INC=${HOME}/opt-ot/include", "OT_INC=${HOME}/opt-ot/include",
"OT_LIB=${HOME}/opt-ot/lib", "OT_LIB=${HOME}/opt-ot/lib",

View file

@ -1,38 +0,0 @@
name: FreeBSD
on:
push:
branches:
- master
- next
workflow_dispatch:
permissions:
contents: read
jobs:
clang:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- name: "Checkout repository"
uses: actions/checkout@v6
- name: "Build and test on FreeBSD"
uses: vmactions/freebsd-vm@v1
with:
release: "14.3"
prepare: |
pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua54 socat pcre2 python3
run: |
sysctl kern.corefile=/tmp/%N.%P.core
sysctl kern.sugid_coredump=1
scripts/build-vtest.sh
gmake CC=clang V=1 ERR=1 TARGET=freebsd USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 USE_LUA=1 LUA_INC=/usr/local/include/lua54 LUA_LIB=/usr/local/lib LUA_LIB_NAME=lua-5.4
./haproxy -vv
ldd haproxy
if ! env VTEST_PROGRAM=../vtest/vtest gmake reg-tests REGTESTS_TYPES=default,bug,devel; then
for folder in /tmp/*regtest*/vtc.*; do cat $folder/INFO $folder/LOG; done
ls /tmp/haproxy.*.core 2>/dev/null && gdb -ex 'thread apply all bt full' ./haproxy /tmp/haproxy.*.core
exit 1
fi

View file

@ -2,7 +2,7 @@ name: Illumos
on: on:
schedule: schedule:
- cron: "0 3 * * 1" - cron: "0 0 25 * *"
workflow_dispatch: workflow_dispatch:
permissions: permissions:

View file

@ -65,6 +65,13 @@ jobs:
path: '~/opt/' path: '~/opt/'
key: ssl-${{ steps.generate-cache-key.outputs.key }} key: ssl-${{ steps.generate-cache-key.outputs.key }}
- name: Cache OpenTracing
if: ${{ contains(matrix.FLAGS, 'USE_OT=1') }}
id: cache_ot
uses: actions/cache@v5
with:
path: '~/opt-ot/'
key: ${{ matrix.os }}-ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
- name: Add i386 architecture - name: Add i386 architecture
if: ${{ matrix.CC == 'i686-linux-gnu-gcc' }} if: ${{ matrix.CC == 'i686-linux-gnu-gcc' }}
run: sudo dpkg --add-architecture i386 run: sudo dpkg --add-architecture i386
@ -92,6 +99,10 @@ jobs:
- name: Install SSL ${{ matrix.ssl }} - name: Install SSL ${{ matrix.ssl }}
if: ${{ matrix.ssl && matrix.ssl != 'stock' && steps.cache_ssl.outputs.cache-hit != 'true' }} if: ${{ matrix.ssl && matrix.ssl != 'stock' && steps.cache_ssl.outputs.cache-hit != 'true' }}
run: env ${{ matrix.ssl }} scripts/build-ssl.sh run: env ${{ matrix.ssl }} scripts/build-ssl.sh
- name: Install OpenTracing libs
if: ${{ contains(matrix.FLAGS, 'USE_OT=1') && steps.cache_ot.outputs.cache-hit != 'true' }}
run: |
OT_PREFIX=${HOME}/opt-ot scripts/build-ot.sh
- name: Build WURFL - name: Build WURFL
if: ${{ contains(matrix.FLAGS, 'USE_WURFL=1') }} if: ${{ contains(matrix.FLAGS, 'USE_WURFL=1') }}
run: make -C addons/wurfl/dummy run: make -C addons/wurfl/dummy

View file

@ -137,7 +137,7 @@ release. These branches were emitted at a pace of one per year since 1.5 in
2014. As of 2019, 1.5 is still supported and widely used, even though it very 2014. As of 2019, 1.5 is still supported and widely used, even though it very
rarely receives updates. After a few years these LTS branches enter a rarely receives updates. After a few years these LTS branches enter a
"critical fixes only" status, which means that they will rarely receive a fix "critical fixes only" status, which means that they will rarely receive a fix
but if that a critical issue affects them, a release will be made, with or but if that a critital issue affects them, a release will be made, with or
without any other fix. Once a version is not supported anymore, it will not without any other fix. Once a version is not supported anymore, it will not
receive any fix at all and it will really be time for you to upgrade to a more receive any fix at all and it will really be time for you to upgrade to a more
recent branch. Please note that even when an upgrade is needed, a great care is recent branch. Please note that even when an upgrade is needed, a great care is

422
CHANGELOG
View file

@ -1,428 +1,6 @@
ChangeLog : ChangeLog :
=========== ===========
2026/06/25 : 3.5-dev1
- BUG/MEDIUM: check: Skip tcpcheck post-config for external checks
- BUG/MEDIUM: check: Ignore small-buffer option when starting an external check
- MINOR: check: Don't dump buffers state in check traces for external checks
- BUG/MEDIUM: server/checks: Support healtcheck keyword on default-server lines
- BUG/MEDIUM: mux_quic: prevent risk of infinite loop on recv
- OPTIM: mux_quic: remove QCS from recv_list on reset
- BUG/MINOR: mux_quic: do not interrupt recv on error/incomplete data
- BUG/MINOR: tcpcheck: Override external check if healthcheck section is set
- REGTESTS: checks: Add script for external healthchecks
- BUG/MEDIUM: regex: initialize the match array earlier during boot
- BUG/MEDIUM: threads: Fiw build when using no thread
- BUG/MEDIUM: xprt_qmux: implement ->get_ssl_sock_ctx() to get the SSL laye
- CLEANUP: sessions: simplify the sess_priv_conns pool name
- MINOR: pools: reject creation of pools containing invalid chars in their name
- BUG/MINOR: acl: report "ACL" not "map" in ACL ID lookup failures
- MINOR: memprof: make in_memprof a bitfield instead of a counter
- MINOR: memprof: be careful to account allocations only once
- BUG/MEDIUM: checks: Dequeue checks on purge
- MINOR: servers: Add a back-pointer to the server in srv_per_thread
- MEDIUM: servers: Move to a per-thread idle connection cleanup task
- REGTESTS: Fix log matching in healthcheck-section.vtc
- BUG/MINOR: quic: fix Initial length value in sent packets
- BUILD: servers: Fix build with -std=gnu89
- BUG/MEDIUM: acme: stuck ACME task when authz is already "valid"
- MINOR: acme: introduce acme_challenge_ready() for reuse outside the CLI
- MINOR: h3: extend trace verbosity
- MINOR: h3: trace HTTP headers on FE side
- MINOR: h3: trace HTTP headers on BE side
- BUILD: h3: fix compilation with USE_TRACE=0
- MINOR: lua: add REGISTER_HLUA_STATE_INIT() to register state init callbacks
- MEDIUM: lua: move longjmp annotation macros to hlua.h
- MINOR: acme/lua: implement ACME.challenge_ready() Lua function
- BUG/MEDIUM: ktls: defer enabling TLS ULP on a socket until connected
- MINOR: errors: add ha_diag_notice() to report diag-level notifications
- BUG/MINOR: cpu-topo: use ha_diag_notice() to report thread creations
- MINOR: acme: publish ACME_NEWCERT event via event_hdl
- MINOR: acme: publish ACME_DEPLOY event via event_hdl
- EXAMPLES: lua/acme: add a dns-01 handler for Gandi LiveDNS API
- DOC: acme: add mentions of lua features
- MINOR: tasks: Introduce __task_set_state_and_tid
- MINOR: tasks: Add __task_get_new_tid_field()
- MINOR: tasks: Introduce __task_get_current_owner
- MINOR: tasks: Use __task_get_current_owner() in task_kill.
- MINOR: tasks: Start using __task_set_state_and_tid()
- MEDIUM: tasks: Remove the per-thread group wait queue
- MINOR: tasks: Use __task_set_state_and_tid() in task_instant_wakeup()
- MINOR: tasks: Remove wq_lock and the per-thread group wait queues
- MEDIUM: tasks: Redispatch shared tasks when the thread is loaded
- BUG/MEDIUM: h3: Properly handle PUSH_PROMISE on backend connections
- BUG/MINOR: server: fix add server with consistent hash balancing
- MINOR: lua: export hlua_pusherror() and check_args()
- REORG: httpclient/lua: move the lua httpclient code to http_client.c
- MEDIUM: httpclient/lua: allow multiple requests from a single core.httpclient() instance
- MEDIUM: httpclient: set res.status to 0 upon SF_ERR_MASK
- DOC: httpclient: document status 0 on internal error
- DEBUG: stconn: Add a BUG_ON on shut flags when the endpoint is shut
- BUG/MINOR: http-ana: Remove a debugging memset on redirect
- BUG/MEDIUM: http-ana: Don't ignore L7 retry errors
- BUG/MINOR: mux-h1: Properly resolve file path for 'h1-case-adjust-file'
- BUG/MINOR: quic: fix rxbuf settings on backend side
- EXAMPLES: lua/acme: fix acme-gandi-livedns.lua configuration example
- BUG/MEDIUM: ssl: Don't free the early data buffer too early
- BUG/MINOR: hpack-tbl: add missing NULL check after hpack_dht_defrag()
- BUG/MEDIUM: mux_quic: fix freeze transfer after QCS rxbuf realign
- BUG/MEDIUM: http-act: Make a copy of the sample expr in (set/add)-headers-bin
- BUG/MEDIUM: mux-fcgi: fix uint16_t overflow in drl += drp
- OPTIM: mux-fcgi: Reorganise fcgi_conn structure to fill some holes
- BUG/MINOR: hq-interop: reject too big content
- MINOR: hq_interop: do not rely on stream layer for HTX stline encoding
- BUG/MINOR: hq-interop: prevent reset if missing content-length
- BUG/MEDIUM: hlua: Properly report EOS when http applet exits
- BUG/MINOR: hq-interop: support full demux buf on large response
- BUG/MINOR: hq-interop: support response buffer wrapping
- DOC: sched: Document the wait queue modifications
- DOC: lua: remove incorrect init tags
- BUG/MEDIUM: h3: increment unknown request payload length
- REGTESTS: quic: test H3 request without content-length
- DEBUG: cli: relax tid check in "debug dev task" for recent sched changes
- MINOR: debug: add "print" to "debug dev sched"
- BUG/MINOR: poller: fix wait time calculation that is always 1 extra ms
- BUILD: quic_pacing: add missing includes for api and activity in the file
- MINOR: task: move the profiling checks to the called functions not callers
- MINOR: task: add a new explicitly local tasklet wakeup function
- MINOR: task: make tasklet_wakeup() explicitly call _tasklet_wakeup_here()
- MINOR: task: make task_instant_wakeup() explicitly call _tasklet_wakeup_here()
- MEDIUM: task: make __tasklet_wakeup_on() only accept non-local threads
- MEDIUM: task: add a new flag TASK_RT to permit a task to skip the priority queue
- MINOR: debug: add "rt=1" to "debug dev task" to tune the RT flag
- BUG/MEDIUM: mux-fcgi: Truly drain outgoing HTX data when the stream is closed
- BUG/MEDIUM: mux-h2: Truly drain outgoing HTX data when the stream is closed
- BUG/MEDIUM: mux-spop: Truly drain outgoing data when the stream is closed
- BUG/MEDIUM: mux-quic: Drain the given amount of data in qcs_http_reset_buf()
- CLEANUP: task: remove duplicated code in __tasklet_wakeup_after()
- BUILD: task: silence a build warning with threads disabled
- MINOR: task: do not try to redistribute the WQ when single-threaded
- MEDIUM: task: add a new tasklet class for real-time: TL_RT
2026/06/03 : 3.5-dev0
- MINOR: version: mention that it's development again
2026/06/03 : 3.4.0
- BUG/MINOR: tcpcheck: Check LDAP response to not read more data than available
- BUG/MINOR: ssl-gencert: validate SNI characters to prevent SAN certificate injection
- BUG/MINOR: mux-h1: H2 preface rejection doesn't update stick-table glitches
- BUG/MEDIUM: cpu-topo: Enforce thread-hard-limit on policy
- BUG/MEDIUM: qmux: do not crash on too large record
- BUG/MEDIUM: qmux: do not crash on receiving an invalid first frame
- BUG/MINOR: qmux: reject too large initial record
- Revert "BUG/MEDIUM: dns: fix long loops in additional records parse on name failure"
- BUG/MINOR: qpack: Fix index calculation in debug functions
- BUG/MINOR: qpack: fix potential null-pointer dereference in qpack_dht_insert()
- CLEANUP: qpack: fix copy-paste typo in value Huffman debug string
- BUG/MINOR: qpack: fix sign bit mask in qpack_decode_fs_pfx()
- CLEANUP: qpack: fix copy-paste typo in value Huffman debug string for WLN
- BUG/MINOR: qpack: fix huff_dec() error handling in qpack_decode_fs()
- CLEANUP: qpack: move encoded macros to qpack-t.h to avoid duplication
- BUG/MEDIUM: quic: handle ECONNREFUSED on RX side
- BUG/MINOR: quic: Fix memory leak in quic_deallocate_dghdlrs()
- BUG/MEDIUM: lua: defer Lua VM initialisation to the first Lua config keyword
- REGTESTS: lua: fix tune.lua.openlibs in Lua reg-tests
- BUG/MINOR: mux-h2: Count padding for connection flow control on error path
- BUILD: addons: convert 51d addon to EXTRA_MAKE
- BUILD: addons: convert deviceatlas addon to EXTRA_MAKE
- BUILD: addons: convert WURFL addon to EXTRA_MAKE
- MINOR: mux_quic/flags: add missing flags
- BUG/MINOR: mux_quic: open an idle QCS on reset on BE side
- BUG/MINOR: mux_quic: fix BE conn removal on app shutdown
- BUG/MINOR: mux_quic: prevent BE reuse with an errored conn
- BUG/MINOR: quic: fix ack range node pool_free call passing wrong pointer type
- MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts
- BUG/MEDIUM: quic: reset cwnd in slow_start on persistent congestion (cubic)
- BUG/MEDIUM: quic: reset consecutive_losses on exit from recovery period (cubic)
- BUG/MINOR: quic: update drs->lost before calling on_ack_recv
- Revert "MEDIUM: quic: optimize HKDF operations by reusing per-thread contexts"
- BUG/MEDIUM: lua: register hlua_init() as a pre-check to fix crash without Lua config
- REGTESTS: quic: disable quic/ocsp_auto_update for now
- BUG/MINOR: threads: set at least grp_max when mtpg is too small
- BUG/MEDIUM: threads: ignore max-threads-per-group when thread-groups is set
- CLEANUP: thread: indicate when max-threads-per-group is ignored
- MINOR: cpu-topo: notify when cpu-policy is ignored due to other settings
- MINOR: thread: report when thread-groups or nbthread results in less threads
- BUILD: makefile: include EXTRA_MAKE in the .build_opts construction
- BUG/MINOR: quic: Fix another buffer overflow with sockaddr_in46
- MINOR: quic: Copy sin6_flowinfo and sin6_scope_id too
- BUILD: Makefile: put EXTRA_MAKE help at the right place
- BUG/MINOR: cache: fix cache tree iteration
- BUG/MEDIUM: resolvers: Wait a bit before calling the xprt prepare_srv
- CLEANUP: addons/51degrees: initialize variables
- MINOR: addons/51degrees: handle memory allocation failures
- CLEANUP: ncbmbuf: improve handling of memory allocation errors in unit tests
- CLEANUP: admin/halog: improve handling of memory allocation errors
- DOC: internals: clarify ambiguous wording in core-principles
- DOC: internals: add a threat model definition
- DOC: add security.txt describing how to report security issues
- DOC: security: also add a note to exclude dev/ and admin/
- BUG/MEDIUM: qmux: Close connection on invalid frame
- CLEANUP: fix comment typo
- BUG/MEDIUM: h3: fix MAX_PUSH_ID handling
- BUG/MINOR: cache: Fix copy of value when parsing maxage
- BUG/MEDIUM: mux-h1: Dup connection/upgrade value to parse it when making headers
- BUG/MEDIUM: htx: Fix headers rollback on partial copy in htx_xfer()
- MINOR: deinit: release the in-memory copy of shared libs
- MINOR: debug: add -dA to dump an archive of all dependencies
- BUG/MEDIUM: ssl: Make sure the alpn length is small enough
- BUG/MINOR: applet: Commit changes into input buffer after sending HTX data
- BUG/MINOR: mux-spop: Fix possible off-by-one OOB read in spop_get_varint()
- BUG/MEDIUM: leastconn: Unlock the write lock on allocation failure
- BUG/MINOR: tasks: Increase the right niced_task counter
- BUILD: makefile: search for Lua 5.5 as well
- DEV: dev/gdb: improve ebtree pointer handling
- DEV: dev/gdb: add simple task dump
- DEV: dev/gdb: add simple thread dump
- DEV: dev/gdb: add fdtab dump
- DOC: config: add a few more explanation in http-reusee regarding sni-auto
- REGTESTS: add basic QMux tests
- BUG/MINOR: http-act: Properly handle final evaluation in pause action
- BUILD: makefile/lua: use the system's default library before all other variants
- BUG/MINOR: startup: unbreak chroot with CAP_SYS_CHROOT
- BUG/MINOR: haterm: do not try to bind QUIC when not supported
- BUG/MINOR: haterm: also apply the tcp-bind-opts to clear TCP "bind" lines
- CLEANUP: haterm: do not try to bind to SSL when not built in
- MINOR: haterm: enable ktls on the SSL bind line when supported
- CI: github: replace cirrus by a vmactions/freebsd-vm job
- BUILD: makefile: fix build error with GNU make 4.2.1 and /bin/dash
- BUG/MEDIUM: channel: Fix condition to know if a channel may send
- BUG/MEDIUM: vars: Properly eval set-var-fmt action for emtpy log-format string
- CI: github: run illumos job weekly on Mondays at 03:00 instead of monthly
- BUG/MEDIUM: stream: Don't use small buffer on queuing with a request data filter
- BUG/MINOR: jwe: don't write randoms past MAX_DECRYPTED_CEK_LEN in RSA_PKCS1_PADDING
- BUG/MEDIUM: chunk: do not rely on small trash by default for expressions
- CLEANUP: map: always test pat->ref in sample_conv_map_key()
- DEV: patchbot: prepare for new version 3.5-dev
- MINOR: version: mention that it's 3.4 LTS now.
2026/05/26 : 3.4-dev14
- MINOR: config: shm-stats-file is no longer experimental
- BUILD: proxy: unstatify the proxies_del_lock to avoid a warning without threads
- BUG/MEDIUM: net_helper: fix a remaining possibly infinite loop in converters
- MINOR: ssl_sock: remove unneeded check on QMux flags
- MINOR: connection: define xprt_add_l6hs()
- MINOR: xprt_qmux: define default value for get_alpn
- MINOR: connection: define mask CO_FL_WAIT_XPRT_L6
- MINOR: session: support QMux in clear on FE side
- MINOR: backend: support QMux in clear for BE side
- BUG/MINOR: ocsp: Manage date too far away in the future
- MINOR: mux_quic: handle STOP_SENDING in QMux
- MINOR: mux_quic: handle MAX_STREAMS for uni stream in QMux
- MINOR: mux_quic: do not crash on unhandled QMux frame reception
- BUG/MEDIUM: applet: Properly handle receives of size 0
- BUG/MEDIUM: resolvers: Fix test on dn label size in resolv_dn_label_to_str()
- BUG/MEDIUM: ssl-gencert: Unlock LRU cache if failing to generate certificate
- BUG/MINOR: quic: fix ODCID lookup from derived value
- BUG/MEDIUM: dict: hold lock while decrementing refcount in dict_entry_unref
- BUG/MINOR: tcpchecks: Limit parsing of agent-check reply to the buffer
- BUG/MEDIUM: hlua: Fix integer underflow when receiving line from lua cosocket
- BUG/MEDIUM: cli: Fix parsing of pattern finishing a command payload
- BUG/MEDIUM: acme: NUL terminate response buffer before PEM parsing
- BUILD: intops: mask the fail value in array_size_or_fail()
- BUG/MEDIUM: log-forward: make sure the month is unsigned
- BUG/MEDIUM: regex: allocate a large enough pcre2 match for all matches
- BUG/MEDIUM: tcpcheck/spoe: bound the SPOP error code to valid values
- BUG/MEDIUM: cache: fix a refcount leak for missed secondary entries
- BUG/MINOR: log: free logformat expr on compile failure in cfg_parse_log_profile
- BUG/MINOR: resolvers: fix room for trailing zero in resolv_dn_label_to_str()
- BUG/MINOR: resolvers: fix risk of appending garbage past the domain name
- BUG/MINOR: mux-h2: validate HEADERS frame length before reading stream dep
- BUG/MINOR: log: look for the end of priority before the end of the buffer
- BUG/MINOR: dict: fix refcount race on insert collision
- BUG/MINOR: init: use more than ha_random64() for the cluster secret
- BUG/MINOR: sample: limit the be2hex converter's chunk size
- CLEANUP: resolvers: use read_n32() instead of open-coded big-endian read
- CLEANUP: resolvers: remove pool_free(NULL) in SRV additional record matching
- CLEANUP: resolvers: fix comment typos and wrong filenames in file headers
- BUG/MINOR: haterm: fix the random suffix multiplication
- MINOR: haterm: enable h3 for TCP bindings
- MINOR: haterm: do not emit a warning when not using SSL
- BUG/MEDIUM: h1: drop headers whose names contain invalid chars
- BUG/MEDIUM: h1: limit status codes to 3 digits by default
- BUG/MEDIUM: cache: always verify the primary hash in get_secondary_entry()
- BUG/MINOR: cache: also recognize directives in the form "token="
- BUG/MINOR: resolvers: relax size checks in authority record parsing
- BUG/MINOR: sample: request an extra output byte for the url_dec converter
- BUG/MINOR: http-fetch: check against the whole token in get_http_auth()
- BUG/MEDIUM: acme: protect against risk of null-deref on connection failure
- BUG/MINOR: http-ext: always check remaining data when reading rfc7239 nodeport
- BUG/MINOR: base64: return empty string for empty input in base64dec()
- BUG/MINOR: payload: fix the handshake length bounds check smp_client_hello_parse()
- BUG/MINOR: ssl-hello: make use of the null-terminated servername
- BUG/MINOR: resolvers: switch to a better PRNG for query IDs
- BUG/MINOR: addons/51d: NUL-terminate headers before passing them to Trie API
- BUG/MEDIUM: tools: insert an XXH64 layer on the PRNG output
- MINOR: tools: provide a function to generate a hashed random pair
- MEDIUM: init: fall back to ha_random64_pair_hashed() for the cluster secret
- MEDIUM: tools: use the hashed random pair for UUID generation
- MEDIUM: h1: use ha_random64_pair_hashed() for the WebSocket key
- MEDIUM: quic: use ha_random64_pair_hashed() to generate the QUIC retry tokens
- MEDIUM: tools: switch the main PRNG to a thread-local xoshiro256**
- BUG/MEDIUM: h3: reject client push stream
- BUG/MINOR: h3: reject server push stream
- BUG/MINOR: h3: reject client CANCEL_PUSH frame
- BUG/MINOR: h3: adjust error on PUSH_PROMISE frame reception
- BUG/MINOR: h3: reject server MAX_PUSH_ID frame
- BUG/MEDIUM: auth: fix unconfigured password NULL deref
- BUG/MINOR: h3: add missing break on rcv_buf()
- BUG/MINOR: hlua: prevent Lua from passing CR/LF/NUL in HTTP headers
- BUG/MINOR: qmux: do not crash on frame parsing issue
- BUG/MINOR: quic: reject packet too short for HP decryption
- BUG/MINOR: jwe: enforce GCM tag length to 128 bits
- BUG/MEDIUM: jwe: substitute random CEK on RSA1_5 decryption failure per RFC 7516 #11.5
- BUG/MEDIUM: mux-fcgi: reject stream ID 0 for application records
- MINOR: http: Add function to remove all occurrences of a value in a header
- MINOR: h1: Add a H1M flag to specify a non-empty 'Upgrade:' header was parsed
- BUG/MEDIUM: h1-htx: Sanitize parsing to properly handle upgrade requests
- BUG/MINOR: mux-fcgi: Use relative offset to compute contig data in demux buf
- BUG/MINOR: mux-spop: Use relative offset to compute contig data in demux buf
- CLEANUP: mux-fcgi/mux-spop: Remove copy/pasted comment about slow realign
2026/05/20 : 3.4-dev13
- BUG/MINOR: backend: correct parameter value validation in get_server_ph_post()
- BUG/MINOR: config/dns: properly fail on duplicate nameserver name detection
- BUG/MEDIUM: dns: fix long loops in additional records parse on name failure
- BUG/MEDIUM: resolvers: fix name compression pointer validation in resolv_read_name()
- BUG/MEDIUM: dns: fix memory leak of sockaddr in dns_session_init() error path
- CLEANUP: proxy: fix tiny mistakes in parse error messages
- CLEANUP: dns: fix misleading error messages in dns_stream_init()
- BUG/MINOR: server: better handling of OOM in srv_set_fqdn()
- BUG/MINOR: servers: use proper source of pool_conn_name in srv_settings_cpy()
- BUG/MEDIUM: server/cli: unlock server lock on failure in cli_parse_set_server
- BUG/MINOR: resolvers: fix dangling list pointer in resolvers_new() error paths
- BUG/MINOR: dns: fix dangling dgram pointer on dns_dgram_init() failure path
- BUG/MINOR: proxy: use proxy_drop() in parse_new_proxy() error path
- CLEANUP: resolvers: properly initialize the sample in resolv_action_do_resolve()
- BUG/MINOR: resolvers: report the expression error in the do-resolve() action parser
- BUG/MINOR: resolvers: fix leaked dgram and dns_ring struct in parse_resolve_conf()
- BUG/MINOR: resolvers: fix leaked fields on cfg_parse_resolvers() error paths
- BUG/MINOR: resolvers: fix missing task_idle destruction in resolvers_destroy()
- CLEANUP: proxy: fix duplicate declaration of cli_find_frontend in proxy.h
- CLEANUP: address a few typos and copy-paste errors in httpclient and dns
- DOC: internal: add a few rules about internal core principles
- BUG/MINOR: session/trace: use distinct flags for SESS_EV_END and _ERR
- CLEANUP: stick-table: uniformize the different action_inc_gpc*()
- REGTESTS: do not run quic/tls13_ssl_crt-list_filters in quic openssl compat mode
- REGTESTS: quic/issuers_chain_path: do not forget to enable QUIC compat mode
- BUG/MINOR: sock: store the connection error status
- BUG/MINOR: check: properly report errno in chk_report_conn_err()
- CLEANUP: tcpcheck: mention that we're a bit far for a sync errno
- BUG/MINOR: jwt: fix possible memory leak in convert_ecdsa_sig() error path
- CLEANUP: jwe: fix theoretical overflow in AAD length calculation
- DOC: config: further clarify that resolvers "default" exists
- MINOR: proxy: remove the experimental status on dynamic backends
- BUG/MEDIUM: limits: properly account for global.maxpipes in compute_ideal_maxconn()
- BUG/MINOR: jws: fix OpenSSL 3.0 version check from > to >=
- BUG/MINOR: jws: Add missing return value check (EVP_PKEY_get_bn_param)
- BUG/MINOR: server: Properly handle init-state value during haproxy startup
- BUG/MINOR: httpclient-cli: Destroy http-client context if failing to start it
- BUG/MEDIUM: h1: Skip all h2c values from Upgrade headers during parsing
- BUG/MINOR: h1: Don't mask websocket protocol if multiple protocols used
- MINOR: haterm: Don't init haterm master pipe if not used
- CLEANUP: haterm: Remove "(too old kernel)" from warning message during init
- BUG/MINOR: httpclient-cli: fix uninit variable in error label
- MINOR: mux: Rename the "token" from mux_proto_list to mux_proto
- MEDIUM: connections: Use both mux_proto and alpn to pick a mux
- MINOR: connection: define conn_select_mux_fe()
- MINOR: connection: define conn_select_mux_be()
- MINOR: connection/mux_quic: add MUX <init_xprt> field for QMux handshake
- MINOR: proxy/server: reject TCP ALPN h3 without experimental
- MEDIUM: ssl: allow h3/QMux negotiation without explicit proto
- BUG/MINOR: server: accept server IDs above 2^31 and clarify error message
- BUG/MINOR: backend: fix balance hash calculation when using hash-type none
- MINOR: server: support hash-key id32 for a cleaner distribution
- MINOR: backend: support hash-key guid for a stabler distribution
- MINOR: startup: support unprivileged chroot if possible
- MEDIUM: startup: add automatic chroot feature
- MINOR: h2: explain committed_extra_streams dec on h2_init() error
- OPTIM: h2: do not update committed streams if elasticity disabled
- MINOR: mux_quic: implement basic committed_extra_streams accounting
- MINOR: quic: use stream elasticity value for initial advertisement
- MINOR: mux_quic: define ms_bidi_rel QCC member
- MAJOR: mux_quic: support stream elasticity during connection lifetime
- BUG/MEDIUM: servers: Store the connection hash with the parameter cache
- BUG/MINOR: prevent conn leak in case of xprt_qmux init failure
- BUILD: traces: set a few __maybe_unused on vars used only for traces
- BUILD: traces: add USE_TRACE allowing to disable traces
- MINOR: startup: do not execute chroot() when "/"
- MEDIUM: startup: warn when chroot is not set for root
- BUG/MEDIUM: servers: Don't forget to set srv_hash when needed
- DOC: fix typo on QUIC stream.max-concurrent reference
- BUG/MINOR: mux_quic: do not exceed stream.max-concurrent on backend side
- BUG/MINOR: htx: Fix value of HTX_XFER_HDRS_ONLY flag
- MEDIUM: htx: Improve htx_xfer API to not count HTX meta-data
- BUG/MEDIUM: applet: Fix transfer of HTX data to the applet
- BUG/MEDIUM: htx: Alloc a chunk of right size in htx_replace_blk_value()
- MEDIUM: stick-tables: Avoid freeing elements while holding a lock
- MINOR: intops: add a multiply overflow detection for ulong and size_t
- CLEANUP: tree-wide: use array_size_or_fail() in array size for allocations
- DOC: update supported gcc and openssl versions in INSTALL
2026/05/13 : 3.4-dev12
- SCRIPTS: announce-release: add a link to the OpenTelemetry filter
- BUG/MEDIUM: servers: Only requeue servers if they are up
- MINOR: tinfo: store the number of committed extra streams in the tgroup
- MINOR: connection: add a function to calculate elastic streams limit
- MINOR: mux-h2: consider the elastic streams limit on frontend
- MINOR: lb: make LB initialization even more declarative
- BUG/MINOR: cfgparse-listen: do not emit extraneous line in rule order warnings
- CLEANUP: tree-wide: fix typos in non user-visible comments in 15 files
- CLEANUP: h1/htx: fix a few typos in warning, debug and trace messages
- BUG/MINOR: mux-h1: only check h1s if not NULL
- BUG/MINOR: http-fetch: fix smp_fetch_hdr_ip()'s handling of brackets for IPv6
- BUG/MINOR: http-fetch: make http_first_req() check for HTTP first
- BUG/MINOR: http-act: set-status() must check the response message, not the request
- BUG/MINOR: tools: fix memory leak in env_expand() error path
- BUG/MINOR: auth: free user groups on error paths in userlist_postinit()
- BUG/MINOR: uri-auth: avoid leaks on initialization error
- BUG/MINOR: cache: fix memory leak in parse_cache_rule error path
- BUG/MINOR: cfgcond: make KQUEUE check for GTUNE_USE_KQUEUE not GTUNE_USE_EPOLL
- BUG/MINOR: mqtt: connack parser returns MQTT_NEED_MORE_DATA on unknown property
- BUG/MINOR: mqtt: connect parser uses wrong bit field for TOPIC_ALIAS_MAXIMUM
- BUG/MINOR: mqtt: connack parser uses wrong bit for SUBSCRIPTION_IDENTIFIERS_AVAILABLE
- BUG/MINOR: mqtt: fix PUBLISH flags validation that want all bits to be set
- CLEANUP: http_htx: rename inner 'type' to 'ptype' to avoid variable shadowing
- CLEANUP: mux-h2: fix minor output debugging format issues
- CLEANUP: http-rules: fix a few '&' vs '&&' checks for clarity
- CLEANUP: auth: remove undeclared auth_resolve_groups() from auth.h
- CLEANUP: cache: remove redundant res_htx assignment in http_cache_io_handler()
- CLEANUP: channel: remove bogus and unused definition of channel_empty()
- CLEANUP: flt_http_comp: remove duplicate rate limit and CPU usage checks
- CLEANUP: mqtt: remove duplicate MQTT_FN_BIT_USER_PROPERTY in CONNECT fields
- BUG/MINOR: uri-auth: fix possible null-deref in latest fix for leaks
- BUG/MEDIUM: tasks: Keep the TASK_RUNNING flag until queued
- CLEANUP: mqtt: fix spelling of shared_subscription_available
- CLEANUP: regex: pre-initialize error variable in regex_comp() to calm analysis
- BUILD: compiler: fix redefinition of __nonstring
- CLEANUP: defaults: adjust MAX_THREADS multiplier number in comment
- CLEANUP: src/cpuset.c: fix missing return in functions returning int
- REGTESTS: Use ${tmpdir} instead of hardcoded /tmp/
- REGTESTS: Don't try to use real nameservers for testcases
- CLEANUP: tree-wide: fix typos in non user-visible comments in 3 more files
- MINOR: cli: improve forward compatibility for show fd
- DOC: management: document the <tgid>/<fd> form of show fd
- CLEANUP: tree-wide: fix more typos and outdated explanations in comments
- BUG/MEDIUM: dict: hold read lock while incrementing refcount in dict_insert
- BUG/MEDIUM: http-client: Only consume input buffer when hc one is empty
- BUG/MINOR: xprt_qstrm: fix conflicting prototype
- REORG: mux_quic: use newer qcm prefix for legacy qmux files
- MINOR: mux_quic: use qcm prefix for mux callbacks
- MINOR: mux_quic: use qcm prefix for mux functions
- MINOR: mux_quic: use qcm prefix for traces functions/structs
- MINOR: mux_quic: rename qstrm files to qmux
- MINOR: mux_quic: remove qstrm naming in QUIC MUX
- MINOR: connection: rename QMux related flags
- MINOR: xprt_qmux: use qmux instead of qstrm naming
- MINOR: trace: implement source alias
- MEDIUM: mux_quic: rename qmux traces to qcm
- MINOR: sample: add a generic reverse converter
- MINOR: sample: add a reverse_dom converter
- DOC: proxy-protocol: clarify UDP usage
- BUILD: 51d.c: cleanup, fix preprocessor ifdefs
- CLEANUP: tree-wide: fix typos in user-invisible files
- CLEANUP: htx: Adjust numbering of HTX blocks' types in the description
2026/05/08 : 3.4-dev11 2026/05/08 : 3.4-dev11
- BUG/MEDIUM: acme: fix segfault on newOrder with empty authorizations - BUG/MEDIUM: acme: fix segfault on newOrder with empty authorizations
- BUG/MINOR: acme: skip auth/challenge steps when newOrder returns a certificate - BUG/MINOR: acme: skip auth/challenge steps when newOrder returns a certificate

14
INSTALL
View file

@ -11,7 +11,7 @@ this task seriously and are doing a good job at backporting important fixes.
If for any reason you would prefer a different version than the one packaged If for any reason you would prefer a different version than the one packaged
for your system, you want to be certain to have all the fixes or to get some for your system, you want to be certain to have all the fixes or to get some
commercial support, other choices are available at https://www.haproxy.com/. commercial support, other choices are available at http://www.haproxy.com/.
Areas covered in this document Areas covered in this document
@ -111,12 +111,12 @@ HAProxy requires a working GCC or Clang toolchain and GNU make :
may want to retry with "gmake" which is the name commonly used for GNU make may want to retry with "gmake" which is the name commonly used for GNU make
on BSD systems. on BSD systems.
- GCC >= 4.7 (up to 16 tested). Older versions are no longer supported due to - GCC >= 4.7 (up to 15 tested). Older versions are no longer supported due to
the latest mt_list update which only uses c11-like atomics. Newer versions the latest mt_list update which only uses c11-like atomics. Newer versions
may sometimes break due to compiler regressions or behaviour changes. The may sometimes break due to compiler regressions or behaviour changes. The
version shipped with your operating system is very likely to work with no version shipped with your operating system is very likely to work with no
trouble. Clang >= 3.0 is also known to work as an alternative solution, and trouble. Clang >= 3.0 is also known to work as an alternative solution, and
versions up to 21 were successfully tested. Recent versions may emit a bit versions up to 19 were successfully tested. Recent versions may emit a bit
more warnings that are worth reporting as they may reveal real bugs. TCC more warnings that are worth reporting as they may reveal real bugs. TCC
(https://repo.or.cz/tinycc.git) is also usable for developers but will not (https://repo.or.cz/tinycc.git) is also usable for developers but will not
support threading and was found at least once to produce bad code in some support threading and was found at least once to produce bad code in some
@ -237,7 +237,7 @@ to forcefully enable it using "USE_LIBCRYPT=1".
----------------- -----------------
For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently For SSL/TLS, it is necessary to use a cryptography library. HAProxy currently
supports the OpenSSL library, and is known to build and work with branches supports the OpenSSL library, and is known to build and work with branches
1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 4.0. It is recommended to use 1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, and 3.0 to 3.6. It is recommended to use
at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration at least OpenSSL 1.1.1 to have support for all SSL keywords and configuration
in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's, in HAProxy. OpenSSL follows a long-term support cycle similar to HAProxy's,
and each of the branches above receives its own fixes, without forcing you to and each of the branches above receives its own fixes, without forcing you to
@ -426,9 +426,9 @@ Lua is an embedded programming language supported by HAProxy to provide more
advanced scripting capabilities. Only versions 5.3 and above are supported. advanced scripting capabilities. Only versions 5.3 and above are supported.
In order to enable Lua support, please specify "USE_LUA=1" on the command line. In order to enable Lua support, please specify "USE_LUA=1" on the command line.
Some systems provide this library under various names to avoid conflicts with Some systems provide this library under various names to avoid conflicts with
previous versions. By default, HAProxy looks for "lua5.5", "lua55", "lua5.4", previous versions. By default, HAProxy looks for "lua5.4", "lua54", "lua5.3",
"lua54", "lua5.3", "lua53", "lua". If your system uses a different naming, you "lua53", "lua". If your system uses a different naming, you may need to set the
may need to set the library name in the "LUA_LIB_NAME" variable. library name in the "LUA_LIB_NAME" variable.
If Lua is not provided on your system, it can be very simply built locally. It If Lua is not provided on your system, it can be very simply built locally. It
can be downloaded from https://www.lua.org/, extracted and built, for example : can be downloaded from https://www.lua.org/, extracted and built, for example :

View file

@ -44,7 +44,6 @@
# USE_CLOSEFROM : enable use of closefrom() on *bsd, solaris. Automatic. # USE_CLOSEFROM : enable use of closefrom() on *bsd, solaris. Automatic.
# USE_PRCTL : enable use of prctl(). Automatic. # USE_PRCTL : enable use of prctl(). Automatic.
# USE_PROCCTL : enable use of procctl(). Automatic. # USE_PROCCTL : enable use of procctl(). Automatic.
# USE_TRACE : enable trace subsystem. Always on.
# USE_ZLIB : enable zlib library support and disable SLZ # USE_ZLIB : enable zlib library support and disable SLZ
# USE_SLZ : enable slz library instead of zlib (default=enabled) # USE_SLZ : enable slz library instead of zlib (default=enabled)
# USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic. # USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic.
@ -60,6 +59,8 @@
# USE_WURFL : enable WURFL detection library from Scientiamobile # USE_WURFL : enable WURFL detection library from Scientiamobile
# USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init # 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_THREAD_DUMP : use the more advanced thread state dump system. Automatic.
# USE_OT : enable the OpenTracing filter
# EXTRA_MAKE : space-separated list of external addons using a Makefile.inc
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only. # USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
# USE_LIBATOMIC : force to link with/without libatomic. Automatic. # USE_LIBATOMIC : force to link with/without libatomic. Automatic.
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours # USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
@ -93,7 +94,6 @@
# SILENT_DEFINE may be used to specify other defines which will not be # SILENT_DEFINE may be used to specify other defines which will not be
# reported by "haproxy -vv". # reported by "haproxy -vv".
# EXTRA is used to force building or not building some extra tools. # EXTRA is used to force building or not building some extra tools.
# EXTRA_MAKE space-separated list of external addons using a Makefile.inc
# DESTDIR is not set by default and is used for installation only. # DESTDIR is not set by default and is used for installation only.
# It might be useful to set DESTDIR if you want to install haproxy # It might be useful to set DESTDIR if you want to install haproxy
# in a sandbox. # in a sandbox.
@ -123,7 +123,12 @@
# LUA_LIB : force the lib path to lua # LUA_LIB : force the lib path to lua
# LUA_INC : force the include path to lua # LUA_INC : force the include path to lua
# LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of # LUA_LIB_NAME : force the lib name (or automatically evaluated, by order of
# priority: lua5.5, lua55, lua5.4, lua54, lua5.3, lua53, lua). # priority : lua5.4, lua54, 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
# OT_USE_VARS : allows the use of variables for the OpenTracing context
# IGNOREGIT : ignore GIT commit versions if set. # IGNOREGIT : ignore GIT commit versions if set.
# VERSION : force haproxy version reporting. # VERSION : force haproxy version reporting.
# SUBVERS : add a sub-version (eg: platform, model, ...). # SUBVERS : add a sub-version (eg: platform, model, ...).
@ -338,12 +343,12 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \ USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \ USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \ USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
USE_ECH USE_TRACE \ USE_ECH \
USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \ USE_SSL USE_LUA USE_ACCEPT4 USE_CLOSEFROM USE_ZLIB USE_SLZ \
USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \ USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \
USE_MATH USE_DEVICEATLAS USE_51DEGREES \ USE_MATH USE_DEVICEATLAS USE_51DEGREES \
USE_WURFL USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL \ USE_WURFL USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL \
USE_THREAD_DUMP USE_EVPORTS USE_QUIC USE_PROMEX \ USE_THREAD_DUMP USE_EVPORTS USE_OT USE_QUIC USE_PROMEX \
USE_MEMORY_PROFILING USE_SHM_OPEN \ USE_MEMORY_PROFILING USE_SHM_OPEN \
USE_STATIC_PCRE USE_STATIC_PCRE2 \ USE_STATIC_PCRE USE_STATIC_PCRE2 \
USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT \ USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT \
@ -361,9 +366,6 @@ $(warn_unknown_options)
# on the make command line. # on the make command line.
USE_POLL = default USE_POLL = default
# traces are always enabled
USE_TRACE = default
# SLZ is always supported unless explicitly disabled by passing USE_SLZ="" # SLZ is always supported unless explicitly disabled by passing USE_SLZ=""
# or disabled by enabling ZLIB using USE_ZLIB=1 # or disabled by enabling ZLIB using USE_ZLIB=1
ifeq ($(USE_ZLIB:0=),) ifeq ($(USE_ZLIB:0=),)
@ -665,11 +667,11 @@ OPTIONS_OBJS += src/mux_quic.o src/h3.o src/quic_rx.o src/quic_tx.o \
src/quic_cc_bbr.o src/quic_retry.o \ src/quic_cc_bbr.o src/quic_retry.o \
src/cfgparse-quic.o src/xprt_quic.o src/quic_token.o \ src/cfgparse-quic.o src/xprt_quic.o src/quic_token.o \
src/quic_ack.o src/qpack-dec.o src/quic_cc_newreno.o \ src/quic_ack.o src/qpack-dec.o src/quic_cc_newreno.o \
src/qcm_http.o src/qcm_trace.o src/quic_rules.o \ src/qmux_http.o src/qmux_trace.o src/quic_rules.o \
src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \ src/quic_cc_nocc.o src/quic_cc.o src/quic_pacing.o \
src/h3_stats.o src/quic_stats.o src/qpack-enc.o \ src/h3_stats.o src/quic_stats.o src/qpack-enc.o \
src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \ src/qpack-tbl.o src/quic_cc_drs.o src/quic_fctl.o \
src/quic_enc.o src/qcm_qmux.o src/xprt_qmux.o \ src/quic_enc.o src/mux_quic_qstrm.o src/xprt_qstrm.o \
src/mpring.o src/mpring.o
endif endif
@ -678,15 +680,15 @@ OPTIONS_OBJS += src/quic_openssl_compat.o
endif endif
ifneq ($(USE_LUA:0=),) ifneq ($(USE_LUA:0=),)
check_lua_inc = $(shell if [ ! -e /usr/include/lua.h -a -e $(2)$(1)/lua.h ]; then echo $(2)$(1); fi;) check_lua_inc = $(shell if [ -d $(2)$(1) ]; then echo $(2)$(1); fi;)
LUA_INC := $(firstword $(foreach lib,lua5.5 lua55 lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/"))) LUA_INC := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
check_lua_lib = $(shell echo "int main(){}" | $(CC) $(if $(LUA_INC),-I$(LUA_INC)) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1)) check_lua_lib = $(shell echo "int main(){}" | $(CC) -o /dev/null -x c - $(2) -l$(1) 2>/dev/null && echo $(1))
LUA_LD_FLAGS := -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic) $(if $(LUA_LIB),-L$(LUA_LIB)) LUA_LD_FLAGS := -Wl,$(if $(EXPORT_SYMBOL),$(EXPORT_SYMBOL),--export-dynamic) $(if $(LUA_LIB),-L$(LUA_LIB))
# Try to automatically detect the Lua library if not set # Try to automatically detect the Lua library if not set
ifeq ($(LUA_LIB_NAME),) ifeq ($(LUA_LIB_NAME),)
LUA_LIB_NAME := $(firstword $(foreach lib,lua lua5.5 lua55 lua5.4 lua54 lua5.3 lua53,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS)))) LUA_LIB_NAME := $(firstword $(foreach lib,lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_lib,$(lib),$(LUA_LD_FLAGS))))
endif endif
# Lua lib name must be set now (forced/detected above) # Lua lib name must be set now (forced/detected above)
@ -716,15 +718,70 @@ ifneq ($(USE_PROMEX:0=),)
endif endif
ifneq ($(USE_DEVICEATLAS:0=),) ifneq ($(USE_DEVICEATLAS:0=),)
EXTRA_MAKE += addons/deviceatlas # Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
# can be used to disable the cache support if needed (this also removes the necessity of having
# a C++ toolchain installed).
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
include addons/deviceatlas/Makefile.inc
OPTIONS_OBJS += addons/deviceatlas/da.o
endif endif
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
# regardless of the version since they are mutually exclusive. The version
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
51DEGREES_INC = $(51DEGREES_SRC)
51DEGREES_LIB = $(51DEGREES_SRC)
51DEGREES_VER = 3
ifneq ($(USE_51DEGREES:0=),) ifneq ($(USE_51DEGREES:0=),)
EXTRA_MAKE += addons/51degrees ifeq ($(51DEGREES_VER),4) # v4 here
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
endif
USE_LIBATOMIC = implicit
endif # 51DEGREES_VER==4
ifeq ($(51DEGREES_VER),3) # v3 here
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
else
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
endif
else
ifneq ($(51DEGREES_VER),4)
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
endif
endif # 51DEGREES_VER==3
OPTIONS_OBJS += addons/51degrees/51d.o
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
USE_MATH = implicit
endif # USE_51DEGREES endif # USE_51DEGREES
ifneq ($(USE_WURFL:0=),) ifneq ($(USE_WURFL:0=),)
EXTRA_MAKE += addons/wurfl # Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
# to WURFL headers and libraries if needed.
WURFL_INC = $(WURFL_SRC)
WURFL_LIB = $(WURFL_SRC)
OPTIONS_OBJS += addons/wurfl/wurfl.o
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
ifneq ($(WURFL_DEBUG),)
WURFL_CFLAGS += -DWURFL_DEBUG
endif
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
endif
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl
endif endif
ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),) ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),)
@ -803,6 +860,11 @@ ifneq ($(USE_LINUX_CAP:0=),)
OPTIONS_OBJS += src/linuxcap.o OPTIONS_OBJS += src/linuxcap.o
endif endif
ifneq ($(USE_OT:0=),)
$(call warning, The opentracing filter was deprecated in haproxy 3.3 and will be removed in 3.5.)
include addons/ot/Makefile
endif
ifneq ($(EXTRA_MAKE),) ifneq ($(EXTRA_MAKE),)
include $(addsuffix /Makefile.mk,$(EXTRA_MAKE)) include $(addsuffix /Makefile.mk,$(EXTRA_MAKE))
endif endif
@ -992,7 +1054,7 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
ifneq ($(TARGET),) ifneq ($(TARGET),)
ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),) ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(EXTRA_MAKE) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi) build_opts = $(shell rm -f .build_opts.new; echo \'$(TARGET) $(BUILD_OPTIONS) $(VERBOSE_CFLAGS) $(WARN_CFLAGS) $(NOWARN_CFLAGS) $(DEBUG)\' > .build_opts.new; if cmp -s .build_opts .build_opts.new; then rm -f .build_opts.new; else mv -f .build_opts.new .build_opts; fi)
.build_opts: $(build_opts) .build_opts: $(build_opts)
else else
.build_opts: .build_opts:

View file

@ -4,7 +4,7 @@
[![Illumos](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml) [![Illumos](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/illumos.yml)
[![NetBSD](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml) [![NetBSD](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/netbsd.yml)
[![CrossCompile](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml) [![CrossCompile](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/cross-zoo.yml)
[![FreeBSD](https://github.com/haproxy/haproxy/actions/workflows/freebsd.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/freebsd.yml) [![FreeBSD](https://api.cirrus-ci.com/github/haproxy/haproxy.svg?task=FreeBSD)](https://cirrus-ci.com/github/haproxy/haproxy/)
[![VTest](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml) [![VTest](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/vtest.yml)
![HAProxy logo](doc/HAProxyCommunityEdition_60px.png) ![HAProxy logo](doc/HAProxyCommunityEdition_60px.png)

View file

@ -1,2 +1,2 @@
$Format:%ci$ $Format:%ci$
2026/06/25 2026/05/08

View file

@ -1 +1 @@
3.5-dev1 3.4-dev11

View file

@ -127,16 +127,7 @@ static int _51d_property_name_list(char **args, int section_type, struct proxy *
while (*(args[cur_arg])) { while (*(args[cur_arg])) {
name = calloc(1, sizeof(*name)); name = calloc(1, sizeof(*name));
if (!name) {
memprintf(err, "'%s' failed to allocate memory.", args[0]);
return -1;
}
name->name = strdup(args[cur_arg]); name->name = strdup(args[cur_arg]);
if (!name->name) {
free(name);
memprintf(err, "'%s' failed to allocate memory.", args[0]);
return -1;
}
LIST_APPEND(&global_51degrees.property_names, &name->list); LIST_APPEND(&global_51degrees.property_names, &name->list);
++cur_arg; ++cur_arg;
} }
@ -312,7 +303,6 @@ static void _51d_init_device_offsets(fiftyoneDegreesDeviceOffsets *offsets) {
static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets) static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOffsets *offsets)
{ {
struct buffer *temp = get_trash_chunk();
struct channel *chn; struct channel *chn;
struct htx *htx; struct htx *htx;
struct http_hdr_ctx ctx; struct http_hdr_ctx ctx;
@ -334,15 +324,7 @@ static void _51d_set_device_offsets(struct sample *smp, fiftyoneDegreesDeviceOff
if (http_find_header(htx, name, &ctx, 1)) { if (http_find_header(htx, name, &ctx, 1)) {
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i); (offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
/* Copy value into trash and NUL-terminate before passing to the (offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
* 51Degrees Trie API, which expects a C string.
*/
if (ctx.value.len >= temp->size)
continue;
memcpy(temp->area, ctx.value.ptr, ctx.value.len);
temp->area[ctx.value.len] = '\0';
temp->data = ctx.value.len + 1;
(offsets->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, temp->area);
offsets->size++; offsets->size++;
} }
} }
@ -568,8 +550,6 @@ static void _51d_process_match(const struct arg *args, struct sample *smp)
char valuesBuffer[1024]; char valuesBuffer[1024];
#endif #endif
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) || defined(FIFTYONE_DEGREES_HASH_INCLUDED)
char no_data[] = "NoData"; /* response when no data could be found */ char no_data[] = "NoData"; /* response when no data could be found */
struct buffer *temp = get_trash_chunk(); struct buffer *temp = get_trash_chunk();
int i = 0, found; int i = 0, found;
@ -656,7 +636,6 @@ static void _51d_process_match(const struct arg *args, struct sample *smp)
smp->data.u.str.area = temp->area; smp->data.u.str.area = temp->area;
smp->data.u.str.data = temp->data; smp->data.u.str.data = temp->data;
} }
#endif
/* Sets the sample data as a constant string. This ensures that the /* Sets the sample data as a constant string. This ensures that the
* string will be processed correctly. * string will be processed correctly.
@ -937,10 +916,6 @@ static int init_51degrees(void)
list_for_each_entry(name, &global_51degrees.property_names, list) list_for_each_entry(name, &global_51degrees.property_names, list)
++i; ++i;
_51d_property_list = calloc(i, sizeof(*_51d_property_list)); _51d_property_list = calloc(i, sizeof(*_51d_property_list));
if (!_51d_property_list) {
ha_alert("51Degrees: Failed to allocate property list.\n");
return (ERR_FATAL | ERR_ALERT);
}
i = 0; i = 0;
list_for_each_entry(name, &global_51degrees.property_names, list) list_for_each_entry(name, &global_51degrees.property_names, list)
@ -1075,7 +1050,7 @@ static int init_51degrees(void)
static void deinit_51degrees(void) static void deinit_51degrees(void)
{ {
struct _51d_property_names *_51d_prop_name = NULL, *_51d_prop_nameb = NULL; struct _51d_property_names *_51d_prop_name, *_51d_prop_nameb;
#if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED) #if defined(FIFTYONEDEGREES_H_PATTERN_INCLUDED) || defined(FIFTYONEDEGREES_H_TRIE_INCLUDED)
free(global_51degrees.header_names); free(global_51degrees.header_names);

View file

@ -1,37 +0,0 @@
# Use 51DEGREES_SRC and possibly 51DEGREES_INC and 51DEGREES_LIB to force path
# to 51degrees v3/v4 headers and libraries if needed. Note that the SRC/INC/
# LIB/CFLAGS/LDFLAGS variables names all use 51DEGREES as the prefix,
# regardless of the version since they are mutually exclusive. The version
# (51DEGREES_VER) must be either 3 or 4, and defaults to 3 if not set.
51DEGREES_INC = $(51DEGREES_SRC)
51DEGREES_LIB = $(51DEGREES_SRC)
51DEGREES_VER = 3
ifeq ($(51DEGREES_VER),4) # v4 here
_51DEGREES_SRC = $(shell find $(51DEGREES_LIB) -maxdepth 2 -name '*.c')
OPTIONS_OBJS += $(_51DEGREES_SRC:%.c=%.o)
51DEGREES_CFLAGS += -DUSE_51DEGREES_V4
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING -DFIFTYONE_DEGREES_NO_THREADING
endif
USE_LIBATOMIC = implicit
endif # 51DEGREES_VER==4
ifeq ($(51DEGREES_VER),3) # v3 here
OPTIONS_OBJS += $(51DEGREES_LIB)/../cityhash/city.o
OPTIONS_OBJS += $(51DEGREES_LIB)/51Degrees.o
ifeq ($(USE_THREAD:0=),)
51DEGREES_CFLAGS += -DFIFTYONEDEGREES_NO_THREADING
else
OPTIONS_OBJS += $(51DEGREES_LIB)/../threading.o
endif
else
ifneq ($(51DEGREES_VER),4)
$(error 51Degrees version (51DEGREES_VER) must be either 3 or 4)
endif
endif # 51DEGREES_VER==3
OPTIONS_OBJS += addons/51degrees/51d.o
51DEGREES_CFLAGS += $(if $(51DEGREES_INC),-I$(51DEGREES_INC))
51DEGREES_LDFLAGS += $(if $(51DEGREES_LIB),-L$(51DEGREES_LIB))
USE_MATH = implicit

View file

@ -1,13 +1,6 @@
# DEVICEATLAS_SRC : DeviceAtlas API source root path # DEVICEATLAS_SRC : DeviceAtlas API source root path
# Use DEVICEATLAS_SRC and possibly DEVICEATLAS_INC and DEVICEATLAS_LIB to force path
# to DeviceAtlas headers and libraries if needed. In this context, DEVICEATLAS_NOCACHE
# can be used to disable the cache support if needed (this also removes the necessity of having
# a C++ toolchain installed).
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
CXX := c++ CXX := c++
CXXLIB := -lstdc++ CXXLIB := -lstdc++
@ -35,7 +28,5 @@ OPTIONS_OBJS += $(DEVICEATLAS_SRC)/dadwcurl.o
OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o
endif endif
OPTIONS_OBJS += addons/deviceatlas/da.o
addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp
$(cmd_CXX) $(CXXFLAGS) -c -o $@ $< $(cmd_CXX) $(CXXFLAGS) -c -o $@ $<

1
addons/ot/AUTHORS Normal file
View file

@ -0,0 +1 @@
Miroslav Zagorac <mzagorac@haproxy.com>

1
addons/ot/MAINTAINERS Normal file
View file

@ -0,0 +1 @@
Miroslav Zagorac <mzagorac@haproxy.com>

73
addons/ot/Makefile Normal file
View file

@ -0,0 +1,73 @@
# 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_USE_VARS : allows the use of variables for the OpenTracing context
OT_DEFINE =
OT_CFLAGS =
OT_LDFLAGS =
OT_DEBUG_EXT =
OT_PKGSTAT =
OTC_WRAPPER = opentracing-c-wrapper
ifneq ($(OT_DEBUG),)
OT_DEBUG_EXT = _dbg
OT_DEFINE = -DDEBUG_OT
endif
ifeq ($(OT_INC),)
OT_PKGSTAT = $(shell pkg-config --exists $(OTC_WRAPPER)$(OT_DEBUG_EXT); echo $$?)
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_PKGSTAT),)
ifeq ($(OT_CFLAGS),)
$(error OpenTracing C wrapper : can't find headers)
endif
else
ifneq ($(OT_PKGSTAT),0)
$(error OpenTracing C wrapper : can't find package)
endif
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 += \
addons/ot/src/cli.o \
addons/ot/src/conf.o \
addons/ot/src/event.o \
addons/ot/src/filter.o \
addons/ot/src/group.o \
addons/ot/src/http.o \
addons/ot/src/opentracing.o \
addons/ot/src/parser.o \
addons/ot/src/pool.o \
addons/ot/src/scope.o \
addons/ot/src/util.o
ifneq ($(OT_USE_VARS),)
OT_DEFINE += -DUSE_OT_VARS
OPTIONS_OBJS += \
addons/ot/src/vars.o
endif
OT_CFLAGS := $(OT_CFLAGS) $(OT_DEFINE)

799
addons/ot/README Normal file
View file

@ -0,0 +1,799 @@
-----------------------------------------
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.
Note: The OpenTracing filter shouldn't be used for new designs as OpenTracing
itself is no longer maintained nor supported by its authors. As such
OpenTracing will be deprecated in 3.3 and removed in 3.5. A replacement
filter based on OpenTelemetry is available since 3.4 with complete build
instructions currently at:
https://github.com/haproxytech/haproxy-opentelemetry/
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%).

298
addons/ot/README-func Normal file
View file

@ -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() {
}
}

25
addons/ot/README-pool Normal file
View file

@ -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 debugging 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%).

50
addons/ot/include/cli.h Normal file
View file

@ -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
*/

228
addons/ot/include/conf.h Normal file
View file

@ -0,0 +1,228 @@
/***
* 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' %p '%s' %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %s %s %s }", \
FLT_OT_CONF_HDR_ARGS(a, id), (a)->config, (a)->cfgbuf, (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.loggers)), (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 *cfgbuf; /* The OpenTracing configuration. */
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
*/

View file

@ -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
*/

104
addons/ot/include/debug.h Normal file
View file

@ -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_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, date.tv_sec, date.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), &date) / 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"), \
flt_ot_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__); flt_ot_dbg_indent_level += 3; } while (0)
# define FLT_OT_RETURN(a) do { flt_ot_dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
# define FLT_OT_RETURN_EX(a,t,f) do { flt_ot_dbg_indent_level -= 3; { t _r = (a); FLT_OT_DBG(1, "} = " f, _r); return _r; } } while (0)
# define FLT_OT_RETURN_INT(a) FLT_OT_RETURN_EX((a), int, "%d")
# define FLT_OT_RETURN_PTR(a) FLT_OT_RETURN_EX((a), void *, "%p")
# define FLT_OT_DBG_IFDEF(a,b) a
# define FLT_OT_DBG_ARGS(a, ...) a, ##__VA_ARGS__
# define FLT_OT_DBG_BUF(a,b) do { FLT_OT_DBG((a), "%p:{ %zu %p %zu %zu }", (b), (b)->size, (b)->area, (b)->data, (b)->head); } while (0)
struct flt_ot_debug {
#ifndef DEBUG_OT_SYSTIME
struct timeval start;
#endif
uint8_t level;
};
extern THREAD_LOCAL int flt_ot_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_RETURN_EX(a,t,f) return a
# define FLT_OT_RETURN_INT(a) return a
# define FLT_OT_RETURN_PTR(a) return a
# define FLT_OT_DBG_IFDEF(a,b) b
# define FLT_OT_DBG_ARGS(...)
# define FLT_OT_DBG_BUF(a,b) while (0)
#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
*/

107
addons/ot/include/define.h Normal file
View file

@ -0,0 +1,107 @@
/***
* 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_DELETE(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__); \
\
FLT_OT_DBG(3, "%d err: '%s'", __LINE__, *err); \
} \
} 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
*/

120
addons/ot/include/event.h Normal file
View file

@ -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
*/

View file

@ -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 FLT_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
*/

61
addons/ot/include/group.h Normal file
View file

@ -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
*/

41
addons/ot/include/http.h Normal file
View file

@ -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
*/

View file

@ -0,0 +1,66 @@
/***
* 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/clock.h>
#include <haproxy/filters.h>
#include <haproxy/http_htx.h>
#include <haproxy/http_rules.h>
#include <haproxy/log.h>
#include <haproxy/proxy.h>
#include <haproxy/sample.h>
#include <haproxy/tcp_rules.h>
#include <haproxy/tools.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
*/

View file

@ -0,0 +1,86 @@
/***
* 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 *plugin, char **err);
int ot_start(struct otc_tracer *tracer, const char *cfgbuf, 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
*/

172
addons/ot/include/parser.h Normal file
View file

@ -0,0 +1,172 @@
/***
* 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_IGNORE_NAME '-'
#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, CHAR, 2, 2, "ot-tracer", " <name>") \
FLT_OT_PARSE_TRACER_DEF( ACL, 0, CHAR, 3, 0, "acl", " <name> <criterion> [flags] [operator] <value> ...") \
FLT_OT_PARSE_TRACER_DEF( LOG, 0, CHAR, 2, 0, "log", " { global | <addr> [len <len>] [format <fmt>] <facility> [<level> [<minlevel>]] }") \
FLT_OT_PARSE_TRACER_DEF( CONFIG, 0, NONE, 2, 2, "config", " <file>") \
FLT_OT_PARSE_TRACER_DEF( PLUGIN, 0, NONE, 2, 2, "plugin", " <file>") \
FLT_OT_PARSE_TRACER_DEF( GROUPS, 0, NONE, 2, 0, "groups", " <name> ...") \
FLT_OT_PARSE_TRACER_DEF( SCOPES, 0, NONE, 2, 0, "scopes", " <name> ...") \
FLT_OT_PARSE_TRACER_DEF( RATE_LIMIT, 0, NONE, 2, 2, "rate-limit", " <value>") \
FLT_OT_PARSE_TRACER_DEF( OPTION, 0, NONE, 2, 2, "option", " { disabled | dontlog-normal | hard-errors }") \
FLT_OT_PARSE_TRACER_DEF(DEBUG_LEVEL, 0, NONE, 2, 2, "debug-level", " <value>")
#define FLT_OT_PARSE_GROUP_DEFINES \
FLT_OT_PARSE_GROUP_DEF( ID, 0, CHAR, 2, 2, "ot-group", " <name>") \
FLT_OT_PARSE_GROUP_DEF(SCOPES, 0, NONE, 2, 0, "scopes", " <name> ...")
#ifdef USE_OT_VARS
# define FLT_OT_PARSE_SCOPE_INJECT_HELP " <name-prefix> [use-vars] [use-headers]"
# define FLT_OT_PARSE_SCOPE_EXTRACT_HELP " <name-prefix> [use-vars | use-headers]"
#else
# define FLT_OT_PARSE_SCOPE_INJECT_HELP " <name-prefix> [use-headers]"
# define FLT_OT_PARSE_SCOPE_EXTRACT_HELP " <name-prefix> [use-headers]"
#endif
/*
* In case the possibility of working with OpenTracing context via HAProxyu
* variables is not used, args_max member of the structure flt_ot_parse_data
* should be reduced for 'inject' keyword. However, this is not critical
* because in this case the 'use-vars' argument cannot be entered anyway,
* so I will not complicate it here with additional definitions.
*/
#define FLT_OT_PARSE_SCOPE_DEFINES \
FLT_OT_PARSE_SCOPE_DEF( ID, 0, CHAR, 2, 2, "ot-scope", " <name>") \
FLT_OT_PARSE_SCOPE_DEF( SPAN, 0, NONE, 2, 5, "span", " <name> [<reference>] [root]") \
FLT_OT_PARSE_SCOPE_DEF( TAG, 1, NONE, 3, 0, "tag", " <name> <sample> ...") \
FLT_OT_PARSE_SCOPE_DEF( LOG, 1, NONE, 3, 0, "log", " <name> <sample> ...") \
FLT_OT_PARSE_SCOPE_DEF(BAGGAGE, 1, VAR, 3, 0, "baggage", " <name> <sample> ...") \
FLT_OT_PARSE_SCOPE_DEF( INJECT, 1, CTX, 2, 4, "inject", FLT_OT_PARSE_SCOPE_INJECT_HELP) \
FLT_OT_PARSE_SCOPE_DEF(EXTRACT, 0, CTX, 2, 3, "extract", FLT_OT_PARSE_SCOPE_EXTRACT_HELP) \
FLT_OT_PARSE_SCOPE_DEF( FINISH, 0, NONE, 2, 0, "finish", " <name> ...") \
FLT_OT_PARSE_SCOPE_DEF( ACL, 0, CHAR, 3, 0, "acl", " <name> <criterion> [flags] [operator] <value> ...") \
FLT_OT_PARSE_SCOPE_DEF( EVENT, 0, NONE, 2, 0, "event", " <name> [{ if | unless } <condition>]")
enum FLT_OT_PARSE_INVCHAR_enum {
FLT_OT_PARSE_INVALID_NONE,
FLT_OT_PARSE_INVALID_CHAR,
FLT_OT_PARSE_INVALID_DOM,
FLT_OT_PARSE_INVALID_CTX,
FLT_OT_PARSE_INVALID_VAR,
};
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
*/

39
addons/ot/include/pool.h Normal file
View file

@ -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
*/

126
addons/ot/include/scope.h Normal file
View file

@ -0,0 +1,126 @@
/***
* 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 '%s' %hhu %hhu 0x%02hhx 0x%08x %s %s }", \
(f), (a), (a)->stream, (a)->filter, (a)->uuid, (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. */
};
/* 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. */
char uuid[40]; /* 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
*/

109
addons/ot/include/util.h Normal file
View file

@ -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
*/

55
addons/ot/include/vars.h Normal file
View file

@ -0,0 +1,55 @@
/***
* 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_CTX_SIZE int8_t
#define FLT_OT_VAR_CHAR_DASH 'D'
#define FLT_OT_VAR_CHAR_SPACE 'S'
struct flt_ot_ctx {
char value[BUFSIZ];
int value_len;
};
typedef int (*flt_ot_ctx_loop_cb)(struct sample *, size_t, const char *, const char *, const char *, FLT_OT_VAR_CTX_SIZE, char **, void *);
#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_vars_unset(struct stream *s, const char *scope, const char *prefix, 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
*/

397
addons/ot/src/cli.c Normal file
View file

@ -0,0 +1,397 @@
/***
* 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/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)
{
struct cli_print_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
FLT_OT_FUNC("%p, %p, %p, %d", appctx, err, msg, cli_state);
if ((appctx == NULL) || ((err == NULL) && (msg == NULL)))
FLT_OT_RETURN();
ctx->err = (err == NULL) ? msg : err;
appctx->st0 = (ctx->err == NULL) ? CLI_ST_PROMPT : cli_state;
FLT_OT_DBG(1, "err(%d): \"%s\"", appctx->st0, ctx->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_DYNERR);
FLT_OT_RETURN_INT(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_DYNERR);
FLT_OT_RETURN_INT(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_DYNERR);
FLT_OT_RETURN_INT(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_DYNERR);
FLT_OT_RETURN_INT(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_DYNERR);
FLT_OT_RETURN_INT(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_DYNERR);
FLT_OT_RETURN_INT(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
*/

764
addons/ot/src/conf.c Normal file
View file

@ -0,0 +1,764 @@
/***
* 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/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_PTR(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_APPEND(head, &(retptr->list));
} else {
FLT_OT_ERR("out of memory");
}
FLT_OT_RETURN_PTR(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_PTR(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_PTR(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_PTR(retptr);
flt_ot_args_to_str(args, 2, &(retptr->value));
if (retptr->value == NULL) {
FLT_OT_FREE_CLEAR(retptr);
FLT_OT_RETURN_PTR(retptr);
}
retptr->num_exprs = flt_ot_args_count(args) - 2;
LIST_INIT(&(retptr->exprs));
FLT_OT_DBG_CONF_SAMPLE("- init ", retptr);
FLT_OT_RETURN_PTR(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_PTR(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_PTR(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_PTR(retptr);
LIST_INIT(&(retptr->tags));
LIST_INIT(&(retptr->logs));
LIST_INIT(&(retptr->baggages));
FLT_OT_DBG_CONF_SPAN("- init ", retptr);
FLT_OT_RETURN_PTR(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_PTR(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_PTR(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);
}
free_acl_cond((*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_PTR(retptr);
LIST_INIT(&(retptr->ph_scopes));
FLT_OT_DBG_CONF_GROUP("- init ", retptr);
FLT_OT_RETURN_PTR(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_PTR(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_PTR(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 logger *logger, *loggerback;
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)->cfgbuf);
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.loggers list %s", flt_ot_list_debug(&((*ptr)->proxy_log.loggers)));
list_for_each_entry_safe(logger, loggerback, &((*ptr)->proxy_log.loggers), list) {
LIST_DELETE(&(logger->list));
FLT_OT_FREE(logger);
}
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_PTR(retptr);
retptr->proxy = px;
LIST_INIT(&(retptr->groups));
LIST_INIT(&(retptr->scopes));
FLT_OT_DBG_CONF("- init ", retptr);
FLT_OT_RETURN_PTR(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
*/

338
addons/ot/src/event.c Normal file
View file

@ -0,0 +1,338 @@
/***
* 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/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_INT(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++) {
#ifdef USE_OT_VARS
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;
#endif
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_INT(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_INT(retval);
}
}
list_for_each_entry(conf_ctx, &(conf_scope->contexts), list) {
struct otc_text_map *text_map = NULL;
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);
#ifdef USE_OT_VARS
else
text_map = flt_ot_vars_get(s, FLT_OT_VARS_SCOPE, conf_ctx->id, dir, err);
#endif
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_INT(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;
}
#ifdef USE_OT_VARS
flt_ot_vars_dump(s);
#endif
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_INT(retval);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*
* vi: noexpandtab shiftwidth=8 tabstop=8
*/

1182
addons/ot/src/filter.c Normal file

File diff suppressed because it is too large Load diff

354
addons/ot/src/group.c Normal file
View file

@ -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/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_EX(ACT_RET_CONT, enum act_return, "%d");
}
if (conf->tracer->flag_disabled) {
FLT_OT_DBG(1, "filter '%s' disabled, group action '%s' ignored", conf->id, conf_group->id);
FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
}
/* 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_EX(ACT_RET_CONT, enum act_return, "%d");
}
else if (flt_ot_is_disabled(filter FLT_OT_DBG_ARGS(, -1))) {
FLT_OT_RETURN_EX(ACT_RET_CONT, enum act_return, "%d");
}
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_EX(ACT_RET_CONT, enum act_return, "%d");
}
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_EX(ACT_RET_CONT, enum act_return, "%d");
}
/***
* 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_INT(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_INT(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_INT(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_INT(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_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
}
/* 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_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
}
/* 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_EX(ACT_RET_PRS_ERR, enum act_parse_ret, "%d");
}
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_EX(ACT_RET_PRS_OK, enum act_parse_ret, "%d");
}
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
*/

312
addons/ot/src/http.c Normal file
View file

@ -0,0 +1,312 @@
/***
* 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/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_PTR(retptr);
/*
* The keyword 'inject' allows you to define the name of the OpenTracing
* context without using a prefix. In that case all HTTP headers are
* transferred because it is not possible to separate them from the
* OpenTracing context (this separation is usually done via a prefix).
*
* When using the 'extract' keyword, the context name must be specified.
* To allow all HTTP headers to be extracted, the first character of
* that name must be set to FLT_OT_PARSE_CTX_IGNORE_NAME.
*/
if (FLT_OT_STR_ISVALID(prefix) && (*prefix == FLT_OT_PARSE_CTX_IGNORE_NAME))
prefix_len = 0;
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);
/*
* In case the data of the HTTP header is not
* specified, v.ptr will have some non-null
* value and v.len will be equal to 0. The
* otc_text_map_add() function will not
* interpret this well. In this case v.ptr
* is set to an empty string.
*/
if (v.len == 0)
v = ist("");
/*
* 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_PTR(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_INT(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_INT(retval);
}
if (!FLT_OT_STR_ISVALID(prefix)) {
ist_name = ist2((char *)name, strlen(name));
}
else if (!FLT_OT_STR_ISVALID(name)) {
ist_name = ist2((char *)prefix, strlen(prefix));
}
else {
buffer = flt_ot_trash_alloc(0, err);
if (buffer == NULL)
FLT_OT_RETURN_INT(retval);
(void)chunk_printf(buffer, "%s-%s", prefix, name);
ist_name = ist2(buffer->area, 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) == 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_INT(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_INT(retval);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*
* vi: noexpandtab shiftwidth=8 tabstop=8
*/

1067
addons/ot/src/opentracing.c Normal file

File diff suppressed because it is too large Load diff

1230
addons/ot/src/parser.c Normal file

File diff suppressed because it is too large Load diff

223
addons/ot/src/pool.c Normal file
View file

@ -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/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(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_PTR(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(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_PTR(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
* -
*/
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_PTR(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
*/

634
addons/ot/src/scope.c Normal file
View file

@ -0,0 +1,634 @@
/***
* 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/include.h"
static struct pool_head *pool_head_ot_scope_span __read_mostly = NULL;
static struct pool_head *pool_head_ot_scope_context __read_mostly = NULL;
static struct pool_head *pool_head_ot_runtime_context __read_mostly = 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 buffer uuid;
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_PTR(retptr);
retptr->stream = s;
retptr->filter = f;
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));
uuid = b_make(retptr->uuid, sizeof(retptr->uuid), 0, 0);
ha_generate_uuid_v4(&uuid);
#ifdef USE_OT_VARS
/*
* The HAProxy variable 'sess.ot.uuid' is registered here,
* after which its value is set to runtime context UUID.
*/
if (flt_ot_var_register(FLT_OT_VAR_UUID, err) != -1)
(void)flt_ot_var_set(s, FLT_OT_VAR_UUID, retptr->uuid, SMP_OPT_DIR_REQ, err);
#endif
FLT_OT_DBG_RUNTIME_CONTEXT("session context: ", retptr);
FLT_OT_RETURN_PTR(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_PTR(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_PTR(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_PTR(retptr);
}
}
}
retptr = flt_ot_pool_alloc(pool_head_ot_scope_span, sizeof(*retptr), 1, err);
if (retptr == NULL)
FLT_OT_RETURN_PTR(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_INSERT(&(rt_ctx->spans), &(retptr->list));
FLT_OT_DBG_SCOPE_SPAN("new span ", retptr);
FLT_OT_RETURN_PTR(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_PTR(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_PTR(retptr);
}
retptr = flt_ot_pool_alloc(pool_head_ot_scope_context, sizeof(*retptr), 1, err);
if (retptr == NULL)
FLT_OT_RETURN_PTR(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_PTR(retptr);
}
retptr->id = id;
retptr->id_len = id_len;
retptr->smp_opt_dir = dir;
retptr->context = span_ctx;
LIST_INSERT(&(rt_ctx->contexts), &(retptr->list));
FLT_OT_DBG_SCOPE_CONTEXT("new context ", retptr);
FLT_OT_RETURN_PTR(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_INT(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);
#ifdef USE_OT_VARS
(void)flt_ot_vars_unset(rt_ctx->stream, FLT_OT_VARS_SCOPE, ctx->id, ctx->smp_opt_dir, NULL);
#endif
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
*/

815
addons/ot/src/util.c Normal file
View file

@ -0,0 +1,815 @@
/***
* 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/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, argc;
argc = flt_ot_args_count(args);
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), flt_ot_dbg_indent_level, FLT_OT_DBG_INDENT, argc, args[0]);
for (i = 1; i < argc; 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_EX(-1, ssize_t, "%ld");
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_EX(-1, ssize_t, "%ld");
}
else if (n > (chk->size - chk->data)) {
FLT_OT_ERR("chunk size too small");
FLT_OT_RETURN_EX(-1, ssize_t, "%ld");
}
(void)memcpy(chk->area + chk->data, src, n);
chk->data += n;
FLT_OT_RETURN_EX(chk->data, ssize_t, "%ld");
}
/***
* NAME
* flt_ot_args_count -
*
* ARGUMENTS
* args -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
int flt_ot_args_count(char **args)
{
int i, retval = 0;
if (args == NULL)
return retval;
/*
* It is possible that some arguments within the configuration line
* are not specified; that is, they are set to a blank string.
*
* For example:
* keyword '' arg_2
*
* In that case the content of the args field will be like this:
* args[0]: 'keyword'
* args[1]: NULL pointer
* args[2]: 'arg_2'
* args[3 .. MAX_LINE_ARGS): NULL pointers
*
* The total number of arguments is the index of the last argument
* (increased by 1) that is not a NULL pointer.
*/
for (i = 0; i < MAX_LINE_ARGS; i++)
if (FLT_OT_ARG_ISVALID(i))
retval = i + 1;
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, argc;
if ((args == NULL) || (*args == NULL))
return;
argc = flt_ot_args_count(args);
for (i = idx; i < argc; i++)
(void)memprintf(str, "%s%s%s", (*str == NULL) ? "" : *str, (i == idx) ? "" : " ", (args[i] == NULL) ? "" : 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_INT(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;
memcpy(value, data->u.str.area, retval);
value[retval] = '\0';
}
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;
memcpy(value, data->u.meth.str.area, retval);
value[retval] = '\0';
}
}
else {
FLT_OT_ERR("invalid HTTP method");
}
FLT_OT_RETURN_INT(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_INT(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_INT(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_INT(retval);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*
* vi: noexpandtab shiftwidth=8 tabstop=8
*/

841
addons/ot/src/vars.c Normal file
View file

@ -0,0 +1,841 @@
/***
* 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/include.h"
#ifdef DEBUG_OT
/***
* NAME
* flt_ot_vars_scope_dump -
*
* ARGUMENTS
* vars -
* scope -
*
* DESCRIPTION
* Function prints the contents of all variables defined for a particular
* scope.
*
* RETURN VALUE
* This function does not return a value.
*/
static void flt_ot_vars_scope_dump(struct vars *vars, const char *scope)
{
int i;
if (vars == NULL)
return;
vars_rdlock(vars);
for (i = 0; i < VAR_NAME_ROOTS; i++) {
struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
for ( ; node != NULL; node = cebu64_imm_next(&(vars->name_root[i]), node)) {
struct var *var = container_of(node, struct var, name_node);
FLT_OT_DBG(2, "'%s.%016" PRIx64 "' -> '%.*s'", scope, var->name_hash, (int)b_data(&(var->data.u.str)), b_orig(&(var->data.u.str)));
}
}
vars_rdunlock(vars);
}
/***
* NAME
* flt_ot_vars_dump -
*
* ARGUMENTS
* s -
*
* DESCRIPTION
* Function prints the contents of all variables grouped by individual
* scope.
*
* 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(&(proc_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_smp_init -
*
* ARGUMENTS
* s -
* smp -
* opt -
* type -
* data -
*
* DESCRIPTION
* The function initializes the value of the 'smp' structure. If the 'data'
* argument is set, then the 'sample_data' member of the 'smp' structure is
* also initialized.
*
* RETURN VALUE
* This function does not return a value.
*/
static inline void flt_ot_smp_init(struct stream *s, struct sample *smp, uint opt, int type, const char *data)
{
(void)memset(smp, 0, sizeof(*smp));
(void)smp_set_owner(smp, s->be, s->sess, s, opt | SMP_OPT_FINAL);
if (data != NULL) {
smp->data.type = type;
chunk_initstr(&(smp->data.u.str), data);
}
}
/***
* NAME
* flt_ot_smp_add -
*
* ARGUMENTS
* data -
* blk -
* len -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_smp_add(struct sample_data *data, const char *name, size_t len, char **err)
{
bool flag_alloc = 0;
int retval = FLT_OT_RET_ERROR;
FLT_OT_FUNC("%p, \"%.*s\", %zu, %p:%p", data, (int)len, name, len, FLT_OT_DPTR_ARGS(err));
FLT_OT_DBG_BUF(2, &(data->u.str));
if (b_orig(&(data->u.str)) == NULL) {
data->type = SMP_T_BIN;
chunk_init(&(data->u.str), FLT_OT_MALLOC(global.tune.bufsize), global.tune.bufsize);
flag_alloc = (b_orig(&(data->u.str)) != NULL);
}
if (b_orig(&(data->u.str)) == NULL) {
FLT_OT_ERR("failed to add ctx '%.*s', not enough memory", (int)len, name);
}
else if (len > ((UINT64_C(1) << ((sizeof(FLT_OT_VAR_CTX_SIZE) << 3) - 1)) - 1)) {
FLT_OT_ERR("failed to add ctx '%.*s', too long name", (int)len, name);
}
else if ((len + sizeof(FLT_OT_VAR_CTX_SIZE)) > b_room(&(data->u.str))) {
FLT_OT_ERR("failed to add ctx '%.*s', too many names", (int)len, name);
}
else {
retval = b_data(&(data->u.str));
b_putchr(&(data->u.str), len);
(void)__b_putblk(&(data->u.str), name, len);
FLT_OT_DBG_BUF(2, &(data->u.str));
}
if ((retval == FLT_OT_RET_ERROR) && flag_alloc)
FLT_OT_FREE(b_orig(&(data->u.str)));
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_normalize_name -
*
* ARGUMENTS
* var_name -
* size -
* len -
* name -
* flag_cpy -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_normalize_name(char *var_name, size_t size, int *len, const char *name, bool flag_cpy, char **err)
{
int retval = 0;
FLT_OT_FUNC("%p, %zu, %p, \"%s\", %hhu, %p:%p", var_name, size, len, name, flag_cpy, FLT_OT_DPTR_ARGS(err));
if (!FLT_OT_STR_ISVALID(name))
FLT_OT_RETURN_INT(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 {
FLT_OT_ERR("failed to normalize variable name, buffer too small");
retval = -1;
}
if (flag_cpy) {
/* Copy variable name without modification. */
retval = strlen(name);
if ((*len + retval + 1) > size) {
FLT_OT_ERR("failed to normalize variable name, buffer too small");
retval = -1;
} else {
(void)memcpy(var_name + *len, name, retval + 1);
*len += retval;
}
} else {
/*
* 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_INT(retval);
}
/***
* NAME
* flt_ot_var_name -
*
* ARGUMENTS
* scope -
* prefix -
* name -
* flag_cpy -
* var_name -
* size -
* err -
*
* DESCRIPTION
* The function initializes the value of the 'smp' structure. If the 'data'
* argument is set, then the 'sample_data' member of the 'smp' structure is
* also initialized.
*
* RETURN VALUE
* -
*/
static int flt_ot_var_name(const char *scope, const char *prefix, const char *name, bool flag_cpy, char *var_name, size_t size, char **err)
{
int retval = 0;
FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %hhu, %p, %zu, %p:%p", scope, prefix, name, flag_cpy, var_name, size, FLT_OT_DPTR_ARGS(err));
if (flt_ot_normalize_name(var_name, size, &retval, scope, 0, err) >= 0)
if (flt_ot_normalize_name(var_name, size, &retval, prefix, 0, err) >= 0)
(void)flt_ot_normalize_name(var_name, size, &retval, name, flag_cpy, err);
if (retval == -1)
FLT_OT_ERR("failed to construct variable name '%s.%s.%s'", scope, prefix, name);
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_ctx_loop -
*
* ARGUMENTS
* smp -
* scope -
* prefix -
* err -
* func -
* ptr -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_ctx_loop(struct sample *smp, const char *scope, const char *prefix, char **err, flt_ot_ctx_loop_cb func, void *ptr)
{
FLT_OT_VAR_CTX_SIZE var_ctx_size;
char var_name[BUFSIZ], var_ctx[BUFSIZ];
int i, var_name_len, var_ctx_len, rc, n = 1, retval = 0;
FLT_OT_FUNC("%p, \"%s\", \"%s\", %p:%p, %p, %p", smp, scope, prefix, FLT_OT_DPTR_ARGS(err), func, ptr);
/*
* The variable in which we will save the name of the OpenTracing
* context variable.
*/
var_name_len = flt_ot_var_name(scope, prefix, NULL, 0, var_name, sizeof(var_name), err);
if (var_name_len == -1)
FLT_OT_RETURN_INT(FLT_OT_RET_ERROR);
/*
* Here we will try to find all the previously recorded variables from
* the currently set OpenTracing context. If we find the required
* variable and it is marked as deleted, we will mark it as active.
* If we do not find it, then it is added to the end of the previously
* saved names.
*/
if (vars_get_by_name(var_name, var_name_len, smp, NULL) == 0) {
FLT_OT_DBG(2, "ctx '%s' no variable found", var_name);
}
else if (smp->data.type != SMP_T_BIN) {
FLT_OT_ERR("ctx '%s' invalid data type %d", var_name, smp->data.type);
retval = FLT_OT_RET_ERROR;
}
else {
FLT_OT_DBG_BUF(2, &(smp->data.u.str));
for (i = 0; i < b_data(&(smp->data.u.str)); i += sizeof(var_ctx_size) + var_ctx_len, n++) {
var_ctx_size = *((typeof(var_ctx_size) *)(b_orig(&(smp->data.u.str)) + i));
var_ctx_len = abs(var_ctx_size);
if ((i + sizeof(var_ctx_size) + var_ctx_len) > b_data(&(smp->data.u.str))) {
FLT_OT_ERR("ctx '%s' invalid data size", var_name);
retval = FLT_OT_RET_ERROR;
break;
}
(void)memcpy(var_ctx, b_orig(&(smp->data.u.str)) + i + sizeof(var_ctx_size), var_ctx_len);
var_ctx[var_ctx_len] = '\0';
rc = func(smp, i, scope, prefix, var_ctx, var_ctx_size, err, ptr);
if (rc == FLT_OT_RET_ERROR) {
retval = FLT_OT_RET_ERROR;
break;
}
else if (rc > 0) {
retval = n;
break;
}
}
}
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_ctx_set_cb -
*
* ARGUMENTS
* smp -
* idx -
* scope -
* prefix -
* name -
* name_len -
* err -
* ptr -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_ctx_set_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OT_VAR_CTX_SIZE name_len, char **err, void *ptr)
{
struct flt_ot_ctx *ctx = ptr;
int retval = 0;
FLT_OT_FUNC("%p, %zu, \"%s\", \"%s\", \"%s\", %hhd, %p:%p, %p", smp, idx, scope, prefix, name, name_len, FLT_OT_DPTR_ARGS(err), ptr);
if ((name_len == ctx->value_len) && (strncmp(name, ctx->value, name_len) == 0)) {
FLT_OT_DBG(2, "ctx '%s' found\n", name);
retval = 1;
}
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_ctx_set -
*
* ARGUMENTS
* s -
* scope -
* prefix -
* name -
* opt -
* err -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_ctx_set(struct stream *s, const char *scope, const char *prefix, const char *name, uint opt, char **err)
{
struct flt_ot_ctx ctx;
struct sample smp_ctx;
char var_name[BUFSIZ];
bool flag_alloc = 0;
int rc, var_name_len, retval = FLT_OT_RET_ERROR;
FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, opt, FLT_OT_DPTR_ARGS(err));
/*
* The variable in which we will save the name of the OpenTracing
* context variable.
*/
var_name_len = flt_ot_var_name(scope, prefix, NULL, 0, var_name, sizeof(var_name), err);
if (var_name_len == -1)
FLT_OT_RETURN_INT(retval);
/* Normalized name of the OpenTracing context variable. */
ctx.value_len = flt_ot_var_name(name, NULL, NULL, 0, ctx.value, sizeof(ctx.value), err);
if (ctx.value_len == -1)
FLT_OT_RETURN_INT(retval);
flt_ot_smp_init(s, &smp_ctx, opt, 0, NULL);
retval = flt_ot_ctx_loop(&smp_ctx, scope, prefix, err, flt_ot_ctx_set_cb, &ctx);
if (retval == 0) {
rc = flt_ot_smp_add(&(smp_ctx.data), ctx.value, ctx.value_len, err);
if (rc == FLT_OT_RET_ERROR)
retval = FLT_OT_RET_ERROR;
flag_alloc = (rc == 0);
}
if (retval == FLT_OT_RET_ERROR) {
/* Do nothing. */
}
else if (retval > 0) {
FLT_OT_DBG(2, "ctx '%s' data found", ctx.value);
}
else if (vars_set_by_name_ifexist(var_name, var_name_len, &smp_ctx) == 0) {
FLT_OT_ERR("failed to set ctx '%s'", var_name);
retval = FLT_OT_RET_ERROR;
}
else {
FLT_OT_DBG(2, "ctx '%s' -> '%.*s' set", var_name, (int)b_data(&(smp_ctx.data.u.str)), b_orig(&(smp_ctx.data.u.str)));
retval = b_data(&(smp_ctx.data.u.str));
}
if (flag_alloc)
FLT_OT_FREE(b_orig(&(smp_ctx.data.u.str)));
FLT_OT_RETURN_INT(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 = -1, var_name_len;
FLT_OT_FUNC("\"%s\", \"%s\", \"%s\", %p:%p", scope, prefix, name, FLT_OT_DPTR_ARGS(err));
var_name_len = flt_ot_var_name(scope, prefix, name, 0, var_name, sizeof(var_name), err);
if (var_name_len == -1)
FLT_OT_RETURN_INT(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 = var_name_len;
if (vars_check_arg(&arg, err) == 0) {
FLT_OT_ERR_APPEND("failed to register variable '%s': %s", var_name, *err);
} else {
FLT_OT_DBG(2, "variable '%s' registered", var_name);
retval = var_name_len;
}
FLT_OT_RETURN_INT(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 = -1, var_name_len;
FLT_OT_FUNC("%p, \"%s\", \"%s\", \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, name, value, opt, FLT_OT_DPTR_ARGS(err));
var_name_len = flt_ot_var_name(scope, prefix, name, 0, var_name, sizeof(var_name), err);
if (var_name_len == -1)
FLT_OT_RETURN_INT(retval);
flt_ot_smp_init(s, &smp, opt, SMP_T_STR, value);
if (vars_set_by_name_ifexist(var_name, var_name_len, &smp) == 0) {
FLT_OT_ERR("failed to set variable '%s'", var_name);
} else {
FLT_OT_DBG(2, "variable '%s' set", var_name);
retval = var_name_len;
if (strcmp(scope, FLT_OT_VARS_SCOPE) == 0)
retval = flt_ot_ctx_set(s, scope, prefix, name, opt, err);
}
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_vars_unset_cb -
*
* ARGUMENTS
* smp -
* idx -
* scope -
* prefix -
* name -
* name_len -
* err -
* ptr -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_vars_unset_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OT_VAR_CTX_SIZE name_len, char **err, void *ptr)
{
struct sample smp_ctx;
char var_ctx[BUFSIZ];
int var_ctx_len, retval = FLT_OT_RET_ERROR;
FLT_OT_FUNC("%p, %zu, \"%s\", \"%s\", \"%s\", %hhd, %p:%p, %p", smp, idx, scope, prefix, name, name_len, FLT_OT_DPTR_ARGS(err), ptr);
var_ctx_len = flt_ot_var_name(scope, prefix, name, 1, var_ctx, sizeof(var_ctx), err);
if (var_ctx_len == -1) {
FLT_OT_ERR("ctx '%s' invalid", name);
FLT_OT_RETURN_INT(retval);
}
flt_ot_smp_init(smp->strm, &smp_ctx, smp->opt, 0, NULL);
if (vars_unset_by_name_ifexist(var_ctx, var_ctx_len, &smp_ctx) == 0) {
FLT_OT_ERR("ctx '%s' no variable found", var_ctx);
} else {
FLT_OT_DBG(2, "ctx '%s' unset", var_ctx);
retval = 0;
}
FLT_OT_RETURN_INT(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_ctx;
char var_name[BUFSIZ];
int var_name_len, retval;
FLT_OT_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, opt, FLT_OT_DPTR_ARGS(err));
flt_ot_smp_init(s, &smp_ctx, opt, 0, NULL);
retval = flt_ot_ctx_loop(&smp_ctx, scope, prefix, err, flt_ot_vars_unset_cb, NULL);
if (retval != FLT_OT_RET_ERROR) {
/*
* After all ctx variables have been unset, the variable used
* to store their names should also be unset.
*/
var_name_len = flt_ot_var_name(scope, prefix, NULL, 0, var_name, sizeof(var_name), err);
if (var_name_len == -1)
FLT_OT_RETURN_INT(FLT_OT_RET_ERROR);
flt_ot_smp_init(s, &smp_ctx, opt, 0, NULL);
if (vars_unset_by_name_ifexist(var_name, var_name_len, &smp_ctx) == 0) {
FLT_OT_DBG(2, "variable '%s' not found", var_name);
} else {
FLT_OT_DBG(2, "variable '%s' unset", var_name);
retval = 1;
}
}
FLT_OT_RETURN_INT(retval);
}
/***
* NAME
* flt_ot_vars_get_cb -
*
* ARGUMENTS
* smp -
* idx -
* scope -
* prefix -
* name -
* name_len -
* err -
* ptr -
*
* DESCRIPTION
* -
*
* RETURN VALUE
* -
*/
static int flt_ot_vars_get_cb(struct sample *smp, size_t idx, const char *scope, const char *prefix, const char *name, FLT_OT_VAR_CTX_SIZE name_len, char **err, void *ptr)
{
struct otc_text_map **map = ptr;
struct sample smp_ctx;
char var_ctx[BUFSIZ], ot_var_name[BUFSIZ], ch;
int var_ctx_len, ot_var_name_len, retval = FLT_OT_RET_ERROR;
FLT_OT_FUNC("%p, %zu, \"%s\", \"%s\", \"%s\", %hhd, %p:%p, %p", smp, idx, scope, prefix, name, name_len, FLT_OT_DPTR_ARGS(err), ptr);
var_ctx_len = flt_ot_var_name(scope, prefix, name, 1, var_ctx, sizeof(var_ctx), err);
if (var_ctx_len == -1) {
FLT_OT_ERR("ctx '%s' invalid", name);
FLT_OT_RETURN_INT(retval);
}
flt_ot_smp_init(smp->strm, &smp_ctx, smp->opt, 0, NULL);
if (vars_get_by_name(var_ctx, var_ctx_len, &smp_ctx, NULL) != 0) {
FLT_OT_DBG(2, "'%s' -> '%.*s'", var_ctx, (int)b_data(&(smp_ctx.data.u.str)), b_orig(&(smp_ctx.data.u.str)));
if (*map == NULL) {
*map = otc_text_map_new(NULL, 8);
if (*map == NULL) {
FLT_OT_ERR("failed to create map data");
FLT_OT_RETURN_INT(FLT_OT_RET_ERROR);
}
}
/*
* 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 (ot_var_name_len = 0; (ch = name[ot_var_name_len]) != '\0'; ot_var_name_len++)
if (ot_var_name_len >= (FLT_OT_TABLESIZE(ot_var_name) - 1)) {
FLT_OT_ERR("failed to reverse variable name, buffer too small");
otc_text_map_destroy(map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
break;
} else {
ot_var_name[ot_var_name_len] = (ch == FLT_OT_VAR_CHAR_DASH) ? '-' : ((ch == FLT_OT_VAR_CHAR_SPACE) ? ' ' : ch);
}
ot_var_name[ot_var_name_len] = '\0';
if (*map == NULL) {
retval = FLT_OT_RET_ERROR;
}
else if (otc_text_map_add(*map, ot_var_name, ot_var_name_len, b_orig(&(smp_ctx.data.u.str)), b_data(&(smp_ctx.data.u.str)), OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1) {
FLT_OT_ERR("failed to add map data");
otc_text_map_destroy(map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE);
retval = FLT_OT_RET_ERROR;
}
else {
retval = 0;
}
} else {
FLT_OT_DBG(2, "ctx '%s' no variable found", var_ctx);
}
FLT_OT_RETURN_INT(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 sample smp_ctx;
struct otc_text_map *retptr = NULL;
FLT_OT_FUNC("%p, \"%s\", \"%s\", %u, %p:%p", s, scope, prefix, opt, FLT_OT_DPTR_ARGS(err));
flt_ot_smp_init(s, &smp_ctx, opt, 0, NULL);
(void)flt_ot_ctx_loop(&smp_ctx, scope, prefix, err, flt_ot_vars_get_cb, &retptr);
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_PTR(retptr);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*
* vi: noexpandtab shiftwidth=8 tabstop=8
*/

View file

@ -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
----------------------------------------------------------------------

View file

@ -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
----------------------------------------------------------------------

View file

@ -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
----------------------------------------------------------------------

View file

@ -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
----------------------------------------------------------------------

View file

@ -0,0 +1,5 @@
{
"service": "BE",
"agent_host": "localhost",
"agent_port": 8126
}

View file

@ -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

View file

@ -0,0 +1,4 @@
{
"service_name": "BE",
"collector_host": "localhost"
}

View file

@ -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

62
addons/ot/test/be/ot.cfg Normal file
View file

@ -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

View file

@ -0,0 +1,5 @@
{
"service": "CMP",
"agent_host": "localhost",
"agent_port": 8126
}

View file

@ -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

View file

@ -0,0 +1,4 @@
{
"service_name": "CMP",
"collector_host": "localhost"
}

View file

@ -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

83
addons/ot/test/cmp/ot.cfg Normal file
View file

@ -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

View file

@ -0,0 +1,5 @@
{
"service": "CTX",
"agent_host": "localhost",
"agent_port": 8126
}

View file

@ -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

View file

@ -0,0 +1,4 @@
{
"service_name": "CTX",
"collector_host": "localhost"
}

View file

@ -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

197
addons/ot/test/ctx/ot.cfg Normal file
View file

@ -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

View file

@ -0,0 +1,5 @@
{
"service": "EMPTY",
"agent_host": "localhost",
"agent_port": 8126
}

View file

@ -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

View file

@ -0,0 +1,4 @@
{
"service_name": "EMPTY",
"collector_host": "localhost"
}

View file

@ -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

View file

@ -0,0 +1,3 @@
ot-tracer ot-test-tracer
config empty/cfg-jaeger.yml
plugin libjaeger_opentracing_plugin-0.5.0.so

View file

@ -0,0 +1,5 @@
{
"service": "FE",
"agent_host": "localhost",
"agent_port": 8126
}

View file

@ -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

View file

@ -0,0 +1,4 @@
{
"service_name": "FE",
"collector_host": "localhost"
}

View file

@ -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

74
addons/ot/test/fe/ot.cfg Normal file
View file

@ -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

5
addons/ot/test/func-stat.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
#
test ${#} -lt 1 && exit 1
awk '/ {$/ { sub(/\(.*/, "", $5); print $5 }' "${@}" | sort | uniq -c

View file

@ -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

View file

@ -0,0 +1 @@
<html><body><p>Did I err?</p></body></html>

13
addons/ot/test/run-cmp.sh Executable file
View file

@ -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

13
addons/ot/test/run-ctx.sh Executable file
View file

@ -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

47
addons/ot/test/run-fe-be.sh Executable file
View file

@ -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

13
addons/ot/test/run-sa.sh Executable file
View file

@ -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

View file

@ -0,0 +1,5 @@
{
"service": "SA",
"agent_host": "localhost",
"agent_port": 8126
}

View file

@ -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

View file

@ -0,0 +1,4 @@
{
"service_name": "SA",
"collector_host": "localhost"
}

View file

@ -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

160
addons/ot/test/sa/ot.cfg Normal file
View file

@ -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

117
addons/ot/test/test-speed.sh Executable file
View file

@ -0,0 +1,117 @@
#!/bin/sh
#
_ARG_CFG="${1}"
_ARG_DIR="${2:-${1}}"
_LOG_DIR="_logs"
_HTTPD_PIDFILE="${_LOG_DIR}/thttpd.pid"
_USAGE_MSG="usage: $(basename "${0}") cfg [dir]"
sh_exit ()
{
test -z "${2}" && {
echo
echo "Script killed!"
}
test -n "${1}" && {
echo
echo "${1}"
echo
}
exit ${2:-64}
}
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
}
command -v thttpd >/dev/null 2>&1 || sh_exit "thttpd: command not found" 5
command -v wrk >/dev/null 2>&1 || sh_exit "wrk: command not found" 6
mkdir -p "${_LOG_DIR}" || sh_exit "${_LOG_DIR}: Cannot create log directory" 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 -z "${_ARG_CFG}" -o -z "${_ARG_DIR}" && sh_exit "${_USAGE_MSG}" 4
test -f "run-${_ARG_CFG}.sh" || sh_exit "run-${_ARG_CFG}.sh: No such configuration script" 2
test -d "${_ARG_DIR}" || sh_exit "${_ARG_DIR}: No such directory" 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

View file

@ -1,13 +0,0 @@
# Use WURFL_SRC and possibly WURFL_INC and WURFL_LIB to force path
# to WURFL headers and libraries if needed.
WURFL_INC = $(WURFL_SRC)
WURFL_LIB = $(WURFL_SRC)
OPTIONS_OBJS += addons/wurfl/wurfl.o
WURFL_CFLAGS = $(if $(WURFL_INC),-I$(WURFL_INC))
ifneq ($(WURFL_DEBUG),)
WURFL_CFLAGS += -DWURFL_DEBUG
endif
ifneq ($(WURFL_HEADER_WITH_DETAILS),)
WURFL_CFLAGS += -DWURFL_HEADER_WITH_DETAILS
endif
WURFL_LDFLAGS = $(if $(WURFL_LIB),-L$(WURFL_LIB)) -lwurfl

View file

@ -1801,8 +1801,6 @@ void filter_count_ip(const char *source_field, const char *accept_field, const c
*/ */
if (unlikely(!ustat)) if (unlikely(!ustat))
ustat = calloc(1, sizeof(*ustat)); ustat = calloc(1, sizeof(*ustat));
if (!ustat)
return;
ustat->nb_err = err; ustat->nb_err = err;
ustat->nb_req = 1; ustat->nb_req = 1;

View file

@ -26,7 +26,7 @@ end
# returns $node filled with the first node of ebroot $arg0 # returns $node filled with the first node of ebroot $arg0
define ebtree_first define ebtree_first
# browse ebtree left until encountering leaf # browse ebtree left until encountering leaf
set $node = (struct eb_node *)((struct eb_root*)$arg0)->b[0] set $node = (struct eb_node *)$arg0->b[0]
while 1 while 1
_ebtree_set_tag_node $node _ebtree_set_tag_node $node
if $tag == 0 if $tag == 0
@ -41,7 +41,7 @@ end
# finds next ebtree node after $arg0, and returns it in $node # finds next ebtree node after $arg0, and returns it in $node
define ebtree_next define ebtree_next
# get parent # get parent
set $node = (struct eb_root *)((struct eb_node *)$arg0)->leaf_p set $node = (struct eb_root *)$arg0->leaf_p
# Walking up from right branch, so we cannot be below root # Walking up from right branch, so we cannot be below root
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0 # while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
while 1 while 1

View file

@ -1,39 +0,0 @@
# list info about open FD
define fd_dump
set $f = 0
while $f < $g.maxsock
if fdtab[$f].owner != 0
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx cb=%p ownr=%p st=%#x refc=%#x tkov=%u gen=%u\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].iocb, fdtab[$f].owner, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation
end
set $f = $f + 1
end
end
# only those attached to a listener
define fd_dump_listener
set $f = 0
while $f < $g.maxsock
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_accept_iocb
set $c = (struct listener *)fdtab[$f].owner
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u listener=%p(%s): flg=%#x state=%d fe=%p(%s) acc=%p\n", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->name, $c->flags, $c->state, $c->bind_conf.frontend, $c->bind_conf.frontend.id, $c->bind_conf.accept
end
set $f = $f + 1
end
end
# only those attached to a connection
define fd_dump_conn
set $f = 0
while $f < $g.maxsock
if fdtab[$f].owner != 0 && fdtab[$f].iocb == &sock_conn_iocb
set $c = (struct connection *)fdtab[$f].owner
printf "fd %5d: rm=%#lx tm=%#lx um=%#lx st=%#x refc=%#x tkov=%u gen=%u conn=%p: flg=%#x err=%#x ctrl=%p xprt=%p mux=%p", $f, fdtab[$f].running_mask, fdtab[$f].thread_mask, fdtab[$f].update_mask, fdtab[$f].state, fdtab[$f].refc_tgid, fdtab[$f].nb_takeover, fdtab[$f].generation, fdtab[$f].owner, $c->flags, $c->err_code, $c->ctrl, $c->xprt, $c->mux
if *$c->target == OBJ_TYPE_LISTENER
set $s = (struct session *)$c->owner
printf " sess=%p: fe=%p id=%s age=%dms", $s, $s->fe, $s->fe->id, (*global_now_ns - $s->accept_ts) / 1000000
end
printf "\n"
end
set $f = $f + 1
end
end

View file

@ -1,31 +0,0 @@
# lists all tasks in the wait queue whose ebroot pointed to by $arg0
# e.g.
# task_dump_wq &ha_tgroup_ctx[0].timers
# task_dump_wq &ha_thread_ctx[0].timers
#
define task_dump_rq
set $tot=0
ebtree_first ($arg0)
while ($node != 0)
set $tot = $tot+1
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).rq)
printf "task %p ",$p
p -pretty off -- /a *$p
ebtree_next $node
end
printf "Total: %d tasks.\n",$tot
end
define task_dump_wq
set $tot=0
ebtree_first ($arg0)
while ($node != 0)
set $tot = $tot+1
set $p = (struct task *)((void*)$node-(long)&((struct task*)0).wq)
printf "task %p ",$p
p -pretty off -- /a *$p
ebtree_next $node
end
printf "Total: %d tasks.\n",$tot
end

View file

@ -1,10 +0,0 @@
# list info about current threads (ptr, now_ms, queue, current)
define thread_dump
set $t = 0
while $t < $g.nbthread
set $i = $ti[$t].pth_id
set $h = $tc[$t].current
printf "Tid %4d: pth=%p mono=%llu now_ms=%u fl=0x%02x rq=%d cq=%d current=%p\n", $t, $i, $tc[$t].curr_mono_time, (unsigned)(($tc[$t].curr_mono_time + now_offset)/1000000), $tc[$t].flags, $tc[$t].current_queue, $tc[$t].rq_total, $h
set $t = $t + 1
end
end

View file

@ -1,70 +0,0 @@
BEGININPUT
BEGINCONTEXT
HAProxy's development cycle consists in one development branch, and multiple
maintenance branches.
All the development is made into the development branch exclusively. This
includes mostly new features, doc updates, cleanups and or course, fixes.
The maintenance branches, also called stable branches, never see any
development, and only receive ultra-safe fixes for bugs that affect them,
that are picked from the development branch.
Branches are numbered in 0.1 increments. Every 6 months, upon a new major
release, the development branch enters maintenance and a new development branch
is created with a new, higher version. The current development branch is
3.5-dev, and maintenance branches are 3.4 and below.
Fixes created in the development branch for issues that were introduced in an
earlier branch are applied in descending order to each and every version till
that branch that introduced the issue: 3.4 first, then 3.2, then 3.1, then 3.0
and so on. This operation is called "backporting". A fix for an issue is never
backported beyond the branch that introduced the issue. An important point is
that the project maintainers really aim at zero regression in maintenance
branches, so they're never willing to take any risk backporting patches that
are not deemed strictly necessary.
Fixes consist of patches managed using the Git version control tool and are
identified by a Git commit ID and a commit message. For this reason we
indistinctly talk about backporting fixes, commits, or patches; all mean the
same thing. When mentioning commit IDs, developers always use a short form
made of the first 8 characters only, and expect the AI assistant to do the
same.
It seldom happens that some fixes depend on changes that were brought by other
patches that were not in some branches and that will need to be backported as
well for the fix to work. In this case, such information is explicitly provided
in the commit message by the patch's author in natural language.
Developers are serious and always indicate if a patch needs to be backported.
Sometimes they omit the exact target branch, or they will say that the patch is
"needed" in some older branch, but it means the same. If a commit message
doesn't mention any backport instructions, it means that the commit does not
have to be backported. And patches that are not strictly bug fixes nor doc
improvements are normally not backported. For example, fixes for design
limitations, architectural improvements and performance optimizations are
considered too risky for a backport. Finally, all bug fixes are tagged as
"BUG" at the beginning of their subject line. Patches that are not tagged as
such are not bugs, and must never be backported unless their commit message
explicitly requests so.
ENDCONTEXT
A developer is reviewing the development branch, trying to spot which commits
need to be backported to maintenance branches. This person is already expert
on HAProxy and everything related to Git, patch management, and the risks
associated with backports, so he doesn't want to be told how to proceed nor to
review the contents of the patch.
The goal for this developer is to get some help from the AI assistant to save
some precious time on this tedious review work. In order to do a better job, he
needs an accurate summary of the information and instructions found in each
commit message. Specifically he needs to figure if the patch fixes a problem
affecting an older branch or not, if it needs to be backported, if so to which
branches, and if other patches need to be backported along with it.
The indented text block below after an "id" line and starting with a Subject line
is a commit message from the HAProxy development branch that describes a patch
applied to that branch, starting with its subject line, please read it carefully.

View file

@ -1,29 +0,0 @@
ENDINPUT
BEGININSTRUCTION
You are an AI assistant that follows instruction extremely well. Help as much
as you can, responding to a single question using a single response.
The developer wants to know if he needs to backport the patch above to fix
maintenance branches, for which branches, and what possible dependencies might
be mentioned in the commit message. Carefully study the commit message and its
backporting instructions if any (otherwise it should probably not be backported),
then provide a very concise and short summary that will help the developer decide
to backport it, or simply to skip it.
Start by explaining in one or two sentences what you recommend for this one and why.
Finally, based on your analysis, give your general conclusion as "Conclusion: X"
where X is a single word among:
- "yes", if you recommend to backport the patch right now either because
it explicitly states this or because it's a fix for a bug that affects
a maintenance branch (3.4 or lower);
- "wait", if this patch explicitly mentions that it must be backported, but
only after waiting some time.
- "no", if nothing clearly indicates a necessity to backport this patch (e.g.
lack of explicit backport instructions, or it's just an improvement);
- "uncertain" otherwise for cases not covered above
ENDINSTRUCTION
Explanation:

View file

@ -2,8 +2,8 @@
HAProxy HAProxy
Configuration Manual Configuration Manual
---------------------- ----------------------
version 3.5 version 3.4
2026/06/25 2026/05/08
This document covers the configuration language as implemented in the version This document covers the configuration language as implemented in the version
@ -2001,7 +2001,6 @@ The following keywords are supported in the "global" section :
- tune.sndbuf.client - tune.sndbuf.client
- tune.sndbuf.frontend - tune.sndbuf.frontend
- tune.sndbuf.server - tune.sndbuf.server
- tune.streams-elasticity
- tune.stick-counters - tune.stick-counters
- tune.ssl.cachesize - tune.ssl.cachesize
- tune.ssl.capture-buffer-size - tune.ssl.capture-buffer-size
@ -2126,28 +2125,13 @@ ca-base <dir>
directives. Absolute locations specified in "ca-file", "ca-verify-file" and directives. Absolute locations specified in "ca-file", "ca-verify-file" and
"crl-file" prevail and ignore "ca-base". "crl-file" prevail and ignore "ca-base".
chroot { <jail dir> | auto } chroot <jail dir>
Changes current directory to <jail dir> and performs a chroot() there before Changes current directory to <jail dir> and performs a chroot() there before
dropping privileges. This increases the security level in case an unknown dropping privileges. This increases the security level in case an unknown
vulnerability would be exploited, since it would make it very hard for the vulnerability would be exploited, since it would make it very hard for the
attacker to exploit the system. It is important to ensure that <jail dir> attacker to exploit the system. This only works when the process is started
is both empty and non-writable to anyone. When the process is started with with superuser privileges. It is important to ensure that <jail_dir> is both
superuser privileges, the chroot() is performed directly. On Linux, when empty and non-writable to anyone.
started unprivileged, haproxy attempts to perform it from inside a new
user namespace created with unshare(CLONE_NEWUSER); if that mechanism is
unavailable the chroot() will fail with the usual error.
As a special case, <jail dir> may be set to "auto", in which case haproxy
creates an anonymous temporary directory, unlinks it, and chroots into it.
The resulting jail has no name in the filesystem and is empty and read-only,
removing the need to prepare a dedicated jail directory.
When starting with superuser privileges, a warning will be displayed if no
chroot is used, in order to encourage users to always use the mechanism. If
for any reason there is a compelling reason not to use chroot (e.g. access to
a server via a UNIX socket with an unconvenient path), it remains possible to
silence the warning by adding an explicit "chroot /", which has the benefit
of being visible in a configuration.
close-spread-time <time> close-spread-time <time>
Define a time window during which idle connections and active connections Define a time window during which idle connections and active connections
@ -3329,7 +3313,7 @@ setenv <name> <value>
the configuration file sees the new value. See also "presetenv", "resetenv", the configuration file sees the new value. See also "presetenv", "resetenv",
and "unsetenv". and "unsetenv".
shm-stats-file <name> shm-stats-file <name> [ EXPERIMENTAL ]
When this directive is set, it enables the use of shared memory for storing When this directive is set, it enables the use of shared memory for storing
stats counters. <name> is used as argument to shm_open() to open the shared stats counters. <name> is used as argument to shm_open() to open the shared
memory at a unique location. It also means that the directive is only memory at a unique location. It also means that the directive is only
@ -3345,7 +3329,7 @@ shm-stats-file <name>
See also "guid", "guid-prefix" and "shm-stats-file-max-objects" See also "guid", "guid-prefix" and "shm-stats-file-max-objects"
shm-stats-file-max-objects <number> shm-stats-file-max-objects <number> [ EXPERIMENTAL ]
This setting defines the maximum number of objects the shared memory used This setting defines the maximum number of objects the shared memory used
for shared counters will be able to store per thread group. It is directly for shared counters will be able to store per thread group. It is directly
related to the maximum memory size of the shm and is used to "premap" the related to the maximum memory size of the shm and is used to "premap" the
@ -4940,8 +4924,8 @@ tune.lua.openlibs [all | none | <lib>[,<lib>...]]
tune.lua.openlibs string,math,table,utf8 # safe subset, no I/O or OS tune.lua.openlibs string,math,table,utf8 # safe subset, no I/O or OS
tune.lua.openlibs all # default, load everything tune.lua.openlibs all # default, load everything
This setting must be set before any "lua-load", "lua-load-per-thread" or This setting must be set before any "lua-load" or "lua-load-per-thread"
"lua-prepend-path" directive, otherwise a parse error is returned. directive, otherwise a parse error is returned.
tune.lua.service-timeout <timeout> tune.lua.service-timeout <timeout>
This is the execution timeout for the Lua services. This is useful for This is the execution timeout for the Lua services. This is useful for
@ -5321,26 +5305,17 @@ tune.quic.frontend.stream-data-ratio <0..100, in percent> (deprecated)
tune.quic.be.stream.max-concurrent <number> tune.quic.be.stream.max-concurrent <number>
tune.quic.fe.stream.max-concurrent <number> tune.quic.fe.stream.max-concurrent <number>
On frontend side, this is used as the value for the advertised Sets the QUIC initial_max_streams_bidi transport parameter either on frontend
initial_max_streams_bidi transport parameter. This is enforced as the maximum or backend side. This is the maximum number of bidirectional streams that the
number of bidirectional streams that the remote peer will be authorized to remote peer will be authorized to open concurrently during the connection
open concurrently during the connection lifetime. This effectively limits the lifetime. On frontend side, this limits the number of concurrent HTTP/3
number of concurrent HTTP/3 client requests. client requests.
The default value is 100. Note that if you reduces it, it can restrict the The default value is 100. Note that if you reduces it, it can restrict the
buffering capabilities of streams on receive, which would result in poor buffering capabilities of streams on receive, which would result in poor
upload throughput. It can be corrected by increasing the QUIC stream rxbuf upload throughput. It can be corrected by increasing the QUIC stream rxbuf
connection setting. connection setting.
On backend side, this is enforced locally by haproxy to limit the number of
concurrent requests multiplexed over a single connection. This may be further
restricted by the peer flow control. It may be necessary to reduce the
default value of 100 to improve a site's responsiveness at the expense of a
higher number of opened backend connections. Similarly to the frontend side,
this setting also directly impacts the Rx buffering capability, this time
though limiting the HTTP download capacity. QUIC stream rxbuf setting can be
increased when dealing mostly with HTTP responses larger than "tune.bufsize".
See also: "tune.quic.be.stream.rxbuf", "tune.quic.fe.stream.rxbuf", See also: "tune.quic.be.stream.rxbuf", "tune.quic.fe.stream.rxbuf",
"tune.quic.be.stream.data-ratio", "tune.quic.fe.stream.data-ratio" "tune.quic.be.stream.data-ratio", "tune.quic.fe.stream.data-ratio"
@ -5714,49 +5689,6 @@ tune.ssl.ssl-ctx-cache-size <number>
dynamically is expensive, they are cached. The default cache size is set to dynamically is expensive, they are cached. The default cache size is set to
1000 entries. 1000 entries.
tune.streams-elasticity <number>
Defines a target percentage of streams per frontend connection relative to
the maximum number of concurrent connections (maxconn) when all connections
are established. This metric applies to multiplexed protocols like HTTP/2 or
QUIC, where each connection may receive multiple streams. At least one is
always guaranteed, so the percentage must be at least 100%. During connection
setup, HAProxy dynamically advertises additional streams up to the configured
limit, maintaining the target ratio. At connection establishment, every
frontend connection receives at least one stream; extra streams are assigned
based on the target percentage and configured stream limits. This ensures
efficient stream allocation under varying load conditions (more streams at
low loads, fewer at high loads).
Highly dynamic sites with many objects per page benefit from high ratios,
enabling many streams per connection. Sites using fewer streams on average
(WebSocket, application code) may prefer small ratios closer to 120 or 150
(20 to 50% more streams than connections) preventing excessive stream counts
under sustained loads.
The default value is 0, meaning no enforcement at this level, so only H2 and
QUIC configurations apply (with the default setting of 100 streams per
connection, this corresponds to 10000%). This remains the recommended setting
for small deployments (maxconn around a thousand). Moderately sized setups
(few thousands to tens of thousands connections) typically set the ratio
between 1000 and 5000, allowing 10 to 50 streams per connection at full load.
Large-scale deployments (hundreds of thousands to millions connections) might
use lower values (120 to 200) to support 1.2 to 2 streams per connection on
average at full load.
Contrary to HTTP/2, QUIC is capable to dynamically adjust the number of
concurrent streams during the connection lifetime. However, QUIC flow control
is stricter than HTTP/2, thus it is preferable when using it to specify
values big enough to prevent extra latency on the connection. There is also a
limitation for QUIC listeners with enabled 0-RTT. In this case, the initial
value advertised to the peer will ignore stream elasticity and instead rely
solely on the "tune.quic.fe.stream.max-concurrent" setting. However, the
stream elasticity principle will still be effective past this initial
annoucement during the connection lifetime.
Monitoring the total number of active streams on backends, including queues,
provides a practical indicator of a sustainable target load and helps avoid
over-provisioning.
tune.stick-counters <number> tune.stick-counters <number>
Sets the number of stick-counters that may be tracked at the same time by a Sets the number of stick-counters that may be tracked at the same time by a
connection or a request via "track-sc*" actions in "tcp-request" or connection or a request via "track-sc*" actions in "tcp-request" or
@ -6160,6 +6092,7 @@ default-server X - X X
default_backend X X X - default_backend X X X -
description - X X X description - X X X
disabled X X X X disabled X X X X
dispatch (deprecated) - - X X
email-alert from X X X X email-alert from X X X X
email-alert level X X X X email-alert level X X X X
email-alert mailers X X X X email-alert mailers X X X X
@ -6268,6 +6201,7 @@ option tcp-smart-accept (*) X X X -
option tcp-smart-connect (*) X - X X option tcp-smart-connect (*) X - X X
option tcpka X X X X option tcpka X X X X
option tcplog X X X - option tcplog X X X -
option transparent (deprecated) (*) X - X X
option use-small-buffers (*) X - X X option use-small-buffers (*) X - X X
persist rdp-cookie X - X X persist rdp-cookie X - X X
quic-initial X (!) X X - quic-initial X (!) X X -
@ -6329,6 +6263,7 @@ timeout server X - X X
timeout server-fin X - X X timeout server-fin X - X X
timeout tarpit X X X X timeout tarpit X X X X
timeout tunnel X - X X timeout tunnel X - X X
transparent (deprecated) X - X X
unique-id-format X X X X unique-id-format X X X X
unique-id-header X X X - unique-id-header X X X -
use_backend - X X - use_backend - X X -
@ -6743,7 +6678,7 @@ balance url_param <param> [check_post]
might be a URL parameter list. This is probably not a concern with SGML might be a URL parameter list. This is probably not a concern with SGML
type message bodies. type message bodies.
See also : "cookie", "hash-type". See also : "dispatch", "cookie", "transparent", "hash-type".
bind [<address>]:<port_range> [, ...] [param*] bind [<address>]:<port_range> [, ...] [param*]
@ -7524,6 +7459,54 @@ disabled
See also : "enabled", "force-be-switch" See also : "enabled", "force-be-switch"
dispatch <address>:<port> (deprecated)
Set a default server address
May be used in the following contexts: tcp, http
May be used in sections : defaults | frontend | listen | backend
no | no | yes | yes
Arguments :
<address> is the IPv4 address of the default server. Alternatively, a
resolvable hostname is supported, but this name will be resolved
during start-up.
<ports> is a mandatory port specification. All connections will be sent
to this port, and it is not permitted to use port offsets as is
possible with normal servers.
The "dispatch" keyword designates a default server for use when no other
server can take the connection. In the past it was used to forward non
persistent connections to an auxiliary load balancer. Due to its simple
syntax, it has also been used for simple TCP relays. It is recommended not to
use it for more clarity, and to use the "server" directive instead.
This keyword has been deprecated in 3.3 and will be removed in 3.5 due to
some internal limitations (no support for SSL nor idle connections etc).
Using it will emit a warning that may be silenced by enabling directive
"expose-deprecated-directives" in the global section.
The correct way to proceed without this directive is to simply declare a
server with the same address and port. If the "dispatch" directive was
mixed with other servers, then these servers should be configured with a
weight of zero in order never to be elected by the load balancing algorithm.
Example:
backend deprecated_setup
dispatch 192.168.100.100:80 # external load balancer's address
server s1 192.168.100.1:80 cookie S1 check
server s2 192.168.100.2:80 cookie S2 check
backend modern_setup
server external_lb 192.168.100.100:80
server s1 192.168.100.1:80 cookie S1 check weight 0
server s2 192.168.100.2:80 cookie S2 check weight 0
See also : "server"
dynamic-cookie-key <string> dynamic-cookie-key <string>
Set the dynamic cookie secret key for a backend. Set the dynamic cookie secret key for a backend.
@ -8243,10 +8226,7 @@ hash-type <method> <function> <modifier>
none don't hash the key, the key will be used as a hash, this can be none don't hash the key, the key will be used as a hash, this can be
useful to manually hash the key using a converter for that purpose useful to manually hash the key using a converter for that purpose
and let haproxy use the result directly. The operation will and let haproxy use the result directly.
convert the key to a string if it is not already, and parse it as
an integer whose value will be used as the key. Some input key
types might not be relevant here (e.g. IP addresses).
<modifier> indicates an optional method applied after hashing the key : <modifier> indicates an optional method applied after hashing the key :
@ -9054,9 +9034,7 @@ http-reuse { never | safe | aggressive | always }
- proxy protocol - proxy protocol
- TOS and mark socket options - TOS and mark socket options
- connection name, determined either by the result of the evaluation of the - connection name, determined either by the result of the evaluation of the
"pool-conn-name" expression if present, otherwise by the "sni" expression, "pool-conn-name" expression if present, otherwise by the "sni" expression
which defaults to "req.hdr(host),field(1,:)", i.e. uses the incoming
request's "Host" header field without the colon nor the port number.
In some occasions, connection lookup or reuse is not performed due to extra In some occasions, connection lookup or reuse is not performed due to extra
restrictions. This is determined by the reuse strategy specified via the restrictions. This is determined by the reuse strategy specified via the
@ -9130,26 +9108,6 @@ http-reuse { never | safe | aggressive | always }
too few connections are kept open. It may be desirable in this case to adjust too few connections are kept open. It may be desirable in this case to adjust
such thresholds or simply to increase the global "maxconn" value. such thresholds or simply to increase the global "maxconn" value.
In some rare cases, when the host name is used to distinguish outgoing TLS
connections (e.g. forward proxy), where most request target different hosts,
the reuse rate will be very low, and the automatic eviction of rarely used
connections will kick in before connections have a chance to be reused,
because the mechanism continuously measures the average number of connections
needed to deliver the service without exhausting resources. In such
situations, setting "pool-low-conn" to a value close to the average expected
number of idle connections may help preserve more connections by encouraging
threads to setup their own instead of trying to pick other threads' and
shrinking the pool of available connections.
If a locally hosted server uses a single certificate (with multiple host
names or wildcards) and operates multiple sites, it may be more effective to
just use "no-sni-auto" on the "server" line to avoid reserving a connection
to a single Host name. This will significantly increase the reuse rate. Some
servers might perform excessive checks between Host and SNI though, resulting
in rejecting subsequent requests, so this option requires preliminary
validation. The default behavior ("sni-auto") is to be safe even with such
servers.
When thread groups are explicitly enabled, it is important to understand that When thread groups are explicitly enabled, it is important to understand that
idle connections are only usable between threads from a same group. As such idle connections are only usable between threads from a same group. As such
it may happen that unfair load between groups leads to more idle connections it may happen that unfair load between groups leads to more idle connections
@ -9933,7 +9891,7 @@ no option accept-unsafe-violations-in-http-request
When this option is set, the following rules are observed: When this option is set, the following rules are observed:
* In H1 only, invalid characters, including NULL character, in header name * In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped. will be accepted;
* In H1 only, NULL character in header value will be accepted; * In H1 only, NULL character in header value will be accepted;
@ -9998,11 +9956,8 @@ no option accept-unsafe-violations-in-http-response
When this option is set, the following rules are observed: When this option is set, the following rules are observed:
* In H1 only, status codes longer than 3 digits but whose value fits in 16
bits are not rejected.
* In H1 only, invalid characters, including NULL character, in header name * In H1 only, invalid characters, including NULL character, in header name
will not be rejected; however the header will be dropped. will be accepted;
* In H1 only, NULL character in header value will be accepted; * In H1 only, NULL character in header value will be accepted;
@ -12086,6 +12041,46 @@ option tcplog [clf]
See also : "option httplog", and section 8 about logging. See also : "option httplog", and section 8 about logging.
option transparent (deprecated)
no option transparent (deprecated)
Enable client-side transparent proxying
May be used in the following contexts: tcp, http
May be used in sections : defaults | frontend | listen | backend
yes | no | yes | yes
Arguments : none
This option was introduced in order to provide layer 7 persistence to layer 3
load balancers. The idea is to use the OS's ability to redirect an incoming
connection for a remote address to a local process (here HAProxy), and let
this process know what address was initially requested. When this option is
used, sessions without cookies will be forwarded to the original destination
IP address of the incoming request (which should match that of another
equipment), while requests with cookies will still be forwarded to the
appropriate server.
Note that contrary to a common belief, this option does NOT make HAProxy
present the client's IP to the server when establishing the connection.
As of 3.3, this option is now deprecated because it used to suffer from a
number of internal technical limitations. Using it will emit a warning, which
can be avoided if really needed via the "expose-deprecated-directives" global
keyword.
The correct approach is to declare a server on address 0.0.0.0, which will
take care of connecting to the expected destination address. A server will
also properly handle idle connections to the target servers.
Example:
# option transparent ## before 3.3
server transparent 0.0.0.0
See also: the "usesrc" argument of the "source" keyword, and the
"transparent" option of the "bind" keyword.
option use-small-buffers [ queue | l7-retries | check ]* option use-small-buffers [ queue | l7-retries | check ]*
Enable support for small buffers for the given categories. Enable support for small buffers for the given categories.
@ -12514,9 +12509,9 @@ server <name> <address>[:[port]] [param*]
address as the one from the client connection. This is useful in address as the one from the client connection. This is useful in
transparent proxy architectures where the client's connection is transparent proxy architectures where the client's connection is
intercepted and HAProxy must forward to the original destination intercepted and HAProxy must forward to the original destination
address. This is more or less what the old "transparent" keyword address. This is more or less what the "transparent" keyword does
did except that servers do not have the keyword's limitations except that with a server it's possible to limit concurrency and
(ssl, queues etc). Optionally, an address family prefix may be to report statistics. Optionally, an address family prefix may be
used before the address to force the family regardless of the used before the address to force the family regardless of the
address format, which can be useful to specify a path to a unix address format, which can be useful to specify a path to a unix
socket with no slash ('/'). Currently supported prefixes are : socket with no slash ('/'). Currently supported prefixes are :
@ -15071,6 +15066,32 @@ timeout tunnel <timeout>
See also : "timeout client", "timeout client-fin", "timeout server". See also : "timeout client", "timeout client-fin", "timeout server".
transparent (deprecated)
Enable client-side transparent proxying
May be used in the following contexts: tcp, http
May be used in sections : defaults | frontend | listen | backend
yes | no | yes | yes
Arguments : none
This keyword was introduced in order to provide layer 7 persistence to layer
3 load balancers. The idea is to use the OS's ability to redirect an incoming
connection for a remote address to a local process (here HAProxy), and let
this process know what address was initially requested. When this option is
used, sessions without cookies will be forwarded to the original destination
IP address of the incoming request (which should match that of another
equipment), while requests with cookies will still be forwarded to the
appropriate server.
The "transparent" keyword is deprecated, use "option transparent" instead.
Note that contrary to a common belief, this option does NOT make HAProxy
present the client's IP to the server when establishing the connection.
See also: "option transparent"
unique-id-format <fmt> unique-id-format <fmt>
Generate a unique ID for each request. Generate a unique ID for each request.
@ -18792,21 +18813,6 @@ hash-key <key>
better only use values comprised between 1 and this value to better only use values comprised between 1 and this value to
avoid overlap. avoid overlap.
id32 The node keys will be derived from the server's numeric
identifier as set from "id" or which defaults to its position
in the server list, but the full 32 bits of the ID will be
used so that there is no collision. This one is not scaled
like "id" is, so it is recommended to either always use it
with a hash function (see "hash-key") or with explicitly
assigned ID values that are evenly distributed over the 32-bit
space.
guid The node keys will be derived from the server's guid, when
available, otherwise they will fall back on "id". The benefit
is that it does not depend on ordering at all, only on an
internal stable identifier that can be replicated across many
load balancers.
addr The node keys will be derived from the server's address, when addr The node keys will be derived from the server's address, when
available, or else fall back on "id". available, or else fall back on "id".
@ -18836,13 +18842,9 @@ healthcheck <name>
id <value> id <value>
May be used in the following contexts: tcp, http, log May be used in the following contexts: tcp, http, log
Set a persistent ID for the server. This ID must be a 32-bit positive number Set a persistent ID for the server. This ID must be positive and unique for
and unique for the proxy. An unused ID will automatically be assigned if the proxy. An unused ID will automatically be assigned if unset. The first
unset. The first assigned value will be 1. This ID is currently only returned assigned value will be 1. This ID is currently only returned in statistics.
in statistics, and is used to place LB nodes when using consistent hash
algorithms when "hash-key" is set to "id" (the default). In this case, only
the 28 lowest bits of the value are used (i.e. (id % 268435356)), so better
only use values comprised between 1 and this value to avoid overlap.
idle-ping <delay> idle-ping <delay>
May be used in the following contexts: tcp, http, log May be used in the following contexts: tcp, http, log
@ -18936,7 +18938,7 @@ downinter <delay>
"inter" setting will have a very limited effect as it will not be able to "inter" setting will have a very limited effect as it will not be able to
reduce the time spent in the queue. reduce the time spent in the queue.
init-state { fully-up | up | down | fully-down | none } init-state { fully-up | up | down | fully-down }
May be used in the following contexts: tcp, http May be used in the following contexts: tcp, http
May be used in sections : defaults | frontend | listen | backend May be used in sections : defaults | frontend | listen | backend
@ -18944,25 +18946,20 @@ init-state { fully-up | up | down | fully-down | none }
The "init-state" option sets the initial state of the server: The "init-state" option sets the initial state of the server:
- when set to 'fully-up', the server is considered immediately available - when set to 'fully-up', the server is considered immediately available
and, if health checks are enabled for this server, it will be turned to and can turn to the DOWN state when ALL health checks fail.
the DOWN state when ALL health checks fail. - when set to 'up' (the default), the server is considered immediately
- when set to 'up', the server is considered immediately available and, if available and will initiate a health check that can turn it to the DOWN
health checks are enabled for this server, it will be turned to the DOWN state immediately if it fails.
state immediately if the next health check fails. - when set to 'down', the server initially is considered unavailable and
- when set to 'down', the server initially is considered unavailable and, will initiate a health check that can turn it to the UP state immediately
if health checks are enabled for this server, it can be turned to the UP if it succeeds.
state if the next health check succeeds.
- when set to 'fully-down', the server is initially considered unavailable - when set to 'fully-down', the server is initially considered unavailable
and, if health checks are enabled for this server, it will turned to the and can turn to the UP state when ALL health checks succeed.
UP state when ALL health checks succeed.
- when set to 'none' (the default value), init-state management is
disabled. It can be used to restore the default behavior when this
parameter was inherited from a 'default-server' directive.
The server's init-state is considered when the HAProxy instance is The server's init-state is considered when the HAProxy instance is
(re)started, a new server is detected (for example via service discovery / (re)started, a new server is detected (for example via service discovery /
DNS resolution), a dynamic server is inlived, a server exits maintenance, DNS resolution), a dynamic server is inlived, a server exits maintenance,
etc. This directive cannot be used when the server is tracking another one. etc.
Examples: Examples:
# pass client traffic ONLY to Redis "master" node # pass client traffic ONLY to Redis "master" node
@ -19804,13 +19801,11 @@ sni-auto
May be used in the following contexts: tcp, http, log, peers, ring May be used in the following contexts: tcp, http, log, peers, ring
The "sni-auto" parameter enables the automatic SNI selection, if no value was The "sni-auto" parameter enables the automatic SNI selection, if no value was
already set. It sets the "sni" expression to "req.hdr(host),field(1,:)", already set. It is enabled by default but this parameter may be used as
which means that an SNI will be presented with the Host name of the request "server" setting to reset any "no-sni-auto" setting which would have been
that is being sent to the server, but dropping the port number. It is enabled inherited from "default-server" directive as default value. It may also be
by default but this parameter may be used as "server" setting to reset any used as "default-server" setting to reset any previous "default-server"
"no-sni-auto" setting which would have been inherited from "default-server" "no-sni-auto" setting.
directive as default value. It may also be used as "default-server" setting
to reset any previous "default-server" "no-sni-auto" setting.
For HTTPS connections, the selected SNI is based on the request host header For HTTPS connections, the selected SNI is based on the request host header
value, if found. Otherwise it remains unset. For other protocols, the option value, if found. Otherwise it remains unset. For other protocols, the option
@ -20160,11 +20155,7 @@ a cache of previous answers, an answer will be considered obsolete after
resolvers <resolvers id> resolvers <resolvers id>
Creates a new name server list labeled <resolvers id>. As mentioned above, Creates a new name server list labeled <resolvers id>
the special name "default" always exists and will be automatically created if
not explicitly declared; this will be the one internal services such as
httpclient rely on. Declaring a "default" entry will affect how such services
perform their name resolution.
A resolvers section accept the following parameters: A resolvers section accept the following parameters:
@ -21120,8 +21111,6 @@ param(name[,delim]) string string
port_only string integer port_only string integer
protobuf(field_number[,field_type]) binary binary protobuf(field_number[,field_type]) binary binary
regsub(regex,subst[,flags]) string string regsub(regex,subst[,flags]) string string
reverse string string
reverse_dom string string
rfc7239_field(field) string string rfc7239_field(field) string string
rfc7239_is_valid string boolean rfc7239_is_valid string boolean
rfc7239_n2nn string address / str rfc7239_n2nn string address / str
@ -22619,58 +22608,6 @@ regsub(<regex>,<subst>[,<flags>])
http-request redirect location %[url,'regsub("(foo|bar)([0-9]+)?","\2\1",i)'] http-request redirect location %[url,'regsub("(foo|bar)([0-9]+)?","\2\1",i)']
http-request redirect location %[url,regsub(\"(foo|bar)([0-9]+)?\",\"\2\1\",i)] http-request redirect location %[url,regsub(\"(foo|bar)([0-9]+)?\",\"\2\1\",i)]
reverse
Reverses the input string byte by byte.
This converter is encoding-agnostic and reverses bytes, not characters; it is
not suitable for reversing human text encoded as UTF-8.
This can turn suffix lookups on the original string into prefix lookups on
the reversed string, allowing the use of indexed prefix matchers such as
"map_beg" on large maps.
Examples:
"example.com" -> "moc.elpmaxe"
"ab cd" -> "dc ba"
# Given a map file where each key contains a reversed hostname:
# moc.elpmaxe.ppa app1
# moc.elpmaxe.bd dbcluster
# Pick a backend based on the domain suffix of the Host header:
use_backend %[req.hdr(host),lower,reverse,map_beg(/etc/haproxy/hosts.map,default)]
reverse_dom
Converts a string containing an FQDN-like hostname into its reversed-label
form. A single trailing dot on the input is ignored. Empty labels cause the
converter to fail.
This converter does not lowercase its input and does not strip any port.
It is meant to be combined with existing converters such as "lower" or
"host_only" when needed.
The trailing-dot policy is intentionally left to the caller. This allows
callers to decide whether they want to match the apex too or only
subdomains.
The reversed-label form is useful for large domain maps because it turns
domain suffix lookups into prefix lookups, allowing the use of indexed prefix
matchers such as "map_beg".
Examples:
"example.com" -> "com.example"
"mail.example.com" -> "com.example.mail"
"example.com." -> "com.example"
# match only subdomains of example.net, not the apex
acl example_net_sub req.hdr(Host),host_only,reverse_dom -m beg net.example.
# match only the apex
acl example_net_apex req.hdr(Host),host_only,reverse_dom -i net.example
# exact-or-subdomain prefix lookup using an explicit dotted form
http-request set-var(txn.rev_host) req.hdr(Host),host_only,reverse_dom,concat(.)
use_backend %[var(txn.rev_host),map_beg(/etc/haproxy/domains.map)]
rfc7239_field(<field>) rfc7239_field(<field>)
Extracts a single field/parameter from RFC 7239 compliant header value input. Extracts a single field/parameter from RFC 7239 compliant header value input.
@ -30576,9 +30513,41 @@ See also: "use-fcgi-app", section 9.2 about the compression filter, section 9.4
9.6. OpenTracing 9.6. OpenTracing
---------------- ----------------
The OpenTracing filter was removed in version 3.5. A replacement filter based The OpenTracing filter adds native support for using distributed tracing in
on OpenTelemetry is available since 3.4 with complete build instructions HAProxy. This is enabled by sending an OpenTracing compliant request to one
currently at: of the supported tracers such as Datadog, Jaeger, Lightstep and Zipkin tracers.
Please note: tracers are not listed by any preference, but alphabetically.
This feature is only enabled when HAProxy was built with USE_OT=1.
The OpenTracing filter activation is done explicitly by specifying it in the
HAProxy configuration. If this is not done, the OpenTracing filter in no way
participates in the work of HAProxy.
filter opentracing [id <id>] config <file>
Arguments :
<id> is the OpenTracing filter id that will be used to find the
right scope in the configuration file. If no filter id is
specified, 'ot-filter' is used as default. If scope is not
specified in the configuration file, it applies to all defined
OpenTracing filters.
<file> is the path of the OpenTracing configuration file. The same
file can contain configurations for multiple OpenTracing
filters simultaneously. In that case we do not need to define
scope so the same configuration applies to all filters or each
filter must have its own scope defined.
More detailed documentation related to the operation, configuration and use
of the filter can be found in the addons/ot directory.
Note: The OpenTracing filter shouldn't be used for new designs as OpenTracing
itself is no longer maintained nor supported by its authors. As such
OpenTracing will be deprecated in 3.3 and removed in 3.5. A replacement
filter based on OpenTelemetry is available since 3.4 with complete build
instructions currently at:
https://github.com/haproxytech/haproxy-opentelemetry/ https://github.com/haproxytech/haproxy-opentelemetry/
@ -32488,9 +32457,9 @@ https://github.com/haproxy/wiki/wiki/ACME:--native-haproxy
Current limitations: Current limitations:
- The feature is limited to the http-01, dns-01 or dns-persist-01 challenges - The feature is limited to the http-01, dns-01 or dns-persist-01 challenges
for now. http-01 is completely handled by HAProxy, but dns-01 and for now. http-01 is completely handled by HAProxy, but dns-01 and
dns-persist-01 needs either the dataplaneAPI, a lua script using event_hdl or dns-persist-01 needs either the dataplaneAPI or another 3rd party
another 3rd party tool to talk to a DNS provider API. dns-persist-01 only tool to talk to a DNS provider API. dns-persist-01 only needs the TXT entry
needs the TXT entry to be set once, so it could be set manually without a tool. to be set once, so it could be set manually without a tool.
- It is possible to start without an existing certificate on the disk. To do - It is possible to start without an existing certificate on the disk. To do
so, the certificate must configured in a crt-store. so, the certificate must configured in a crt-store.
When using the "acme" keyword in a crt-store, a temporary key pair will be When using the "acme" keyword in a crt-store, a temporary key pair will be
@ -32561,8 +32530,6 @@ challenge-ready <value>[,<value>]*
"acme challenge_ready <crt> domain <domain>" on the master CLI or "acme challenge_ready <crt> domain <domain>" on the master CLI or
the stats socket. This allows an external DNS provisioning tool to the stats socket. This allows an external DNS provisioning tool to
confirm that the TXT record has been set before HAProxy proceeds. confirm that the TXT record has been set before HAProxy proceeds.
It is also possible to signal the "cli" readiness using the
ACME.challenge_ready() lua function.
dns - perform a DNS pre-check by resolving the TXT record for dns - perform a DNS pre-check by resolving the TXT record for
"_acme-challenge.<domain>" using the configured "default" resolvers "_acme-challenge.<domain>" using the configured "default" resolvers

Some files were not shown because too many files have changed in this diff Show more