Compare commits

..

No commits in common. "master" and "v3.2-dev16" have entirely different histories.

959 changed files with 27209 additions and 73728 deletions

18
.cirrus.yml Normal file
View file

@ -0,0 +1,18 @@
FreeBSD_task:
freebsd_instance:
matrix:
image_family: freebsd-14-2
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)

View file

@ -1,49 +0,0 @@
name: 'setup VTest'
description: 'ssss'
runs:
using: "composite"
steps:
- name: Setup coredumps
if: ${{ runner.os == 'Linux' }}
shell: sh
run: |
sudo mkdir -p /tmp/core
sudo sysctl fs.suid_dumpable=1
sudo sysctl kernel.core_pattern=/tmp/core/core.%h.%e.%t
- name: Setup ulimit for core dumps
shell: sh
run: |
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
ulimit -c unlimited
- name: Get VTest latest commit SHA
id: vtest-sha
shell: sh
run: |
echo "sha=$(git ls-remote https://code.vinyl-cache.org/vtest/VTest2 HEAD | cut -f1)" >> $GITHUB_OUTPUT
- name: Cache VTest
id: cache-vtest
uses: actions/cache@v5
with:
path: ${{ github.workspace }}/vtest
key: vtest-${{ runner.os }}-${{ runner.arch }}-${{ steps.vtest-sha.outputs.sha }}
- name: Install VTest
if: ${{ steps.cache-vtest.outputs.cache-hit != 'true' }}
shell: sh
run: |
DESTDIR=${{ github.workspace }}/vtest scripts/build-vtest.sh
- name: Install problem matcher for VTest
shell: sh
# This allows one to more easily see which tests fail.
run: echo "::add-matcher::.github/vtest.json"

View file

@ -19,7 +19,7 @@ defaults
frontend h2
mode http
bind 127.0.0.1:8443 ssl crt reg-tests/ssl/certs/common.pem alpn h2,http/1.1
bind 127.0.0.1:8443 ssl crt reg-tests/ssl/common.pem alpn h2,http/1.1
default_backend h2b
backend h2b

210
.github/matrix.py vendored
View file

@ -12,7 +12,6 @@ import functools
import json
import re
import sys
import urllib.error
import urllib.request
from os import environ
from packaging import version
@ -20,10 +19,9 @@ from packaging import version
#
# this CI is used for both development and stable branches of HAProxy
#
# naming convention used, if branch/tag name matches:
# naming convention used, if branch name matches:
#
# "haproxy-" - stable branches
# "vX.Y.Z" - release tags
# otherwise - development branch (i.e. "latest" ssl variants, "latest" github images)
#
@ -34,24 +32,13 @@ def get_all_github_tags(url):
headers = {}
if environ.get("GITHUB_TOKEN") is not None:
headers["Authorization"] = "token {}".format(environ.get("GITHUB_TOKEN"))
all_tags = []
page = 1
sep = "&" if "?" in url else "?"
while True:
paginated_url = "{}{}per_page=100&page={}".format(url, sep, page)
request = urllib.request.Request(paginated_url, headers=headers)
try:
response = urllib.request.urlopen(request)
except urllib.error.URLError:
return all_tags if all_tags else None
tags = json.loads(response.read().decode("utf-8"))
if not tags:
break
all_tags.extend([tag['name'] for tag in tags])
if len(tags) < 100:
break
page += 1
return all_tags if all_tags else None
request = urllib.request.Request(url, headers=headers)
try:
tags = urllib.request.urlopen(request)
except:
return None
tags = json.loads(tags.read().decode("utf-8"))
return [tag['name'] for tag in tags]
@functools.lru_cache(5)
def determine_latest_openssl(ssl):
@ -69,7 +56,7 @@ def aws_lc_version_string_to_num(version_string):
return tuple(map(int, version_string[1:].split('.')))
def aws_lc_version_valid(version_string):
return re.match(r'^v[0-9]+(\.[0-9]+)*$', version_string)
return re.match('^v[0-9]+(\.[0-9]+)*$', version_string)
@functools.lru_cache(5)
def determine_latest_aws_lc(ssl):
@ -77,8 +64,6 @@ def determine_latest_aws_lc(ssl):
if not tags:
return "AWS_LC_VERSION=failed_to_detect"
valid_tags = list(filter(aws_lc_version_valid, tags))
if not valid_tags:
return "AWS_LC_VERSION=failed_to_detect"
latest_tag = max(valid_tags, key=aws_lc_version_string_to_num)
return "AWS_LC_VERSION={}".format(latest_tag[1:])
@ -86,16 +71,15 @@ def aws_lc_fips_version_string_to_num(version_string):
return tuple(map(int, version_string[12:].split('.')))
def aws_lc_fips_version_valid(version_string):
return re.match(r'^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
return re.match('^AWS-LC-FIPS-[0-9]+(\.[0-9]+)*$', version_string)
@functools.lru_cache(5)
def determine_latest_aws_lc_fips(ssl):
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags")
# the AWS-LC-FIPS tags are at the end of the list, so let's get a lot
tags = get_all_github_tags("https://api.github.com/repos/aws/aws-lc/tags?per_page=200")
if not tags:
return "AWS_LC_FIPS_VERSION=failed_to_detect"
valid_tags = list(filter(aws_lc_fips_version_valid, tags))
if not valid_tags:
return "AWS_LC_FIPS_VERSION=failed_to_detect"
latest_tag = max(valid_tags, key=aws_lc_fips_version_string_to_num)
return "AWS_LC_FIPS_VERSION={}".format(latest_tag[12:])
@ -103,7 +87,7 @@ def wolfssl_version_string_to_num(version_string):
return tuple(map(int, version_string[1:].removesuffix('-stable').split('.')))
def wolfssl_version_valid(version_string):
return re.match(r'^v[0-9]+(\.[0-9]+)*-stable$', version_string)
return re.match('^v[0-9]+(\.[0-9]+)*-stable$', version_string)
@functools.lru_cache(5)
def determine_latest_wolfssl(ssl):
@ -136,20 +120,14 @@ def clean_compression(compression):
def main(ref_name):
print("Generating matrix for branch '{}'.".format(ref_name))
is_stable = "haproxy-" in ref_name or re.match(r'^v\d+\.\d+\.\d+$', ref_name)
matrix = []
# Ubuntu
if is_stable:
os = "ubuntu-24.04" # stable branch
os_arm = "ubuntu-24.04-arm" # stable branch
os_i686 = "ubuntu-24.04" # stable branch
if "haproxy-" in ref_name:
os = "ubuntu-22.04" # stable branch
else:
os = "ubuntu-24.04" # development branch
os_arm = "ubuntu-24.04-arm" # development branch
os_i686 = "ubuntu-24.04" # development branch
os = "ubuntu-24.04" # development branch
TARGET = "linux-glibc"
for CC in ["gcc", "clang"]:
@ -194,38 +172,36 @@ def main(ref_name):
# ASAN
for os_asan in [os, os_arm]:
matrix.append(
{
"name": "{}, {}, ASAN, all features".format(os_asan, CC),
"os": os_asan,
"TARGET": TARGET,
"CC": CC,
"FLAGS": [
"USE_OBSOLETE_LINKER=1",
'ARCH_FLAGS="-g -fsanitize=address"',
'OPT_CFLAGS="-O1"',
"USE_ZLIB=1",
"USE_OT=1",
"DEBUG=-DDEBUG_STRICT=2",
"OT_INC=${HOME}/opt-ot/include",
"OT_LIB=${HOME}/opt-ot/lib",
"OT_RUNPATH=1",
"USE_PCRE2=1",
"USE_PCRE2_JIT=1",
"USE_LUA=1",
"USE_OPENSSL=1",
"USE_WURFL=1",
"WURFL_INC=addons/wurfl/dummy",
"WURFL_LIB=addons/wurfl/dummy",
"USE_DEVICEATLAS=1",
"DEVICEATLAS_SRC=addons/deviceatlas/dummy",
"USE_PROMEX=1",
"USE_51DEGREES=1",
"51DEGREES_SRC=addons/51degrees/dummy/pattern",
],
}
)
matrix.append(
{
"name": "{}, {}, ASAN, all features".format(os, CC),
"os": os,
"TARGET": TARGET,
"CC": CC,
"FLAGS": [
"USE_OBSOLETE_LINKER=1",
'ARCH_FLAGS="-g -fsanitize=address"',
'OPT_CFLAGS="-O1"',
"USE_ZLIB=1",
"USE_OT=1",
"OT_INC=${HOME}/opt-ot/include",
"OT_LIB=${HOME}/opt-ot/lib",
"OT_RUNPATH=1",
"USE_PCRE2=1",
"USE_PCRE2_JIT=1",
"USE_LUA=1",
"USE_OPENSSL=1",
"USE_WURFL=1",
"WURFL_INC=addons/wurfl/dummy",
"WURFL_LIB=addons/wurfl/dummy",
"USE_DEVICEATLAS=1",
"DEVICEATLAS_SRC=addons/deviceatlas/dummy",
"USE_PROMEX=1",
"USE_51DEGREES=1",
"51DEGREES_SRC=addons/51degrees/dummy/pattern",
],
}
)
for compression in ["USE_ZLIB=1"]:
matrix.append(
@ -242,14 +218,13 @@ def main(ref_name):
"stock",
"OPENSSL_VERSION=1.0.2u",
"OPENSSL_VERSION=1.1.1s",
"OPENSSL_VERSION=3.5.1",
"QUICTLS_VERSION=OpenSSL_1_1_1w-quic1",
"QUICTLS=yes",
"WOLFSSL_VERSION=5.7.0",
"AWS_LC_VERSION=1.39.0",
# "BORINGSSL=yes",
]
if not is_stable: # development branch
if "haproxy-" not in ref_name: # development branch
ssl_versions = ssl_versions + [
"OPENSSL_VERSION=latest",
"LIBRESSL_VERSION=latest",
@ -257,7 +232,8 @@ def main(ref_name):
for ssl in ssl_versions:
flags = ["USE_OPENSSL=1"]
skipdup=0
if ssl == "BORINGSSL=yes" or ssl == "QUICTLS=yes" or "LIBRESSL" in ssl or "WOLFSSL" in ssl or "AWS_LC" in ssl:
flags.append("USE_QUIC=1")
if "WOLFSSL" in ssl:
flags.append("USE_OPENSSL_WOLFSSL=1")
if "AWS_LC" in ssl:
@ -267,23 +243,8 @@ def main(ref_name):
flags.append("SSL_INC=${HOME}/opt/include")
if "LIBRESSL" in ssl and "latest" in ssl:
ssl = determine_latest_libressl(ssl)
skipdup=1
if "OPENSSL" in ssl and "latest" in ssl:
ssl = determine_latest_openssl(ssl)
skipdup=1
# if "latest" equals a version already in the list
if ssl in ssl_versions and skipdup == 1:
continue
openssl_supports_quic = False
try:
openssl_supports_quic = version.Version(ssl.split("OPENSSL_VERSION=",1)[1]) >= version.Version("3.5.0")
except:
pass
if ssl == "BORINGSSL=yes" or "QUICTLS" in ssl or "LIBRESSL" in ssl or "WOLFSSL" in ssl or "AWS_LC" in ssl or openssl_supports_quic:
flags.append("USE_QUIC=1")
matrix.append(
{
@ -296,63 +257,24 @@ def main(ref_name):
}
)
# macOS on dev branches
if not is_stable:
os = "macos-26" # development branch
# macOS
TARGET = "osx"
for CC in ["clang"]:
matrix.append(
{
"name": "{}, {}, no features".format(os, CC),
"os": os,
"TARGET": TARGET,
"CC": CC,
"FLAGS": [],
}
)
if "haproxy-" in ref_name:
os = "macos-13" # stable branch
else:
os = "macos-15" # development branch
# Alpine / musl
matrix.append(
{
"name": "Alpine+musl, gcc",
"os": "ubuntu-latest",
"container": {
"image": "alpine:latest",
"options": "--privileged --ulimit core=-1 --security-opt seccomp=unconfined",
"volumes": ["/tmp/core:/tmp/core"],
},
"TARGET": "linux-musl",
"CC": "gcc",
"FLAGS": [
"ARCH_FLAGS='-ggdb3'",
"USE_LUA=1",
"LUA_INC=/usr/include/lua5.3",
"LUA_LIB=/usr/lib/lua5.3",
"USE_OPENSSL=1",
"USE_PCRE2=1",
"USE_PCRE2_JIT=1",
"USE_PROMEX=1",
],
}
)
# i686
matrix.append(
{
"name": "{}, i686-linux-gnu-gcc".format(os_i686),
"os": os_i686,
"TARGET": "linux-glibc",
"CC": "i686-linux-gnu-gcc",
"FLAGS": [
"USE_OPENSSL=1",
"USE_PCRE2=1",
"USE_PCRE2_JIT=1",
],
}
)
TARGET = "osx"
for CC in ["clang"]:
matrix.append(
{
"name": "{}, {}, no features".format(os, CC),
"os": os,
"TARGET": TARGET,
"CC": CC,
"FLAGS": [],
}
)
# Print matrix

View file

@ -1,8 +1,8 @@
name: openssl master
name: AWS-LC-FIPS
on:
schedule:
- cron: "0 3 * * *"
- cron: "0 0 * * 4"
workflow_dispatch:
permissions:
@ -13,19 +13,33 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Determine latest AWS-LC release
id: get_aws_lc_release
run: |
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))")
echo $result
echo "result=$result" >> $GITHUB_OUTPUT
- name: Cache AWS-LC
id: cache_aws_lc
uses: actions/cache@v4
with:
path: '~/opt/'
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb
sudo apt-get --no-install-recommends -y install libpsl-dev
- uses: ./.github/actions/setup-vtest
- name: Install OpenSSL master
run: env OPENSSL_VERSION="git-master" GIT_TYPE="branch" scripts/build-ssl.sh
sudo apt-get --no-install-recommends -y install socat gdb jose
- name: Install AWS-LC
if: ${{ steps.cache_ssl.outputs.cache-hit != 'true' }}
run: env ${{ steps.get_aws_lc_release.outputs.result }} scripts/build-ssl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
USE_QUIC=1 USE_OPENSSL=1 \
USE_OPENSSL_AWSLC=1 USE_QUIC=1 \
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
@ -35,7 +49,7 @@ jobs:
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
@ -46,7 +60,11 @@ jobs:
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
@ -57,10 +75,6 @@ jobs:
echo "::endgroup::"
done
exit 1
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
@ -75,3 +89,13 @@ jobs:
if [ "$failed" = true ]; then
exit 1;
fi
- name: Show Unit-Tests results
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
run: |
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
printf "::group::"
cat $result
echo "::endgroup::"
done
exit 1

View file

@ -9,28 +9,23 @@ permissions:
contents: read
jobs:
Test:
name: ${{ matrix.name }}
test:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: AWS-LC
command: "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))"
- name: AWS-LC (FIPS)
command: "from matrix import determine_latest_aws_lc_fips; print(determine_latest_aws_lc_fips(''))"
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Determine latest AWS-LC release
id: get_aws_lc_release
run: |
result=$(cd .github && python3 -c "${{ matrix.command }}")
result=$(cd .github && python3 -c "from matrix import determine_latest_aws_lc; print(determine_latest_aws_lc(''))")
echo $result
echo "result=$result" >> $GITHUB_OUTPUT
- name: Cache AWS-LC
id: cache_aws_lc
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: '~/opt/'
key: ssl-${{ steps.get_aws_lc_release.outputs.result }}-Ubuntu-latest-gcc
@ -54,12 +49,18 @@ jobs:
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
- uses: ./.github/actions/setup-vtest
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Run Unit tests
id: unittests
run: |
@ -97,3 +98,4 @@ jobs:
echo "::endgroup::"
done
exit 1

View file

@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: codespell-project/codespell-problem-matcher@v1.2.0
- uses: codespell-project/actions-codespell@master
with:

View file

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install h2spec
id: install-h2spec
run: |
@ -45,7 +45,7 @@ jobs:
fi
echo "::endgroup::"
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Launch HAProxy ${{ steps.show-version.outputs.version }}
run: haproxy -f .github/h2spec.config -D
- name: Run h2spec ${{ steps.install-h2spec.outputs.version }}

View file

@ -7,30 +7,19 @@ permissions:
contents: read
jobs:
compile:
name: ${{ matrix.name }}
runs-on: ubuntu-slim
strategy:
matrix:
include:
- name: admin/halog/
targets:
- admin/halog/halog
- name: dev/flags/
targets:
- dev/flags/flags
- name: dev/haring/
targets:
- dev/haring/haring
- name: dev/hpack/
targets:
- dev/hpack/decode
- dev/hpack/gen-enc
- dev/hpack/gen-rht
- name: dev/poll/
targets:
- dev/poll/poll
fail-fast: false
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: make ${{ join(matrix.targets, ' ') }}
- uses: actions/checkout@v4
- name: Compile admin/halog/halog
run: |
make admin/halog/halog
- name: Compile dev/flags/flags
run: |
make dev/flags/flags
- name: Compile dev/poll/poll
run: |
make dev/poll/poll
- name: Compile dev/hpack
run: |
make dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht

View file

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
@ -27,7 +27,7 @@ jobs:
libsystemd-dev
- name: Install QUICTLS
run: |
QUICTLS_VERSION=OpenSSL_1_1_1w-quic1 scripts/build-ssl.sh
QUICTLS=yes scripts/build-ssl.sh
- name: Download Coverity build tool
run: |
wget -c -N https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=Haproxy" -O coverity_tool.tar.gz
@ -38,7 +38,7 @@ jobs:
- name: Build with Coverity build tool
run: |
export PATH=`pwd`/coverity_tool/bin:$PATH
cov-build --dir cov-int make CC=clang TARGET=linux-glibc USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 USE_OPENSSL=1 USE_QUIC=1 USE_WURFL=1 WURFL_INC=addons/wurfl/dummy WURFL_LIB=addons/wurfl/dummy USE_DEVICEATLAS=1 DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_51DEGREES=1 51DEGREES_SRC=addons/51degrees/dummy/pattern ADDLIB=\"-Wl,-rpath,$HOME/opt/lib/\" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include DEBUG+=-DDEBUG_STRICT=2 DEBUG+=-DDEBUG_USE_ABORT=1
cov-build --dir cov-int make CC=clang TARGET=linux-glibc USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 USE_OPENSSL=1 USE_QUIC=1 USE_WURFL=1 WURFL_INC=addons/wurfl/dummy WURFL_LIB=addons/wurfl/dummy USE_DEVICEATLAS=1 DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_51DEGREES=1 51DEGREES_SRC=addons/51degrees/dummy/pattern ADDLIB=\"-Wl,-rpath,$HOME/opt/lib/\" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include DEBUG+=-DDEBUG_STRICT=1 DEBUG+=-DDEBUG_USE_ABORT=1
- name: Submit build result to Coverity Scan
run: |
tar czvf cov.tar.gz cov-int

View file

@ -5,7 +5,7 @@ name: Cross Compile
on:
schedule:
- cron: "0 2 * * 1"
- cron: "0 0 21 * *"
workflow_dispatch:
permissions:
@ -17,10 +17,6 @@ jobs:
matrix:
platform: [
{
arch: i686-linux-gnu,
libs: libc6-dev-i386-cross,
target: linux-x86
}, {
arch: aarch64-linux-gnu,
libs: libc6-dev-arm64-cross,
target: linux-aarch64
@ -103,12 +99,12 @@ jobs:
sudo apt-get -yq --force-yes install \
gcc-${{ matrix.platform.arch }} \
${{ matrix.platform.libs }}
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: install quictls
run: |
QUICTLS_EXTRA_ARGS="--cross-compile-prefix=${{ matrix.platform.arch }}- ${{ matrix.platform.target }}" QUICTLS_VERSION=openssl-3.1.7+quic scripts/build-ssl.sh
QUICTLS_EXTRA_ARGS="--cross-compile-prefix=${{ matrix.platform.arch }}- ${{ matrix.platform.target }}" QUICTLS=yes scripts/build-ssl.sh
- name: Build
run: |

View file

@ -1,4 +1,4 @@
name: Fedora/Rawhide/OpenSSL
name: Fedora/Rawhide/QuicTLS
on:
schedule:
@ -13,28 +13,35 @@ jobs:
strategy:
matrix:
platform: [
{ name: x64, cc: gcc, ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
{ name: x64, cc: clang, ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
{ name: x86, cc: gcc, ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" },
{ name: x86, cc: clang, ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" }
{ name: x64, cc: gcc, QUICTLS_EXTRA_ARGS: "", ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
{ name: x64, cc: clang, QUICTLS_EXTRA_ARGS: "", ADDLIB_ATOMIC: "", ARCH_FLAGS: "" },
{ name: x86, cc: gcc, QUICTLS_EXTRA_ARGS: "-m32 linux-generic32", ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" },
{ name: x86, cc: clang, QUICTLS_EXTRA_ARGS: "-m32 linux-generic32", ADDLIB_ATOMIC: "-latomic", ARCH_FLAGS: "-m32" }
]
fail-fast: false
name: ${{ matrix.platform.cc }}.${{ matrix.platform.name }}
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
container:
image: fedora:rawhide
options: --privileged
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install dependencies
run: |
dnf -y install awk diffutils git zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang openssl-devel.x86_64 procps-ng
dnf -y install 'perl(FindBin)' 'perl(File::Compare)' perl-IPC-Cmd 'perl(File::Copy)' glibc-devel.i686 lua-devel.i686 lua-devel.x86_64 systemd-devel.i686 zlib-ng-compat-devel.i686 libatomic.i686 openssl-devel.i686 pcre2-devel.i686
- uses: ./.github/actions/setup-vtest
dnf -y install awk diffutils git pcre-devel zlib-devel pcre2-devel 'perl(FindBin)' perl-IPC-Cmd 'perl(File::Copy)' 'perl(File::Compare)' lua-devel socat findutils systemd-devel clang
dnf -y install 'perl(FindBin)' 'perl(File::Compare)' perl-IPC-Cmd 'perl(File::Copy)' glibc-devel.i686 lua-devel.i686 lua-devel.x86_64 systemd-devel.i686 zlib-ng-compat-devel.i686 pcre-devel.i686 libatomic.i686
- name: Install VTest
run: scripts/build-vtest.sh
- name: Install QuicTLS
run: QUICTLS=yes QUICTLS_EXTRA_ARGS="${{ matrix.platform.QUICTLS_EXTRA_ARGS }}" scripts/build-ssl.sh
- name: Build contrib tools
run: |
make admin/halog/halog
make dev/flags/flags
make dev/poll/poll
make dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
- name: Compile HAProxy with ${{ matrix.platform.cc }}
run: |
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" USE_PROMEX=1 USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }}" ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
make -j3 CC=${{ matrix.platform.cc }} V=1 ERR=1 TARGET=linux-glibc DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" USE_OPENSSL=1 USE_QUIC=1 USE_ZLIB=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_LUA=1 ADDLIB="${{ matrix.platform.ADDLIB_ATOMIC }} -Wl,-rpath,${HOME}/opt/lib" SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include ARCH_FLAGS="${{ matrix.platform.ARCH_FLAGS }}"
make install
- name: Show HAProxy version
id: show-version
@ -43,18 +50,11 @@ jobs:
ldd $(command -v haproxy)
echo "::endgroup::"
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
#
# TODO: review this workaround later
- name: relax crypto policies
run: |
dnf -y install crypto-policies-scripts
echo LEGACY > /etc/crypto-policies/config
update-crypto-policies
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
id: vtest
run: |
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
@ -67,4 +67,4 @@ jobs:
- name: Run Unit tests
id: unittests
run: |
make unit-tests
make unit-tests

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,19 +2,18 @@ name: Illumos
on:
schedule:
- cron: "0 3 * * 1"
- cron: "0 0 25 * *"
workflow_dispatch:
permissions:
contents: read
jobs:
gcc:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
steps:
- name: "Checkout repository"
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: "Build on VM"
uses: vmactions/solaris-vm@v1

76
.github/workflows/musl.yml vendored Normal file
View file

@ -0,0 +1,76 @@
name: alpine/musl
on:
push:
permissions:
contents: read
jobs:
musl:
name: gcc
runs-on: ubuntu-latest
container:
image: alpine:latest
options: --privileged --ulimit core=-1 --security-opt seccomp=unconfined
volumes:
- /tmp/core:/tmp/core
steps:
- name: Setup coredumps
run: |
ulimit -c unlimited
echo '/tmp/core/core.%h.%e.%t' > /proc/sys/kernel/core_pattern
- uses: actions/checkout@v4
- name: Install dependencies
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg jose
- name: Install VTest
run: scripts/build-vtest.sh
- name: Build
run: make -j$(nproc) TARGET=linux-musl DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" ARCH_FLAGS='-ggdb3' CC=cc V=1 USE_LUA=1 LUA_INC=/usr/include/lua5.3 LUA_LIB=/usr/lib/lua5.3 USE_OPENSSL=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_PROMEX=1
- name: Show version
run: ./haproxy -vv
- name: Show linked libraries
run: ldd haproxy
- name: Install problem matcher for VTest
# This allows one to more easily see which tests fail.
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest
id: vtest
run: make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
failed=false
ls /tmp/core/
for file in /tmp/core/core.*; do
failed=true
printf "::group::"
gdb -ex 'thread apply all bt full' ./haproxy $file
echo "::endgroup::"
done
if [ "$failed" = true ]; then
exit 1;
fi
- name: Show results
if: ${{ failure() }}
run: |
for folder in /tmp/haregtests-*/vtc.*; do
printf "::group::"
cat $folder/INFO
cat $folder/LOG
echo "::endgroup::"
done
- name: Show Unit-Tests results
if: ${{ failure() && steps.unittests.outcome == 'failure' }}
run: |
for result in ${TMPDIR:-/tmp}/ha-unittests-*/results/res.*; do
printf "::group::"
cat $result
echo "::endgroup::"
done
exit 1

View file

@ -5,16 +5,15 @@ on:
- cron: "0 0 25 * *"
workflow_dispatch:
permissions:
contents: read
jobs:
gcc:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
steps:
- name: "Checkout repository"
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: "Build on VM"
uses: vmactions/netbsd-vm@v1

View file

@ -1,80 +0,0 @@
name: openssl ECH
on:
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb
sudo apt-get --no-install-recommends -y install libpsl-dev
- uses: ./.github/actions/setup-vtest
- name: Install OpenSSL+ECH
run: env OPENSSL_VERSION="git-feature/ech" GIT_TYPE="branch" scripts/build-ssl.sh
- name: Install curl+ECH
run: env SSL_LIB=${HOME}/opt/ scripts/build-curl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) CC=gcc TARGET=linux-glibc \
USE_QUIC=1 USE_OPENSSL=1 USE_ECH=1 \
SSL_LIB=${HOME}/opt/lib SSL_INC=${HOME}/opt/include \
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/" \
ARCH_FLAGS="-ggdb3 -fsanitize=address"
sudo make install
- name: Show HAProxy version
id: show-version
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
for folder in ${TMPDIR:-/tmp}/haregtests-*/vtc.*; do
printf "::group::"
cat $folder/INFO
cat $folder/LOG
echo "::endgroup::"
done
exit 1
- name: Run Unit tests
id: unittests
run: |
make unit-tests
- name: Show coredumps
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
failed=false
shopt -s nullglob
for file in /tmp/core.*; do
failed=true
printf "::group::"
gdb -ex 'thread apply all bt full' ./haproxy $file
echo "::endgroup::"
done
if [ "$failed" = true ]; then
exit 1;
fi

View file

@ -0,0 +1,34 @@
#
# special purpose CI: test against OpenSSL built in "no-deprecated" mode
# let us run those builds weekly
#
# for example, OpenWRT uses such OpenSSL builds (those builds are smaller)
#
#
# some details might be found at NL: https://www.mail-archive.com/haproxy@formilux.org/msg35759.html
# GH: https://github.com/haproxy/haproxy/issues/367
name: openssl no-deprecated
on:
schedule:
- cron: "0 0 * * 4"
workflow_dispatch:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Compile HAProxy
run: |
make DEFINE="-DOPENSSL_API_COMPAT=0x10100000L -DOPENSSL_NO_DEPRECATED" -j3 CC=gcc ERR=1 TARGET=linux-glibc USE_OPENSSL=1
- name: Run VTest
run: |
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel

View file

@ -9,58 +9,96 @@ on:
schedule:
- cron: "0 0 * * 2"
permissions:
contents: read
jobs:
combined-build-and-run:
build:
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v5
with:
context: https://github.com/haproxytech/haproxy-qns.git
push: true
build-args: |
SSLLIB: AWS-LC
tags: ghcr.io/${{ github.repository }}:aws-lc
- name: Cleanup registry
uses: actions/delete-package-versions@v5
with:
owner: ${{ github.repository_owner }}
package-name: 'haproxy'
package-type: container
min-versions-to-keep: 1
delete-only-untagged-versions: 'true'
run:
needs: build
strategy:
matrix:
suite: [
{ client: chrome, tests: "http3" },
{ client: picoquic, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" },
{ client: quic-go, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" },
{ client: ngtcp2, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" }
]
fail-fast: false
name: ${{ matrix.suite.client }}
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Update Docker to the latest
uses: docker/setup-docker-action@v4
- name: Build Docker image
id: push
uses: docker/build-push-action@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
context: https://github.com/haproxytech/haproxy-qns.git
platforms: linux/amd64
build-args: |
SSLLIB=AWS-LC
tags: local:aws-lc
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Install tshark
run: |
sudo apt-get update
sudo apt-get -y install tshark
- name: Pull image
run: |
docker pull ghcr.io/${{ github.repository }}:aws-lc
- name: Run
run: |
git clone https://github.com/quic-interop/quic-interop-runner
cd quic-interop-runner
pip install -r requirements.txt --break-system-packages
python run.py -j result.json -l logs-chrome -r haproxy=local:aws-lc -t "http3" -c chrome -s haproxy
python run.py -j result.json -l logs-picoquic -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c picoquic -s haproxy
python run.py -j result.json -l logs-quic-go -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c quic-go -s haproxy
python run.py -j result.json -l logs-ngtcp2 -r haproxy=local:aws-lc -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,resumption,zerortt,http3,blackhole,keyupdate,ecn,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,ipv6,v2" -c ngtcp2 -s haproxy
python run.py -j result.json -l logs -r haproxy=ghcr.io/${{ github.repository }}:aws-lc -t ${{ matrix.suite.tests }} -c ${{ matrix.suite.client }} -s haproxy
- name: Delete succeeded logs
if: ${{ failure() }}
if: failure()
run: |
for client in chrome picoquic quic-go ngtcp2; do
pushd quic-interop-runner/logs-${client}/haproxy_${client}
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
popd
done
cd quic-interop-runner/logs/haproxy_${{ matrix.suite.client }}
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
- name: Logs upload
if: ${{ failure() }}
uses: actions/upload-artifact@v7
if: failure()
uses: actions/upload-artifact@v4
with:
name: logs
path: quic-interop-runner/logs*/
name: logs-${{ matrix.suite.client }}
path: quic-interop-runner/logs/
retention-days: 6

View file

@ -9,56 +9,94 @@ on:
schedule:
- cron: "0 0 * * 2"
permissions:
contents: read
jobs:
combined-build-and-run:
build:
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v5
with:
context: https://github.com/haproxytech/haproxy-qns.git
push: true
build-args: |
SSLLIB: LibreSSL
tags: ghcr.io/${{ github.repository }}:libressl
- name: Cleanup registry
uses: actions/delete-package-versions@v5
with:
owner: ${{ github.repository_owner }}
package-name: 'haproxy'
package-type: container
min-versions-to-keep: 1
delete-only-untagged-versions: 'true'
run:
needs: build
strategy:
matrix:
suite: [
{ client: picoquic, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,v2" },
{ client: quic-go, tests: "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,transferloss,transfercorruption,v2" }
]
fail-fast: false
name: ${{ matrix.suite.client }}
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Update Docker to the latest
uses: docker/setup-docker-action@v4
- name: Build Docker image
id: push
uses: docker/build-push-action@v6
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
context: https://github.com/haproxytech/haproxy-qns.git
platforms: linux/amd64
build-args: |
SSLLIB=LibreSSL
tags: local:libressl
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Install tshark
run: |
sudo apt-get update
sudo apt-get -y install tshark
- name: Pull image
run: |
docker pull ghcr.io/${{ github.repository }}:libressl
- name: Run
run: |
git clone https://github.com/quic-interop/quic-interop-runner
cd quic-interop-runner
pip install -r requirements.txt --break-system-packages
python run.py -j result.json -l logs-picoquic -r haproxy=local:libressl -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,handshakeloss,transferloss,handshakecorruption,transfercorruption,v2" -c picoquic -s haproxy
python run.py -j result.json -l logs-quic-go -r haproxy=local:libressl -t "handshake,transfer,longrtt,chacha20,multiplexing,retry,http3,blackhole,amplificationlimit,transferloss,transfercorruption,v2" -c quic-go -s haproxy
python run.py -j result.json -l logs -r haproxy=ghcr.io/${{ github.repository }}:libressl -t ${{ matrix.suite.tests }} -c ${{ matrix.suite.client }} -s haproxy
- name: Delete succeeded logs
if: ${{ failure() }}
if: failure()
run: |
for client in picoquic quic-go; do
pushd quic-interop-runner/logs-${client}/haproxy_${client}
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
popd
done
cd quic-interop-runner/logs/haproxy_${{ matrix.suite.client }}
cat ../../result.json | jq -r '.results[][] | select(.result=="succeeded") | .name' | xargs rm -rf
- name: Logs upload
if: ${{ failure() }}
uses: actions/upload-artifact@v7
if: failure()
uses: actions/upload-artifact@v4
with:
name: logs
path: quic-interop-runner/logs*/
name: logs-${{ matrix.suite.client }}
path: quic-interop-runner/logs/
retention-days: 6

View file

@ -17,13 +17,16 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb
- name: Install QuicTLS
run: env QUICTLS_VERSION=main QUICTLS_URL=https://github.com/quictls/quictls scripts/build-ssl.sh
run: env QUICTLS=yes QUICTLS_URL=https://github.com/quictls/quictls scripts/build-ssl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
@ -38,12 +41,18 @@ jobs:
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
- uses: ./.github/actions/setup-vtest
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |

View file

@ -19,11 +19,11 @@ jobs:
# generated by .github/matrix.py.
generate-matrix:
name: Generate Build Matrix
runs-on: ubuntu-slim
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Generate Build Matrix
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -38,17 +38,22 @@ jobs:
strategy:
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
fail-fast: false
container: ${{ matrix.container }}
env:
# Configure a short TMPDIR to prevent failures due to long unix socket
# paths.
TMPDIR: /tmp
OT_CPP_VERSION: 1.6.0
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 100
- name: Setup coredumps
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
run: |
sudo sysctl -w fs.suid_dumpable=1
sudo sysctl kernel.core_pattern=/tmp/core.%h.%e.%t
#
# Github Action cache key cannot contain comma, so we calculate it based on job name
#
@ -58,9 +63,9 @@ jobs:
echo "key=$(echo ${{ matrix.name }} | sha256sum | awk '{print $1}')" >> $GITHUB_OUTPUT
- name: Cache SSL libs
if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && !contains(matrix.ssl, 'QUICTLS') }}
if: ${{ matrix.ssl && matrix.ssl != 'stock' && matrix.ssl != 'BORINGSSL=yes' && matrix.ssl != 'QUICTLS=yes' }}
id: cache_ssl
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: '~/opt/'
key: ssl-${{ steps.generate-cache-key.outputs.key }}
@ -68,34 +73,29 @@ jobs:
- name: Cache OpenTracing
if: ${{ contains(matrix.FLAGS, 'USE_OT=1') }}
id: cache_ot
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: '~/opt-ot/'
key: ${{ matrix.os }}-ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
- name: Add i386 architecture
if: ${{ matrix.CC == 'i686-linux-gnu-gcc' }}
run: sudo dpkg --add-architecture i386
key: ot-${{ matrix.CC }}-${{ env.OT_CPP_VERSION }}-${{ contains(matrix.name, 'ASAN') }}
- name: Install apt dependencies
if: ${{ startsWith(matrix.os, 'ubuntu-') && matrix.TARGET != 'linux-musl' }}
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install \
${{ case(contains(matrix.FLAGS, 'USE_LUA=1'), 'liblua5.4-dev', '') }} \
${{ case(contains(matrix.FLAGS, 'USE_PCRE2=1'), 'libpcre2-dev', '') }} \
${{ case(contains(matrix.ssl, 'BORINGSSL=yes'), 'ninja-build', '') }} \
${{ case(matrix.CC == 'i686-linux-gnu-gcc', 'gcc-i686-linux-gnu libc6-dev-i386-cross libssl-dev:i386 libpcre2-dev:i386', '') }} \
${{ contains(matrix.FLAGS, 'USE_LUA=1') && 'liblua5.4-dev' || '' }} \
${{ contains(matrix.FLAGS, 'USE_PCRE2=1') && 'libpcre2-dev' || '' }} \
${{ contains(matrix.ssl, 'BORINGSSL=yes') && 'ninja-build' || '' }} \
socat \
gdb \
jose
- name: Install apk dependencies
if: ${{ matrix.TARGET == 'linux-musl' }}
run: apk add gcc gdb make tar git python3 libc-dev linux-headers pcre-dev pcre2-dev openssl-dev lua5.3-dev grep socat curl musl-dbg lua5.3-dbg jose sudo
- name: Install brew dependencies
if: ${{ startsWith(matrix.os, 'macos-') }}
run: |
brew install socat
brew install lua
- uses: ./.github/actions/setup-vtest
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Install SSL ${{ matrix.ssl }}
if: ${{ matrix.ssl && matrix.ssl != 'stock' && steps.cache_ssl.outputs.cache-hit != 'true' }}
run: env ${{ matrix.ssl }} scripts/build-ssl.sh
@ -121,7 +121,7 @@ jobs:
DEBUG="-DDEBUG_POOL_INTEGRITY -DDEBUG_UNIT" \
${{ join(matrix.FLAGS, ' ') }} \
ADDLIB="-Wl,-rpath,/usr/local/lib/ -Wl,-rpath,$HOME/opt/lib/"
sudo make install-bin
sudo make install
- name: Show HAProxy version
id: show-version
run: |
@ -135,11 +135,18 @@ jobs:
fi
echo "::endgroup::"
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
# This allows one to more easily see which tests fail.
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy ${{ steps.show-version.outputs.version }}
id: vtest
run: |
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Show VTest results
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
@ -167,7 +174,8 @@ jobs:
if: ${{ failure() && steps.vtest.outcome == 'failure' }}
run: |
failed=false
for file in /tmp/core/core.*; do
shopt -s nullglob
for file in /tmp/core.*; do
failed=true
printf "::group::"
gdb -ex 'thread apply all bt full' ./haproxy $file

View file

@ -18,7 +18,6 @@ jobs:
msys2:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
defaults:
run:
shell: msys2 {0}
@ -36,7 +35,7 @@ jobs:
- USE_THREAD=1
- USE_ZLIB=1
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
install: >-

View file

@ -13,13 +13,16 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'haproxy' || github.event_name == 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install VTest
run: |
scripts/build-vtest.sh
- name: Install apt dependencies
run: |
sudo apt-get update -o Acquire::Languages=none -o Acquire::Translation=none
sudo apt-get --no-install-recommends -y install socat gdb jose
- name: Install WolfSSL
run: env WOLFSSL_VERSION=git-master WOLFSSL_DEBUG=1 CFLAGS="-fsanitize=address -g" scripts/build-ssl.sh
run: env WOLFSSL_VERSION=git-master WOLFSSL_DEBUG=1 scripts/build-ssl.sh
- name: Compile HAProxy
run: |
make -j$(nproc) ERR=1 CC=gcc TARGET=linux-glibc \
@ -34,12 +37,18 @@ jobs:
run: |
ldd $(which haproxy)
haproxy -vv
echo "version=$(haproxy -vq)" >> $GITHUB_OUTPUT
- uses: ./.github/actions/setup-vtest
echo "version=$(haproxy -v |awk 'NR==1{print $3}')" >> $GITHUB_OUTPUT
- name: Install problem matcher for VTest
run: echo "::add-matcher::.github/vtest.json"
- name: Run VTest for HAProxy
id: vtest
run: |
make reg-tests VTEST_PROGRAM=${{ github.workspace }}/vtest/vtest REGTESTS_TYPES=default,bug,devel
# This is required for macOS which does not actually allow to increase
# the '-n' soft limit to the hard limit, thus failing to run.
ulimit -n 65536
# allow to catch coredumps
ulimit -c unlimited
make reg-tests VTEST_PROGRAM=../vtest/vtest REGTESTS_TYPES=default,bug,devel
- name: Run Unit tests
id: unittests
run: |

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
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
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
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
@ -171,17 +171,7 @@ feedback for developers:
as the previous releases that had 6 months to stabilize. In terms of
stability it really means that the point zero version already accumulated
6 months of fixes and that it is much safer to use even just after it is
released. There is one exception though, features marked as "experimental"
are not guaranteed to be maintained beyond the release of the next LTS
branch. The rationale here is that the experimental status is made to
expose an early preview of a feature, that is often incomplete, not always
in its definitive form regarding configuration, and for which developers
are seeking feedback from the users. It is even possible that changes will
be brought within the stable branch and it may happen that the feature
breaks. It is not imaginable to always be able to backport bug fixes too
far in this context since the code and configuration may change quite a
bit. Users who want to try experimental features are expected to upgrade
quickly to benefit from the improvements made to that feature.
released.
- for developers, given that the odd versions are solely used by highly
skilled users, it's easier to get advanced traces and captures, and there

2818
CHANGELOG

File diff suppressed because it is too large Load diff

55
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
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
@ -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
on BSD systems.
- GCC >= 4.7 (up to 16 tested). Older versions are no longer supported due to
- GCC >= 4.7 (up to 14 tested). Older versions are no longer supported due to
the latest mt_list update which only uses c11-like atomics. Newer versions
may sometimes break due to compiler regressions or behaviour changes. The
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
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
(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
@ -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
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.4. It is recommended to use
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,
and each of the branches above receives its own fixes, without forcing you to
@ -259,15 +259,11 @@ reported to work as well. While there are some efforts from the community to
ensure they work well, OpenSSL remains the primary target and this means that
in case of conflicting choices, OpenSSL support will be favored over other
options. Note that QUIC is not fully supported when haproxy is built with
OpenSSL < 3.5.2 version. In this case, QUICTLS or AWS-LC are the preferred
alternatives. As of writing this, the QuicTLS project follows OpenSSL very
closely and provides update simultaneously, but being a volunteer-driven
project, its long-term future does not look certain enough to convince
operating systems to package it, so it needs to be build locally. Recent
versions of AWS-LC (>= 1.22 and the FIPS branches) are pretty complete and
generally more performant than other OpenSSL derivatives, but may behave
slightly differently, particularly when dealing with outdated setups. See
the section about QUIC in this document.
OpenSSL. In this case, QUICTLS is the preferred alternative. As of writing
this, the QuicTLS project follows OpenSSL very closely and provides update
simultaneously, but being a volunteer-driven project, its long-term future does
not look certain enough to convince operating systems to package it, so it
needs to be build locally. See the section about QUIC in this document.
A fifth option is wolfSSL (https://github.com/wolfSSL/wolfssl). It is the only
supported alternative stack not based on OpenSSL, yet which implements almost
@ -426,9 +422,9 @@ Lua is an embedded programming language supported by HAProxy to provide more
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.
Some systems provide this library under various names to avoid conflicts with
previous versions. By default, HAProxy looks for "lua5.5", "lua55", "lua5.4",
"lua54", "lua5.3", "lua53", "lua". If your system uses a different naming, you
may need to set the library name in the "LUA_LIB_NAME" variable.
previous versions. By default, HAProxy looks for "lua5.4", "lua54", "lua5.3",
"lua53", "lua". If your system uses a different naming, you may need to set the
library name in the "LUA_LIB_NAME" variable.
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 :
@ -504,11 +500,10 @@ QUIC is the new transport layer protocol and is required for HTTP/3. This
protocol stack is currently supported as an experimental feature in haproxy on
the frontend side. In order to enable it, use "USE_QUIC=1 USE_OPENSSL=1".
Note that QUIC is not always fully supported by the OpenSSL library depending on
its version. Indeed QUIC 0-RTT cannot be supported by OpenSSL for versions before
3.5 contrary to others libraries with full QUIC support. The preferred option is
to use QUICTLS. This is a fork of OpenSSL with a QUIC-compatible API. Its
repository is available at this location:
Note that QUIC is not fully supported by the OpenSSL library. Indeed QUIC 0-RTT
cannot be supported by OpenSSL contrary to others libraries with full QUIC
support. The preferred option is to use QUICTLS. This is a fork of OpenSSL with
a QUIC-compatible API. Its repository is available at this location:
https://github.com/quictls/openssl
@ -536,18 +531,14 @@ way assuming that wolfSSL was installed in /opt/wolfssl-5.6.0 as shown in 4.5:
SSL_INC=/opt/wolfssl-5.6.0/include SSL_LIB=/opt/wolfssl-5.6.0/lib
LDFLAGS="-Wl,-rpath,/opt/wolfssl-5.6.0/lib"
As last resort, haproxy may be compiled against OpenSSL as follows from 3.5
version with 0-RTT support:
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1
or as follows for all OpenSSL versions but without O-RTT support:
As last resort, haproxy may be compiled against OpenSSL as follows:
$ make TARGET=generic USE_OPENSSL=1 USE_QUIC=1 USE_QUIC_OPENSSL_COMPAT=1
In addition to this requirements, the QUIC listener bindings must be explicitly
enabled with a specific QUIC tuning parameter. (see "limited-quic" global
parameter of haproxy Configuration Manual).
Note that QUIC 0-RTT is not supported by haproxy QUIC stack when built against
OpenSSL. In addition to this compilation requirements, the QUIC listener
bindings must be explicitly enabled with a specific QUIC tuning parameter.
(see "limited-quic" global parameter of haproxy Configuration Manual).
5) How to build HAProxy
@ -563,9 +554,9 @@ It goes into more details with the main options.
To build haproxy, you have to choose your target OS amongst the following ones
and assign it to the TARGET variable :
- linux-glibc for Linux kernel 4.17 and above
- linux-glibc for Linux kernel 2.6.28 and above
- linux-glibc-legacy for Linux kernel 2.6.28 and above without new features
- linux-musl for Linux kernel 4.17 and above with musl libc
- linux-musl for Linux kernel 2.6.28 and above with musl libc
- solaris for Solaris 10 and above
- freebsd for FreeBSD 10 and above
- dragonfly for DragonFlyBSD 4.3 and above

162
Makefile
View file

@ -35,7 +35,6 @@
# USE_OPENSSL : enable use of OpenSSL. Recommended, but see below.
# USE_OPENSSL_AWSLC : enable use of AWS-LC
# USE_OPENSSL_WOLFSSL : enable use of wolfSSL with the OpenSSL API
# USE_ECH : enable use of ECH with the OpenSSL API
# USE_QUIC : enable use of QUIC with the quictls API (quictls, libressl, boringssl)
# USE_QUIC_OPENSSL_COMPAT : enable use of QUIC with the standard openssl API (limited features)
# USE_ENGINE : enable use of OpenSSL Engine.
@ -44,7 +43,6 @@
# USE_CLOSEFROM : enable use of closefrom() on *bsd, solaris. Automatic.
# USE_PRCTL : enable use of prctl(). 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_SLZ : enable slz library instead of zlib (default=enabled)
# USE_CPU_AFFINITY : enable pinning processes to CPU on Linux. Automatic.
@ -64,8 +62,6 @@
# USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only.
# USE_LIBATOMIC : force to link with/without libatomic. Automatic.
# USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours
# USE_SHM_OPEN : use shm_open() for features that can make use of shared memory
# USE_KTLS : use kTLS.(requires at least Linux 4.17).
#
# Options can be forced by specifying "USE_xxx=1" or can be disabled by using
# "USE_xxx=" (empty string). The list of enabled and disabled options for a
@ -94,7 +90,6 @@
# SILENT_DEFINE may be used to specify other defines which will not be
# reported by "haproxy -vv".
# 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.
# It might be useful to set DESTDIR if you want to install haproxy
# in a sandbox.
@ -124,7 +119,7 @@
# LUA_LIB : force the lib path to lua
# LUA_INC : force the include path to lua
# 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
@ -216,8 +211,7 @@ UNIT_TEST_SCRIPT=./scripts/run-unittests.sh
# undefined behavior to silently produce invalid code. For this reason we have
# to use -fwrapv or -fno-strict-overflow to guarantee the intended behavior.
# It is preferable not to change this option in order to avoid breakage.
STD_CFLAGS := $(call cc-opt-alt,-fwrapv,-fno-strict-overflow) \
$(call cc-opt,-fvect-cost-model=very-cheap)
STD_CFLAGS := $(call cc-opt-alt,-fwrapv,-fno-strict-overflow)
#### Compiler-specific flags to enable certain classes of warnings.
# Some are hard-coded, others are enabled only if supported.
@ -344,16 +338,14 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \
USE_TPROXY USE_LINUX_TPROXY USE_LINUX_CAP \
USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \
USE_GETADDRINFO USE_OPENSSL USE_OPENSSL_WOLFSSL USE_OPENSSL_AWSLC \
USE_ECH USE_TRACE \
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_MATH USE_DEVICEATLAS USE_51DEGREES \
USE_WURFL USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL \
USE_THREAD_DUMP USE_EVPORTS USE_OT USE_QUIC USE_PROMEX \
USE_MEMORY_PROFILING USE_SHM_OPEN \
USE_MEMORY_PROFILING \
USE_STATIC_PCRE USE_STATIC_PCRE2 \
USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT \
USE_QUIC_OPENSSL_COMPAT USE_KTLS
USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT USE_QUIC_OPENSSL_COMPAT
# preset all variables for all supported build options among use_opts
$(reset_opts_vars)
@ -367,9 +359,6 @@ $(warn_unknown_options)
# on the make command line.
USE_POLL = default
# traces are always enabled
USE_TRACE = default
# SLZ is always supported unless explicitly disabled by passing USE_SLZ=""
# or disabled by enabling ZLIB using USE_ZLIB=1
ifeq ($(USE_ZLIB:0=),)
@ -387,13 +376,13 @@ ifeq ($(TARGET),haiku)
set_target_defaults = $(call default_opts,USE_POLL USE_TPROXY USE_OBSOLETE_LINKER)
endif
# For linux >= 4.17 and glibc
# For linux >= 2.6.28 and glibc
ifeq ($(TARGET),linux-glibc)
set_target_defaults = $(call default_opts, \
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
USE_GETADDRINFO USE_BACKTRACE USE_SHM_OPEN USE_KTLS)
USE_GETADDRINFO USE_BACKTRACE)
INSTALL = install -v
endif
@ -406,13 +395,13 @@ ifeq ($(TARGET),linux-glibc-legacy)
INSTALL = install -v
endif
# For linux >= 4.17 and musl
# For linux >= 2.6.28 and musl
ifeq ($(TARGET),linux-musl)
set_target_defaults = $(call default_opts, \
USE_POLL USE_TPROXY USE_LIBCRYPT USE_DL USE_RT USE_CRYPT_H USE_NETFILTER \
USE_CPU_AFFINITY USE_THREAD USE_EPOLL USE_LINUX_TPROXY USE_LINUX_CAP \
USE_ACCEPT4 USE_LINUX_SPLICE USE_PRCTL USE_THREAD_DUMP USE_NS USE_TFO \
USE_GETADDRINFO USE_BACKTRACE USE_SHM_OPEN USE_KTLS)
USE_GETADDRINFO USE_BACKTRACE)
INSTALL = install -v
endif
@ -606,10 +595,6 @@ ifneq ($(USE_BACKTRACE:0=),)
BACKTRACE_CFLAGS = -fno-omit-frame-pointer
endif
ifneq ($(USE_MEMORY_PROFILING:0=),)
MEMORY_PROFILING_CFLAGS = -fno-optimize-sibling-calls
endif
ifneq ($(USE_CPU_AFFINITY:0=),)
OPTIONS_OBJS += src/cpuset.o
OPTIONS_OBJS += src/cpu_topo.o
@ -648,7 +633,7 @@ ifneq ($(USE_OPENSSL:0=),)
OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o \
src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o \
src/ssl_utils.o src/jwt.o src/ssl_clienthello.o src/jws.o src/acme.o \
src/acme_resolvers.o src/ssl_trace.o src/jwe.o
src/ssl_trace.o
endif
ifneq ($(USE_ENGINE:0=),)
@ -671,12 +656,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/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/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/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/quic_enc.o src/qcm_qmux.o src/xprt_qmux.o \
src/mpring.o
src/cbuf.o src/quic_enc.o
endif
ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),)
@ -684,15 +668,15 @@ OPTIONS_OBJS += src/quic_openssl_compat.o
endif
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;)
LUA_INC := $(firstword $(foreach lib,lua5.5 lua55 lua5.4 lua54 lua5.3 lua53 lua,$(call check_lua_inc,$(lib),"/usr/include/")))
check_lua_inc = $(shell if [ -d $(2)$(1) ]; then echo $(2)$(1); fi;)
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))
# Try to automatically detect the Lua library if not set
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
# Lua lib name must be set now (forced/detected above)
@ -722,15 +706,70 @@ ifneq ($(USE_PROMEX:0=),)
endif
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
# 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=),)
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
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
ifneq ($(USE_PCRE:0=)$(USE_STATIC_PCRE:0=)$(USE_PCRE_JIT:0=),)
@ -810,14 +849,9 @@ ifneq ($(USE_LINUX_CAP:0=),)
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),)
include $(addsuffix /Makefile.mk,$(EXTRA_MAKE))
endif
# better keep this one close to the end, as several libs above may need it
ifneq ($(USE_DL:0=),)
DL_LDFLAGS = -ldl
@ -912,7 +946,6 @@ endif # obsolete targets
endif # TARGET
OBJS =
HATERM_OBJS =
ifneq ($(EXTRA_OBJS),)
OBJS += $(EXTRA_OBJS)
@ -926,15 +959,15 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
src/cache.o src/stconn.o src/http_htx.o src/debug.o \
src/check.o src/stats-html.o src/haproxy.o src/listener.o \
src/applet.o src/pattern.o src/cfgparse-listen.o \
src/flt_spoe.o src/cebis_tree.o src/http_ext.o \
src/http_act.o src/http_fetch.o src/cebs_tree.o \
src/cebib_tree.o src/http_client.o src/dns.o \
src/cebb_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
src/flt_spoe.o src/cebuis_tree.o src/http_ext.o \
src/http_act.o src/http_fetch.o src/cebus_tree.o \
src/cebuib_tree.o src/http_client.o src/dns.o \
src/cebub_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
src/trace.o src/stats-proxy.o src/pool.o src/stats.o \
src/cfgparse-global.o src/filters.o src/mux_pt.o \
src/flt_http_comp.o src/sock.o src/h1.o src/sink.o \
src/ceba_tree.o src/session.o src/payload.o src/htx.o \
src/cebl_tree.o src/ceb32_tree.o src/ceb64_tree.o \
src/cebua_tree.o src/session.o src/payload.o src/htx.o \
src/cebul_tree.o src/cebu32_tree.o src/cebu64_tree.o \
src/server_state.o src/proto_rhttp.o src/flt_trace.o src/fd.o \
src/task.o src/map.o src/fcgi-app.o src/h2.o src/mworker.o \
src/tcp_sample.o src/mjson.o src/h1_htx.o src/tcp_act.o \
@ -949,9 +982,9 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
src/cfgcond.o src/proto_udp.o src/lb_fwlc.o src/ebmbtree.o \
src/proto_uxdg.o src/cfgdiag.o src/sock_unix.o src/sha1.o \
src/lb_fas.o src/clock.o src/sock_inet.o src/ev_select.o \
src/lb_map.o src/shctx.o src/hpack-dec.o src/net_helper.o \
src/lb_map.o src/shctx.o src/mworker-prog.o src/hpack-dec.o \
src/arg.o src/signal.o src/fix.o src/dynbuf.o src/guid.o \
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o src/counters.o \
src/cfgparse-tcp.o src/lb_ss.o src/chunk.o \
src/cfgparse-unix.o src/regex.o src/fcgi.o src/uri_auth.o \
src/eb64tree.o src/eb32tree.o src/eb32sctree.o src/lru.o \
src/limits.o src/ebimtree.o src/wdt.o src/hpack-tbl.o \
@ -959,15 +992,12 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \
src/ebsttree.o src/freq_ctr.o src/systemd.o src/init.o \
src/http_acl.o src/dict.o src/dgram.o src/pipe.o \
src/hpack-huff.o src/hpack-enc.o src/ebtree.o src/hash.o \
src/httpclient_cli.o src/version.o src/ncbmbuf.o src/ech.o \
src/cfgparse-peers.o src/haterm.o
src/version.o
ifneq ($(TRACE),)
OBJS += src/calltrace.o
endif
HATERM_OBJS += $(OBJS) src/haterm_init.o
# Used only for forced dependency checking. May be cleared during development.
INCLUDES = $(wildcard include/*/*.h)
DEP = $(INCLUDES) .build_opts
@ -999,11 +1029,11 @@ IGNORE_OPTS=help install install-man install-doc install-bin \
uninstall clean tags cscope tar git-tar version update-version \
opts reg-tests reg-tests-help unit-tests admin/halog/halog dev/flags/flags \
dev/haring/haring dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop \
dev/term_events/term_events dev/gdb/pm-from-core dev/gdb/libs-from-core
dev/term_events/term_events
ifneq ($(TARGET),)
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)
else
.build_opts:
@ -1015,9 +1045,6 @@ endif # non-empty target
haproxy: $(OPTIONS_OBJS) $(OBJS)
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
haterm: $(OPTIONS_OBJS) $(HATERM_OBJS)
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
objsize: haproxy
$(Q)objdump -t $^|grep ' g '|grep -F '.text'|awk '{print $$5 FS $$6}'|sort
@ -1033,12 +1060,6 @@ admin/dyncookie/dyncookie: admin/dyncookie/dyncookie.o
dev/flags/flags: dev/flags/flags.o
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
dev/gdb/libs-from-core: dev/gdb/libs-from-core.o
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
dev/gdb/pm-from-core: dev/gdb/pm-from-core.o
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
dev/haring/haring: dev/haring/haring.o
$(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
@ -1092,11 +1113,6 @@ install-doc:
$(INSTALL) -m 644 doc/$$x.txt "$(DESTDIR)$(DOCDIR)" ; \
done
install-admin:
$(Q)$(INSTALL) -d "$(DESTDIR)$(SBINDIR)"
$(Q)$(INSTALL) admin/cli/haproxy-dump-certs "$(DESTDIR)$(SBINDIR)"
$(Q)$(INSTALL) admin/cli/haproxy-reload "$(DESTDIR)$(SBINDIR)"
install-bin:
$(Q)for i in haproxy $(EXTRA); do \
if ! [ -e "$$i" ]; then \
@ -1107,7 +1123,7 @@ install-bin:
$(Q)$(INSTALL) -d "$(DESTDIR)$(SBINDIR)"
$(Q)$(INSTALL) haproxy $(EXTRA) "$(DESTDIR)$(SBINDIR)"
install: install-bin install-admin install-man install-doc
install: install-bin install-man install-doc
uninstall:
$(Q)rm -f "$(DESTDIR)$(MANDIR)"/man1/haproxy.1
@ -1118,7 +1134,7 @@ uninstall:
$(Q)rm -f "$(DESTDIR)$(SBINDIR)"/haproxy
clean:
$(Q)rm -f *.[oas] src/*.[oas] haproxy haterm test .build_opts .build_opts.new
$(Q)rm -f *.[oas] src/*.[oas] haproxy test .build_opts .build_opts.new
$(Q)for dir in . src dev/* admin/* addons/* include/* doc; do rm -f $$dir/*~ $$dir/*.rej $$dir/core; done
$(Q)rm -f haproxy-$(VERSION).tar.gz haproxy-$(VERSION)$(SUBVERS)$(EXTRAVERSION).tar.gz
$(Q)rm -f haproxy-$(VERSION) haproxy-$(VERSION)$(SUBVERS)$(EXTRAVERSION) nohup.out gmon.out
@ -1137,7 +1153,7 @@ distclean: clean
$(Q)rm -f admin/dyncookie/dyncookie
$(Q)rm -f dev/haring/haring dev/ncpu/ncpu{,.so} dev/poll/poll dev/tcploop/tcploop
$(Q)rm -f dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
$(Q)rm -f dev/qpack/decode dev/gdb/pm-from-core dev/gdb/libs-from-core
$(Q)rm -f dev/qpack/decode
tags:
$(Q)find src include \( -name '*.c' -o -name '*.h' \) -print0 | \
@ -1264,8 +1280,6 @@ unit-tests:
# options for all commits within RANGE. RANGE may be either a git range
# such as ref1..ref2 or a single commit, in which case all commits from
# the master branch to this one will be tested.
# Will execute TEST_CMD for each commit if defined, and will stop in case of
# failure.
range:
$(Q)[ -d .git/. ] || { echo "## Fatal: \"make $@\" may only be used inside a Git repository."; exit 1; }
@ -1291,8 +1305,6 @@ range:
echo "[ $$index/$$count ] $$commit #############################"; \
git checkout -q $$commit || die 1; \
$(MAKE) all || die 1; \
set -- $(TEST_CMD); \
[ "$$#" -eq 0 ] || "$$@" || die 1; \
index=$$((index + 1)); \
done; \
echo;echo "Done! $${count} commit(s) built successfully for RANGE $${RANGE}" ; \

View file

@ -1,10 +1,11 @@
# HAProxy
[![alpine/musl](https://github.com/haproxy/haproxy/actions/workflows/musl.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/musl.yml)
[![AWS-LC](https://github.com/haproxy/haproxy/actions/workflows/aws-lc.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/aws-lc.yml)
[![openssl no-deprecated](https://github.com/haproxy/haproxy/actions/workflows/openssl-nodeprecated.yml/badge.svg)](https://github.com/haproxy/haproxy/actions/workflows/openssl-nodeprecated.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)
[![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)
![HAProxy logo](doc/HAProxyCommunityEdition_60px.png)

View file

@ -1,2 +1,2 @@
$Format:%ci$
2026/06/03
2025/05/14

View file

@ -1 +1 @@
3.5-dev0
3.2-dev16

View file

@ -127,16 +127,7 @@ static int _51d_property_name_list(char **args, int section_type, struct proxy *
while (*(args[cur_arg])) {
name = calloc(1, sizeof(*name));
if (!name) {
memprintf(err, "'%s' failed to allocate memory.", args[0]);
return -1;
}
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);
++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)
{
struct buffer *temp = get_trash_chunk();
struct channel *chn;
struct htx *htx;
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)) {
(offsets->firstOffset + offsets->size)->httpHeaderOffset = *(global_51degrees.header_offsets + i);
/* Copy value into trash and NUL-terminate before passing to the
* 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->firstOffset + offsets->size)->deviceOffset = fiftyoneDegreesGetDeviceOffset(&global_51degrees.data_set, ctx.value.ptr);
offsets->size++;
}
}
@ -568,8 +550,6 @@ static void _51d_process_match(const struct arg *args, struct sample *smp)
char valuesBuffer[1024];
#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 */
struct buffer *temp = get_trash_chunk();
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.data = temp->data;
}
#endif
/* Sets the sample data as a constant string. This ensures that the
* string will be processed correctly.
@ -937,10 +916,6 @@ static int init_51degrees(void)
list_for_each_entry(name, &global_51degrees.property_names, list)
++i;
_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;
list_for_each_entry(name, &global_51degrees.property_names, list)
@ -1075,7 +1050,7 @@ static int init_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)
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

@ -40,7 +40,8 @@
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
typedef int bool;
enum { false, true };
typedef unsigned char byte;

View file

@ -1,19 +1,11 @@
# 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++
CXXLIB := -lstdc++
ifeq ($(DEVICEATLAS_SRC),)
OPTIONS_CFLAGS += -I$(DEVICEATLAS_INC)
OPTIONS_LDFLAGS += -Wl,-rpath,$(DEVICEATLAS_LIB) -L$(DEVICEATLAS_LIB) -lda
OPTIONS_LDFLAGS += -lda
else
DEVICEATLAS_INC = $(DEVICEATLAS_SRC)
DEVICEATLAS_LIB = $(DEVICEATLAS_SRC)
@ -35,7 +27,5 @@ OPTIONS_OBJS += $(DEVICEATLAS_SRC)/dadwcurl.o
OPTIONS_OBJS += $(DEVICEATLAS_SRC)/Os/daunix.o
endif
OPTIONS_OBJS += addons/deviceatlas/da.o
addons/deviceatlas/dummy/%.o: addons/deviceatlas/dummy/%.cpp
$(cmd_CXX) $(CXXFLAGS) -c -o $@ $<

View file

@ -31,7 +31,6 @@ static struct {
da_atlas_t atlas;
da_evidence_id_t useragentid;
da_severity_t loglevel;
size_t maxhdrlen;
char separator;
unsigned char daset:1;
} global_deviceatlas = {
@ -43,7 +42,6 @@ static struct {
.atlasmap = NULL,
.atlasfd = -1,
.useragentid = 0,
.maxhdrlen = 0,
.daset = 0,
.separator = '|',
};
@ -59,10 +57,6 @@ static int da_json_file(char **args, int section_type, struct proxy *curpx,
return -1;
}
global_deviceatlas.jsonpath = strdup(args[1]);
if (unlikely(global_deviceatlas.jsonpath == NULL)) {
memprintf(err, "deviceatlas json file : out of memory.\n");
return -1;
}
return 0;
}
@ -79,7 +73,6 @@ static int da_log_level(char **args, int section_type, struct proxy *curpx,
loglevel = atol(args[1]);
if (loglevel < 0 || loglevel > 3) {
memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]);
return -1;
} else {
global_deviceatlas.loglevel = (da_severity_t)loglevel;
}
@ -108,10 +101,6 @@ static int da_properties_cookie(char **args, int section_type, struct proxy *cur
return -1;
} else {
global_deviceatlas.cookiename = strdup(args[1]);
if (unlikely(global_deviceatlas.cookiename == NULL)) {
memprintf(err, "deviceatlas cookie name : out of memory.\n");
return -1;
}
}
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
return 0;
@ -130,7 +119,6 @@ static int da_cache_size(char **args, int section_type, struct proxy *curpx,
cachesize = atol(args[1]);
if (cachesize < 0 || cachesize > DA_CACHE_MAX) {
memprintf(err, "deviceatlas cache size : expects a cache size between 0 and %d, %s given.\n", DA_CACHE_MAX, args[1]);
return -1;
} else {
#ifdef APINOCACHE
fprintf(stdout, "deviceatlas cache size : no-op, its support is disabled.\n");
@ -177,7 +165,7 @@ static int init_deviceatlas(void)
da_status_t status;
jsonp = fopen(global_deviceatlas.jsonpath, "r");
if (unlikely(jsonp == 0)) {
if (jsonp == 0) {
ha_alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
global_deviceatlas.jsonpath);
err_code |= ERR_ALERT | ERR_FATAL;
@ -189,11 +177,9 @@ static int init_deviceatlas(void)
status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
&global_deviceatlas.atlasimgptr, &atlasimglen);
fclose(jsonp);
if (unlikely(status != DA_OK)) {
if (status != DA_OK) {
ha_alert("deviceatlas : '%s' json file is invalid.\n",
global_deviceatlas.jsonpath);
free(global_deviceatlas.atlasimgptr);
da_fini();
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
@ -201,10 +187,8 @@ static int init_deviceatlas(void)
status = da_atlas_open(&global_deviceatlas.atlas, extraprops,
global_deviceatlas.atlasimgptr, atlasimglen);
if (unlikely(status != DA_OK)) {
if (status != DA_OK) {
ha_alert("deviceatlas : data could not be compiled.\n");
free(global_deviceatlas.atlasimgptr);
da_fini();
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
@ -213,28 +197,11 @@ static int init_deviceatlas(void)
if (global_deviceatlas.cookiename == 0) {
global_deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT);
if (unlikely(global_deviceatlas.cookiename == NULL)) {
ha_alert("deviceatlas : out of memory.\n");
da_atlas_close(&global_deviceatlas.atlas);
free(global_deviceatlas.atlasimgptr);
da_fini();
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
}
global_deviceatlas.useragentid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
"user-agent");
{
size_t hi;
global_deviceatlas.maxhdrlen = 16;
for (hi = 0; hi < global_deviceatlas.atlas.header_evidence_count; hi++) {
size_t nl = strlen(global_deviceatlas.atlas.header_priorities[hi].name);
if (nl > global_deviceatlas.maxhdrlen)
global_deviceatlas.maxhdrlen = nl;
}
}
if ((global_deviceatlas.atlasfd = shm_open(ATLASMAPNM, O_RDWR, 0660)) != -1) {
global_deviceatlas.atlasmap = mmap(NULL, ATLASTOKSZ, PROT_READ | PROT_WRITE, MAP_SHARED, global_deviceatlas.atlasfd, 0);
if (global_deviceatlas.atlasmap == MAP_FAILED) {
@ -264,22 +231,24 @@ static void deinit_deviceatlas(void)
free(global_deviceatlas.cookiename);
da_atlas_close(&global_deviceatlas.atlas);
free(global_deviceatlas.atlasimgptr);
da_fini();
}
if (global_deviceatlas.atlasfd != -1) {
munmap(global_deviceatlas.atlasmap, ATLASTOKSZ);
close(global_deviceatlas.atlasfd);
shm_unlink(ATLASMAPNM);
}
da_fini();
}
static void da_haproxy_checkinst(void)
{
if (global_deviceatlas.atlasmap != 0) {
char *base;
base = (char *)global_deviceatlas.atlasmap;
char *base;
base = (char *)global_deviceatlas.atlasmap;
if (base[0] != 0) {
if (base[0] != 0) {
FILE *jsonp;
void *cnew;
da_status_t status;
@ -289,10 +258,6 @@ static void da_haproxy_checkinst(void)
da_property_decl_t extraprops[1] = {{NULL, 0}};
#ifdef USE_THREAD
HA_SPIN_LOCK(OTHER_LOCK, &dadwsch_lock);
if (base[0] == 0) {
HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock);
return;
}
#endif
strlcpy2(atlasp, base + sizeof(char), sizeof(atlasp));
jsonp = fopen(atlasp, "r");
@ -310,20 +275,10 @@ static void da_haproxy_checkinst(void)
fclose(jsonp);
if (status == DA_OK) {
if (da_atlas_open(&inst, extraprops, cnew, atlassz) == DA_OK) {
inst.config.cache_size = global_deviceatlas.cachesize;
da_atlas_close(&global_deviceatlas.atlas);
free(global_deviceatlas.atlasimgptr);
global_deviceatlas.atlasimgptr = cnew;
global_deviceatlas.atlas = inst;
{
size_t hi;
global_deviceatlas.maxhdrlen = 16;
for (hi = 0; hi < inst.header_evidence_count; hi++) {
size_t nl = strlen(inst.header_priorities[hi].name);
if (nl > global_deviceatlas.maxhdrlen)
global_deviceatlas.maxhdrlen = nl;
}
}
base[0] = 0;
ha_notice("deviceatlas : new instance, data file date `%s`.\n",
da_getdatacreationiso8601(&global_deviceatlas.atlas));
@ -331,8 +286,6 @@ static void da_haproxy_checkinst(void)
ha_alert("deviceatlas : instance update failed.\n");
free(cnew);
}
} else {
free(cnew);
}
#ifdef USE_THREAD
HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock);
@ -344,7 +297,7 @@ static void da_haproxy_checkinst(void)
static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo)
{
struct buffer *tmp;
da_propid_t prop;
da_propid_t prop, *pprop;
da_status_t status;
da_type_t proptype;
const char *propname;
@ -364,15 +317,13 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
chunk_appendf(tmp, "%c", global_deviceatlas.separator);
continue;
}
if (unlikely(da_atlas_getproptype(&global_deviceatlas.atlas, prop, &proptype) != DA_OK)) {
chunk_appendf(tmp, "%c", global_deviceatlas.separator);
continue;
}
pprop = &prop;
da_atlas_getproptype(&global_deviceatlas.atlas, *pprop, &proptype);
switch (proptype) {
case DA_TYPE_BOOLEAN: {
bool val;
status = da_getpropboolean(devinfo, prop, &val);
status = da_getpropboolean(devinfo, *pprop, &val);
if (status == DA_OK) {
chunk_appendf(tmp, "%d", val);
}
@ -381,7 +332,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
case DA_TYPE_INTEGER:
case DA_TYPE_NUMBER: {
long val;
status = da_getpropinteger(devinfo, prop, &val);
status = da_getpropinteger(devinfo, *pprop, &val);
if (status == DA_OK) {
chunk_appendf(tmp, "%ld", val);
}
@ -389,7 +340,7 @@ static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_
}
case DA_TYPE_STRING: {
const char *val;
status = da_getpropstring(devinfo, prop, &val);
status = da_getpropstring(devinfo, *pprop, &val);
if (status == DA_OK) {
chunk_appendf(tmp, "%s", val);
}
@ -420,26 +371,29 @@ static int da_haproxy_conv(const struct arg *args, struct sample *smp, void *pri
{
da_deviceinfo_t devinfo;
da_status_t status;
char useragentbuf[1024];
const char *useragent;
char useragentbuf[1024] = { 0 };
int i;
if (unlikely(global_deviceatlas.daset == 0) || smp->data.u.str.data == 0) {
if (global_deviceatlas.daset == 0 || smp->data.u.str.data == 0) {
return 1;
}
da_haproxy_checkinst();
i = smp->data.u.str.data > sizeof(useragentbuf) - 1 ? sizeof(useragentbuf) - 1 : smp->data.u.str.data;
memcpy(useragentbuf, smp->data.u.str.area, i);
useragentbuf[i] = 0;
i = smp->data.u.str.data > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.data;
memcpy(useragentbuf, smp->data.u.str.area, i - 1);
useragentbuf[i - 1] = 0;
useragent = (const char *)useragentbuf;
status = da_search(&global_deviceatlas.atlas, &devinfo,
global_deviceatlas.useragentid, useragentbuf, 0);
global_deviceatlas.useragentid, useragent, 0);
return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
}
#define DA_MAX_HEADERS 32
#define DA_MAX_HEADERS 24
static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
@ -449,10 +403,10 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
struct channel *chn;
struct htx *htx;
struct htx_blk *blk;
char vbuf[DA_MAX_HEADERS][1024];
char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }};
int i, nbh = 0;
if (unlikely(global_deviceatlas.daset == 0)) {
if (global_deviceatlas.daset == 0) {
return 0;
}
@ -460,17 +414,18 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
chn = (smp->strm ? &smp->strm->req : NULL);
htx = smp_prefetch_htx(smp, chn, NULL, 1);
if (unlikely(!htx))
if (!htx)
return 0;
i = 0;
for (blk = htx_get_first_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) {
size_t vlen;
char *pval;
da_evidence_id_t evid;
enum htx_blk_type type;
struct ist n, v;
char hbuf[64];
char tval[1024];
char hbuf[24] = { 0 };
char tval[1024] = { 0 };
type = htx_get_blk_type(blk);
@ -483,18 +438,20 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
continue;
}
if (n.len > global_deviceatlas.maxhdrlen || n.len >= sizeof(hbuf)) {
/* The HTTP headers used by the DeviceAtlas API are not longer */
if (n.len >= sizeof(hbuf)) {
continue;
}
memcpy(hbuf, n.ptr, n.len);
hbuf[n.len] = 0;
pval = v.ptr;
vlen = v.len;
evid = -1;
i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len;
memcpy(tval, v.ptr, i);
tval[i] = 0;
pval = tval;
vlen = i;
if (strcasecmp(hbuf, "Accept-Language") == 0) {
evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas);
@ -512,7 +469,7 @@ static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const ch
continue;
}
vlen = pl;
vlen -= global_deviceatlas.cookienamelen - 1;
pval = p;
evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
} else {

View file

@ -141,11 +141,6 @@ enum {
DA_INITIAL_MEMORY_ESTIMATE = 1024 * 1024 * 14
};
struct header_evidence_entry {
const char *name;
da_evidence_id_t id;
};
struct da_config {
unsigned int cache_size;
unsigned int __reserved[15]; /* enough reserved keywords for future use */

View file

@ -70,4 +70,4 @@ OPTIONS_OBJS += \
addons/ot/src/vars.o
endif
OT_CFLAGS := $(OT_CFLAGS) $(OT_DEFINE)
OT_CFLAGS := $(OT_CFLAGS) -Iaddons/ot/include $(OT_DEFINE)

View file

@ -48,12 +48,13 @@ 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:
itself is no longer maintained nor supported by its authors. A
replacement filter base on OpenTelemetry is currently under development
and is expected to be ready around HAProxy 3.2. As such OpenTracing will
be deprecated in 3.3 and removed in 3.5.
https://github.com/haproxytech/haproxy-opentelemetry/
The OT filter was primarily tested with the Jaeger tracer, while configurations
for both Datadog and Zipkin tracers were also set in the test directory.
The OT filter is a standard HAProxy filter, so what applies to others also
applies to this one (of course, by that I mean what is described in the

View file

@ -35,11 +35,11 @@
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__); \
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_FUNC(f, ...) do { FLT_OT_DBG(1, "%s(" f ") {", __func__, ##__VA_ARGS__); dbg_indent_level += 3; } while (0)
# define FLT_OT_RETURN(a) do { dbg_indent_level -= 3; FLT_OT_DBG(1, "}"); return a; } while (0)
# define FLT_OT_RETURN_EX(a,t,f) do { 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
@ -54,7 +54,7 @@ struct flt_ot_debug {
};
extern THREAD_LOCAL int flt_ot_dbg_indent_level;
extern THREAD_LOCAL int dbg_indent_level;
extern struct flt_ot_debug flt_ot_debug;
#else

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
/***

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
/***

View file

@ -17,7 +17,7 @@
* 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"
#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 },

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
/*
@ -155,18 +155,12 @@ static void flt_ot_return_void(const struct filter *f, char **err)
*/
static int flt_ot_init(struct proxy *p, struct flt_conf *fconf)
{
static int warnings_emitted = 0;
struct flt_ot_conf *conf = FLT_OT_DEREF(fconf, conf, NULL);
char *err = NULL;
int retval = FLT_OT_RET_ERROR;
FLT_OT_FUNC("%p, %p", p, fconf);
if (!warnings_emitted && !deprecated_directives_allowed) {
warnings_emitted++;
ha_warning("The opentracing filter was deprecated in haproxy 3.3 and will be removed in 3.5.\n");
}
if (conf == NULL)
FLT_OT_RETURN_INT(retval);

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
#define FLT_OT_GROUP_DEF(a,b,c) { a, b, c },

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
#ifdef DEBUG_OT
@ -261,7 +261,7 @@ int flt_ot_http_header_set(struct channel *chn, const char *prefix, const char *
if (value == NULL) {
/* Do nothing. */
}
else if (http_add_header(htx, ist_name, ist(value), 1) == 1) {
else if (http_add_header(htx, ist_name, ist(value)) == 1) {
retval = 0;
FLT_OT_DBG(3, "HTTP header '%s: %s' added", ist_name.ptr, value);

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
static struct pool_head *pool_head_ot_span_context __read_mostly = NULL;

View file

@ -17,12 +17,12 @@
* 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"
#include "include.h"
#ifdef DEBUG_OT
struct flt_ot_debug flt_ot_debug;
THREAD_LOCAL int flt_ot_dbg_indent_level = 0;
THREAD_LOCAL int dbg_indent_level = 0;
#endif
#ifdef OTC_DBG_MEM
@ -359,7 +359,8 @@ static int flt_ot_parse_cfg_sample(const char *file, int linenum, char **args, s
*/
static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, struct list *head, char **err)
{
int i, retval = ERR_NONE;
struct flt_ot_conf_str *str = NULL;
int i, retval = ERR_NONE;
FLT_OT_FUNC("\"%s\", %d, %p, %p, %p:%p", file, linenum, args, head, FLT_OT_DPTR_ARGS(err));
@ -367,6 +368,9 @@ static int flt_ot_parse_cfg_str(const char *file, int linenum, char **args, stru
if (flt_ot_conf_str_init(args[i], linenum, head, err) == NULL)
retval |= ERR_ABORT | ERR_ALERT;
if (retval & ERR_CODE)
flt_ot_conf_str_free(&str);
FLT_OT_RETURN_INT(retval);
}
@ -640,7 +644,7 @@ static int flt_ot_parse_cfg_group(const char *file, int linenum, char **args, in
if (pdata->keyword == FLT_OT_PARSE_GROUP_ID) {
flt_ot_current_group = flt_ot_conf_group_init(args[1], linenum, &(flt_ot_current_config->groups), &err);
if (flt_ot_current_group == NULL)
if (flt_ot_current_config == NULL)
retval |= ERR_ABORT | ERR_ALERT;
}
else if (pdata->keyword == FLT_OT_PARSE_GROUP_SCOPES) {

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
/***

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
static struct pool_head *pool_head_ot_scope_span __read_mostly = NULL;

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
#ifdef DEBUG_OT
@ -41,7 +41,7 @@ void flt_ot_args_dump(char **args)
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]);
(void)fprintf(stderr, FLT_OT_DBG_FMT("%.*sargs[%d]: { '%s' "), dbg_indent_level, FLT_OT_DBG_INDENT, argc, args[0]);
for (i = 1; i < argc; i++)
(void)fprintf(stderr, "'%s' ", args[i]);

View file

@ -17,7 +17,7 @@
* 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"
#include "include.h"
#ifdef DEBUG_OT
@ -46,10 +46,10 @@ static void flt_ot_vars_scope_dump(struct vars *vars, const char *scope)
vars_rdlock(vars);
for (i = 0; i < VAR_NAME_ROOTS; i++) {
struct ceb_node *node = cebu64_imm_first(&(vars->name_root[i]));
struct ceb_node *node = cebu64_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);
for ( ; node != NULL; node = cebu64_next(&(vars->name_root[i]), node)) {
struct var *var = container_of(node, struct var, 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)));
}

View file

@ -389,9 +389,6 @@ listed below. Metrics from extra counters are not listed.
| haproxy_server_max_connect_time_seconds |
| haproxy_server_max_response_time_seconds |
| haproxy_server_max_total_time_seconds |
| haproxy_server_agent_status |
| haproxy_server_agent_code |
| haproxy_server_agent_duration_seconds |
| haproxy_server_internal_errors_total |
| haproxy_server_unsafe_idle_connections_current |
| haproxy_server_safe_idle_connections_current |
@ -407,7 +404,6 @@ listed below. Metrics from extra counters are not listed.
+----------------------------------------------------+
| haproxy_sticktable_size |
| haproxy_sticktable_used |
| haproxy_sticktable_local_updates |
+----------------------------------------------------+
* Resolvers metrics

View file

@ -32,7 +32,7 @@
/* Prometheus exporter flags (ctx->flags) */
#define PROMEX_FL_METRIC_HDR 0x00000001
#define PROMEX_FL_BODYLESS_RESP 0x00000002
/* unused: 0x00000002 */
/* unused: 0x00000004 */
/* unused: 0x00000008 */
/* unused: 0x00000010 */

View file

@ -36,7 +36,6 @@
#include <haproxy/stats.h>
#include <haproxy/stconn.h>
#include <haproxy/stream.h>
#include <haproxy/stress.h>
#include <haproxy/task.h>
#include <haproxy/tools.h>
#include <haproxy/version.h>
@ -83,8 +82,6 @@ struct promex_ctx {
unsigned field_num; /* current field number (ST_I_PX_* etc) */
unsigned mod_field_num; /* first field number of the current module (ST_I_PX_* etc) */
int obj_state; /* current state among PROMEX_{FRONT|BACK|SRV|LI}_STATE_* */
struct watcher px_watch; /* watcher to automatically update next pointer */
struct watcher srv_watch; /* watcher to automatically update next pointer */
struct list modules; /* list of promex modules to export */
struct eb_root filters; /* list of filters to apply on metrics name */
};
@ -176,8 +173,6 @@ const struct ist promex_st_metric_desc[ST_I_PX_MAX] = {
[ST_I_PX_CTIME] = IST("Avg. connect time for last 1024 successful connections."),
[ST_I_PX_RTIME] = IST("Avg. response time for last 1024 successful connections."),
[ST_I_PX_TTIME] = IST("Avg. total time for last 1024 successful connections."),
[ST_I_PX_AGENT_STATUS] = IST("Status of last agent check, per state label value."),
[ST_I_PX_AGENT_DURATION] = IST("Total duration of the latest server agent check, in seconds."),
[ST_I_PX_QT_MAX] = IST("Maximum observed time spent in the queue"),
[ST_I_PX_CT_MAX] = IST("Maximum observed time spent waiting for a connection to complete"),
[ST_I_PX_RT_MAX] = IST("Maximum observed time spent waiting for a server response"),
@ -245,8 +240,8 @@ void promex_register_module(struct promex_module *m)
}
/* Pools used to allocate ref on Promex modules and filters */
DECLARE_STATIC_TYPED_POOL(pool_head_promex_mod_ref, "promex_module_ref", struct promex_module_ref);
DECLARE_STATIC_TYPED_POOL(pool_head_promex_metric_flt, "promex_metric_filter", struct promex_metric_filter);
DECLARE_STATIC_POOL(pool_head_promex_mod_ref, "promex_module_ref", sizeof(struct promex_module_ref));
DECLARE_STATIC_POOL(pool_head_promex_metric_flt, "promex_metric_filter", sizeof(struct promex_metric_filter));
/* Return the server status. */
enum promex_srv_state promex_srv_status(struct server *sv)
@ -349,10 +344,6 @@ static int promex_dump_ts(struct appctx *appctx, struct ist prefix,
istcat(&n, prefix, PROMEX_MAX_NAME_LEN);
istcat(&n, name, PROMEX_MAX_NAME_LEN);
/* In stress mode, force yielding on each metric. */
if (STRESS_RUN1(istlen(*out), 0))
goto full;
if ((ctx->flags & PROMEX_FL_METRIC_HDR) &&
!promex_dump_ts_header(n, desc, type, out, max))
goto full;
@ -434,8 +425,9 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
static struct ist prefix = IST("haproxy_process_");
struct promex_ctx *ctx = appctx->svcctx;
struct field val;
struct channel *chn = sc_ic(appctx_sc(appctx));
struct ist name, desc, out = ist2(trash.area, 0);
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
int ret = 1;
if (!stats_fill_info(stat_line_info, ST_I_INF_MAX, 0))
@ -501,6 +493,7 @@ static int promex_dump_global_metrics(struct appctx *appctx, struct htx *htx)
if (out.len) {
if (!htx_add_data_atonce(htx, out))
return -1; /* Unexpected and unrecoverable error */
channel_add_input(chn, out.len);
}
return ret;
full:
@ -517,8 +510,9 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
struct proxy *px = ctx->p[0];
struct stats_module *mod = ctx->p[1];
struct field val;
struct channel *chn = sc_ic(appctx_sc(appctx));
struct ist name, desc, out = ist2(trash.area, 0);
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
int ret = 1;
enum promex_front_state state;
@ -632,6 +626,8 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
}
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
void *counters;
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE))
continue;
@ -668,7 +664,8 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_FE))
goto next_px2;
if (!mod->fill_stats(mod, px->extra_counters_fe, stats + ctx->field_num, &ctx->mod_field_num))
counters = EXTRA_COUNTERS_GET(px->extra_counters_fe, mod);
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
return -1;
val = stats[ctx->field_num + ctx->mod_field_num];
@ -695,6 +692,7 @@ static int promex_dump_front_metrics(struct appctx *appctx, struct htx *htx)
if (out.len) {
if (!htx_add_data_atonce(htx, out))
return -1; /* Unexpected and unrecoverable error */
channel_add_input(chn, out.len);
}
/* Save pointers (0=current proxy, 1=current stats module) of the current context */
@ -716,8 +714,9 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
struct listener *li = ctx->p[1];
struct stats_module *mod = ctx->p[2];
struct field val;
struct channel *chn = sc_ic(appctx_sc(appctx));
struct ist name, desc, out = ist2(trash.area, 0);
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
int ret = 1;
enum li_status status;
@ -820,6 +819,8 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
}
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
void *counters;
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI))
continue;
@ -865,7 +866,8 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx+1].name = ist("mod");
labels[lb_idx+1].value = ist2(mod->name, strlen(mod->name));
if (!mod->fill_stats(mod, li->extra_counters, stats + ctx->field_num, &ctx->mod_field_num))
counters = EXTRA_COUNTERS_GET(li->extra_counters, mod);
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
return -1;
val = stats[ctx->field_num + ctx->mod_field_num];
@ -895,6 +897,7 @@ static int promex_dump_listener_metrics(struct appctx *appctx, struct htx *htx)
if (out.len) {
if (!htx_add_data_atonce(htx, out))
return -1; /* Unexpected and unrecoverable error */
channel_add_input(chn, out.len);
}
/* Save pointers (0=current proxy, 1=current listener, 2=current stats module) of the current context */
ctx->p[0] = px;
@ -916,8 +919,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
struct stats_module *mod = ctx->p[1];
struct server *sv;
struct field val;
struct channel *chn = sc_ic(appctx_sc(appctx));
struct ist name, desc, out = ist2(trash.area, 0);
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
int ret = 1;
double secs;
@ -941,6 +945,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
if (promex_filter_metric(appctx, prefix, name))
continue;
if (!px)
px = proxies_list;
while (px) {
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
unsigned int srv_state_count[PROMEX_SRV_STATE_COUNT] = { 0 };
@ -1095,16 +1102,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
&val, labels, &out, max))
goto full;
next_px:
px = watcher_next(&ctx->px_watch, px->next);
px = px->next;
}
watcher_detach(&ctx->px_watch);
ctx->flags |= PROMEX_FL_METRIC_HDR;
/* Prepare a new iteration for the next stat column.
* Update ctx.p[0] via watcher.
*/
watcher_attach(&ctx->px_watch, proxies_list);
px = proxies_list;
}
/* Skip extra counters */
@ -1117,6 +1117,8 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
}
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
void *counters;
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE))
continue;
@ -1127,6 +1129,9 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
if (promex_filter_metric(appctx, prefix, name))
continue;
if (!px)
px = proxies_list;
while (px) {
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
struct promex_metric metric;
@ -1150,7 +1155,8 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
goto next_px2;
if (!mod->fill_stats(mod, px->extra_counters_be, stats + ctx->field_num, &ctx->mod_field_num))
counters = EXTRA_COUNTERS_GET(px->extra_counters_be, mod);
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
return -1;
val = stats[ctx->field_num + ctx->mod_field_num];
@ -1161,39 +1167,26 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
goto full;
next_px2:
px = watcher_next(&ctx->px_watch, px->next);
px = px->next;
}
watcher_detach(&ctx->px_watch);
ctx->flags |= PROMEX_FL_METRIC_HDR;
/* Prepare a new iteration for the next stat column.
* Update ctx.p[0] via watcher.
*/
watcher_attach(&ctx->px_watch, proxies_list);
px = proxies_list;
}
ctx->field_num += mod->stats_count;
ctx->mod_field_num = 0;
}
px = NULL;
mod = NULL;
end:
if (ret) {
watcher_detach(&ctx->px_watch);
mod = NULL;
}
if (out.len) {
if (!htx_add_data_atonce(htx, out)) {
watcher_detach(&ctx->px_watch);
if (!htx_add_data_atonce(htx, out))
return -1; /* Unexpected and unrecoverable error */
}
channel_add_input(chn, out.len);
}
/* Save pointers of the current context for dump resumption :
* 0=current proxy, 1=current stats module
* Note that p[0] is already automatically updated via px_watch.
*/
/* Save pointers (0=current proxy, 1=current stats module) of the current context */
ctx->p[0] = px;
ctx->p[1] = mod;
return ret;
full:
@ -1211,8 +1204,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
struct server *sv = ctx->p[1];
struct stats_module *mod = ctx->p[2];
struct field val;
struct channel *chn = sc_ic(appctx_sc(appctx));
struct ist name, desc, out = ist2(trash.area, 0);
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
struct field *stats = stat_lines[STATS_DOMAIN_PROXY];
int ret = 1;
double secs;
@ -1235,6 +1229,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
if (promex_filter_metric(appctx, prefix, name))
continue;
if (!px)
px = proxies_list;
while (px) {
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
enum promex_mt_type type;
@ -1254,12 +1251,15 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
goto next_px;
if (!sv)
sv = px->srv;
while (sv) {
labels[lb_idx].name = ist("server");
labels[lb_idx].value = ist2(sv->id, strlen(sv->id));
if (!stats_fill_sv_line(px, sv, 0, stats, ST_I_PX_MAX, &(ctx->field_num)))
goto error;
return -1;
if ((ctx->flags & PROMEX_FL_NO_MAINT_SRV) && (sv->cur_admin & SRV_ADMF_MAINT))
goto next_sv;
@ -1342,7 +1342,6 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
secs = (double)sv->check.duration / 1000.0;
val = mkf_flt(FN_DURATION, secs);
break;
case ST_I_PX_REQ_TOT:
if (px->mode != PR_MODE_HTTP) {
sv = NULL;
@ -1365,36 +1364,6 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
labels[lb_idx+1].value = promex_hrsp_code[ctx->field_num - ST_I_PX_HRSP_1XX];
break;
case ST_I_PX_AGENT_STATUS:
if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
goto next_sv;
for (; ctx->obj_state < HCHK_STATUS_SIZE; ctx->obj_state++) {
if (get_check_status_result(ctx->obj_state) < CHK_RES_FAILED)
continue;
val = mkf_u32(FO_STATUS, sv->agent.status == ctx->obj_state);
check_state = get_check_status_info(ctx->obj_state);
labels[lb_idx+1].name = ist("state");
labels[lb_idx+1].value = ist(check_state);
if (!promex_dump_ts(appctx, prefix, name, desc,
type,
&val, labels, &out, max))
goto full;
}
ctx->obj_state = 0;
goto next_sv;
case ST_I_PX_AGENT_CODE:
if ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) != CHK_ST_ENABLED)
goto next_sv;
val = mkf_u32(FN_OUTPUT, (sv->agent.status < HCHK_STATUS_L57DATA) ? 0 : sv->agent.code);
break;
case ST_I_PX_AGENT_DURATION:
if (sv->agent.status < HCHK_STATUS_CHECKED)
goto next_sv;
secs = (double)sv->agent.duration / 1000.0;
val = mkf_flt(FN_DURATION, secs);
break;
default:
break;
}
@ -1404,30 +1373,13 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
&val, labels, &out, max))
goto full;
next_sv:
sv = watcher_next(&ctx->srv_watch, sv->next);
sv = sv->next;
}
next_px:
watcher_detach(&ctx->srv_watch);
px = watcher_next(&ctx->px_watch, px->next);
if (px) {
/* Update ctx.p[1] via watcher. */
watcher_attach(&ctx->srv_watch, px->srv);
sv = ctx->p[1];
}
px = px->next;
}
watcher_detach(&ctx->px_watch);
ctx->flags |= PROMEX_FL_METRIC_HDR;
/* Prepare a new iteration for the next stat column.
* Update ctx.p[0]/p[1] via px_watch/srv_watch.
*/
watcher_attach(&ctx->px_watch, proxies_list);
px = proxies_list;
if (likely(px)) {
watcher_attach(&ctx->srv_watch, px->srv);
sv = ctx->p[1];
}
}
/* Skip extra counters */
@ -1440,6 +1392,8 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
}
list_for_each_entry_from(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
void *counters;
if (!(stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV))
continue;
@ -1450,6 +1404,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
if (promex_filter_metric(appctx, prefix, name))
continue;
if (!px)
px = proxies_list;
while (px) {
struct promex_label labels[PROMEX_MAX_LABELS-1] = {};
struct promex_metric metric;
@ -1470,6 +1427,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
if ((px->flags & PR_FL_DISABLED) || px->uuid <= 0 || !(px->cap & PR_CAP_BE))
goto next_px2;
if (!sv)
sv = px->srv;
while (sv) {
labels[lb_idx].name = ist("server");
labels[lb_idx].value = ist2(sv->id, strlen(sv->id));
@ -1481,8 +1441,9 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
goto next_sv2;
if (!mod->fill_stats(mod, sv->extra_counters, stats + ctx->field_num, &ctx->mod_field_num))
goto error;
counters = EXTRA_COUNTERS_GET(sv->extra_counters, mod);
if (!mod->fill_stats(counters, stats + ctx->field_num, &ctx->mod_field_num))
return -1;
val = stats[ctx->field_num + ctx->mod_field_num];
metric.type = ((val.type == FN_GAUGE) ? PROMEX_MT_GAUGE : PROMEX_MT_COUNTER);
@ -1492,62 +1453,43 @@ static int promex_dump_srv_metrics(struct appctx *appctx, struct htx *htx)
goto full;
next_sv2:
sv = watcher_next(&ctx->srv_watch, sv->next);
sv = sv->next;
}
next_px2:
watcher_detach(&ctx->srv_watch);
px = watcher_next(&ctx->px_watch, px->next);
if (px) {
/* Update ctx.p[1] via watcher. */
watcher_attach(&ctx->srv_watch, px->srv);
sv = ctx->p[1];
}
px = px->next;
}
watcher_detach(&ctx->px_watch);
ctx->flags |= PROMEX_FL_METRIC_HDR;
/* Prepare a new iteration for the next stat column.
* Update ctx.p[0]/p[1] via px_watch/srv_watch.
*/
watcher_attach(&ctx->px_watch, proxies_list);
px = proxies_list;
if (likely(px)) {
watcher_attach(&ctx->srv_watch, px->srv);
sv = ctx->p[1];
}
}
ctx->field_num += mod->stats_count;
ctx->mod_field_num = 0;
}
end:
if (ret) {
watcher_detach(&ctx->px_watch);
watcher_detach(&ctx->srv_watch);
mod = NULL;
}
px = NULL;
sv = NULL;
mod = NULL;
end:
if (out.len) {
if (!htx_add_data_atonce(htx, out))
return -1; /* Unexpected and unrecoverable error */
channel_add_input(chn, out.len);
}
/* Save pointers of the current context for dump resumption :
* 0=current proxy, 1=current server, 2=current stats module
* Note that p[0]/p[1] are already automatically updated via px_watch/srv_watch.
*/
/* Decrement server refcount if it was saved through ctx.p[1]. */
srv_drop(ctx->p[1]);
if (sv)
srv_take(sv);
/* Save pointers (0=current proxy, 1=current server, 2=current stats module) of the current context */
ctx->p[0] = px;
ctx->p[1] = sv;
ctx->p[2] = mod;
return ret;
full:
ret = 0;
goto end;
error:
watcher_detach(&ctx->px_watch);
watcher_detach(&ctx->srv_watch);
return -1;
}
/* Dump metrics of module <mod>. It returns 1 on success, 0 if <out> is full and
@ -1628,8 +1570,9 @@ static int promex_dump_ref_modules_metrics(struct appctx *appctx, struct htx *ht
{
struct promex_ctx *ctx = appctx->svcctx;
struct promex_module_ref *ref = ctx->p[0];
struct channel *chn = sc_ic(appctx_sc(appctx));
struct ist out = ist2(trash.area, 0);
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
int ret = 1;
if (!ref) {
@ -1653,6 +1596,7 @@ static int promex_dump_ref_modules_metrics(struct appctx *appctx, struct htx *ht
if (out.len) {
if (!htx_add_data_atonce(htx, out))
return -1; /* Unexpected and unrecoverable error */
channel_add_input(chn, out.len);
}
ctx->p[0] = ref;
return ret;
@ -1667,8 +1611,9 @@ static int promex_dump_all_modules_metrics(struct appctx *appctx, struct htx *ht
{
struct promex_ctx *ctx = appctx->svcctx;
struct promex_module *mod = ctx->p[0];
struct channel *chn = sc_ic(appctx_sc(appctx));
struct ist out = ist2(trash.area, 0);
size_t max = htx_get_max_blksz(htx, applet_htx_output_room(appctx));
size_t max = htx_get_max_blksz(htx, channel_htx_recv_max(chn, htx));
int ret = 1;
if (!mod) {
@ -1692,6 +1637,7 @@ static int promex_dump_all_modules_metrics(struct appctx *appctx, struct htx *ht
if (out.len) {
if (!htx_add_data_atonce(htx, out))
return -1; /* Unexpected and unrecoverable error */
channel_add_input(chn, out.len);
}
ctx->p[0] = mod;
return ret;
@ -1706,7 +1652,7 @@ static int promex_dump_all_modules_metrics(struct appctx *appctx, struct htx *ht
* Uses <appctx.ctx.stats.px> as a pointer to the current proxy and <sv>/<li>
* as pointers to the current server/listener respectively.
*/
static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
static int promex_dump_metrics(struct appctx *appctx, struct stconn *sc, struct htx *htx)
{
struct promex_ctx *ctx = appctx->svcctx;
int ret;
@ -1768,11 +1714,6 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
ctx->field_num = ST_I_PX_PXNAME;
ctx->mod_field_num = 0;
appctx->st1 = PROMEX_DUMPER_BACK;
if (ctx->flags & PROMEX_FL_SCOPE_BACK) {
/* Update ctx.p[0] via watcher. */
watcher_attach(&ctx->px_watch, proxies_list);
}
__fallthrough;
case PROMEX_DUMPER_BACK:
@ -1790,15 +1731,6 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
ctx->field_num = ST_I_PX_PXNAME;
ctx->mod_field_num = 0;
appctx->st1 = PROMEX_DUMPER_SRV;
if (ctx->flags & PROMEX_FL_SCOPE_SERVER) {
/* Update ctx.p[0] via watcher. */
watcher_attach(&ctx->px_watch, proxies_list);
if (likely(proxies_list)) {
/* Update ctx.p[1] via watcher. */
watcher_attach(&ctx->srv_watch, proxies_list->srv);
}
}
__fallthrough;
case PROMEX_DUMPER_SRV:
@ -1844,7 +1776,7 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
return 1;
full:
applet_have_more_data(appctx);
sc_need_room(sc, channel_htx_recv_max(sc_ic(appctx_sc(appctx)), htx) + 1);
return 0;
error:
/* unrecoverable error */
@ -1857,11 +1789,12 @@ static int promex_dump_metrics(struct appctx *appctx, struct htx *htx)
/* Parse the query string of request URI to filter the metrics. It returns 1 on
* success and -1 on error. */
static int promex_parse_uri(struct appctx *appctx)
static int promex_parse_uri(struct appctx *appctx, struct stconn *sc)
{
struct promex_ctx *ctx = appctx->svcctx;
struct buffer *outbuf;
struct htx *req_htx;
struct channel *req = sc_oc(sc);
struct channel *res = sc_ic(sc);
struct htx *req_htx, *res_htx;
struct htx_sl *sl;
char *p, *key, *value;
const char *end;
@ -1871,13 +1804,10 @@ static int promex_parse_uri(struct appctx *appctx)
int len;
/* Get the query-string */
req_htx = htxbuf(DISGUISE(applet_get_inbuf(appctx)));
req_htx = htxbuf(&req->buf);
sl = http_get_stline(req_htx);
if (!sl)
goto bad_req_error;
if (sl->info.req.meth == HTTP_METH_HEAD)
ctx->flags |= PROMEX_FL_BODYLESS_RESP;
goto error;
p = http_find_param_list(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), '?');
if (!p)
goto end;
@ -1910,27 +1840,27 @@ static int promex_parse_uri(struct appctx *appctx)
*p = 0;
len = url_decode(key, 1);
if (len == -1)
goto bad_req_error;
goto error;
/* decode value */
if (value) {
while (p < end && *p != '=' && *p != '&' && *p != '#')
++p;
if (*p == '=')
goto bad_req_error;
goto error;
if (*p == '&')
*(p++) = 0;
else if (*p == '#')
*p = 0;
len = url_decode(value, 1);
if (len == -1)
goto bad_req_error;
goto error;
}
if (strcmp(key, "scope") == 0) {
default_scopes = 0; /* at least a scope defined, unset default scopes */
if (!value)
goto bad_req_error;
goto error;
else if (*value == 0)
ctx->flags &= ~PROMEX_FL_SCOPE_ALL;
else if (*value == '*' && *(value+1) == 0)
@ -1961,14 +1891,14 @@ static int promex_parse_uri(struct appctx *appctx)
}
}
if (!(ctx->flags & PROMEX_FL_SCOPE_MODULE))
goto bad_req_error;
goto error;
}
}
else if (strcmp(key, "metrics") == 0) {
struct ist args;
if (!value)
goto bad_req_error;
goto error;
for (args = ist(value); istlen(args); args = istadv(istfind(args, ','), 1)) {
struct eb32_node *node;
@ -2019,28 +1949,30 @@ static int promex_parse_uri(struct appctx *appctx)
ctx->flags |= (default_scopes | default_metrics_filter);
return 1;
bad_req_error:
error:
err = &http_err_chunks[HTTP_ERR_400];
goto error;
channel_erase(res);
res->buf.data = b_data(err);
memcpy(res->buf.area, b_head(err), b_data(err));
res_htx = htx_from_buf(&res->buf);
channel_add_input(res, res_htx->data);
return -1;
internal_error:
err = &http_err_chunks[HTTP_ERR_500];
goto error;
error:
outbuf = DISGUISE(applet_get_outbuf(appctx));
b_reset(outbuf);
outbuf->data = b_data(err);
memcpy(outbuf->area, b_head(err), b_data(err));
applet_set_eoi(appctx);
applet_set_eos(appctx);
err = &http_err_chunks[HTTP_ERR_400];
channel_erase(res);
res->buf.data = b_data(err);
memcpy(res->buf.area, b_head(err), b_data(err));
res_htx = htx_from_buf(&res->buf);
channel_add_input(res, res_htx->data);
return -1;
}
/* Send HTTP headers of the response. It returns 1 on success and 0 if <htx> is
* full. */
static int promex_send_headers(struct appctx *appctx, struct htx *htx)
static int promex_send_headers(struct appctx *appctx, struct stconn *sc, struct htx *htx)
{
struct channel *chn = sc_ic(sc);
struct htx_sl *sl;
unsigned int flags;
@ -2055,10 +1987,11 @@ static int promex_send_headers(struct appctx *appctx, struct htx *htx)
!htx_add_endof(htx, HTX_BLK_EOH))
goto full;
channel_add_input(chn, htx->data);
return 1;
full:
htx_reset(htx);
applet_have_more_data(appctx);
sc_need_room(sc, 0);
return 0;
}
@ -2076,8 +2009,6 @@ static int promex_appctx_init(struct appctx *appctx)
LIST_INIT(&ctx->modules);
ctx->filters = EB_ROOT;
appctx->st0 = PROMEX_ST_INIT;
watcher_init(&ctx->px_watch, &ctx->p[0], offsetof(struct proxy, watcher_list));
watcher_init(&ctx->srv_watch, &ctx->p[1], offsetof(struct server, watcher_list));
return 0;
}
@ -2091,14 +2022,11 @@ static void promex_appctx_release(struct appctx *appctx)
struct promex_metric_filter *flt;
struct eb32_node *node, *next;
if (appctx->st1 == PROMEX_DUMPER_BACK ||
appctx->st1 == PROMEX_DUMPER_SRV) {
watcher_detach(&ctx->px_watch);
if (appctx->st1 == PROMEX_DUMPER_SRV) {
struct server *srv = objt_server(ctx->p[1]);
srv_drop(srv);
}
if (appctx->st1 == PROMEX_DUMPER_SRV)
watcher_detach(&ctx->srv_watch);
list_for_each_entry_safe(ref, back, &ctx->modules, list) {
LIST_DELETE(&ref->list);
pool_free(pool_head_promex_mod_ref, ref);
@ -2117,51 +2045,52 @@ static void promex_appctx_release(struct appctx *appctx)
/* The main I/O handler for the promex applet. */
static void promex_appctx_handle_io(struct appctx *appctx)
{
struct promex_ctx *ctx = appctx->svcctx;
struct buffer *outbuf;
struct htx *res_htx;
struct stconn *sc = appctx_sc(appctx);
struct stream *s = __sc_strm(sc);
struct channel *req = sc_oc(sc);
struct channel *res = sc_ic(sc);
struct htx *req_htx, *res_htx;
int ret;
if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR)))
res_htx = htx_from_buf(&res->buf);
if (unlikely(se_fl_test(appctx->sedesc, (SE_FL_EOS|SE_FL_ERROR|SE_FL_SHR|SE_FL_SHW))))
goto out;
/* Check if the input buffer is available. */
outbuf = applet_get_outbuf(appctx);
if (outbuf == NULL) {
applet_have_more_data(appctx);
if (!b_size(&res->buf)) {
sc_need_room(sc, 0);
goto out;
}
res_htx = htx_from_buf(outbuf);
switch (appctx->st0) {
case PROMEX_ST_INIT:
if (!applet_get_inbuf(appctx) || !applet_htx_input_data(appctx)) {
if (!co_data(req)) {
applet_need_more_data(appctx);
break;
goto out;
}
ret = promex_parse_uri(appctx);
ret = promex_parse_uri(appctx, sc);
if (ret <= 0) {
if (ret == -1)
applet_set_error(appctx);
break;
goto error;
goto out;
}
appctx->st0 = PROMEX_ST_HEAD;
appctx->st1 = PROMEX_DUMPER_INIT;
__fallthrough;
case PROMEX_ST_HEAD:
if (!promex_send_headers(appctx, res_htx))
break;
appctx->st0 = ((ctx->flags & PROMEX_FL_BODYLESS_RESP) ? PROMEX_ST_DONE : PROMEX_ST_DUMP);
if (!promex_send_headers(appctx, sc, res_htx))
goto out;
appctx->st0 = ((s->txn->meth == HTTP_METH_HEAD) ? PROMEX_ST_DONE : PROMEX_ST_DUMP);
__fallthrough;
case PROMEX_ST_DUMP:
ret = promex_dump_metrics(appctx, res_htx);
ret = promex_dump_metrics(appctx, sc, res_htx);
if (ret <= 0) {
if (ret == -1)
applet_set_error(appctx);
break;
goto error;
goto out;
}
appctx->st0 = PROMEX_ST_DONE;
__fallthrough;
@ -2175,36 +2104,41 @@ static void promex_appctx_handle_io(struct appctx *appctx)
*/
if (htx_is_empty(res_htx)) {
if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
applet_have_more_data(appctx);
break;
sc_need_room(sc, sizeof(struct htx_blk) + 1);
goto out;
}
channel_add_input(res, 1);
}
res_htx->flags |= HTX_FL_EOM;
applet_set_eoi(appctx);
se_fl_set(appctx->sedesc, SE_FL_EOI);
appctx->st0 = PROMEX_ST_END;
__fallthrough;
case PROMEX_ST_END:
applet_set_eos(appctx);
se_fl_set(appctx->sedesc, SE_FL_EOS);
}
htx_to_buf(res_htx, outbuf);
out:
htx_to_buf(res_htx, &res->buf);
/* eat the whole request */
applet_reset_input(appctx);
if (co_data(req)) {
req_htx = htx_from_buf(&req->buf);
co_htx_skip(req, req_htx, co_data(req));
}
return;
error:
se_fl_set(appctx->sedesc, SE_FL_ERROR);
goto out;
}
struct applet promex_applet = {
.obj_type = OBJ_TYPE_APPLET,
.flags = APPLET_FL_NEW_API|APPLET_FL_HTX,
.name = "<PROMEX>", /* used for logging */
.init = promex_appctx_init,
.release = promex_appctx_release,
.fct = promex_appctx_handle_io,
.rcv_buf = appctx_htx_rcv_buf,
.snd_buf = appctx_htx_snd_buf,
};
static enum act_parse_ret service_parse_prometheus_exporter(const char **args, int *cur_arg, struct proxy *px,

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

@ -1,235 +0,0 @@
#!/bin/bash
#
# Dump certificates from the HAProxy stats or master socket to the filesystem
# Experimental script
#
set -e
export BASEPATH=${BASEPATH:-/etc/haproxy}/
export SOCKET=${SOCKET:-/var/run/haproxy-master.sock}
export DRY_RUN=0
export DEBUG=
export VERBOSE=
export M="@1 "
export TMP
vecho() {
[ -n "$VERBOSE" ] && echo "$@"
return 0
}
read_certificate() {
name=$1
crt_filename=
key_filename=
OFS=$IFS
IFS=":"
while read -r key value; do
case "$key" in
"Crt filename")
crt_filename="${value# }"
key_filename="${value# }"
;;
"Key filename")
key_filename="${value# }"
;;
esac
done < <(echo "${M}show ssl cert ${name}" | socat "${SOCKET}" -)
IFS=$OFS
if [ -z "$crt_filename" ] || [ -z "$key_filename" ]; then
return 1
fi
# handle fields without a crt-base/key-base
[ "${crt_filename:0:1}" != "/" ] && crt_filename="${BASEPATH}${crt_filename}"
[ "${key_filename:0:1}" != "/" ] && key_filename="${BASEPATH}${key_filename}"
vecho "name:$name"
vecho "crt:$crt_filename"
vecho "key:$key_filename"
export NAME="$name"
export CRT_FILENAME="$crt_filename"
export KEY_FILENAME="$key_filename"
return 0
}
cmp_certkey() {
prev=$1
new=$2
if [ ! -f "$prev" ]; then
return 1;
fi
if ! cmp -s <(openssl x509 -in "$prev" -noout -fingerprint -sha256) <(openssl x509 -in "$new" -noout -fingerprint -sha256); then
return 1
fi
return 0
}
dump_certificate() {
name=$1
prev_crt=$2
prev_key=$3
r="tmp.${RANDOM}"
d="old.$(date +%s)"
new_crt="$TMP/$(basename "$prev_crt").${r}"
new_key="$TMP/$(basename "$prev_key").${r}"
if ! touch "${new_crt}" || ! touch "${new_key}"; then
echo "[ALERT] ($$) : can't dump \"$name\", can't create tmp files" >&2
return 1
fi
echo "${M}dump ssl cert ${name}" | socat "${SOCKET}" - | openssl pkey >> "${new_key}"
# use crl2pkcs7 as a way to dump multiple x509, storeutl could be used in modern versions of openssl
echo "${M}dump ssl cert ${name}" | socat "${SOCKET}" - | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs >> "${new_crt}"
if ! cmp -s <(openssl x509 -in "${new_crt}" -pubkey -noout) <(openssl pkey -in "${new_key}" -pubout); then
echo "[ALERT] ($$) : Private key \"${new_key}\" and public key \"${new_crt}\" don't match" >&2
return 1
fi
if cmp_certkey "${prev_crt}" "${new_crt}"; then
echo "[NOTICE] ($$) : ${crt_filename} is already up to date" >&2
return 0
fi
# dry run will just return before trying to move the files
if [ "${DRY_RUN}" != "0" ]; then
return 0
fi
# move the current certificates to ".old.timestamp"
if [ -f "${prev_crt}" ] && [ -f "${prev_key}" ]; then
mv "${prev_crt}" "${prev_crt}.${d}"
[ "${prev_crt}" != "${prev_key}" ] && mv "${prev_key}" "${prev_key}.${d}"
fi
# move the new certificates to old place
mv "${new_crt}" "${prev_crt}"
[ "${prev_crt}" != "${prev_key}" ] && mv "${new_key}" "${prev_key}"
return 0
}
dump_all_certificates() {
echo "${M}show ssl cert" | socat "${SOCKET}" - | grep -v '^#' | grep -v '^$' | while read -r line; do
export NAME
export CRT_FILENAME
export KEY_FILENAME
if read_certificate "$line"; then
dump_certificate "$NAME" "$CRT_FILENAME" "$KEY_FILENAME"
else
echo "[WARNING] ($$) : can't dump \"$name\", crt/key filename details not found in \"show ssl cert\"" >&2
fi
done
}
usage() {
echo "Usage:"
echo " $0 [options]* [cert]*"
echo ""
echo " Dump certificates from the HAProxy stats or master socket to the filesystem"
echo " Require socat and openssl"
echo " EXPERIMENTAL script, backup your files!"
echo " The script will move your previous files to FILE.old.unixtimestamp (ex: foo.com.pem.old.1759044998)"
echo ""
echo "Options:"
echo " -S, --master-socket <path> Use the master socket at <path> (default: ${SOCKET})"
echo " -s, --socket <path> Use the stats socket at <path>"
echo " -p, --path <path> Specify a base path for relative files (default: ${BASEPATH})"
echo " -n, --dry-run Read certificates on the socket but don't dump them"
echo " -d, --debug Debug mode, set -x"
echo " -v, --verbose Verbose mode"
echo " -h, --help This help"
echo " -- End of options"
echo ""
echo "Examples:"
echo " $0 -v -p ${BASEPATH} -S ${SOCKET}"
echo " $0 -v -p ${BASEPATH} -S ${SOCKET} bar.com.rsa.pem"
echo " $0 -v -p ${BASEPATH} -S ${SOCKET} -- foo.com.ecdsa.pem bar.com.rsa.pem"
}
main() {
while [ -n "$1" ]; do
case "$1" in
-S|--master-socket)
SOCKET="$2"
M="@1 "
shift 2
;;
-s|--socket)
SOCKET="$2"
M=
shift 2
;;
-p|--path)
BASEPATH="$2/"
shift 2
;;
-n|--dry-run)
DRY_RUN=1
shift
;;
-d|--debug)
DEBUG=1
shift
;;
-v|--verbose)
VERBOSE=1
shift
;;
-h|--help)
usage "$@"
exit 0
;;
--)
shift
break
;;
-*)
echo "[ALERT] ($$) : Unknown option '$1'" >&2
usage "$@"
exit 1
;;
*)
break
;;
esac
done
if [ -n "$DEBUG" ]; then
set -x
fi
TMP=${TMP:-$(mktemp -d)}
if [ -z "$1" ]; then
dump_all_certificates
else
# compute the certificates names at the end of the command
while [ -n "$1" ]; do
if ! read_certificate "$1"; then
echo "[ALERT] ($$) : can't dump \"$1\", crt/key filename details not found in \"show ssl cert\"" >&2
exit 1
fi
[ "${DRY_RUN}" = "0" ] && dump_certificate "$NAME" "$CRT_FILENAME" "$KEY_FILENAME"
shift
done
fi
}
trap 'rm -rf -- "$TMP"' EXIT
main "$@"

View file

@ -1,118 +0,0 @@
#!/bin/sh
set -e
export VERBOSE=1
export TIMEOUT=90
export MASTER_SOCKET="${MASTER_SOCKET:-/var/run/haproxy-master.sock}"
alert() {
if [ "$VERBOSE" -ge "1" ]; then
echo "[ALERT] $*" >&2
fi
}
reload() {
if [ -S "$MASTER_SOCKET" ]; then
socat_addr="UNIX-CONNECT:${MASTER_SOCKET}"
else
case "$MASTER_SOCKET" in
*:[0-9]*)
socat_addr="TCP:${MASTER_SOCKET}"
;;
*)
alert "Invalid master socket address '${MASTER_SOCKET}': expected a UNIX socket file or <host>:<port>"
return 1
;;
esac
fi
echo "reload" | socat -t"${TIMEOUT}" "$socat_addr" - | {
read -r status || { alert "No status received (connection error or timeout after ${TIMEOUT}s)."; exit 1; }
case "$status" in
"Success=1") ret=0 ;;
"Success=0") ret=1 ;;
*) alert "Unexpected response: '$status'"; exit 1 ;;
esac
read -r _ # consume "--"
if [ "$VERBOSE" -ge 3 ] || { [ "$ret" = 1 ] && [ "$VERBOSE" -ge 2 ]; }; then
cat >&2
else
cat >/dev/null
fi
exit "$ret"
}
}
usage() {
echo "Usage:"
echo " $0 [options]*"
echo ""
echo " Trigger a reload from the master socket"
echo " Require socat"
echo " EXPERIMENTAL script!"
echo ""
echo "Options:"
echo " -S, --master-socket <addr> Unix socket path or <host>:<port> (default: ${MASTER_SOCKET})"
echo " -d, --debug Debug mode, set -x"
echo " -t, --timeout Timeout (socat -t) (default: ${TIMEOUT})"
echo " -s, --silent Silent mode (no output)"
echo " -v, --verbose Verbose output (output from haproxy on failure)"
echo " -vv --verbose=all Very verbose output (output from haproxy on success and failure)"
echo " -h, --help This help"
echo ""
echo "Examples:"
echo " $0 -S ${MASTER_SOCKET} -d ${TIMEOUT}"
}
main() {
while [ -n "$1" ]; do
case "$1" in
-S|--master-socket)
MASTER_SOCKET="$2"
shift 2
;;
-t|--timeout)
TIMEOUT="$2"
shift 2
;;
-s|--silent)
VERBOSE=0
shift
;;
-v|--verbose)
VERBOSE=2
shift
;;
-vv|--verbose=all)
VERBOSE=3
shift
;;
-d|--debug)
DEBUG=1
shift
;;
-h|--help)
usage "$@"
exit 0
;;
*)
echo "[ALERT] ($$) : Unknown option '$1'" >&2
usage "$@"
exit 1
;;
esac
done
if [ -n "$DEBUG" ]; then
set -x
fi
}
main "$@"
reload

View file

@ -123,22 +123,6 @@ struct url_stat {
#define FILT2_PRESERVE_QUERY 0x02
#define FILT2_EXTRACT_CAPTURE 0x04
#define FILT_OUTPUT_FMT (FILT_COUNT_ONLY| \
FILT_COUNT_STATUS| \
FILT_COUNT_SRV_STATUS| \
FILT_COUNT_COOK_CODES| \
FILT_COUNT_TERM_CODES| \
FILT_COUNT_URL_ONLY| \
FILT_COUNT_URL_COUNT| \
FILT_COUNT_URL_ERR| \
FILT_COUNT_URL_TAVG| \
FILT_COUNT_URL_TTOT| \
FILT_COUNT_URL_TAVGO| \
FILT_COUNT_URL_TTOTO| \
FILT_COUNT_URL_BAVG| \
FILT_COUNT_URL_BTOT| \
FILT_COUNT_IP_COUNT)
unsigned int filter = 0;
unsigned int filter2 = 0;
unsigned int filter_invert = 0;
@ -208,7 +192,7 @@ void help()
" you can also use -n to start from earlier then field %d\n"
" -query preserve the query string for per-URL (-u*) statistics\n"
"\n"
"Output format - **only one** may be used at a time\n"
"Output format - only one may be used at a time\n"
" -c only report the number of lines that would have been printed\n"
" -pct output connect and response times percentiles\n"
" -st output number of requests per HTTP status code\n"
@ -914,9 +898,6 @@ int main(int argc, char **argv)
if (!filter && !filter2)
die("No action specified.\n");
if ((filter & FILT_OUTPUT_FMT) & ((filter & FILT_OUTPUT_FMT) - 1))
die("Please, set only one output filter.\n");
if (filter & FILT_ACC_COUNT && !filter_acc_count)
filter_acc_count=1;
@ -1571,10 +1552,6 @@ void filter_count_srv_status(const char *accept_field, const char *time_field, s
if (!srv_node) {
/* server not yet in the tree, let's create it */
srv = (void *)calloc(1, sizeof(struct srv_st) + e - b + 1);
if (unlikely(!srv)) {
fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
exit(1);
}
srv_node = &srv->node;
memcpy(&srv_node->key, b, e - b);
srv_node->key[e - b] = '\0';
@ -1684,10 +1661,6 @@ void filter_count_url(const char *accept_field, const char *time_field, struct t
*/
if (unlikely(!ustat))
ustat = calloc(1, sizeof(*ustat));
if (unlikely(!ustat)) {
fprintf(stderr, "%s: not enough memory\n", __FUNCTION__);
exit(1);
}
ustat->nb_err = err;
ustat->nb_req = 1;
@ -1801,8 +1774,6 @@ void filter_count_ip(const char *source_field, const char *accept_field, const c
*/
if (unlikely(!ustat))
ustat = calloc(1, sizeof(*ustat));
if (!ustat)
return;
ustat->nb_err = err;
ustat->nb_req = 1;

View file

@ -6,9 +6,9 @@ Wants=network-online.target
[Service]
EnvironmentFile=-/etc/default/haproxy
EnvironmentFile=-/etc/sysconfig/haproxy
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "CFGDIR=/etc/haproxy/conf.d" "EXTRAOPTS=-S /run/haproxy-master.sock"
ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -f $CFGDIR -p $PIDFILE $EXTRAOPTS
ExecReload=@SBINDIR@/haproxy -Ws -f $CONFIG -f $CFGDIR -c $EXTRAOPTS
Environment="CONFIG=/etc/haproxy/haproxy.cfg" "PIDFILE=/run/haproxy.pid" "EXTRAOPTS=-S /run/haproxy-master.sock"
ExecStart=@SBINDIR@/haproxy -Ws -f $CONFIG -p $PIDFILE $EXTRAOPTS
ExecReload=@SBINDIR@/haproxy -Ws -f $CONFIG -c $EXTRAOPTS
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
Restart=always

View file

@ -26,7 +26,7 @@ end
# returns $node filled with the first node of ebroot $arg0
define ebtree_first
# 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
_ebtree_set_tag_node $node
if $tag == 0
@ -41,7 +41,7 @@ end
# finds next ebtree node after $arg0, and returns it in $node
define ebtree_next
# 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
# while (eb_gettag(t) != EB_LEFT) // #define EB_LEFT 0
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,162 +0,0 @@
/*
* Extracts the libs archives from a core dump
*
* Copyright (C) 2026 Willy Tarreau <w@1wt.eu>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/* Note: builds with no option under glibc, and can be built as a minimal
* uploadable static executable using nolibc as well:
gcc -o libs-from-core -nostdinc -nostdlib -s -Os -static -fno-ident \
-fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables \
-Wl,--gc-sections,--orphan-handling=discard,-znoseparate-code \
-I /path/to/nolibc-sysroot/include libs-from-core.c
*/
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <elf.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void usage(const char *progname)
{
const char *slash = strrchr(progname, '/');
if (slash)
progname = slash + 1;
fprintf(stderr,
"Usage: %s [-q] <core_file>\n"
"Locate a libs archive from an haproxy core dump and dump it to stdout.\n"
"Arguments:\n"
" -q Query mode: only report offset and length, do not dump\n"
" core_file Core dump produced by haproxy\n",
progname);
}
int main(int argc, char **argv)
{
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
struct stat st;
uint8_t *mem;
int i, fd;
const char *fname;
int quiet = 0;
int arg;
for (arg = 1; arg < argc; arg++) {
if (*argv[arg] != '-')
break;
if (strcmp(argv[arg], "-q") == 0)
quiet = 1;
else if (strcmp(argv[arg], "--") == 0) {
arg++;
break;
}
}
if (arg < argc) {
fname = argv[arg];
} else {
usage(argv[0]);
exit(1);
}
fd = open(fname, O_RDONLY);
/* Let's just map the core dump as an ELF header */
fstat(fd, &st);
mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mem == MAP_FAILED) {
perror("mmap()");
exit(1);
}
/* get the program headers */
ehdr = (Elf64_Ehdr *)mem;
/* check that it's really a core. Should be "\x7fELF" */
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
fprintf(stderr, "ELF magic not found.\n");
exit(1);
}
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
fprintf(stderr, "Only 64-bit ELF supported.\n");
exit(1);
}
if (ehdr->e_type != ET_CORE) {
fprintf(stderr, "ELF type %d, not a core dump.\n", ehdr->e_type);
exit(1);
}
/* OK we can safely go with program headers */
phdr = (Elf64_Phdr *)(mem + ehdr->e_phoff);
for (i = 0; i < ehdr->e_phnum; i++) {
uint64_t size = phdr[i].p_filesz;
uint64_t offset = phdr[i].p_offset;
int ret = 0;
if (phdr[i].p_type != PT_LOAD)
continue;
//fprintf(stderr, "Scanning segment %d...\n", ehdr->e_phnum);
//fprintf(stderr, "\r%-5d: off=%lx va=%lx sz=%lx ", i, (long)offset, (long)phdr[i].p_vaddr, (long)size);
if (!size)
continue;
if (size < 512) // minimum for a tar header
continue;
/* tar magic */
if (memcmp(mem + offset + 257, "ustar\0""00", 8) != 0)
continue;
/* uid, gid */
if (memcmp(mem + offset + 108, "0000000\0""0000000\0", 16) != 0)
continue;
/* link name */
if (memcmp(mem + offset + 157, "haproxy-libs-dump\0", 18) != 0)
continue;
/* OK that's really it */
if (quiet)
printf("offset=%#lx size=%#lx\n", offset, size);
else
ret = (write(1, mem + offset, size) == size) ? 0 : 1;
return ret;
}
//fprintf(stderr, "\r%75s\n", "\r");
fprintf(stderr, "libs archive not found. Was 'set-dumpable' set to 'libs' ?\n");
return 1;
}

View file

@ -1,19 +0,0 @@
# show non-null memprofile entries with method, alloc/free counts/tot and caller
define memprof_dump
set $i = 0
set $meth={ "UNKN", "MALL", "CALL", "REAL", "STRD", "FREE", "P_AL", "P_FR", "STND", "VALL", "ALAL", "PALG", "MALG", "PVAL" }
while $i < sizeof(memprof_stats) / sizeof(memprof_stats[0])
if memprof_stats[$i].alloc_calls || memprof_stats[$i].free_calls
set $m = memprof_stats[$i].method
printf "m:%s ac:%u fc:%u at:%u ft:%u ", $meth[$m], \
memprof_stats[$i].alloc_calls, memprof_stats[$i].free_calls, \
memprof_stats[$i].alloc_tot, memprof_stats[$i].free_tot
output/a memprof_stats[$i].caller
printf "\n"
end
set $i = $i + 1
end
end

View file

@ -1,141 +0,0 @@
/*
* Find the post-mortem offset from a core dump
*
* Copyright (C) 2026 Willy Tarreau <w@1wt.eu>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/* Note: builds with no option under glibc, and can be built as a minimal
* uploadable static executable using nolibc as well:
gcc -o pm-from-core -nostdinc -nostdlib -s -Os -static -fno-ident \
-fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables \
-Wl,--gc-sections,--orphan-handling=discard,-znoseparate-code \
-I /path/to/nolibc-sysroot/include pm-from-core.c
*/
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>
#include <elf.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__GLIBC__)
# define my_memmem memmem
#else
void *my_memmem(const void *haystack, size_t haystacklen,
const void *needle, size_t needlelen)
{
while (haystacklen >= needlelen) {
if (!memcmp(haystack, needle, needlelen))
return (void*)haystack;
haystack++;
haystacklen--;
}
return NULL;
}
#endif
#define MAGIC "POST-MORTEM STARTS HERE+7654321\0"
int main(int argc, char **argv)
{
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
struct stat st;
uint8_t *mem;
int i, fd;
if (argc < 2) {
printf("Usage: %s <core_file>\n", argv[0]);
exit(1);
}
fd = open(argv[1], O_RDONLY);
/* Let's just map the core dump as an ELF header */
fstat(fd, &st);
mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (mem == MAP_FAILED) {
perror("mmap()");
exit(1);
}
/* get the program headers */
ehdr = (Elf64_Ehdr *)mem;
/* check that it's really a core. Should be "\x7fELF" */
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
fprintf(stderr, "ELF magic not found.\n");
exit(1);
}
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
fprintf(stderr, "Only 64-bit ELF supported.\n");
exit(1);
}
if (ehdr->e_type != ET_CORE) {
fprintf(stderr, "ELF type %d, not a core dump.\n", ehdr->e_type);
exit(1);
}
/* OK we can safely go with program headers */
phdr = (Elf64_Phdr *)(mem + ehdr->e_phoff);
for (i = 0; i < ehdr->e_phnum; i++) {
uint64_t size = phdr[i].p_filesz;
uint64_t offset = phdr[i].p_offset;
uint64_t vaddr = phdr[i].p_vaddr;
uint64_t found_ofs;
uint8_t *found;
if (phdr[i].p_type != PT_LOAD)
continue;
//printf("Scanning segment %d...\n", ehdr->e_phnum);
//printf("\r%-5d: off=%lx va=%lx sz=%lx ", i, (long)offset, (long)vaddr, (long)size);
if (!size)
continue;
if (size >= 1048576) // don't scan large segments
continue;
found = my_memmem(mem + offset, size, MAGIC, sizeof(MAGIC) - 1);
if (!found)
continue;
found_ofs = found - (mem + offset);
printf("Found post-mortem magic in segment %d:\n", i);
printf(" Core File Offset: 0x%lx (0x%lx + 0x%lx)\n", offset + found_ofs, offset, found_ofs);
printf(" Runtime VAddr: 0x%lx (0x%lx + 0x%lx)\n", vaddr + found_ofs, vaddr, found_ofs);
printf(" Segment Size: 0x%lx\n", size);
printf("\nIn gdb, copy-paste this line:\n\n pm_init 0x%lx\n\n", vaddr + found_ofs);
return 0;
}
//printf("\r%75s\n", "\r");
printf("post-mortem magic not found\n");
return 1;
}

View file

@ -14,8 +14,8 @@ define pools_dump
set $idx=$idx + 1
end
set $mem = (unsigned long)$total * $e->size
printf "list=%#lx pool_head=%p name=%s size=%u alloc=%u used=%u mem=%lu\n", $p, $e, $e->name, $e->size, $total, $used, $mem
set $mem = $total * $e->size
printf "list=%#lx pool_head=%p name=%s size=%u alloc=%u used=%u mem=%u\n", $p, $e, $e->name, $e->size, $total, $used, $mem
set $p = *(void **)$p
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

@ -59,9 +59,9 @@ struct ring_v2 {
struct ring_v2a {
size_t size; // storage size
size_t rsvd; // header length (used for file-backed maps)
size_t tail ALIGNED(64); // storage tail
size_t head ALIGNED(64); // storage head
char area[0] ALIGNED(64); // storage area begins immediately here
size_t tail __attribute__((aligned(64))); // storage tail
size_t head __attribute__((aligned(64))); // storage head
char area[0] __attribute__((aligned(64))); // storage area begins immediately here
};
/* display the message and exit with the code */

View file

@ -86,7 +86,7 @@ maintenance model and what the user wants is passed, then the LLM is invited to
provide its opinion on the need for a backport and an explanation of the reason
for its choice. This often helps the user to find a quick summary about the
patch. All these outputs are then converted to a long HTML page with colors and
radio buttons, where patches are preselected based on this classification,
radio buttons, where patches are pre-selected based on this classification,
that the user can consult and adjust, read the commits if needed, and the
selected patches finally provide some copy-pastable commands in a text-area to
select commit IDs to work on, typically in a form that's suitable for a simple

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.3-dev, and maintenance branches are 3.2 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.2 first, then 3.1, then 3.0, then 2.9
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.2 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

@ -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.4-dev, and maintenance branches are 3.3 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.3 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.3 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

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

@ -22,8 +22,7 @@ STABLE=$(cd "$HAPROXY_DIR" && git describe --tags "v${BRANCH}-dev0^" |cut -f1,2
PATCHES_DIR="$PATCHES_PFX"-"$BRANCH"
(cd "$HAPROXY_DIR"
# avoid git pull, it chokes on forced push
git remote update origin; git reset origin/master;git checkout -f
git pull
last_file=$(ls -1 "$PATCHES_DIR"/*.patch 2>/dev/null | tail -n1)
if [ -n "$last_file" ]; then
restart=$(head -n1 "$last_file" | cut -f2 -d' ')

BIN
dev/phash/a.out Executable file

Binary file not shown.

View file

@ -30,10 +30,10 @@ static const char *tevt_fd_types[16] = {
};
static const char *tevt_hs_types[16] = {
[ 0] = "-", [ 1] = "-", [ 2] = "-", [ 3] = "-",
[ 4] = "snd_err", [ 5] = "truncated_shutr", [ 6] = "truncated_rcv_err", [ 7] = "-",
[ 8] = "-", [ 9] = "-", [10] = "-", [11] = "-",
[12] = "-", [13] = "-", [14] = "-", [15] = "-",
[ 0] = "-", [ 1] = "-", [ 2] = "-", [ 3] = "rcv_err",
[ 4] = "snd_err", [ 5] = "-", [ 6] = "-", [ 7] = "-",
[ 8] = "-", [ 9] = "-", [10] = "-", [11] = "-",
[12] = "-", [13] = "-", [14] = "-", [15] = "-",
};
static const char *tevt_xprt_types[16] = {

View file

@ -3,9 +3,7 @@ DeviceAtlas Device Detection
In order to add DeviceAtlas Device Detection support, you would need to download
the API source code from https://deviceatlas.com/deviceatlas-haproxy-module.
Once extracted, two modes are supported :
1/ Build HAProxy and DeviceAtlas in one command
Once extracted :
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_SRC=<path to the API root folder>
@ -16,6 +14,10 @@ directory. Also, in the case the api cache support is not needed and/or a C++ to
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_SRC=<path to the API root folder> DEVICEATLAS_NOCACHE=1
However, if the API had been installed beforehand, DEVICEATLAS_SRC
can be omitted. Note that the DeviceAtlas C API version supported is from the 3.x
releases series (3.2.1 minimum recommended).
For HAProxy developers who need to verify that their changes didn't accidentally
break the DeviceAtlas code, it is possible to build a dummy library provided in
the addons/deviceatlas/dummy directory and to use it as an alternative for the
@ -25,29 +27,6 @@ validate API changes :
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_SRC=$PWD/addons/deviceatlas/dummy
2/ Build and install DeviceAtlas according to https://docs.deviceatlas.com/apis/enterprise/c/<release version>/README.html
For example :
In the deviceatlas library folder :
$ cmake .
$ make
$ sudo make install
In the HAProxy folder :
$ make TARGET=<target> USE_DEVICEATLAS=1
Note that if the -DCMAKE_INSTALL_PREFIX cmake option had been used, it is necessary to set as well DEVICEATLAS_LIB and
DEVICEATLAS_INC as follow :
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_INC=<CMAKE_INSTALL_PREFIX value>/include DEVICEATLAS_LIB=<CMAKE_INSTALL_PREFIX value>/lib
For example :
$ cmake -DCMAKE_INSTALL_PREFIX=/opt/local
$ make
$ sudo make install
$ make TARGET=<target> USE_DEVICEATLAS=1 DEVICEATLAS_INC=/opt/local/include DEVICEATLAS_LIB=/opt/local/lib
Note that DEVICEATLAS_SRC is omitted in this case.
These are supported DeviceAtlas directives (see doc/configuration.txt) :
- deviceatlas-json-file <path to the DeviceAtlas JSON data file>.
- deviceatlas-log-level <number> (0 to 3, level of information returned by

View file

@ -3,7 +3,7 @@
A number of contributors are often embarrassed with coding style issues, they
don't always know if they're doing it right, especially since the coding style
has evolved along the years. What is explained here is not necessarily what is
has elvoved along the years. What is explained here is not necessarily what is
applied in the code, but new code should as much as possible conform to this
style. Coding style fixes happen when code is replaced. It is useless to send
patches to fix coding style only, they will be rejected, unless they belong to

File diff suppressed because it is too large Load diff

View file

@ -526,6 +526,12 @@ In addition, some variables are related to the global runqueue:
unsigned int grq_total; /* total number of entries in the global run queue, atomic */
static unsigned int global_rqueue_ticks; /* insertion count in the grq, use rq_lock */
And others to the global wait queue:
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
__decl_aligned_rwlock(wq_lock); /* RW lock related to the wait queue */
struct eb_root timers; /* sorted timers tree, global, accessed under wq_lock */
2022-06-14 - progress on task affinity
==========

View file

@ -1,140 +0,0 @@
------
HATerm
------
HAProxy's dummy HTTP
server for benchmarks
1. Background
-------------
HATerm is a dummy HTTP server that leverages the flexible and scalable
architecture of HAProxy to ease benchmarking of HTTP agents in all versions of
HTTP currently supported by HAProxy (HTTP/1, HTTP/2, HTTP/3), and both in clear
and TLS / QUIC. It follows the same principle as its ancestor HTTPTerm [1],
consisting in producing HTTP responses entirely configured by the request
parameters (size, response time, status etc). It also preserves the spirit
HTTPTerm which does not require any configuration beyond an optional listening
address and a port number, though it also supports advanced configurations with
the full spectrum of HAProxy features for specific testing. The goal remains
to make it almost as fast as the original HTTPTerm so that it can become a
de-facto replacement, with a compatible command line and request parameters
that will not change users' habits.
[1] https://github.com/wtarreau/httpterm
2. Compilation
--------------
HATerm may be compiled in the same way as HAProxy but with "haterm" as Makefile
target to provide on the "make" command line as follows:
$ make -j $(nproc) TARGET=linux-glibc haterm
HATerm supports HTTPS/SSL/TCP:
$ make TARGET=linux-glibc USE_OPENSSL=1
It also supports QUIC:
$ make -j $(nproc) TARGET=linux-glibc USE_OPENSSL=1 USE_QUIC=1 haterm
Technically speaking, it uses the regular HAProxy source and object code with a
different command line parser. As such, all build options supported by HAProxy
also apply to HATerm. See INSTALL for more details about how to compile them.
3. Execution
------------
HATerm is a very easy to use HTTP server with supports for all the HTTP
versions. It displays its usage when run without argument or wrong arguments:
$ ./haterm
Usage : haterm -L [<ip>]:<clear port>[:<TCP&QUIC SSL port>] [-L...]* [opts]
where <opts> may be any combination of:
-G <line> : multiple option; append <line> to the "global" section
-F <line> : multiple option; append <line> to the "frontend" section
-T <line> : multiple option; append <line> to the "traces" section
-C : dump the configuration and exit
-D : goes daemon
-b <keysize> : RSA key size in bits (ex: "2048", "4096"...)
-c <curves> : ECDSA curves (ex: "P-256", "P-384"...)
-v : shows version
-d : enable the traces for all http protocols
--quic-bind-opts <opts> : append options to QUIC "bind" lines
--tcp-bind-opts <opts> : append options to TCP "bind" lines
Arguments -G, -F, -T permit to append one or multiple lines at the end of their
respective sections. A tab character ('\t') is prepended at the beginning of
the argument, and a line feed ('\n') is appended at the end. It is also
possible to insert multiple lines at once using escape sequences '\n' and '\t'
inside the string argument.
As HAProxy, HATerm may listen on several TCP/UDP addresses which can be
provided by multiple "-L" options. To be functional, it needs at least one
correct "-L" option to be set.
Examples:
$ ./haterm -L 127.0.0.1:8888 # listen on 127.0.0.1:8888 TCP address
$ ./haterm -L 127.0.0.1:8888:8889 # listen on 127.0.0.1:8888 TCP address,
# 127.0.01:8889 SSL/TCP address,
# and 127.0.01:8889 QUIC/UDP address
$ ./haterm -L 127.0.0.1:8888:8889 -L [::1]:8888:8889
With USE_QUIC_OPENSSL_COMPAT support, the user must configure a global
section as for HAProxy. HATerm sets internally its configuration in.
memory as this is done by HAProxy from configuration files:
$ ./haterm -L 127.0.0.1:8888:8889
[NOTICE] (1371578) : haproxy version is 3.4-dev4-ba5eab-28
[NOTICE] (1371578) : path to executable is ./haterm
[ALERT] (1371578) : Binding [haterm cfgfile:12] for frontend
___haterm_frontend___: this SSL library does not
support the QUIC protocol. A limited compatibility
layer may be enabled using the "limited-quic" global
option if desired.
Such an alert may be fixed with "-G' option:
$ ./haterm -L 127.0.0.1:8888:8889 -G "limited-quic"
When the SSL support is not compiled in, the second port is ignored. This is
also the case for the QUIC support.
HATerm adjusts its responses depending on the requests it receives. An empty
query string provides the information about how the URIs are understood by
HATerm:
$ curl http://127.0.0.1:8888/?
HAProxy's dummy HTTP server for benchmarks - version 3.4-dev4.
All integer argument values are in the form [digits]*[kmgr] (r=random(0..1))
The following arguments are supported to override the default objects :
- /?s=<size> return <size> bytes.
E.g. /?s=20k
- /?r=<retcode> present <retcode> as the HTTP return code.
E.g. /?r=404
- /?c=<cache> set the return as not cacheable if <1.
E.g. /?c=0
- /?A=<req-after> drain the request body after sending the response.
E.g. /?A=1
- /?C=<close> force the response to use close if >0.
E.g. /?C=1
- /?K=<keep-alive> force the response to use keep-alive if >0.
E.g. /?K=1
- /?t=<time> wait <time> milliseconds before responding.
E.g. /?t=500
- /?k=<enable> Enable transfer encoding chunked with only one chunk
if >0.
- /?R=<enable> Enable sending random data if >0.
Note that those arguments may be cumulated on one line separated by a set of
delimiters among [&?,;/] :
- GET /?s=20k&c=1&t=700&K=30r HTTP/1.0
- GET /?r=500?s=0?c=0?t=1000 HTTP/1.0

View file

@ -5,7 +5,7 @@
The buffer list API allows one to share a certain amount of buffers between
multiple entities, which will each see their own as lists of buffers, while
keeping a shared free list. The immediate use case is for muxes, which may
keeping a sharedd free list. The immediate use case is for muxes, which may
want to allocate up to a certain number of buffers per connection, shared
among all streams. In this case, each stream will first request a new list
for its own use, then may request extra entries from the free list. At any

View file

@ -1,5 +1,5 @@
-----------------------------------------
Filters Guide - version 3.4
Filters Guide - version 2.9
( Last update: 2021-02-24 )
------------------------------------------
Author : Christopher Faulet
@ -738,10 +738,10 @@ For instance :
switch (an_bit) {
case AN_REQ_WAIT_HTTP:
if (/* A test on received headers before any other treatment */) {
msg = ((chn->flags & CF_ISRESP) ? &s->txn.http->rsp : &s->txn.http->req);
msg = ((chn->flags & CF_ISRESP) ? &s->txn->rsp : &s->txn->req);
txn->status = 400;
msg->msg_state = HTTP_MSG_ERROR;
http_reply_and_close(s, s->txn.http->status, http_error_message(s));
http_reply_and_close(s, s->txn->status, http_error_message(s));
return -1; /* This is an error ! */
}
break;
@ -1161,7 +1161,7 @@ Then, to finish, there are 2 informational callbacks :
if we're retrying to send the request to the server after it failed. It
could be useful to reset the filter context before receiving the true
response.
By checking s->txn.http->status, it is possible to know why this callback is
By checking s->txn->status, it is possible to know why this callback is
called. If it's a 1xx, we're called because of an informational
message. Otherwise, it is a L7 retry.

View file

@ -539,22 +539,10 @@ message. These functions are used by HTX analyzers or by multiplexers.
with the first block not removed, or NULL if everything was removed, and
the amount of data drained.
- htx_xfer() transfers HTX blocks from an HTX message to another, stopping
when a specific amount of bytes, including meta-data, was copied. If the
tail block is a DATA block, it may be partially copied. All other block
are transferred at once. By default, copied blocks are removed from the
original HTX message and headers and trailers parts cannot be partially
copied. But flags can be set to change the default behavior:
- HTX_XFER_KEEP_SRC_BLKS: source blocks are not removed
- HTX_XFER_PARTIAL_HDRS_COPY: partial headers and trailers
part can be xferred
- HTX_XFER_HDRS_ONLY: Only the headers part is xferred
- htx_xfer_blks() [DEPRECATED] transfers HTX blocks from an HTX message to
another, stopping after the first block of a specified type is transferred
or when a specific amount of bytes, including meta-data, was moved. If the
tail block is a DATA block, it may be partially moved. All other block are
- htx_xfer_blks() transfers HTX blocks from an HTX message to another,
stopping after the first block of a specified type is transferred or when
a specific amount of bytes, including meta-data, was moved. If the tail
block is a DATA block, it may be partially moved. All other block are
transferred at once or kept. This function returns a mixed value, with the
last block moved, or NULL if nothing was moved, and the amount of data
transferred. When HEADERS or TRAILERS blocks must be transferred, this

View file

@ -52,16 +52,6 @@ make sure to respect this ordering when adding new ones.
cast and freed. The const char* is here to leave more freedom to use consts
when making such options lists.
- void hap_register_hlua_state_init(int (*fct)())
This adds a call to function <fct> to the list of functions to be called each
time a new Lua state is created by hlua_init_state(). This allows source
files other than hlua.c to register objects and functions into the Lua API
without modifying hlua.c directly. The function <fct> must return an ERR_*
code. If the errmsg pointer is set on return, it is printed as an alert,
warning, or notice depending on the error code. If ERR_ABORT or ERR_FATAL is
returned, haproxy will exit with status 1.
- void hap_register_per_thread_alloc(int (*fct)())
This adds a call to function <fct> to the list of functions to be called when
@ -334,18 +324,6 @@ alphanumerically ordered:
is that it allows to register multiple <post> callbacks and to register them
elsewhere in the code.
- REGISTER_HLUA_STATE_INIT(fct)
Registers function <fct> to be called for each new Lua state created by
hlua_init_state(). This allows source files other than hlua.c to register
objects and functions into the Lua API without modifying hlua.c directly.
This is done by registering a call to hap_register_hlua_state_init(fct) at
stage STG_REGISTER. The function <fct> must be of type
(int (*fct)(lua_State *L, char **errmsg)) and must return an ERR_* code.
If the errmsg pointer is set on return, it is printed as an alert, warning,
or notice depending on the error code. If ERR_ABORT or ERR_FATAL is returned,
haproxy will exit with status 1.
- REGISTER_PER_THREAD_ALLOC(fct)
Registers a call to register_per_thread_alloc(fct) at stage STG_REGISTER.

View file

@ -1,86 +0,0 @@
2025-08-13 - Memory allocation in HAProxy 3.3
The vast majority of dynamic memory allocations are performed from pools. Pools
are optimized to store pre-calibrated objects of the right size for a given
usage, try to favor locality and hot objects as much as possible, and are
heavily instrumented to detect and help debug a wide class of bugs including
buffer overflows, use-after-free, etc.
For objects of random sizes, or those used only at configuration time, pools
are not suited, and the regular malloc/free family is available, in addition of
a few others.
The standard allocation calls are intercepted at the code level (#define) when
the code is compiled with -DDEBUG_MEM_STATS. For this reason, these calls are
redefined as macros in "bug.h", and one must not try to use the pointers to
such functions, as this may break DEBUG_MEM_STATS. This provides fine-grained
stats about allocation/free per line of source code using locally implemented
counters that can be consulted by "debug dev memstats". The calls are
categorized into one of "calloc", "free", "malloc", "realloc", "strdup",
"p_alloc", "p_free", the latter two designating pools. Extra calls such as
memalign() and similar are also intercepted and counted as malloc.
Due to the nature of this replacement, DEBUG_MEM_STATS cannot see operations
performed in libraries or dependencies.
In addition to DEBUG_MEM_STATS, when haproxy is built with USE_MEMORY_PROFILING
the standard functions are wrapped by new ones defined in "activity.c", which
also hold counters by call place. These ones are able to trace activity in
libraries because the functions check the return pointer to figure where the
call was made. The approach is different and relies on a large hash table. The
files, function names and line numbers are not know, but by passing the pointer
to dladdr(), we can often resolve most of these symbols. These operations are
consulted via "show profiling memory". It must first be enabled either in the
global config "profiling.memory on" or the CLI using "set profiling memory on".
Memory profiling can also track pool allocations and frees thanks to knowing
the size of the element and knowing a place where to store it. Some future
evolutions might consider making this possible as well for pure malloc/free
too by leveraging malloc_usable_size() a bit more.
Finally, 3.3 brought aligned allocations. These are made available via a new
family of functions around ha_aligned_alloc() that simply map to either
posix_memalign(), memalign() or _aligned_malloc() for CYGWIN, depending on
which one is available. This latter one requires to pass the pointer to
_aligned_free() instead of free(), so for this reason, all aligned allocations
have to be released using ha_aligned_free(). Since this mostly happens on
configuration elements, in practice it's not as inconvenient as it can sound.
These functions are in reality macros handled in "bug.h" like the previous
ones in order to deal with DEBUG_MEM_STATS. All "alloc" variants are reported
in memstats as "malloc". All "zalloc" variants are reported in memstats as
"calloc".
The currently available allocators are the following:
- void *ha_aligned_alloc(size_t align, size_t size)
- void *ha_aligned_zalloc(size_t align, size_t size)
Equivalent of malloc() but aligned to <align> bytes. The alignment MUST be
at least as large as one word and MUST be a power of two. The "zalloc"
variant also zeroes the area on success. Both return NULL on failure.
- void *ha_aligned_alloc_safe(size_t align, size_t size)
- void *ha_aligned_zalloc_safe(size_t align, size_t size)
Equivalent of malloc() but aligned to <align> bytes. The alignment is
automatically adjusted to the nearest larger power of two that is at least
as large as a word. The "zalloc" variant also zeroes the area on
success. Both return NULL on failure.
- (type *)ha_aligned_alloc_typed(size_t count, type)
(type *)ha_aligned_zalloc_typed(size_t count, type)
This macro returns an area aligned to the required alignment for type
<type>, large enough for <count> objects of this type, and the result is a
pointer of this type. The goal is to ease allocation of known structures
whose alignment is not necessarily known to the developer (and to avoid
encouraging to hard-code alignment). The cast in return also provides a
last-minute control in case a wrong type is mistakenly used due to a poor
copy-paste or an extra "*" after the type. When DEBUG_MEM_STATS is in use,
the type is stored as a string in the ".extra" field so that it can be
displayed in "debug dev memstats". The "zalloc" variant also zeroes the
area on success. Both return NULL on failure.
- void ha_aligned_free(void *ptr)
Frees the area pointed to by ptr. It is the equivalent of free() but for
objects allocated using one of the functions above.

View file

@ -245,30 +245,6 @@ mt_list_pop(l)
#=========#
mt_list_pop_locked(l)
Removes the list's first element, returns it locked. If the list was empty,
NULL is returned. A macro MT_LIST_POP_LOCKED() is provided for a
more convenient use; instead of returning the list element, it will return
the structure holding the element, taking care of preserving the NULL.
before:
+---+ +---+ +---+ +---+ +---+ +---+ +---+
#=>| L |<===>| A |<===>| B |<===>| C |<===>| D |<===>| E |<===>| F |<=#
# +---+ +---+ +---+ +---+ +---+ +---+ +---+ #
#=====================================================================#
after:
+---+ +---+ +---+ +---+ +---+ +---+
#=>| L |<===>| B |<===>| C |<===>| D |<===>| E |<===>| F |<=#
# +---+ +---+ +---+ +---+ +---+ +---+ #
#===========================================================#
+---+
# x| A |x #
# +---+ #
#=========#
_mt_list_lock_next(elt)
Locks the link that starts at the next pointer of the designated element.
The link is replaced by two locked pointers, and a pointer to the next

View file

@ -1,4 +1,4 @@
2025-08-11 - Pools structure and API
2022-02-24 - Pools structure and API
1. Background
-------------
@ -239,6 +239,10 @@ currently in use:
+------------+ +------------+ / is set at build time
or -dMtag at boot time
Right now no provisions are made to return objects aligned on larger boundaries
than those currently covered by malloc() (i.e. two pointers). This need appears
from time to time and the layout above might evolve a little bit if needed.
4. Storage in the process-wide shared pool
------------------------------------------
@ -353,22 +357,6 @@ struct pool_head *create_pool(char *name, uint size, uint flags)
returned pointer is the new (or reused) pool head, or NULL upon error.
Pools created this way must be destroyed using pool_destroy().
struct pool_head *create_aligned_pool(char *name, uint size, uint align, uint flags)
Create a new pool named <name> for objects of size <size> bytes and
aligned to <align> bytes (0 meaning use the platform's default). Pool
names are truncated to their first 11 characters. Pools of very similar
size will usually be merged if both have set the flag MEM_F_SHARED in
<flags>. When DEBUG_DONT_SHARE_POOLS was set at build time, or
"-dMno-merge" is passed on the executable's command line, the pools
also need to have the exact same name to be merged. In addition, unless
MEM_F_EXACT is set in <flags>, the object size will usually be rounded
up to the size of pointers (16 or 32 bytes). MEM_F_UAF may be set on a
per-pool basis to enable the UAF detection only for this specific pool,
saving the massive overhead of global usage. The name that will appear
in the pool upon merging is the name of the first created pool. The
returned pointer is the new (or reused) pool head, or NULL upon error.
Pools created this way must be destroyed using pool_destroy().
void *pool_destroy(struct pool_head *pool)
Destroy pool <pool>, that is, all of its unused objects are freed and
the structure is freed as well if the pool didn't have any used objects
@ -482,20 +470,6 @@ complicate maintenance.
A few macros exist to ease the declaration of pools:
DECLARE_ALIGNED_POOL(ptr, name, size, align)
Placed at the top level of a file, this declares a global memory pool
as variable <ptr>, name <name> and size <size> bytes per element, all
of which will be aligned to <align> bytes. The alignment will be
rounded up to the next power of two and will be at least as large as a
word on the platform. This is made via a call to REGISTER_ALIGNED_POOL()
and by assigning the resulting pointer to variable <ptr>. <ptr> will be
created of type "struct pool_head *". If the pool needs to be visible
outside of the function (which is likely), it will also need to be
declared somewhere as "extern struct pool_head *<ptr>;". It is
recommended to place such declarations very early in the source file so
that the variable is already known to all subsequent functions which
may use it.
DECLARE_POOL(ptr, name, size)
Placed at the top level of a file, this declares a global memory pool
as variable <ptr>, name <name> and size <size> bytes per element. This
@ -507,17 +481,6 @@ DECLARE_POOL(ptr, name, size)
declarations very early in the source file so that the variable is
already known to all subsequent functions which may use it.
DECLARE_STATIC_ALIGNED_POOL(ptr, name, size, align)
Placed at the top level of a file, this declares a global memory pool
as variable <ptr>, name <name> and size <size> bytes per element, all
of which will be aligned to <align> bytes. The alignment will be
rounded up to the next power of two and will be at least as large as a
word on the platform. This is made via a call to REGISTER_ALIGNED_POOL()
and by assigning the resulting pointer to local variable <ptr>. <ptr>
will be created of type "static struct pool_head *". It is recommended
to place such declarations very early in the source file so that the
variable is already known to all subsequent functions which may use it.
DECLARE_STATIC_POOL(ptr, name, size)
Placed at the top level of a file, this declares a static memory pool
as variable <ptr>, name <name> and size <size> bytes per element. This
@ -527,42 +490,6 @@ DECLARE_STATIC_POOL(ptr, name, size)
early in the source file so that the variable is already known to all
subsequent functions which may use it.
DECLARE_STATIC_TYPED_POOL(ptr, name, type[, extra[, align]])
Placed at the top level of a file, this declares a global memory pool
as variable <ptr>, name <name>, and configured to allocate objects of
type <type>. It is optionally possible to grow these objects by <extra>
bytes (e.g. if they contain some variable length data at the end), and
to force them to be aligned to <align> bytes. If only alignment is
desired without extra data, pass 0 as <extra>. Alignment must be at
least as large as the type's, and a control is enforced at declaration
time so that objects cannot be less aligned than what is promised to
the compiler. The default alignment of zero indicates that the default
one (from the type) should be used. This is made via a call to
REGISTER_ALIGNED_POOL() and by assigning the resulting pointer to local
variable <ptr>. <ptr> will be created of type "static struct pool_head
*". It is recommended to place such declarations very early in the
source file so that the variable is already known to all subsequent
functions which may use it.
DECLARE_TYPED_POOL(ptr, name, type[, extra[, align]])
Placed at the top level of a file, this declares a global memory pool
as variable <ptr>, name <name>, and configured to allocate objects of
type <type>. It is optionally possible to grow these objects by <extra>
bytes (e.g. if they contain some variable length data at the end), and
to force them to be aligned to <align> bytes. If only alignment is
desired without extra data, pass 0 as <extra>. Alignment must be at
least as large as the type's, and a control is enforced at declaration
time so that objects cannot be less aligned than what is promised to
the compiler. The default alignment of zero indicates that the default
one (from the type) should be used. This is made via a call to
REGISTER_ALIGNED_POOL() and by assigning the resulting pointer to
variable <ptr>. <ptr> will be created of type "struct pool_head *". If
the pool needs to be visible outside of the function (which is likely),
it will also need to be declared somewhere as "extern struct pool_head
*<ptr>;". It is recommended to place such declarations very early in
the source file so that the variable is already known to all subsequent
functions which may use it.
6. Build options
----------------

View file

@ -1,238 +0,0 @@
HAPROXY CORE PRINCIPLES
0. RULE ZERO: EXCEPTIONS AND JUSTIFICATION
- These rules are mandatory; violations are bugs unless explicitly justified.
- A violation is acceptable if accompanied by a comment explaining WHY the
standard approach was insufficient (e.g., "Performance-critical bypass").
- Reviews should flag unjustified violations but accept commented ones.
1. PROJECT ORGANIZATION
- header files all under "include/", and split between haproxy/<file>-t.h for
type definitions (types, enums, structures), and haproxy/<file>.h for static
definitions and exported symbols. A few imported libs under include/import.
- C source files in src/.
- some API doc in doc/internals/api/ (not always up to date, check date or
version at the top).
2. ENVIRONMENT AND DATA TYPES
- The project targets 32/64-bit POSIX systems (little or big endian).
- Char is signed or unsigned 8-bit, short signed 16-bit, int signed 32-bit.
- Long and pointers always match the native word size. Long long is 64-bit.
- Aliases: uchar (unsigned char), uint (unsigned int), ulong (unsigned long),
ushort (unsigned short), ullong (unsigned long long), llong (long long),
schar (signed char).
- size_t is always the same size as long, but its underlying type is often
uint on 32-bit and ulong on 64-bit. This is a frequent source of build
errors on 32-bit platforms (e.g. passing a size_t where a long* is
expected, or printing one with "%lu"); always cast in printf() (ulong
with "%lu").
- Main platforms are x86_64 and aarch64 with high thread counts (>=64).
- Unaligned accesses are permitted for archs that support them; portable
wrappers in net_helper.h (read_u32(), write_u32() etc).
- signed integer wrapping well-defined via -fwrapv.
- arch-specific asm() statements OK as long as equivalent C-code exists for
generic archs.
- Pointer arithmetics used a lot via container_of(), offset_of(), and void*
casts.
- Floating point not used.
3. MEMORY MANAGEMENT AND POOLS
- Pools are used for runtime allocation; malloc/free are for boot code only.
- pool_alloc() semantics match malloc(); the return must always be tested.
- pool_alloc() and malloc() are not interchangeable: memory obtained from one
must not be released using the other's free function.
- pool_free() semantics match free(); it is a no-op on NULL.
- pool_free() makes the pointer invalid immediately; it must not be touched
or passed to pool_free() again.
- Memory allocated from one pool must be released to the same pool.
- ha_free() calls free() and sets the pointer to NULL before returning.
- my_realloc2() frees the original pointer if the allocation fails.
- never leave dangling pointers in structs after free().
4. BUFFER INVARIANTS (struct buffer)
- Buffers are 4-word inline structs used for data in transit (wrapping,
sliding window).
- Members: area (storage), size (capacity), head (offset), data (count).
- The area pointer is allowed to be NULL when size is zero.
- always true: 0<=data<=size; always true when size>0: 0<=head<size.
- contents start at <head>, for <data> bytes, and may wrap at the end of the
storage area (area+size).
- API (b_*, in buf.h and dynbuf.h) supports empty or unallocated buffers.
- idempotent functions b_alloc() and b_free() use pools to manage the
storage area and check <size> to know if alloc/free still needed.
- a non-contiguous version exists (ncbuf, ncbmbuf), allowing holes anywhere
in data. ncbuf mandates holes of at least 8 bytes, while ncbmbuf relies on
a bitmap of populated places.
- another string API exists, "ist", representing a pointer and a length in a
struct that is returned by inline functions and macros. It is described in
doc/internals/api/ist.txt
- buffers can switch to and from HTX, which is an internal representation of
HTTP elements, with an API supporting header addition/modification/removal,
start-line manipulation, data appending/consumption etc. HTX functions are
all prefixed with "htx_". Between htx_from_buf() and htx_to_buf(), only the
HTX API may be used, not the b_* API.
5. DATA MANIPULATION (CHUNKS, TRASH, LISTS, TREES)
- Chunks use the buffer API but are NOT allowed to wrap.
- Chunks are used for linear operations like chunk_printf().
- Trash is a thread-local temporary buffer; scope stays within the caller.
- trash always the same size as a buffer (global.tune.bufsize).
- get_trash_chunk() provides rotating thread-local trash chunks. Since almost
any function may itself call get_trash_chunk(), a returned chunk is only
guaranteed valid until the next call into another function and must not be
held across such a call. The rotation lets a single function safely use up
to 3 distinct chunks at once for its own data manipulation.
- For longer lived trash chunks, alloc_trash_chunk() is available but must be
released using free_trash_chunk() on leaving.
- standard doubly-linked lists (struct list) are provided via macros LIST_*.
- LIST_INIT() must be used on new heads and elements. LIST_DELETE() only
removes the element and does not reinitialize it, so the idempotent
LIST_DEL_INIT() is generally preferred. Iterators like list_for_each_* are
available, some safe against item removal. See doc/internals/api/list.txt
for details (grep -i "^list_" to list available macros).
- thread-safe doubly-linked lists (struct mt_list) are provided via macros
mt_list_*. They work like lists and use compatible storage, though they may
not be mixed. See doc/internals/api/mt_list.txt (grep -i "^mt_list_" to
list available operations).
- elastic binary trees (ebtree) are used for fast access (O(logN) operations,
O(1) deletion). Idempotent deletion. Main functions are lookup, insert,
delete, first, next, with type-based prefix eb{32,64,st,mb,pt}_*().
- compact elastic binary trees (cebtree) are used for read-mostly focusing on
space savings (O(logN) operations, but higher cost than ebtree). Same ops
as ebtree, with type-based prefix ceb{32,u32,64,u64,s,is}_*.
6. THREAD SYNCHRONIZATION
- Threads are started at boot (one per CPU) and persist for the process life,
arranged in thread groups (tg) by cache locality.
- Each thread has its own polling loop and scheduler. Total parallelism.
- thread_isolate()/thread_release() for total thread isolation (very heavy).
- "tid" always current thread number, "th_ctx" always current thread's context,
"ti" current thread info.
- "tgid" always current tg number, "tg_ctx" current tg context.
- HA_ATOMIC_* for atomic operations on integers and pointers (includes load
and store). DWCAS is available on some platforms but requires an equivalent
fallback on the others (possibly a more complex operation, e.g. emulation
using two or more CAS).
- The _HA_ATOMIC_* version (leading underscore) do not use barriers so these
must be explicit (__ha_barrier_*).
- Atomic loops must use CPU relaxation or exponential back-off.
- For multiple changes at once, threads may use spinlocks (HA_SPIN_LOCK()/
HA_SPIN_UNLOCK/HA_SPIN_TRYLOCK), and upgradable RW locks (HA_RWLOCK_*) if
read accesses dominate.
- No sleeping locks (mutex etc), only spinning/rwlocks/atomic loops.
7. SCHEDULING AND LATENCY
- Latency is critical.
- No runtime filesystem access, no blocking calls, no long loops.
- Complex processing must be split into small steps; the task must yield.
- CPUs are not dedicated to haproxy, high risk of a thread being interrupted
by another process if it works too long, catastrophic if it happens with a
lock held.
- A watchdog kills the process if a task hogs a CPU for > few milliseconds.
- Tasks vs Tasklets: Tasks have tree storage (rq) and timers (wq); tasklets
use list elements instead of rq and are smaller (no wq). Only task.c/h may
distinguish rq vs list access.
- Tasks are aliased to tasklet while they are running (hence why some
functions cast task to tasklets and conversely to access certain fields).
- inter-thread task/tasklet wakeups always safe using the task_* API.
- task/tasklet->state field must always be accessed atomically.
8. ARCHITECTURAL LAYERS (MUX AND STREAMS)
- Naming: Lower layer (multiplexed), attached to the connection uses suffix
'c' (h1c, h2c, qcc, muxc); Upper layer (demultiplexed/application, often a
stream) uses suffix 's' (h1s, h2s, qcs, muxs).
- Application layer stream (struct stream) has two stream connectors (stconn):
front (scf) and back (scb). Responsible for processing requests/responses,
deciding which server to route it, finding a backend connection or creating
one, and exchanging data between the two sides.
- Stream connectors link to a muxs or applet via a stream endpoint descriptor
(sedesc/sd), and exchange data via buffers, which for an HTTP muxs are HTX
buffers containing HTX blocks.
- The sd carries the shared context between layers.
- When a stream detaches from a mux, a new sd is allocated for the stream and
the mux keeps its previous sd: stconn and muxs both always have a valid sd.
- Front connections/streams are tied to the creator thread forever.
- Idle back connections can be stolen via mux->takeover(), but become
thread-bound once a stream attaches. => all streams of a mux are on the
same thread.
- session vs connection vs stream: connection is transport; session lasts for
the client connection's life; stream are request/response pairs.
- applets carry a context specific to the service being executed or the CLI
command in appctx->svcctx, and this one is always zeroed before the handler
is first called.
9. FUNCTION RETURN CONVENTIONS
- Boolean style: Functions named as actions/sentences return 0 (failure) or
non-zero (success).
- Integer style: some syscall-like functions return <0 (error) or >=0 (success).
- Tri-state style, e.g. counts: <0 (error), 0 (no progress), >0 (success).
10. DIAGNOSTICS AND SAFETY
- When DEBUG_STRICT is set, ABORT_NOW() crashes the program immediately, and
BUG_ON(cond[,msg]) crashes the program if the condition is true.
- COUNT_IF() / CHECK_IF() only track if a condition occurs (non-fatal).
- Glitches are counters for uncommon events used to detect hostile behavior.
- strcpy(), strcat() and sprintf() are totally forbidden (the program will
not build).
11. BASIC CODING STYLE
- Linux Kernel-like, but uses tabs for indent, spaces for alignment. Function
definitions have their opening brace on a new line, never on the same line.
- All local variables must be declared at the beginning of the function
block, before any executable statements (gnu89-like).
- Avoid variable shadowing in code blocks.
- Beware of local static and global variables.
- Use const arguments whenever possible.
- Avoid static storage when persistence is not needed.
- Macros in uppercase unless they're used to wrap functions which then get a
leading underscore.
- Explicitly compare against 0 the return of functions that yield an integer
which is not a boolean (e.g. strcmp), unless they return a boolean (e.g.
isalnum) or a pointer (e.g. strchr).
- Unsigned int comparisons to zero never use >0 but !=0 to avoid signedness
mistakes.
- turn non-zero integer to boolean using "!" or "!!".
12. BUILD AND TEST
- Preferred build command:
$ make -j$(nproc) TARGET=linux-glibc OPT_CFLAGS='-std=gnu89 -Os' \
USE_OPENSSL=1 USE_QUIC_OPENSSL_COMPAT=1 USE_QUIC=1 USE_LUA=1
- Individual files can be tested by passing src/file.o as a make argument.
- Compiler warnings are not permitted for new code.
13. COMMIT MESSAGES AND DOCUMENTATION
- Commit messages must follow the project's strict format below. Do not try
to learn better from previous commits, which might be wrong during reviews.
- Structure: <TAG>: <location>: <subject> (max ~70 chars), then blank line,
then description.
- Tags:
- CLEANUP: spelling fixes, refactoring, no new code nor functional change.
- MINOR: new feature or low-impact change, may be backported if needed.
- MEDIUM: new feature or change with moderate severity/impact/risk.
- MAJOR: new feature or change with important severity/impact/risk.
- OPTIM: Performance improvements, may always be reverted if it breaks.
- DOC: Documentation updates or fixes.
- BUG/<severity>: Fixes a bug. Specify if regression or long-standing.
Valid severities are MINOR (low impact), MEDIUM (perf/stability risk
in uncommon configs, MAJOR (most configs), CRITICAL (stability risk
without workaround).
- Regressions: Find original commit via `git blame`; designate using
`git log -1 --format='%h ("%s")'` and version via `git describe --tags`.
- Location: subsystem (stream, tasks, mux-h2, qpack etc).
- Description: Explain technical "WHY", "HOW", and technical impact. Explain
how to trigger the bug for developer testing.
- Backports: only for fixes, mention versions ("Must be backported to 3.0").
- Style: No generic messages like "fix(xxx): blah". Be technically precise.
- Do not mix spelling fixes in comments (not important) with other changes.
However it's preferred to have a single commit for many typo fixes at once.
- Spelling mistakes in user-visible parts (doc, logs, traces, error messages)
must be in their own commit (may need backport).
- One commit per bug.
- Example:
BUG/MEDIUM: sample: fix null pointer dereference in h1_parse_line
When parsing malformed headers, the line buffer was not initialized.
This caused a crash on certain edge cases. Let's fix this by always
initializing the line buffer when first calling the parser. This was
brought by commit 04c9e8f5 ("MINOR: add h1_parse_line") in latest -dev
so no backport is needed.

View file

@ -11,7 +11,7 @@ default init, this was controversial but fedora and archlinux already uses it.
At this time HAProxy still had a multi-process model, and the way haproxy is
working was incompatible with the daemon mode.
Systemd is compatible with traditional forking services, but somehow HAProxy
Systemd is compatible with traditionnal forking services, but somehow HAProxy
is different. To work correctly, systemd needs a main PID, this is the PID of
the process that systemd will supervises.
@ -45,7 +45,7 @@ However the wrapper suffered from several problems:
### mworker V1
HAProxy 1.8 got rid of the wrapper which was replaced by the master worker
HAProxy 1.8 got ride of the wrapper which was replaced by the master worker
mode. This first version was basically a reintegration of the wrapper features
within HAProxy. HAProxy is launched with the -W flag, read the configuration and
then fork. In mworker mode, the master is usually launched as a root process,
@ -86,7 +86,7 @@ retrieved automatically.
The master is supervising the workers, when a current worker (not a previous one
from before the reload) is exiting without being asked for a reload, the master
will emit an "exit-on-failure" error and will kill every workers with a SIGTERM
and exits with the same error code than the failed worker, this behavior can be
and exits with the same error code than the failed master, this behavior can be
changed by using the "no exit-on-failure" option in the global section.
While the master is supervising the workers using the wait() function, the
@ -186,8 +186,8 @@ number that can be found in HAPROXY_PROCESSES. With this change the stats socket
in the configuration is less useful and everything can be done from the master
CLI.
With 2.7, the reload mechanism of the master CLI evolved, with previous versions,
this mechanism was asynchronous, so once the `reload` command was received, the
With 2.7, the reload mecanism of the master CLI evolved, with previous versions,
this mecanism was asynchronous, so once the `reload` command was received, the
master would reload, the active master CLI connection was closed, and there was
no way to return a status as a response to the `reload` command. To achieve a
synchronous reload, a dedicated sockpair is used, one side uses a master CLI
@ -208,38 +208,3 @@ starts with -st to achieve a hard stop on the previous worker.
Version 3.0 got rid of the libsystemd dependencies for sd_notify() after the
events of xz/openssh, the function is now implemented directly in haproxy in
src/systemd.c.
### mworker V3
This version was implemented with HAProxy 3.1, the goal was to stop parsing and
applying the configuration in the master process.
One of the caveats of the previous implementation was that the parser could take
a lot of time, and the master process would be stuck in the parser instead of
handling its polling loop, signals etc. Some parts of the configuration parsing
could also be less reliable with third-party code (EXTRA_OBJS), it could, for
example, allow opening FDs and not closing them before the reload which
would crash the master after a few reloads.
The startup of the master-worker was reorganized this way:
- the "discovery" mode, which is a lighter configuration parsing step, only
applies the configuration which need to be effective for the master process.
For example, "master-worker", "mworker-max-reloads" and less than 20 other
keywords that are identified by KWF_DISCOVERY in the code. It is really fast
as it don't need all the configuration to be applied in the master process.
- the master will then fork a worker, with a PROC_O_INIT flag. This worker has
a temporary sockpair connected to the master CLI. Once the worker is forked,
the master initializes its configuration and starts its polling loop.
- The newly forked worker will try to parse the configuration, which could
result in a failure (exit 1), or any bad error code. In case of success, the
worker will send a "READY" message to the master CLI then close this FD. At
this step everything was initialized and the worker can enter its polling
loop.
- The master then waits for the worker, it could:
* receive the READY message over the mCLI, resulting in a successful loading
of haproxy
* receive a SIGCHLD, meaning the worker exited and couldn't load

View file

@ -114,7 +114,7 @@ SHUT RDY ACT
1 1 1 => shut pending
PB: we can land into final shut if one thread disables the FD while another
one that was waiting on it reports it as shut. Theoretically it should be
one that was waiting on it reports it as shut. Theorically it should be
implicitly ready though, since reported. But if no data is reported, it
will be reportedly shut only. And no event will be reported then. This
might still make sense since it's not active, thus we don't want events.

View file

@ -1,53 +0,0 @@
2025/09/16 - SHM stats file storage description and hints
Shm stats file (used to share thread-groupable statistics over multiple
process through the "shm-stats-file" directive) is made of:
- a main header which describes the file version, the processes making
use of it, the common clock source and hints about the number of
objects that are currently stored or provisionned in the file.
- an indefinite number of "objects" blocks coming right after the
main header, all blocks have the same size which is the size of the
maximum underlying object that may be stored. The main header tells
how many objects are stored in the file.
File header looks like this (32/64 bits systems):
0 8 16 32 48 64
+-------+---------+----------------+-------------------+-------------------+
| VERSION | 2 bytes | global_now_ms (global mono date in ms)|
|MAJOR | MINOR | hole | |
+----------------------------------+---------------------------------------+
| global_now_ns (global mono date in ns) |
+--------------------------------------------------------------------------+
| now_offset (offset applied to global monotonic date |
| on startup) |
+--------------------------------------------------------------------------+
| Process slot : | 1byte x 64
| pid | heartbeat (ticks) |
+----------------------------------+---------------------------------------+
| objects | objects slots |
| (used objects) | (available for use) |
+----------------------------------+---------------------------------------+
| padding (for future use) | 128 bytes
+--------------------------------------------------------------------------+
Object block looks like this:
0 8 16 32 48 64
+-------+---------+----------------+-------------------+-------------------+
| GUID | 128 bytes
+ (zero terminated) +
| |
+-------+---------+--------------------------------------------------------+
| tgid | type | padding |
+-------+---------+--------------------------------------------------------+
| users (bitmask of process slots making use of the obj) |
+--------------------------------------------------------------------------+
| object data |
| (version dependent) |
| struct be_counters_shared_tg or |
| struct fe_counters_shared_tg |
+--------------------------------------------------------------------------+
| padding (to anticipate evolutions) | 64 bytes
+--------------------------------------------------------------------------+

View file

@ -1,50 +0,0 @@
2026-03-12 - thread execution context
Thread execution context (thread_exec_ctx) is a combination of type and pointer
that are set in the current running thread at th_ctx->exec_ctx when entering
certain processing (tasks, sample fetch functions, actions, CLI keywords etc).
They're refined along execution, so that a task such as process_stream could
temporarily switch to a converter while evaluating an expression and switch
back to process_stream. They are reported in thread dumps and are mixed with
caller locations for memory profiling. As such they are intentionally not too
precise in order to avoid an explosion of the number of buckets. At the moment,
the level of granularity it provides is sufficient to try to narrow a
misbehaving origin down to a list of keywords. The context types can currently
be:
- something registered via an initcall, with the initcall's location
- something registered via an ha_caller, with the caller's location
- an explicit sample fetch / converter / action / CLI keyword list
- an explicit function (mainly used for actions without keywords)
- a task / tasklet (no distinction is made), using the ->process pointer
- a filter (e.g. compression), via flt_conf, reporting name
- a mux (via the mux_ops, reporting the name)
- an applet (e.g. cache, stats, CLI)
A macro EXEC_CTX_MAKE(type, pointer) makes a thread_exec_ctx from such
values.
A macro EXEC_CTX_NO_RET(ctx, statement) calls a void statement under the
specified context.
A macro EXEC_CTX_WITH_RET(ctx, expr) calls an expression under the specified
context.
Most locations were modified to directly use these macros on the fly, by
retrieving the context from where it was set on the element being evaluated
(e.g. an action rule contains the context inherited by the action keyword
that was used to create it).
In tools.c, chunk_append_thread_ctx() tries to decode the given exec_ctx and
appends it into the provided buffer. It's used by ha_thread_dump_one() and
cli_io_handler_show_activity() for memory profiling. In this latter case,
the detected thread_ctx are reported in the output under brackets prefixed
with "[via ...]" to distinguish call paths to the same allocators.
A good way to test if a context is properly reported is to place a bleeding
malloc() call into one of the monitored functions, e.g.:
DISGUISE(malloc(8));
and issue "show profiling memory" after stressing the function. Its context
must appear on the right with the number of calls.

View file

@ -1,233 +0,0 @@
HAProxy Threat Model & Trust Boundaries
This document defines the security boundaries of HAProxy, explicitly outlining
what does and does not constitute a security vulnerability. Its purpose is to
give reporters, developers and reviewers a single, predictable basis for
judging an issue's real-world impact.
The project's strong preference is to fix issues quickly and in the open.
Public handling gets fixes to users sooner and spares the ecosystem
(distributions in particular) the heavy cost of embargo coordination, which in
practice has rarely served users. Private, coordinated disclosure is reserved
for the few cases whose real-world impact genuinely warrants it, judged from
the severity ordering (section 6) and the mitigations (section 5). An issue
that is technically in scope but contained in practice does not, by itself,
call for an embargo.
These boundaries apply strictly to officially supported, documented builds
running under a sane, production-ready configuration. Security guarantees are
explicitly voided when using opt-in unsafe knobs, undocumented behavior, or
experimental features. A configuration that merely lacks a recommended
hardening step (for instance, no chroot) does not by itself move a
client-triggered bug out of scope; the missing mitigation only widens the
blast radius (sections 5 and 6).
1. ASSETS TO PROTECT
HAProxy sits on the critical path of the services it fronts, so its
availability and the integrity and confidentiality of the configuration and
secrets it holds are all essential to protect. The assets below are not
ranked here; their relative severity is ranked in section 6.
- Integrity and confidentiality of the host and configuration: a compromise
of the network-facing worker must not extend to the filesystem, nor to the
configuration and its dependencies (private keys, Lua scripts, maps,
crt-lists, ACLs). On a properly configured system the default structural
mitigations prevent this, leaving only a compromise of the master process
as a residual path (see section 5).
- Confidentiality of long-lived secrets: TLS private keys and certificates
above all. Unlike transient client data, their disclosure is permanent and
systemic (impersonation and traffic decryption until every key is rotated
and revoked).
- Availability of the proxied service: being on the critical path, keeping
HAProxy serving is paramount. A small, cheap amount of attacker input
must neither consume a disproportionate amount of CPU or memory
(asymmetric DoS, see section 3) nor crash or stall the process.
- Confidentiality and isolation of client data: data belonging to one
connection, stream or client must never leak to another, and process
memory (including uninitialized memory) must never leak to a client.
- Process integrity (memory safety): no RCE, memory corruption or undefined
behaviour (UB) reachable from untrusted input.
- Correct enforcement of the configured policy: access controls, routing and
header manipulations decided by the configuration must not be bypassable
by crafted input.
2. ATTACKER AND ENTRY POINTS
- The reference attacker is an untrusted client able to send arbitrary
bytes to a frontend: raw TCP payloads, HTTP/1, HTTP/2 and HTTP/3 (QUIC)
traffic, and arbitrary TLS handshake records.
- Entry points in scope are therefore the listeners and everything that
parses or transforms client-supplied data: TLS, the HTTP muxes, HTX,
header/URL processing, sample fetches and converters acting on request
data, stick-tables fed by client data, the cache, and the QUIC/H3 stack.
- A secondary untrusted source is the DNS resolver path: even though
nameservers are configured, their answers arrive over UDP and can be
spoofed by an off-path attacker, so the response parser handles
attacker-influenced input.
3. WHAT QUALIFIES AS A SECURITY BUG (IN SCOPE)
- Memory-safety issues (overflow, out-of-bounds, use-after-free, type
confusion, UB) reachable from untrusted client input.
- Cross-client or cross-stream effects: HTTP request smuggling, response
splitting, cache poisoning, and any mixing of data between concurrent
streams or connections (notably in the H2/H3 multiplexers).
- Disclosure of process memory or of another client's data to a client.
- Bypass of a policy that the configuration is meant to enforce (e.g.
defeating an http-request deny/acl through request crafting).
- Asymmetric / algorithmic denial of service: a single or a few cheap
requests causing disproportionate CPU or memory usage (hash-collision
flooding, catastrophic regex backtracking, quadratic parsing, unbounded
allocation, etc). This is distinct from volumetric DoS (see 4).
- Misuse of a third-party library on untrusted input: feeding malformed
client data into OpenSSL, PCRE, Lua, zlib, etc. in a way that corrupts
memory or crashes the process is in scope. A vulnerability inside the
library itself is handled by that library's project, not here.
- Mishandling of spoofable DNS responses: memory corruption, crashes or
cache/state poisoning in the resolver caused by a crafted DNS answer are
in scope, despite nameservers being nominally trusted (see section 2).
4. WHAT DOES NOT QUALIFY (OUT OF SCOPE)
The following do not fall into the security-bug category.
Trusted peers, servers and protocols:
- attacks that require a non-compliant or malicious server: in a reverse
proxy, servers are trusted, or ejected. This covers server-to-client
attacks in general.
- attacks on protocols only used with trusted peers: peers, PROXY protocol,
CIP (NetScaler Client-IP insertion), SOCKS, a local server reached over
an ABNS or UNIX socket, an FCGI server, etc., as well as TLS servers
contacted by the internal httpclient.
- malfunction of a trusted auxiliary service (log server, ring output,
CLI API consumer, etc.).
Privileged or local access (the actor is already trusted):
- any problem triggered through admin access to the CLI.
- anything requiring access to the master CLI.
- anything requiring access to the command line.
- anything requiring write access to the configuration file or any of its
dependencies (Lua scripts, certificates, crt-list, acl, map, etc.).
- anything requiring a configuration running as root, or chrooted to "/"
(i.e. with no effective chroot).
Opt-in unsafe or experimental knobs (the operator disabled a safety):
- anything requiring "experimental-mode on" on the CLI.
- anything requiring "insecure-fork-wanted".
- anything requiring "accept-unsafe-violations-*".
- anything requiring "expose-experimental-directives".
Misconfiguration:
- anything requiring a configuration that emits warnings at boot.
- anything requiring a nonsensical configuration, e.g. a server looping back
to the frontend, non-standard header processing or URL rewriting, or an
excessively large number of headers or excessively large header/body
sizes.
Volumetric or otherwise detectable activity:
- anything requiring such a high and sustained level of activity that it
would be detected and blocked in production (e.g. billions of requests or
connections). This is volumetric DoS, as opposed to the asymmetric DoS of
section 3.
Inherent protocol limitations:
- anything that is a limitation of a standard protocol rather than an
implementation flaw. For example, HTTP/1 has no way to abort a single
transfer without closing the connection, so a client aborting a transfer
will necessarily cause the corresponding server-side connection to be
closed; this is by design of the protocol, not a vulnerability.
Features that are not security boundaries:
- the stats page, including its admin mode, relies on HTTP basic
authentication and was never meant to be a security boundary. Exposing a
public-facing, admin-enabled stats page is therefore not covered.
- configuring a listener to accept the PROXY protocol or CIP from senders
that are not restricted to trusted ones is a misconfiguration: these
headers are believed on trust, so the listener must be reachable only by
the trusted L4/L7 component that prepends them.
Side channels:
- cryptographic and micro-architectural side channels (timing, cache,
speculative execution, etc.) are out of scope. Constant-time handling of
secrets is pursued on a best-effort basis as ordinary hardening where it
clearly matters, but observable timing or resource variations are not
handled as security bugs.
Log integrity:
- escaping of data emitted to logs is a configuration responsibility.
Injection of control characters or forged fields through logged client
data (e.g. when default escaping is disabled, or when a downstream log
consumer mis-parses) is not covered.
5. DEFENSE IN DEPTH (DEFAULT HARDENING)
A correctly deployed HAProxy combines several built-in mitigations that
bound the impact of a successful compromise. These are deliberately taken
into account when assessing the real-world severity of an issue and the
handling it deserves: when one of them contains the practical impact of a
bug, that bug rarely warrants a coordinated embargo and is usually better
fixed quickly and in the open, where users get the fix sooner. They lower
severity, not the obligation to fix: an exploitable memory-safety bug
reachable from client input is still corrected as a bug.
- No fork()/exec() in the worker: the worker never forks nor runs external
programs, so an attacker who achieves code execution has little ability
to spawn a shell or launch persistent background code. ("insecure-fork-
wanted" deliberately disables this and is itself out of scope, see
section 4.)
- chroot and privilege drop: in the sane configuration this document
assumes, the worker drops to an unprivileged user/group and chroots into
an empty, unwritable directory. Injected code therefore has no filesystem
access and very limited means to act on the host.
- Activity watchdog: a thread that stops making progress, e.g. hijacked
into an attacker-controlled loop or otherwise stuck, no longer services
the event loop; the watchdog detects this lack of activity and kills the
process after a few seconds rather than letting it be silently held.
- Master/worker separation: only the worker is exposed to the network and
runs the parsers reachable by clients, and it is the unprivileged,
chrooted process. The master keeps privileges and filesystem access but
has no network exposure. The master must therefore be protected as the
trusted, more privileged component; an attacker is assumed to face only
the worker. The master must under no circumstances be reachable from the
worker (e.g. a master CLI bound to a TCP socket such as localhost is
trivially reachable from compromised worker code and defeats this critical
separation).
6. SEVERITY ORDERING
The worst-case outcomes below are ranked by their realistic impact on a
standard configuration, from most to least severe, and the effort spent
guarding against each is proportional to that severity. The ranking reflects
the master/worker privilege split and the containment provided by the
section-5 mitigations.
1. Remote code execution in the master process. The master is privileged
and has filesystem access, so compromising it defeats every
containment, leaks every secret, and can subvert or take down the
whole service.
2. Chosen disclosure of long-lived secrets, TLS private keys and
certificates above all. Unlike an outage the damage is permanent and
silent: stolen keys allow impersonation, interception and, absent
forward secrecy, decryption of captured traffic, until every affected
key is rotated and revoked across the ecosystem; a restart does not
undo it. "Chosen" sets this rank, not scope: any disclosure of process
memory or of another client's data to a client is in scope (section 3);
this top rank is reserved for a targeted exfiltration, where the
attacker steers the read to a known secret. A leak that cannot be
steered toward a specific secret is still an in-scope disclosure bug,
but ranks far lower - often no worse than the crash such a read tends
to cause first.
3. Crash of the master process. It brings the entire service down and
prevents workers from being respawned: a full but recoverable outage.
4. Crash of the worker process. A transient outage: in-flight connections
are lost and traffic is interrupted for the fraction of a second it
takes to respawn.
5. Remote code execution in the worker process. Contained by no-fork,
chroot, privilege drop and the watchdog, its availability impact is
usually below a worker crash, except in the unlikely case where it
unlocks the chosen disclosure of level 2, which is hard to reach
through the internals from injected code.
6. Policy bypass. Serious, but with no direct availability impact.
7. SECURITY-RELEVANT INVARIANTS AND DEFAULTS
The values below define the conditions HAProxy is designed to operate
within, and may be relied upon by parsing and processing code. A suspected
vulnerability that can only be triggered by conditions outside them
(typically values pushed beyond the stated limits) does not qualify as
security-relevant:
- trash buffers and struct buffer storage are always at least a few kB.
- default buffer size is 16 kB (15 kB max input, as 1 kB is reserved for
rewrites), tunable up to <256 MB.
- default log line is 1 kB, tunable up to <=64 kB.

View file

@ -21,7 +21,7 @@ falls back to CLOCK_REALTIME. The former is more accurate as it really counts
the time spent in the process, while the latter might also account for time
stuck on paging in etc.
Then wdt_ping() is called to arm the timer. It's set to trigger every
Then wdt_ping() is called to arm the timer. t's set to trigger every
<wdt_warn_blocked_traffic_ns> interval. It is also called by wdt_handler()
to reprogram a new wakeup after it has ticked.
@ -37,18 +37,15 @@ If the thread was not marked as stuck, it's verified that no progress was made
for at least one second, in which case the TH_FL_STUCK flag is set. The lack of
progress is measured by the distance between the thread's current cpu_time and
its prev_cpu_time. If the lack of progress is at least as large as the warning
threshold, then the signal is bounced to the faulty thread if it's not the
current one. Since this bounce is based on the time spent without update, it
already doesn't happen often.
threshold and no context switch happened since last call, ha_stuck_warning() is
called to emit a warning about that thread. In any case the context switch
counter for that thread is updated.
Once on the faulty thread, two checks are performed:
1) if the thread was already marked as stuck, then the thread is considered
as definitely stuck, and ha_panic() is called. It will not return.
2) a check is made to verify if the scheduler is still ticking, by reading
and setting a variable that only the scheduler can clear when leaving a
task. If the scheduler didn't make any progress, ha_stuck_warning() is
called to emit a warning about that thread.
If the thread was already marked as stuck, then the thread is considered as
definitely stuck. Then ha_panic() is directly called if the thread is the
current one, otherwise ha_kill() is used to resend the signal directly to the
target thread, which will in turn go through this handler and handle the panic
itself.
Most of the time there's no panic of course, and a wdt_ping() is performed
before leaving the handler to reprogram a check for that thread.
@ -64,12 +61,12 @@ set TAINTED_WARN_BLOCKED_TRAFFIC.
ha_panic() uses the current thread's trash buffer to produce the messages, as
we don't care about its contents since that thread will never return. However
ha_stuck_warning() instead uses a local 8kB buffer in the thread's stack.
ha_stuck_warning() instead uses a local 4kB buffer in the thread's stack.
ha_panic() will call ha_thread_dump_fill() for each thread, to complete the
buffer being filled with each thread's dump messages. ha_stuck_warning() only
calls ha_thread_dump_one(), which works on the current thread. In both cases
the message is then directly sent to fd #2 (stderr) and ha_thread_dump_done()
is called to release the dumped thread.
calls the function for the current thread. In both cases the message is then
directly sent to fd #2 (stderr) and ha_thread_dump_one() is called to release
the dumped thread.
Both print a few extra messages, but ha_panic() just ends by looping on abort()
until the process dies.
@ -113,19 +110,13 @@ ha_dump_backtrace() before returning.
ha_dump_backtrace() produces a backtrace into a local buffer (100 entries max),
then dumps the code bytes nearby the crashing instrution, dumps pointers and
tries to resolve function names, and sends all of that into the target buffer.
On some architectures (x86_64, arm64), it will also try to detect and decode
call instructions and resolve them to called functions.
3. Improvements
---------------
The symbols resolution is extremely expensive, particularly for the warnings
which should be fast. But we need it, it's just unfortunate that it strikes at
the wrong moment. At least ha_dump_backtrace() does disable signals while it's
resolving, in order to avoid unwanted re-entrance. In addition, the called
function resolve_sym_name() uses some locking and refrains from calling the
dladdr family of functions in a re-entrant way (in the worst case only well
known symbols will be resolved)..
the wrong moment.
In an ideal case, ha_dump_backtrace() would dump the pointers to a local array,
which would then later be resolved asynchronously in a tasklet. This can work

View file

@ -1,7 +1,7 @@
-----------------------
HAProxy Starter Guide
-----------------------
version 3.5
version 3.2
This document is an introduction to HAProxy for all those who don't know it, as
@ -97,9 +97,6 @@ to the mailing list whose responses are present in these documents.
protocol which is implemented by HAProxy and a number of third party
products.
- security.txt : how to report a security issue, and what does and does not
qualify as a vulnerability.
- README : how to build HAProxy from sources
@ -1689,7 +1686,15 @@ information you might later regret. Since the issue tracker presents itself as
a very long thread, please avoid pasting very long dumps (a few hundreds lines
or more) and attach them instead.
If you believe you may have found a security issue, please refer to the file
doc/security.txt. It explains what does and does not qualify as a vulnerability
in HAProxy, and how to report a genuine one privately. Most suspected issues
turn out to be ordinary bugs that are better reported as described above.
If you've found what you're absolutely certain can be considered a critical
security issue that would put many users in serious trouble if discussed in a
public place, then you can send it with the reproducer to security@haproxy.org.
A small team of trusted developers will receive it and will be able to propose
a fix. We usually don't use embargoes and once a fix is available it gets
merged. In some rare circumstances it can happen that a release is coordinated
with software vendors. Please note that this process usually messes up with
eveyone's work, and that rushed up releases can sometimes introduce new bugs,
so it's best avoided unless strictly necessary; as such, there is often little
consideration for reports that needlessly cause such extra burden, and the best
way to see your work credited usually is to provide a working fix, which will
appear in changelogs.

View file

@ -893,9 +893,7 @@ Core class
**context**: init, task, action
This function returns a new object of a *httpclient* class. An *httpclient*
object must be used to process one and only one request. It must never be
reused to process several requests.
This function returns a new object of a *httpclient* class.
:returns: A :ref:`httpclient_class` object.
@ -935,7 +933,7 @@ Core class
Give back the hand at the HAProxy scheduler. Unlike :js:func:`core.yield`
the task will not be woken up automatically to resume as fast as possible.
Instead, it will wait for an event to wake the task. If milliseconds argument
is provided then the Lua execution will be automatically resumed passed this
is provided then the Lua excecution will be automatically resumed passed this
delay even if no event caused the task to wake itself up.
:param integer milliseconds: automatic wakeup passed this delay. (optional)
@ -945,7 +943,7 @@ Core class
**context**: task, action
Give back the hand at the HAProxy scheduler. It is used when the LUA
processing consumes a lot of processing time. Lua execution will be resumed
processing consumes a lot of processing time. Lua excecution will be resumed
automatically (automatic reschedule).
.. js:function:: core.parse_addr(address)
@ -1031,12 +1029,6 @@ Core class
Be careful when subscribing to this type since many events might be
generated.
**ACME** Family:
* **ACME_DEPLOY**: when a dns-01 challenge TXT record must be deployed
externally before HAProxy can proceed with the ACME challenge
* **ACME_NEWCERT**: when a new certificate is successfully installed
.. Note::
Use **SERVER** in **event_types** to subscribe to all server events types
at once. Note that this should only be used for testing purposes since a
@ -1054,8 +1046,7 @@ Core class
* **event** (*string*): the event type (one of the **event_types** specified
when subscribing)
* **event_data**: specific to each event family (For **SERVER** family,
a :ref:`server_event_class` object; for **ACME** family,
a :ref:`acme_event_class` object)
a :ref:`server_event_class` object)
* **sub**: class to manage the subscription from within the event
(a :ref:`event_sub_class` object)
* **when**: timestamp corresponding to the date when the event was generated.
@ -1096,13 +1087,18 @@ Core class
perform the heavy job in a dedicated task and allow remaining events to be
processed more quickly.
.. js:function:: core.use_native_mailers_config()
.. js:function:: core.disable_legacy_mailers()
**context**: body
**LEGACY**
Inform haproxy that the script will make use of the native "mailers"
config section (although legacy). In other words, inform haproxy that
:js:func:`Proxy.get_mailers()` will be used later in the program.
**context**: body, init
Disable the sending of email alerts through the legacy email sending
function when mailers are used in the configuration.
Use this when sending email alerts directly from lua.
:see: :js:func:`Proxy.get_mailers()`
.. _proxy_class:
@ -1231,14 +1227,8 @@ Proxy class
**LEGACY**
Returns a table containing legacy mailers config (from haproxy configuration
file) for the current proxy or nil if mailers are not available for the proxy.
.. warning::
When relying on :js:func:`Proxy.get_mailers()` to retrieve mailers
configuration, :js:func:`core.use_native_mailers_config()` must be called
first from body or init context to inform haproxy that Lua makes use of the
legacy mailers config.
Returns a table containing mailers config for the current proxy or nil
if mailers are not available for the proxy.
:param class_proxy px: A :ref:`proxy_class` which indicates the manipulated
proxy.
@ -1255,6 +1245,10 @@ ProxyMailers class
This class provides mailers config for a given proxy.
If sending emails directly from lua, please consider
:js:func:`core.disable_legacy_mailers()` to disable the email sending from
haproxy. (Or email alerts will be sent twice...)
.. js:attribute:: ProxyMailers.track_server_health
Boolean set to true if the option "log-health-checks" is configured on
@ -2587,9 +2581,7 @@ HTTPClient class
.. js:class:: HTTPClient
The httpclient class allows issue of outbound HTTP requests through a simple
API without the knowledge of HAProxy internals. Any instance must be used to
process one and only one request. It must never be reused to process several
requests.
API without the knowledge of HAProxy internals.
.. js:function:: HTTPClient.get(httpclient, request)
.. js:function:: HTTPClient.head(httpclient, request)
@ -3924,25 +3916,21 @@ AppletTCP class
*size* is missing, the function tries to read all the content of the stream
until the end. An optional timeout may be specified in milliseconds. In this
case the function will return no longer than this delay, with the amount of
available data, or nil if there is no data. An empty string is returned if the
connection is closed.
available data (possibly none).
:param class_AppletTCP applet: An :ref:`applettcp_class`
:param integer size: the required read size.
:returns: return nil if the timeout has expired and no data was available but
can still be received. Otherwise, a string is returned, possibly an empty
string if the connection is closed.
:returns: always return a string, the string can be empty if the connection is
closed.
.. js:function:: AppletTCP.try_receive(applet)
Reads available data from the TCP stream and returns immediately. Returns a
string containing read bytes or nil if no bytes are available at that time. An
empty string is returned if the connection is closed.
string containing read bytes that may possibly be empty if no bytes are
available at that time.
:param class_AppletTCP applet: An :ref:`applettcp_class`
:returns: return nil if no data was available but can still be
received. Otherwise, a string is returned, possibly an empty string if the
connection is closed.
:returns: always return a string, the string can be empty.
.. js:function:: AppletTCP.send(appletmsg)
@ -4619,27 +4607,6 @@ HTTPMessage class
data by default.
:returns: an integer containing the amount of bytes copied or -1.
.. js:function:: HTTPMessage.set_body_len(http_msg, length)
This function changes the expected payload length of the HTTP message
**http_msg**. **length** can be an integer value. In that case, a
"Content-Length" header is added with the given value. It is also possible to
pass the **"chunked"** string instead of an integer value to force the HTTP
message to be chunk-encoded. In that case, a "Transfer-Encoding" header is
added with the "chunked" value. In both cases, all existing "Content-Length"
and "Transfer-Encoding" headers are removed.
This function should be used in the filter context to be able to alter the
payload of the HTTP message. The internal state of the HTTP message is updated
accordingly. :js:func:`HTTPMessage.add_header()` or
:js:func:`HTTPMessage.set_header()` functions must be used in that case.
:param class_httpmessage http_msg: The manipulated HTTP message.
:param type length: The new payload length to set. It can be an integer or
the string "chunked".
:returns: true if the payload length was successfully updated, false
otherwise.
.. js:function:: HTTPMessage.set_eom(http_msg)
This function set the end of message for the HTTP message **http_msg**.
@ -4746,75 +4713,6 @@ CertCache class
CertCache.set{filename="certs/localhost9994.pem.rsa", crt=crt}
ACME class
==========
.. js:class:: ACME
This class provides access to the ACME (Automatic Certificate Management
Environment) subsystem. It allows Lua scripts to interact with ongoing ACME
certificate challenges.
.. js:function:: ACME.challenge_ready(crt, dns)
Marks the ACME challenge for domain <dns> in certificate <crt> as ready.
Returns the number of remaining challenges, or 0 if all challenges are ready
and validation has been triggered. Raises a Lua error if the certificate or
domain is not found.
:param string crt: The filename of the certificate.
:param string dns: The domain name for which the challenge is ready.
:returns: The number of remaining challenges (integer), or 0 when all
challenges are done and validation has been triggered.
.. _acme_event_class:
AcmeEvent class
===============
.. js:class:: AcmeEvent
This class is provided with every **ACME** event.
See :js:func:`core.event_sub()` for more info.
.. js:attribute:: AcmeEvent.crtname
Contains the certificate store name.
.. js:attribute:: AcmeEvent.domain
Contains the domain being challenged.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.thumbprint
Contains the account key JWK thumbprint.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.dns_record
Contains the DNS TXT record value that must be set at
``_acme-challenge.<domain>``.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.provider
Contains the DNS provider name configured in the ACME section.
Only set if a provider was configured.
Only available for **ACME_DEPLOY** events.
.. js:attribute:: AcmeEvent.vars
Contains the ACME vars string configured in the ACME section.
Only set if vars were configured.
Only available for **ACME_DEPLOY** events.
External Lua libraries
======================

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