Merge pull request #9272 from gladiac1337/openwrt-18.06

[openwrt-18.06] haproxy: Update HAProxy to v1.8.20
This commit is contained in:
Hannu Nyman 2019-06-20 13:14:23 +03:00 committed by GitHub
commit dd927adcec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 2917 additions and 345 deletions

View file

@ -1,6 +1,7 @@
#
# Copyright (C) 2010-2016 OpenWrt.org
# Copyright (C) 2009-2016 Thomas Heil <heil@terminal-consulting.de>
# Copyright (C) 2018 Christian Lachner <gladiac@gmail.com>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
@ -9,16 +10,17 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=haproxy
PKG_VERSION:=1.8.4
PKG_RELEASE:=02
PKG_VERSION:=1.8.20
PKG_RELEASE:=1
PKG_SOURCE:=haproxy-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://www.haproxy.org/download/1.8/src/
PKG_HASH:=e305b0a4e7dec08072841eef6ac6dcd1b5586b1eff09c2d51e152a912e8884a6
PKG_HASH:=3228f78d5fe1dfbaccf41bf387e36b08eeef6e16330053cafde5fa303e262b16
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)
PKG_LICENSE:=GPL-2.0
MAINTAINER:=Thomas Heil <heil@terminal-consulting.de>
MAINTAINER:=Thomas Heil <heil@terminal-consulting.de>, \
Christian Lachner <gladiac@gmail.com>
include $(INCLUDE_DIR)/package.mk
@ -30,17 +32,17 @@ define Package/haproxy/Default
URL:=https://www.haproxy.org/
endef
define Download/lua534
FILE:=lua-5.3.4.tar.gz
define Download/lua535
FILE:=lua-5.3.5.tar.gz
URL:=https://www.lua.org/ftp/
HASH:=f681aa518233bc407e23acf0f5887c884f17436f000d453b2491a9f11a52400c
HASH:=0c2eed3f960446e1a3e4b9a1ca2f3ff893b6ce41942cf54d5dd59ab4b3b058ac
endef
define Build/Prepare
$(call Build/Prepare/Default)
ifeq ($(ENABLE_LUA),y)
tar -zxvf $(DL_DIR)/lua-5.3.4.tar.gz -C $(PKG_BUILD_DIR)
ln -s $(PKG_BUILD_DIR)/lua-5.3.4 $(PKG_BUILD_DIR)/lua
tar -zxvf $(DL_DIR)/lua-5.3.5.tar.gz -C $(PKG_BUILD_DIR)
ln -s $(PKG_BUILD_DIR)/lua-5.3.5 $(PKG_BUILD_DIR)/lua
endif
endef
@ -54,7 +56,6 @@ endef
define Package/haproxy
DEPENDS+= +libpcre +libltdl +zlib +libpthread +libopenssl +libncursesw +libreadline +libatomic +@OPENSSL_WITH_COMPRESSION +@OPENSSL_WITH_DTLS +@OPENSSL_ENGINE_CRYPTO
TITLE+= (with SSL support)
VARIANT:=ssl
$(call Package/haproxy/Default)
@ -69,11 +70,6 @@ $(call Package/haproxy/Default/description)
This package is built with SSL and LUA support.
endef
define Package/haproxy/config
select CONFIG_OPENSSL_WITH_DEPRECATED
$(call Package/haproxy/Default/config)
endef
define Package/haproxy-nossl
TITLE+= (without SSL support)
VARIANT:=nossl
@ -94,22 +90,11 @@ endef
ENABLE_LUA:=y
ENABLE_REGPARM:=n
ifeq ($(CONFIG_mips),y)
ENABLE_LUA:=n
endif
ifeq ($(CONFIG_mipsel),y)
ENABLE_LUA:=n
endif
ifeq ($(CONFIG_TARGET_x86),y)
ENABLE_REGPARM:=y
endif
ifeq ($(CONFIG_avr32),y)
LINUX_TARGET:=linux26
else
LINUX_TARGET:=linux2628
endif
LINUX_TARGET:=linux2628
ifeq ($(BUILD_VARIANT),ssl)
ADDON+=USE_OPENSSL=1
@ -118,9 +103,9 @@ endif
ifeq ($(ENABLE_LUA),y)
ADDON+=USE_LUA=1
ADDON+=LUA_LIB_NAME="lua534"
ADDON+=LUA_INC="$(STAGING_DIR)/lua-5.3.4/include"
ADDON+=LUA_LIB="$(STAGING_DIR)/lua-5.3.4/lib"
ADDON+=LUA_LIB_NAME="lua535"
ADDON+=LUA_INC="$(STAGING_DIR)/lua-5.3.5/include"
ADDON+=LUA_LIB="$(STAGING_DIR)/lua-5.3.5/lib"
endif
ifeq ($(ENABLE_REGPARM),y)
@ -130,14 +115,14 @@ endif
ifeq ($(ENABLE_LUA),y)
define Build/Compile/lua
$(MAKE) TARGET=$(LINUX_TARGET) -C $(PKG_BUILD_DIR)/lua \
INSTALL_TOP="$(STAGING_DIR)/lua-5.3.4/" \
INSTALL_TOP="$(STAGING_DIR)/lua-5.3.5/" \
CC="$(TARGET_CC)" \
CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \
LDFLAGS="$(TARGET_LDFLAGS) -lncurses -lreadline" \
LD="$(TARGET_LD)" \
linux install
mv $(STAGING_DIR)/lua-5.3.4/lib/liblua.a $(STAGING_DIR)/lua-5.3.4/lib/liblua534.a
mv $(STAGING_DIR)/lua-5.3.5/lib/liblua.a $(STAGING_DIR)/lua-5.3.5/lib/liblua535.a
endef
endif
@ -147,14 +132,15 @@ define Build/Compile
DESTDIR="$(PKG_INSTALL_DIR)" \
CC="$(TARGET_CC)" \
PCREDIR="$(STAGING_DIR)/usr/" \
SMALL_OPTS="-DBUFSIZE=16384 -DMAXREWRITE=1030 -DSYSTEM_MAXCONN=165530 " \
SMALL_OPTS="-DBUFSIZE=16384 -DMAXREWRITE=1030 -DSYSTEM_MAXCONN=165530" \
USE_LINUX_TPROXY=1 USE_LINUX_SPLICE=1 USE_TFO=1 \
USE_ZLIB=yes USE_PCRE=1 USE_PCRE_JIT=1 USE_GETADDRINFO=1 \
VERSION="$(PKG_VERSION)-patch$(PKG_RELEASE)" \
VERSION="$(PKG_VERSION)" SUBVERS="-$(PKG_RELEASE)" \
VERDATE="$(shell date -d @$(SOURCE_DATE_EPOCH) '+%Y/%m/%d')" IGNOREGIT=1 \
$(ADDON) \
CFLAGS="$(TARGET_CFLAGS)" \
LD="$(TARGET_CC)" \
LDFLAGS="$(TARGET_LDFLAGS) -latomic" \
IGNOREGIT=1
LDFLAGS="$(TARGET_LDFLAGS) -latomic"
$(MAKE_VARS) $(MAKE) -C $(PKG_BUILD_DIR) \
DESTDIR="$(PKG_INSTALL_DIR)" \
@ -166,8 +152,8 @@ define Build/Compile
$(MAKE_VARS) $(MAKE) -C $(PKG_BUILD_DIR)/contrib/halog \
DESTDIR="$(PKG_INSTALL_DIR)" \
$(MAKE_FLAGS) \
OPTIMIZE="$(TARGET_CFLAGS)" \
ADDLIB="-lcrypto" \
VERSION="$(PKG_VERSION)-patch$(PKG_RELEASE)" \
halog
endef
@ -200,7 +186,7 @@ define Package/halog/install
$(INSTALL_BIN) $(PKG_BUILD_DIR)/contrib/halog/halog $(1)/usr/bin/
endef
$(eval $(call Download,lua534))
$(eval $(call Download,lua535))
$(eval $(call BuildPackage,haproxy))
$(eval $(call BuildPackage,halog))
$(eval $(call BuildPackage,haproxy-nossl))

View file

@ -0,0 +1,27 @@
#!/bin/bash
CLONEURL=http://git.haproxy.org/git/haproxy-1.8.git
BASE_TAG=v1.8.20
TMP_REPODIR=tmprepo
PATCHESDIR=patches
if test -d "${TMP_REPODIR}"; then rm -rf "${TMP_REPODIR}"; fi
git clone "${CLONEURL}" "${TMP_REPODIR}"
printf "Cleaning patches\n"
find ${PATCHESDIR} -type f -name "*.patch" -exec rm -f "{}" \;
i=0
for cid in $(git -C "${TMP_REPODIR}" rev-list ${BASE_TAG}..HEAD | tac); do
filename="$(printf "%03d" $i)-$(git -C "${TMP_REPODIR}" log --format=%s -n 1 $cid | sed -e"s/[()']//g" -e's/[^_a-zA-Z0-9+-]\+/-/g' -e's/-$//').patch"
printf "Creating ${filename}\n"
git -C "${TMP_REPODIR}" show $cid > "${PATCHESDIR}/$filename"
git add "${PATCHESDIR}/$filename"
let i++
done
rm -rf "${TMP_REPODIR}"
printf "finished\n"

View file

@ -0,0 +1,40 @@
commit cf2f1243373be97249567ffd259e975cc87068b8
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Mon Apr 29 13:12:02 2019 +0200
BUG/MINOR: http: Call stream_inc_be_http_req_ctr() only one time per request
The function stream_inc_be_http_req_ctr() is called at the beginning of the
analysers AN_REQ_HTTP_PROCESS_FE/BE. It as an effect only on the backend. But we
must be careful to call it only once. If the processing of HTTP rules is
interrupted in the middle, when the analyser is resumed, we must not call it
again. Otherwise, the tracked counters of the backend are incremented several
times.
This bug was reported in github. See issue #74.
This fix should be backported as far as 1.6.
(cherry picked from commit 1907ccc2f75b78ace1ee4acdfc60d48a76e3decd)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 319921866ea9ecc46215fea5679abc8efdfcbea5)
[cf: HTX part was removed]
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/proto_http.c b/src/proto_http.c
index ccacd6a4..556cabad 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3420,8 +3420,10 @@ int http_process_req_common(struct stream *s, struct channel *req, int an_bit, s
req->buf->i,
req->analysers);
- /* just in case we have some per-backend tracking */
- stream_inc_be_http_req_ctr(s);
+ /* just in case we have some per-backend tracking. Only called the first
+ * execution of the analyser. */
+ if (!s->current_rule || s->current_rule_list != &px->http_req_rules)
+ stream_inc_be_http_req_ctr(s);
/* evaluate http-request rules */
if (!LIST_ISEMPTY(&px->http_req_rules)) {

View file

@ -1,61 +0,0 @@
From 2fcd544272a5498ffa49544e9f06b51bc93e55d1 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouchard@haproxy.com>
Date: Tue, 13 Feb 2018 15:17:23 +0100
Subject: [PATCH] BUG/MEDIUM: ssl: Don't always treat SSL_ERROR_SYSCALL as
unrecovarable.
Bart Geesink reported some random errors appearing under the form of
termination flags SD in the logs for connections involving SSL traffic
to reach the servers.
Tomek Gacek and Mateusz Malek finally narrowed down the problem to commit
c2aae74 ("MEDIUM: ssl: Handle early data with OpenSSL 1.1.1"). It happens
that the special case of SSL_ERROR_SYSCALL isn't handled anymore since
this commit.
SSL_read() might return <= 0, and SSL_get_erro() return SSL_ERROR_SYSCALL,
without meaning the connection is gone. Before flagging the connection
as in error, check the errno value.
This should be backported to 1.8.
(cherry picked from commit 7e2e505006feb8f3b4a7f9e0ac5e89b5a8c4895e)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/ssl_sock.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index aecf3dd..f118724 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -5437,6 +5437,12 @@ static int ssl_sock_to_buf(struct connection *conn, struct buffer *buf, int coun
break;
} else if (ret == SSL_ERROR_ZERO_RETURN)
goto read0;
+ /* For SSL_ERROR_SYSCALL, make sure the error is
+ * unrecoverable before flagging the connection as
+ * in error.
+ */
+ if (ret == SSL_ERROR_SYSCALL && (!errno || errno == EAGAIN))
+ goto clear_ssl_error;
/* otherwise it's a real error */
goto out_error;
}
@@ -5451,11 +5457,12 @@ static int ssl_sock_to_buf(struct connection *conn, struct buffer *buf, int coun
conn_sock_read0(conn);
goto leave;
out_error:
+ conn->flags |= CO_FL_ERROR;
+clear_ssl_error:
/* Clear openssl global errors stack */
ssl_sock_dump_errors(conn);
ERR_clear_error();
- conn->flags |= CO_FL_ERROR;
goto leave;
}
--
1.7.10.4

View file

@ -1,63 +0,0 @@
From f7fa1d461aa71bbc8a6c23fdcfc305f2e52ce5dd Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Mon, 19 Feb 2018 14:25:15 +0100
Subject: [PATCH] BUG/MEDIUM: ssl: Shutdown the connection for reading on
SSL_ERROR_SYSCALL
When SSL_read returns SSL_ERROR_SYSCALL and errno is unset or set to EAGAIN, the
connection must be shut down for reading. Else, the connection loops infinitly,
consuming all the CPU.
The bug was introduced in the commit 7e2e50500 ("BUG/MEDIUM: ssl: Don't always
treat SSL_ERROR_SYSCALL as unrecovarable."). This patch must be backported in
1.8 too.
(cherry picked from commit 4ac77a98cda3d0f9b1d9de7bbbda2c91357f0767)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/ssl_sock.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index f118724..a065bbb 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -5437,10 +5437,9 @@ static int ssl_sock_to_buf(struct connection *conn, struct buffer *buf, int coun
break;
} else if (ret == SSL_ERROR_ZERO_RETURN)
goto read0;
- /* For SSL_ERROR_SYSCALL, make sure the error is
- * unrecoverable before flagging the connection as
- * in error.
- */
+ /* For SSL_ERROR_SYSCALL, make sure to clear the error
+ * stack before shutting down the connection for
+ * reading. */
if (ret == SSL_ERROR_SYSCALL && (!errno || errno == EAGAIN))
goto clear_ssl_error;
/* otherwise it's a real error */
@@ -5453,16 +5452,19 @@ static int ssl_sock_to_buf(struct connection *conn, struct buffer *buf, int coun
conn_cond_update_sock_polling(conn);
return done;
+ clear_ssl_error:
+ /* Clear openssl global errors stack */
+ ssl_sock_dump_errors(conn);
+ ERR_clear_error();
read0:
conn_sock_read0(conn);
goto leave;
+
out_error:
conn->flags |= CO_FL_ERROR;
-clear_ssl_error:
/* Clear openssl global errors stack */
ssl_sock_dump_errors(conn);
ERR_clear_error();
-
goto leave;
}
--
1.7.10.4

View file

@ -1,69 +0,0 @@
From 8a5949f2d74c3a3a6c6da25449992c312b183ef3 Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Fri, 2 Feb 2018 15:54:15 +0100
Subject: [PATCH] BUG/MEDIUM: http: Switch the HTTP response in tunnel mode as
earlier as possible
When the body length is undefined (no Content-Length or Transfer-Encoding
headers), The reponse remains in ending mode, waiting the request is done. So,
most of time this is not a problem because the resquest is done before the
response. But when a client sends data to a server that replies without waiting
all the data, it is really not desirable to wait the end of the request to
finish the response.
This bug was introduced when the tunneling of the request and the reponse was
refactored, in commit 4be980391 ("MINOR: http: Switch requests/responses in
TUNNEL mode only by checking txn flag").
This patch should be backported in 1.8 and 1.7.
(cherry picked from commit fd04fcf5edb0a24cd29ce8f4d4dc2aa3a0e2e82c)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/proto_http.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/src/proto_http.c b/src/proto_http.c
index 64bd410..29880ea 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4634,16 +4634,8 @@ int http_sync_res_state(struct stream *s)
* let's enforce it now that we're not expecting any new
* data to come. The caller knows the stream is complete
* once both states are CLOSED.
- *
- * However, there is an exception if the response length
- * is undefined. In this case, we switch in TUNNEL mode.
*/
- if (!(txn->rsp.flags & HTTP_MSGF_XFER_LEN)) {
- channel_auto_read(chn);
- txn->rsp.msg_state = HTTP_MSG_TUNNEL;
- chn->flags |= CF_NEVER_WAIT;
- }
- else if (!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
+ if (!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
channel_shutr_now(chn);
channel_shutw_now(chn);
}
@@ -6241,6 +6233,8 @@ http_msg_forward_body(struct stream *s, struct http_msg *msg)
/* The server still sending data that should be filtered */
if (!(chn->flags & CF_SHUTR) && HAS_DATA_FILTERS(s, chn))
goto missing_data_or_waiting;
+ msg->msg_state = HTTP_MSG_TUNNEL;
+ goto ending;
}
msg->msg_state = HTTP_MSG_ENDING;
@@ -6262,7 +6256,8 @@ http_msg_forward_body(struct stream *s, struct http_msg *msg)
/* default_ret */ 1,
/* on_error */ goto error,
/* on_wait */ goto waiting);
- msg->msg_state = HTTP_MSG_DONE;
+ if (msg->msg_state == HTTP_MSG_ENDING)
+ msg->msg_state = HTTP_MSG_DONE;
return 1;
missing_data_or_waiting:
--
1.7.10.4

View file

@ -1,103 +0,0 @@
From 7ccf7c9791f2b2329f3940d1347618af3a77bebc Mon Sep 17 00:00:00 2001
From: Emeric Brun <ebrun@haproxy.com>
Date: Mon, 19 Feb 2018 15:59:48 +0100
Subject: [PATCH] BUG/MEDIUM: ssl/sample: ssl_bc_* fetch keywords are broken.
Since the split between connections and conn-stream objects, this
keywords are broken.
This patch must be backported in 1.8
(cherry picked from commit eb8def9f34c37537d56a69fcd211d4c4c8006bea)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/ssl_sock.c | 31 ++++++++++++++-----------------
1 file changed, 14 insertions(+), 17 deletions(-)
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 4d0d5db..d832d76 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -6565,8 +6565,8 @@ smp_fetch_ssl_x_key_alg(const struct arg *args, struct sample *smp, const char *
static int
smp_fetch_ssl_fc(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
- struct connection *conn = objt_conn((kw[4] != 'b') ? smp->sess->origin :
- smp->strm ? smp->strm->si[1].end : NULL);
+ struct connection *conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
smp->data.type = SMP_T_BOOL;
smp->data.u.sint = (conn && conn->xprt == &ssl_sock);
@@ -6610,8 +6610,8 @@ smp_fetch_ssl_fc_is_resumed(const struct arg *args, struct sample *smp, const ch
static int
smp_fetch_ssl_fc_cipher(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
- struct connection *conn = objt_conn((kw[4] != 'b') ? smp->sess->origin :
- smp->strm ? smp->strm->si[1].end : NULL);
+ struct connection *conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
smp->flags = 0;
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
@@ -6636,9 +6636,8 @@ smp_fetch_ssl_fc_cipher(const struct arg *args, struct sample *smp, const char *
static int
smp_fetch_ssl_fc_alg_keysize(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
- struct connection *conn = objt_conn((kw[4] != 'b') ? smp->sess->origin :
- smp->strm ? smp->strm->si[1].end : NULL);
-
+ struct connection *conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
int sint;
smp->flags = 0;
@@ -6661,8 +6660,8 @@ smp_fetch_ssl_fc_alg_keysize(const struct arg *args, struct sample *smp, const c
static int
smp_fetch_ssl_fc_use_keysize(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
- struct connection *conn = objt_conn((kw[4] != 'b') ? smp->sess->origin :
- smp->strm ? smp->strm->si[1].end : NULL);
+ struct connection *conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
smp->flags = 0;
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
@@ -6732,8 +6731,8 @@ smp_fetch_ssl_fc_alpn(const struct arg *args, struct sample *smp, const char *kw
static int
smp_fetch_ssl_fc_protocol(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
- struct connection *conn = objt_conn((kw[4] != 'b') ? smp->sess->origin :
- smp->strm ? smp->strm->si[1].end : NULL);
+ struct connection *conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
smp->flags = 0;
if (!conn || !conn->xprt_ctx || conn->xprt != &ssl_sock)
@@ -6758,9 +6757,8 @@ static int
smp_fetch_ssl_fc_session_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
#if OPENSSL_VERSION_NUMBER > 0x0090800fL
- struct connection *conn = objt_conn((kw[4] != 'b') ? smp->sess->origin :
- smp->strm ? smp->strm->si[1].end : NULL);
-
+ struct connection *conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
SSL_SESSION *ssl_sess;
smp->flags = SMP_F_CONST;
@@ -6902,9 +6900,8 @@ static int
smp_fetch_ssl_fc_unique_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
#if OPENSSL_VERSION_NUMBER > 0x0090800fL
- struct connection *conn = objt_conn((kw[4] != 'b') ? smp->sess->origin :
- smp->strm ? smp->strm->si[1].end : NULL);
-
+ struct connection *conn = (kw[4] != 'b') ? objt_conn(smp->sess->origin) :
+ smp->strm ? cs_conn(objt_cs(smp->strm->si[1].end)) : NULL;
int finished_len;
struct chunk *finished_trash;
--
1.7.10.4

View file

@ -0,0 +1,83 @@
commit c1620a52a3def02b4837376385c416c03ca874c4
Author: Kevin Zhu <ipandtcp@gmail.com>
Date: Fri Apr 26 14:00:01 2019 +0800
BUG/MEDIUM: spoe: arg len encoded in previous frag frame but len changed
Fragmented arg will do fetch at every encode time, each fetch may get
different result if SMP_F_MAY_CHANGE, for example res.payload, but
the length already encoded in first fragment of the frame, that will
cause SPOA decode failed and waste resources.
This patch must be backported to 1.9 and 1.8.
(cherry picked from commit f7f54280c8106e92a55243f5d60f8587e79602d1)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 3a838e526cdbc00ded5362e66f1ef3a441abc3c1)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/proto/spoe.h b/include/proto/spoe.h
index 002cf7d7..2cdca10b 100644
--- a/include/proto/spoe.h
+++ b/include/proto/spoe.h
@@ -121,7 +121,7 @@ spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len)
* many bytes has been encoded. If <*off> is zero at the end, it means that all
* data has been encoded. */
static inline int
-spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end)
+spoe_encode_data(unsigned int *len, struct sample *smp, unsigned int *off, char **buf, char *end)
{
char *p = *buf;
int ret;
@@ -183,15 +183,16 @@ spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end)
ret = spoe_encode_frag_buffer(chk->str, chk->len, &p, end);
if (ret == -1)
return -1;
+ *len = chk->len;
}
else {
/* The sample has been fragmented, encode remaining data */
- ret = MIN(chk->len - *off, end - p);
+ ret = MIN(*len - *off, end - p);
memcpy(p, chk->str + *off, ret);
p += ret;
}
/* Now update <*off> */
- if (ret + *off != chk->len)
+ if (ret + *off != *len)
*off += ret;
else
*off = 0;
diff --git a/include/types/spoe.h b/include/types/spoe.h
index 53e7200c..cfaa42f8 100644
--- a/include/types/spoe.h
+++ b/include/types/spoe.h
@@ -304,6 +304,7 @@ struct spoe_context {
struct spoe_message *curmsg; /* SPOE message from which to resume encoding */
struct spoe_arg *curarg; /* SPOE arg in <curmsg> from which to resume encoding */
unsigned int curoff; /* offset in <curarg> from which to resume encoding */
+ unsigned int curlen; /* length of <curarg> need to be encode, for SMP_F_MAY_CHANGE data */
unsigned int flags; /* SPOE_FRM_FL_* */
} frag_ctx; /* Info about fragmented frames, valid on if SPOE_CTX_FL_FRAGMENTED is set */
};
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index f6109778..0c0b3794 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -2172,6 +2172,7 @@ spoe_encode_message(struct stream *s, struct spoe_context *ctx,
list_for_each_entry(arg, &msg->args, list) {
ctx->frag_ctx.curarg = arg;
ctx->frag_ctx.curoff = UINT_MAX;
+ ctx->frag_ctx.curlen = 0;
encode_argument:
if (ctx->frag_ctx.curoff != UINT_MAX)
@@ -2186,7 +2187,7 @@ spoe_encode_message(struct stream *s, struct spoe_context *ctx,
/* Fetch the arguement value */
smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
- ret = spoe_encode_data(smp, &ctx->frag_ctx.curoff, buf, end);
+ ret = spoe_encode_data(&ctx->frag_ctx.curlen, smp, &ctx->frag_ctx.curoff, buf, end);
if (ret == -1 || ctx->frag_ctx.curoff)
goto too_big;
}

View file

@ -0,0 +1,71 @@
commit 72fdff1fdb5b82685dc3d2db23ece042195a0cbd
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Fri Apr 26 14:30:15 2019 +0200
MINOR: spoe: Use the sample context to pass frag_ctx info during encoding
This simplifies the API and hide the details in the sample. This way, only
string and binary are aware of these info, because other types cannot be
partially encoded.
This patch may be backported to 1.9 and 1.8.
(cherry picked from commit 85db3212b87b33f1a39a688546f4f53a5c4ba4da)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit b93366e3ee44f5de93f01569fcdcd602f6d0703f)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/proto/spoe.h b/include/proto/spoe.h
index 2cdca10b..cce13e50 100644
--- a/include/proto/spoe.h
+++ b/include/proto/spoe.h
@@ -117,11 +117,9 @@ spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len)
*
* If the value is too big to be encoded, depending on its type, then encoding
* failed or the value is partially encoded. Only strings and binaries can be
- * partially encoded. In this case, the offset <*off> is updated to known how
- * many bytes has been encoded. If <*off> is zero at the end, it means that all
- * data has been encoded. */
+ * partially encoded. */
static inline int
-spoe_encode_data(unsigned int *len, struct sample *smp, unsigned int *off, char **buf, char *end)
+spoe_encode_data(struct sample *smp, char **buf, char *end)
{
char *p = *buf;
int ret;
@@ -164,12 +162,16 @@ spoe_encode_data(unsigned int *len, struct sample *smp, unsigned int *off, char
case SMP_T_STR:
case SMP_T_BIN: {
+ /* If defined, get length and offset of the sample by reading the sample
+ * context. ctx.a[0] is the pointer to the length and ctx.a[1] is the
+ * pointer to the offset. If the offset is greater than 0, it means the
+ * sample is partially encoded. In this case, we only need to encode the
+ * reamining. When all the sample is encoded, the offset is reset to 0.
+ * So the caller know it can try to encode the next sample. */
struct chunk *chk = &smp->data.u.str;
+ unsigned int *len = (smp->ctx.a[0] ? smp->ctx.a[0] : 0);
+ unsigned int *off = (smp->ctx.a[1] ? smp->ctx.a[1] : 0);
- /* Here, we need to know if the sample has already been
- * partially encoded. If yes, we only need to encode the
- * remaining, <*off> reprensenting the number of bytes
- * already encoded. */
if (!*off) {
/* First evaluation of the sample : encode the
* type (string or binary), the buffer length
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index 0c0b3794..66d8b045 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -2187,7 +2187,9 @@ spoe_encode_message(struct stream *s, struct spoe_context *ctx,
/* Fetch the arguement value */
smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
- ret = spoe_encode_data(&ctx->frag_ctx.curlen, smp, &ctx->frag_ctx.curoff, buf, end);
+ smp->ctx.a[0] = &ctx->frag_ctx.curlen;
+ smp->ctx.a[1] = &ctx->frag_ctx.curoff;
+ ret = spoe_encode_data(smp, buf, end);
if (ret == -1 || ctx->frag_ctx.curoff)
goto too_big;
}

View file

@ -0,0 +1,38 @@
commit dfc3718f0a302ea3deb5f1a325d35fce0e4cfa48
Author: Yann Cézard <ycezard@viareport.com>
Date: Thu Apr 25 14:48:38 2019 +0200
DOC: contrib/modsecurity: Typos and fix the reject example
Thanks to https://www.mail-archive.com/haproxy@formilux.org/msg30056.html
This patch may be backported to 1.9 and 1.8.
(cherry picked from commit 494ddbff478d880e48de490f2689607addac70bc)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 850896603086877641272d6e4075e66bd91f2e50)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/contrib/modsecurity/README b/contrib/modsecurity/README
index e6cb305e..8031389d 100644
--- a/contrib/modsecurity/README
+++ b/contrib/modsecurity/README
@@ -88,15 +88,15 @@ HAProxy configuration. For example:
balance roundrobin
timeout connect 5s
timeout server 3m
- server iprep1 127.0.0.1:12345
+ server modsec1 127.0.0.1:12345
The modsecurity action is returned in a variable called txn.modsec.code. It
contains the HTTP returned code. If the variable contains 0, the request is
clean.
- tcp-request content reject if { var(txn.modsec.code) -m int gt 0 }
+ http-request deny if { var(txn.modsec.code) -m int gt 0 }
-With this rule, all the request not clean are reected.
+With this rule, all the request not clean are rejected.
Known bugs, limitations and TODO list

View file

@ -0,0 +1,36 @@
commit 95cf225d099dcb49eefcf4f5b648be604414ae0c
Author: Yann Cézard <ycezard@viareport.com>
Date: Thu Apr 25 14:30:23 2019 +0200
BUG/MEDIUM: contrib/modsecurity: If host header is NULL, don't try to strdup it
I discovered this bug when running OWASP regression tests against HAProxy +
modsecurity-spoa (it's a POC to evaluate how it is working). I found out that
modsecurity spoa will crash when the request doesn't have any Host header.
See the pull request #86 on github for details.
This patch must be backported to 1.9 and 1.8.
(cherry picked from commit bf60f6b8033deddc86de5357d6099c7593fe44cc)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit d988e3dddcbe1f48f3b24d1bb529fc9ecefde180)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/contrib/modsecurity/modsec_wrapper.c b/contrib/modsecurity/modsec_wrapper.c
index 271ec15d..2f3987b4 100644
--- a/contrib/modsecurity/modsec_wrapper.c
+++ b/contrib/modsecurity/modsec_wrapper.c
@@ -325,7 +325,11 @@ int modsecurity_process(struct worker *worker, struct modsecurity_parameters *pa
req->content_type = apr_table_get(req->headers_in, "Content-Type");
req->content_encoding = apr_table_get(req->headers_in, "Content-Encoding");
req->hostname = apr_table_get(req->headers_in, "Host");
- req->parsed_uri.hostname = chunk_strdup(req, req->hostname, strlen(req->hostname));
+ if (req->hostname != NULL) {
+ req->parsed_uri.hostname = chunk_strdup(req, req->hostname, strlen(req->hostname));
+ } else {
+ req->parsed_uri.hostname = NULL;
+ }
lang = apr_table_get(req->headers_in, "Content-Languages");
if (lang != NULL) {

View file

@ -0,0 +1,23 @@
commit 86860896dc1841eb59cb95832d76a8093e8dc8c5
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue Apr 30 10:55:38 2019 +0200
MINOR: examples: Use right locale for the last changelog date in haproxy.spec
The last changelog entry was stamped with the wrong locale.
No need to backport, it is specific to 1.8
diff --git a/examples/haproxy.spec b/examples/haproxy.spec
index f3e0c7e0..fe5215d7 100644
--- a/examples/haproxy.spec
+++ b/examples/haproxy.spec
@@ -74,7 +74,7 @@ fi
%attr(0755,root,root) %config %{_sysconfdir}/rc.d/init.d/%{name}
%changelog
-* jeu. avril 25 2019 Christopher Faulet <cfaulet@haproxy.com>
+* Thu Apr 25 2019 Christopher Faulet <cfaulet@haproxy.com>
- updated to 1.8.20
* Mon Feb 11 2019 Willy Tarreau <w@1wt.eu>

View file

@ -0,0 +1,67 @@
commit 83af1f6b65806982640679823228976deebf5202
Author: Willy Tarreau <w@1wt.eu>
Date: Tue Apr 30 11:43:43 2019 +0200
BUG/MAJOR: map/acl: real fix segfault during show map/acl on CLI
A previous commit 8d85aa44d ("BUG/MAJOR: map: fix segfault during
'show map/acl' on cli.") was provided to address a concurrency issue
between "show acl" and "clear acl" on the CLI. Sadly the code placed
there was copy-pasted without changing the element type (which was
struct stream in the original code) and not tested since the crash
is still present.
The reproducer is simple : load a large ACL file (e.g. geolocation
addresses), issue "show acl #0" in loops in one window and issue a
"clear acl #0" in the other one, haproxy crashes.
This fix was also tested with threads enabled and looks good since
the locking seems to work correctly in these areas though. It will
have to be backported as far as 1.6 since the commit above went
that far as well...
(cherry picked from commit 49ee3b2f9a9e5d0b8d394938df527aa645ce72b4)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit ac4be10f62ef72962d9cf0e6f2619e1e1c370d62)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/pattern.c b/src/pattern.c
index 7eea9d96..21639569 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -1651,7 +1651,7 @@ int pat_ref_delete_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt)
LIST_DEL(&bref->users);
LIST_INIT(&bref->users);
if (elt->list.n != &ref->head)
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users);
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users);
bref->ref = elt->list.n;
}
list_for_each_entry(expr, &ref->pat, list)
@@ -1691,7 +1691,7 @@ int pat_ref_delete(struct pat_ref *ref, const char *key)
LIST_DEL(&bref->users);
LIST_INIT(&bref->users);
if (elt->list.n != &ref->head)
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users);
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users);
bref->ref = elt->list.n;
}
list_for_each_entry(expr, &ref->pat, list)
@@ -2086,7 +2086,7 @@ void pat_ref_reload(struct pat_ref *ref, struct pat_ref *replace)
LIST_DEL(&bref->users);
LIST_INIT(&bref->users);
if (elt->list.n != &ref->head)
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users);
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users);
bref->ref = elt->list.n;
}
LIST_DEL(&elt->list);
@@ -2175,7 +2175,7 @@ void pat_ref_prune(struct pat_ref *ref)
LIST_DEL(&bref->users);
LIST_INIT(&bref->users);
if (elt->list.n != &ref->head)
- LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users);
+ LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users);
bref->ref = elt->list.n;
}
LIST_DEL(&elt->list);

View file

@ -0,0 +1,79 @@
commit 7bd7a8d2b8889f604b807c21190d2e70328d6674
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue Apr 30 12:17:13 2019 +0200
BUG/MEDIUM: listener: Fix how unlimited number of consecutive accepts is handled
There is a bug when global.tune.maxaccept is set to -1 (no limit). It is pretty
visible with one process (nbproc sets to 1). The functions listener_accept() and
accept_queue_process() don't expect to handle negative maxaccept values. So
instead of accepting incoming connections without any limit, none are never
accepted and HAProxy loop infinitly in the scheduler.
When there are 2 or more processes, the bug is a bit more subtile. The limit for
a listener is set to 1. So only one connection is accepted at a time by a given
listener. This happens because the listener's maxaccept value is an unsigned
integer. In check_config_validity(), it is first set to UINT_MAX (-1 casted in
an unsigned integer), and then some calculations on it leads to an integer
overflow.
To fix the bug, the listener's maxaccept value is now a signed integer. So, if a
negative value is set for global.tune.maxaccept, we keep it untouched for the
listener and no calculation is made on it. Then, in the listener code, this
signed value is casted to a unsigned one. It simplifies all tests instead of
dealing with negative values. So, it limits the number of connections accepted
at a time to UINT_MAX at most. But, honestly, it not an issue.
This patch must be backported to 1.9 and 1.8.
(cherry picked from commit 102854cbbaa4d22466dddec9035d411db244082f)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit bca4fb2d9d7f2966d9f8270fa1796fdc0dfc866d)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/types/listener.h b/include/types/listener.h
index ea2eadb5..16ef6d7a 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -196,7 +196,7 @@ struct listener {
int nbconn; /* current number of connections on this listener */
int maxconn; /* maximum connections allowed on this listener */
unsigned int backlog; /* if set, listen backlog */
- unsigned int maxaccept; /* if set, max number of connections accepted at once */
+ int maxaccept; /* if set, max number of connections accepted at once (-1 when disabled) */
struct list proto_list; /* list in the protocol header */
int (*accept)(struct listener *l, int fd, struct sockaddr_storage *addr); /* upper layer's accept() */
enum obj_type *default_target; /* default target to use for accepted sessions or NULL */
diff --git a/src/listener.c b/src/listener.c
index 821c931a..74990c45 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -406,7 +406,7 @@ void listener_accept(int fd)
{
struct listener *l = fdtab[fd].owner;
struct proxy *p;
- int max_accept;
+ unsigned int max_accept;
int next_conn = 0;
int next_feconn = 0;
int next_actconn = 0;
@@ -420,6 +420,10 @@ void listener_accept(int fd)
if (!l)
return;
p = l->bind_conf->frontend;
+
+ /* if l->maxaccept is -1, then max_accept is UINT_MAX. It is not really
+ * illimited, but it is probably enough.
+ */
max_accept = l->maxaccept ? l->maxaccept : 1;
if (!(l->options & LI_O_UNLIMITED) && global.sps_lim) {
@@ -480,7 +484,7 @@ void listener_accept(int fd)
* worst case. If we fail due to system limits or temporary resource
* shortage, we try again 100ms later in the worst case.
*/
- for (; max_accept-- > 0; next_conn = next_feconn = next_actconn = 0) {
+ for (; max_accept; next_conn = next_feconn = next_actconn = 0, max_accept--) {
struct sockaddr_storage addr;
socklen_t laddr = sizeof(addr);
unsigned int count;

View file

@ -0,0 +1,44 @@
commit 6e580b6e744011e87c337ebe2c082acfd5ca835a
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue Apr 30 14:03:56 2019 +0200
MINOR: config: Test validity of tune.maxaccept during the config parsing
Only -1 and positive integers from 0 to INT_MAX are accepted. An error is
triggered during the config parsing for any other values.
This patch may be backported to all supported versions.
(cherry picked from commit 6b02ab87348090efec73b1dd24f414239669f279)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 2bbc40f8bc9a52ba0d03b25270ac0129cca29bba)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/cfgparse.c b/src/cfgparse.c
index c178538b..8e325416 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -789,6 +789,8 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
global.tune.maxpollevents = atol(args[1]);
}
else if (!strcmp(args[0], "tune.maxaccept")) {
+ long max;
+
if (alertif_too_many_args(1, file, linenum, args, &err_code))
goto out;
if (global.tune.maxaccept != 0) {
@@ -801,7 +803,13 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
- global.tune.maxaccept = atol(args[1]);
+ max = atol(args[1]);
+ if (/*max < -1 || */max > INT_MAX) {
+ ha_alert("parsing [%s:%d] : '%s' expects -1 or an integer from 0 to INT_MAX.\n", file, linenum, args[0]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+ global.tune.maxaccept = max;
}
else if (!strcmp(args[0], "tune.chksize")) {
if (alertif_too_many_args(1, file, linenum, args, &err_code))

View file

@ -0,0 +1,34 @@
commit c6e03c1495fa51f9a98ed0bbe3230313c7c7201c
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue Apr 30 14:08:41 2019 +0200
CLEANUP: config: Don't alter listener->maxaccept when nbproc is set to 1
This patch only removes a useless calculation on listener->maxaccept when nbproc
is set to 1. Indeed, the following formula has no effet in such case:
listener->maxaccept = (listener->maxaccept + nbproc - 1) / nbproc;
This patch may be backported as far as 1.5.
(cherry picked from commit 02f3cf19ed803d20aff9294ce7cb732489951ff5)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 14203e3cf9404e57de5e274b453f0fe4f2174924)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 8e325416..3f6ea352 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -9018,9 +9018,8 @@ out_uri_auth_compat:
* is bound to. Rememeber that maxaccept = -1 must be kept as it is
* used to disable the limit.
*/
- if (listener->maxaccept > 0) {
- if (nbproc > 1)
- listener->maxaccept = (listener->maxaccept + 1) / 2;
+ if (listener->maxaccept > 0 && nbproc > 1) {
+ listener->maxaccept = (listener->maxaccept + 1) / 2;
listener->maxaccept = (listener->maxaccept + nbproc - 1) / nbproc;
}

View file

@ -0,0 +1,54 @@
commit f95cf6ad70565ee2322cf23bc519b7bb0b3831b2
Author: Olivier Houchard <ohouchard@haproxy.com>
Date: Tue Apr 30 13:38:02 2019 +0200
MINOR: threads: Implement HA_ATOMIC_LOAD().
The same way we have HA_ATOMIC_STORE(), implement HA_ATOMIC_LOAD().
This should be backported to 1.8 and 1.9, as we need it for a bug fix
in port ranges.
(cherry picked from commit 9ce62b5498b27fbf4217d9c25779d5b2ceca23f2)
Signed-off-by: Olivier Houchard <cognet@ci0.org>
(cherry picked from commit 358c979611370fa2bc3b8e47ed50a325cf9126cf)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/common/hathreads.h b/include/common/hathreads.h
index 8134839a..11d7cab6 100644
--- a/include/common/hathreads.h
+++ b/include/common/hathreads.h
@@ -62,6 +62,7 @@ enum { tid = 0 };
*(val) = new; \
__old_xchg; \
})
+#define HA_ATOMIC_LOAD(val) *(val)
#define HA_ATOMIC_STORE(val, new) ({*(val) = new;})
#define HA_ATOMIC_UPDATE_MAX(val, new) \
({ \
@@ -203,6 +204,16 @@ static inline unsigned long thread_isolated()
} while (!__sync_bool_compare_and_swap(__val_xchg, __old_xchg, __new_xchg)); \
__old_xchg; \
})
+
+#define HA_ATOMIC_LOAD(val) \
+ ({ \
+ typeof(*(val)) ret; \
+ __sync_synchronize(); \
+ ret = *(volatile typeof(val))val; \
+ __sync_synchronize(); \
+ ret; \
+ })
+
#define HA_ATOMIC_STORE(val, new) \
({ \
typeof((val)) __val_store = (val); \
@@ -221,6 +232,8 @@ static inline unsigned long thread_isolated()
#define HA_ATOMIC_OR(val, flags) __atomic_or_fetch(val, flags, __ATOMIC_SEQ_CST)
#define HA_ATOMIC_XCHG(val, new) __atomic_exchange_n(val, new, __ATOMIC_SEQ_CST)
#define HA_ATOMIC_STORE(val, new) __atomic_store_n(val, new, __ATOMIC_SEQ_CST)
+#define HA_ATOMIC_LOAD(val) __atomic_load_n(val, __ATOMIC_SEQ_CST)
+
#endif
#define HA_ATOMIC_UPDATE_MAX(val, new) \

View file

@ -0,0 +1,116 @@
commit 31470e2ba2aabb4c6340fbc15cb5486ceb8c69c8
Author: Olivier Houchard <ohouchard@haproxy.com>
Date: Mon Apr 29 18:52:06 2019 +0200
BUG/MEDIUM: port_range: Make the ring buffer lock-free.
Port range uses a ring buffer, and unfortunately, when making haproxy
multithreaded, it's been overlooked, and the ring buffer is not thread-safe.
When specifying a source range, 2 or more threads could pick the same
port, and of course only one of them could use the port, the others would
always fail the connection.
To fix this, make it a lock-free ring buffer. This is easier than usual
because we know the ring buffer can never be full.
This should be backported to 1.8 and 1.9.
(cherry picked from commit 07425de71777b688e77a9c70a7088c13e66e41e9)
Signed-off-by: Olivier Houchard <cognet@ci0.org>
(cherry picked from commit bffb51147a4a5939e344b3c838628f9a944febef)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/proto/port_range.h b/include/proto/port_range.h
index 8c63faca..f7e3f1d5 100644
--- a/include/proto/port_range.h
+++ b/include/proto/port_range.h
@@ -24,18 +24,22 @@
#include <types/port_range.h>
+#define GET_NEXT_OFF(range, off) ((off) == (range)->size - 1 ? 0 : (off) + 1)
+
/* return an available port from range <range>, or zero if none is left */
static inline int port_range_alloc_port(struct port_range *range)
{
int ret;
+ int get;
+ int put;
- if (!range->avail)
- return 0;
- ret = range->ports[range->get];
- range->get++;
- if (range->get >= range->size)
- range->get = 0;
- range->avail--;
+ get = HA_ATOMIC_LOAD(&range->get);
+ do {
+ put = HA_ATOMIC_LOAD(&range->put_t);
+ if (unlikely(put == get))
+ return 0;
+ ret = range->ports[get];
+ } while (!(HA_ATOMIC_CAS(&range->get, &get, GET_NEXT_OFF(range, get))));
return ret;
}
@@ -45,14 +49,28 @@ static inline int port_range_alloc_port(struct port_range *range)
*/
static inline void port_range_release_port(struct port_range *range, int port)
{
+ int put;
+
if (!port || !range)
return;
- range->ports[range->put] = port;
- range->avail++;
- range->put++;
- if (range->put >= range->size)
- range->put = 0;
+ put = range->put_h;
+ /* put_h is reserved for producers, so that they can each get a
+ * free slot, put_t is what is used by consumers to know if there's
+ * elements available or not
+ */
+ /* First reserve or slot, we know the ring buffer can't be full,
+ * as we will only ever release port we allocated before
+ */
+ while (!(HA_ATOMIC_CAS(&range->put_h, &put, GET_NEXT_OFF(range, put))));
+ HA_ATOMIC_STORE(&range->ports[put], port);
+ /* Wait until all the threads that got a slot before us are done */
+ while ((volatile int)range->put_t != put)
+ __ha_compiler_barrier();
+ /* Let the world know we're done, and any potential consumer they
+ * can use that port.
+ */
+ HA_ATOMIC_STORE(&range->put_t, GET_NEXT_OFF(range, put));
}
/* return a new initialized port range of N ports. The ports are not
@@ -62,8 +80,10 @@ static inline struct port_range *port_range_alloc_range(int n)
{
struct port_range *ret;
ret = calloc(1, sizeof(struct port_range) +
- n * sizeof(((struct port_range *)0)->ports[0]));
- ret->size = ret->avail = n;
+ (n + 1) * sizeof(((struct port_range *)0)->ports[0]));
+ ret->size = n + 1;
+ /* Start at the first free element */
+ ret->put_h = ret->put_t = n;
return ret;
}
diff --git a/include/types/port_range.h b/include/types/port_range.h
index 1d010f77..33455d2d 100644
--- a/include/types/port_range.h
+++ b/include/types/port_range.h
@@ -25,8 +25,7 @@
#include <netinet/in.h>
struct port_range {
- int size, get, put; /* range size, and get/put positions */
- int avail; /* number of available ports left */
+ int size, get, put_h, put_t; /* range size, and get/put positions */
uint16_t ports[0]; /* array of <size> ports, in host byte order */
};

View file

@ -0,0 +1,81 @@
commit ef9cafc46c13eea2db65152e452607a6566cbeac
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Thu May 16 10:07:30 2019 +0200
BUG/MINOR: http_fetch: Rely on the smp direction for "cookie()" and "hdr()"
A regression was introduced in the commit 89dc49935 ("BUG/MAJOR: http_fetch: Get
the channel depending on the keyword used") on the samples "cookie()" and
"hdr()". Unlike other samples manipulating the HTTP headers, these ones depend
on the sample direction. To fix the bug, these samples use now their own
functions. Depending on the sample direction, they call smp_fetch_cookie() and
smp_fetch_hdr() with the appropriate keyword.
Thanks to Yves Lafon to report this issue.
This patch must be backported wherever the commit 89dc49935 was backported. For
now, 1.9 and 1.8.
(cherry picked from commit c1f40dd4920050ec5a83b2a5d22a3eb4e4be425a)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 5eaf770abfce56951202cb1ea55a968f5ec8be71)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/proto_http.c b/src/proto_http.c
index 556cabad..32aeef2d 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -10218,6 +10218,17 @@ smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void *
return 0;
}
+/* Same than smp_fetch_hdr() but only relies on the sample direction to choose
+ * the right channel. So instead of duplicating the code, we just change the
+ * keyword and then fallback on smp_fetch_hdr().
+ */
+static int
+smp_fetch_chn_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+ kw = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ ? "req.hdr" : "res.hdr");
+ return smp_fetch_hdr(args, smp, kw, private);
+}
+
/* 6. Check on HTTP header count. The number of occurrences is returned.
* Accepts exactly 1 argument of type string.
*/
@@ -10935,6 +10946,17 @@ int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw,
return found;
}
+/* Same than smp_fetch_cookie() but only relies on the sample direction to
+ * choose the right channel. So instead of duplicating the code, we just change
+ * the keyword and then fallback on smp_fetch_cookie().
+ */
+static int
+smp_fetch_chn_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+ kw = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ ? "req.cook" : "res.cook");
+ return smp_fetch_cookie(args, smp, kw, private);
+}
+
/* Iterate over all cookies present in a request to count how many occurrences
* match the name in args and args->data.str.len. If <multi> is non-null, then
* multiple cookies may be parsed on the same line. The returned sample is of
@@ -12855,7 +12877,7 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
* for ACL compatibility only.
*/
{ "cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
- { "cookie", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
+ { "cookie", smp_fetch_chn_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
{ "cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
{ "cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
@@ -12863,7 +12885,7 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
* only here to match the ACL's name, are request-only and are used for
* ACL compatibility only.
*/
- { "hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
+ { "hdr", smp_fetch_chn_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
{ "hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
{ "hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
{ "hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },

View file

@ -0,0 +1,37 @@
commit b50e7fe5e9ae7e8670a467fdd7ece2d08fc02809
Author: Willy Tarreau <w@1wt.eu>
Date: Wed May 22 20:07:45 2019 +0200
BUG/MEDIUM: dns: make the port numbers unsigned
Mustafa Yildirim reported in Discourse that ports >32767 advertised
in SRV records are wrong. Given the high value they definitely
correspond to a sign extension of a negative number. The cause was
indeed that the port is declared as a signed int in the dns_answer_item
structure, and Lukas confirmed in github issue #103 that turning it to
unsigned addresses the issue.
It is worth noting that there are other such fields in this structure
that don't look right (ttl, priority, class, type) and that someone
should audit this part to be certain they are properly typed.
This fix must be backported to 1.9 and likely to 1.8 as well.
(cherry picked from commit d1f56c9a0110805c4a5f3afba2990556cb74ec8b)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 01ceb8a9fb0caecb20a12cc6763230cfc9895de5)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/types/dns.h b/include/types/dns.h
index e8ab9f06..e2d98169 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -144,7 +144,7 @@ struct dns_answer_item {
int32_t ttl; /* response TTL */
int16_t priority; /* SRV type priority */
uint16_t weight; /* SRV type weight */
- int16_t port; /* SRV type port */
+ uint16_t port; /* SRV type port */
uint16_t data_len; /* number of bytes in target below */
struct sockaddr address; /* IPv4 or IPv6, network format */
char target[DNS_MAX_NAME_SIZE+1]; /* Response data: SRV or CNAME type target */

View file

@ -0,0 +1,44 @@
commit 89ff157c3262c8493ed48e6ac48f614791f446f8
Author: Christopher Faulet <cfaulet@haproxy.com>
Date: Thu May 23 22:47:48 2019 +0200
BUG/MEDIUM: spoe: Don't use the SPOE applet after releasing it
In spoe_release_appctx(), the SPOE applet may be used after it was released to
get its exit status code. Of course, HAProxy crashes when this happens.
This patch must be backported to 1.9 and 1.8.
(cherry picked from commit 55ae8a64e4e1175063463921375b279c31bbc6a4)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit df29d11f522044217ea7c1373f494c2a36515b31)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index 66d8b045..aeb1fde7 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -1292,11 +1292,6 @@ spoe_release_appctx(struct appctx *appctx)
task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
}
- /* Release allocated memory */
- spoe_release_buffer(&spoe_appctx->buffer,
- &spoe_appctx->buffer_wait);
- pool_free(pool_head_spoe_appctx, spoe_appctx);
-
if (!LIST_ISEMPTY(&agent->rt[tid].applets))
goto end;
@@ -1317,6 +1312,11 @@ spoe_release_appctx(struct appctx *appctx)
}
end:
+ /* Release allocated memory */
+ spoe_release_buffer(&spoe_appctx->buffer,
+ &spoe_appctx->buffer_wait);
+ pool_free(pool_head_spoe_appctx, spoe_appctx);
+
/* Update runtinme agent info */
agent->rt[tid].frame_size = agent->max_frame_size;
list_for_each_entry(spoe_appctx, &agent->rt[tid].applets, list)

View file

@ -0,0 +1,925 @@
commit 5aa7b32a94ad7ac38f465f0de279f09ff6d529d3
Author: Michael Prokop <haproxy.org@michael-prokop.at>
Date: Fri May 24 10:25:45 2019 +0200
DOC: fix typos
s/accidently/accidentally/
s/any ot these messages/any of theses messages/
s/catched/caught/
s/completly/completely/
s/convertor/converter/
s/desribing/describing/
s/developper/developer/
s/eventhough/even though/
s/exectution/execution/
s/functionnality/functionality/
s/If it receive a/If it receives a/
s/In can even/It can even/
s/informations/information/
s/it will be remove /it will be removed /
s/langage/language/
s/mentionned/mentioned/
s/negociated/negotiated/
s/Optionnaly/Optionally/
s/ouputs/outputs/
s/outweights/outweighs/
s/ressources/resources/
(cherry picked from commit 4438c6061d5a2ffd5b4251027038181af9b8dc22)
[wt: removed the wurfl+da notes as well as the part on peers/server]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit a73e4f3d7a617a51551fffd80c5dbddee81e3aaf)
[wt: removed a few other keywords like ssl_bc_alpn and show peers]
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/doc/DeviceAtlas-device-detection.txt b/doc/DeviceAtlas-device-detection.txt
index 4ecb44a4..144ee318 100644
--- a/doc/DeviceAtlas-device-detection.txt
+++ b/doc/DeviceAtlas-device-detection.txt
@@ -39,7 +39,7 @@ All HTTP headers via the sample / fetch
http-request set-header X-DeviceAtlas-Data %[da-csv-fetch(primaryHardwareType,osName,osVersion,browserName,browserVersion,browserRenderingEngine)]
-Single HTTP header (e.g. User-Agent) via the convertor
+Single HTTP header (e.g. User-Agent) via the converter
http-request set-header X-DeviceAtlas-Data %[req.fhdr(User-Agent),da-csv-conv(primaryHardwareType,osName,osVersion,browserName,browserVersion,browserRenderingEngine)]
diff --git a/doc/SPOE.txt b/doc/SPOE.txt
index dd36d43a..9602df95 100644
--- a/doc/SPOE.txt
+++ b/doc/SPOE.txt
@@ -96,7 +96,7 @@ for several engines. If no name is provided, the SPOE configuration must not
contain any scope directive.
We use a separate configuration file on purpose. By commenting SPOE filter
-line, you completly disable the feature, including the parsing of sections
+line, you completely disable the feature, including the parsing of sections
reserved to SPOE. This is also a way to keep the HAProxy configuration clean.
A SPOE configuration file must contains, at least, the SPOA configuration
@@ -272,7 +272,7 @@ option set-on-error <var name>
* 1 a timeout occurred during the event processing.
- * 2 an error was triggered during the ressources allocation.
+ * 2 an error was triggered during the resources allocation.
* 3 the frame payload exceeds the frame size and it cannot be
fragmented.
@@ -923,7 +923,7 @@ For more information about known errors, see section "Errors & timeouts"
-------------------------------
If an error occurs, at anytime, from the agent size, a AGENT-DISCONNECT frame
-is sent, with information desribing the error. such frame is also sent in reply
+is sent, with information describing the error. such frame is also sent in reply
to a HAPROXY-DISCONNECT. The agent must close the socket just after sending
this frame.
diff --git a/doc/coding-style.txt b/doc/coding-style.txt
index 5f252ed0..9f1bd79e 100644
--- a/doc/coding-style.txt
+++ b/doc/coding-style.txt
@@ -520,7 +520,7 @@ Wrong :
| bit = ! ! (~len++ ^ - (unsigned char) * x) ;
Note that "sizeof" is a unary operator which is sometimes considered as a
-langage keyword, but in no case it is a function. It does not require
+language keyword, but in no case it is a function. It does not require
parenthesis so it is sometimes followed by spaces and sometimes not when
there are no parenthesis. Most people do not really care as long as what
is written is unambiguous.
@@ -814,7 +814,7 @@ common to see such a thing :
This is wrong. The man page says that -1 is returned if an error occurred. It
does not suggest that any other negative value will be an error. It is possible
that a few such issues have been left in existing code. They are bugs for which
-fixes are accepted, eventhough they're currently harmless since open() is not
+fixes are accepted, even though they're currently harmless since open() is not
known for returning negative values at the moment.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 863508f5..e044639a 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3051,7 +3051,7 @@ cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ]
already have a cookie that would have permitted it to access this
server. When used without the "preserve" option, if the server
- emits a cookie with the same name, it will be remove before
+ emits a cookie with the same name, it will be removed before
processing. For this reason, this mode can be used to upgrade
existing configurations running in the "rewrite" mode. The cookie
will only be a session cookie and will not be stored on the
@@ -4786,7 +4786,7 @@ http-reuse { never | safe | aggressive | always }
proving that the server correctly supports connection reuse.
It should only be used when it's sure that the client can
retry a failed request once in a while and where the benefit
- of aggressive connection reuse significantly outweights the
+ of aggressive connection reuse significantly outweighs the
downsides of rare connection failures.
- "always" : this mode is only recommended when the path to the server is
diff --git a/doc/intro.txt b/doc/intro.txt
index 7ad92db0..864fb8d6 100644
--- a/doc/intro.txt
+++ b/doc/intro.txt
@@ -1481,7 +1481,7 @@ they are mentioned here even if not directly related to HAProxy.
Apache is the de-facto standard HTTP server. It's a very complete and modular
project supporting both file serving and dynamic contents. It can serve as a
-frontend for some application servers. In can even proxy requests and cache
+frontend for some application servers. It can even proxy requests and cache
responses. In all of these use cases, a front load balancer is commonly needed.
Apache can work in various modes, some being heavier than others. Certain
modules still require the heavier pre-forked model and will prevent Apache from
diff --git a/doc/lua.txt b/doc/lua.txt
index 7b257ad8..2e266b03 100644
--- a/doc/lua.txt
+++ b/doc/lua.txt
@@ -412,7 +412,7 @@ The max amount of memory is configured with the option:
tune.lua.maxmem
As many other script languages, Lua uses a garbage collector for reusing its
-memory. The Lua developper can work without memory preoccupation. Usually, the
+memory. The Lua developer can work without memory preoccupation. Usually, the
garbage collector is controlled by the Lua core, but sometimes it will be useful
to run when the user/developer requires. So the garbage collector can be called
from C part or Lua part.
@@ -885,7 +885,7 @@ The task entry point
The function "core.register_task(fcn)" executes once the function "fcn" when the
scheduler starts. This way is used for executing background task. For example,
-you can use this functionnality for periodically checking the health of another
+you can use this functionality for periodically checking the health of another
service, and giving the result to each proxy needing it.
The task is started once, if you want periodic actions, you can use the
diff --git a/doc/management.txt b/doc/management.txt
index 1b41558a..8fdea722 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1634,7 +1634,7 @@ set rate-limit ssl-sessions global <value>
set server <backend>/<server> addr <ip4 or ip6 address> [port <port>]
Replace the current IP address of a server by the one provided.
- Optionnaly, the port can be changed using the 'port' parameter.
+ Optionally, the port can be changed using the 'port' parameter.
Note that changing the port also support switching from/to port mapping
(notation with +X or -Y), only if a port is configured for the health check.
diff --git a/doc/netscaler-client-ip-insertion-protocol.txt b/doc/netscaler-client-ip-insertion-protocol.txt
index 559d98a8..dc64327a 100644
--- a/doc/netscaler-client-ip-insertion-protocol.txt
+++ b/doc/netscaler-client-ip-insertion-protocol.txt
@@ -1,4 +1,4 @@
-When NetScaler application switch is used as L3+ switch, informations
+When NetScaler application switch is used as L3+ switch, information
regarding the original IP and TCP headers are lost as a new TCP
connection is created between the NetScaler and the backend server.
diff --git a/doc/peers-v2.0.txt b/doc/peers-v2.0.txt
index a7f70dcc..02914743 100644
--- a/doc/peers-v2.0.txt
+++ b/doc/peers-v2.0.txt
@@ -275,11 +275,11 @@ if no available remote peers are found.
The chosen remote peer will push its all known data ending with a Resync Finished Message or a Resync Partial Message (if it it does not consider itself as full updated).
-If it receive a Resync Finished Message it will consider itself as fully updated and stops to ask for resync.
+If it receives a Resync Finished Message it will consider itself as fully updated and stops to ask for resync.
-If it receive a Resync Partial Message, the current peer will be flagged to anymore be requested and any other connected peer will be randomly chosen for a resync request (5s).
+If it receives a Resync Partial Message, the current peer will be flagged to anymore be requested and any other connected peer will be randomly chosen for a resync request (5s).
-If the session is broken before receiving any ot these messages any other connected peer will be randomly chosen for a resync request (5s).
+If the session is broken before receiving any of these messages any other connected peer will be randomly chosen for a resync request (5s).
If the timeout expire, the process will consider itself as fully updated
diff --git a/doc/proxy-protocol.txt b/doc/proxy-protocol.txt
index 4969180a..52d7bc71 100644
--- a/doc/proxy-protocol.txt
+++ b/doc/proxy-protocol.txt
@@ -561,7 +561,7 @@ Contains the host name value passed by the client, as an UTF8-encoded string.
In case of TLS being used on the client connection, this is the exact copy of
the "server_name" extension as defined by RFC3546 [10], section 3.1, often
referred to as "SNI". There are probably other situations where an authority
-can be mentionned on a connection without TLS being involved at all.
+can be mentioned on a connection without TLS being involved at all.
2.2.3. PP2_TYPE_CRC32C
diff --git a/doc/regression-testing.txt b/doc/regression-testing.txt
new file mode 100644
index 00000000..320c51cd
--- /dev/null
+++ b/doc/regression-testing.txt
@@ -0,0 +1,706 @@
+ +---------------------------------------+
+ | HAProxy regression testing with vtest |
+ +---------------------------------------+
+
+
+The information found in this file are a short starting guide to help you to
+write VTC (Varnish Test Case) scripts (or VTC files) for haproxy regression testing.
+Such VTC files are currently used to test Varnish cache application developed by
+Poul-Henning Kamp. A very big thanks you to him for having helped you to add
+our haproxy C modules to vtest tool. Note that vtest was formally developed for
+varnish cache reg testing and was named varnishtest. vtest is an haproxy specific
+version of varnishtest program which reuses the non varnish cache specific code.
+
+A lot of general information about how to write VTC files may be found in 'man/vtc.7'
+manual of varnish cache sources directory or directly on the web here:
+
+ https://varnish-cache.org/docs/trunk/reference/vtc.html
+
+It is *highly* recommended to read this manual before asking to haproxy ML. This
+documentation only deals with the vtest support for haproxy.
+
+
+vtest installation
+------------------------
+
+To use vtest you will have to download and compile the recent vtest
+sources found at https://github.com/vtest/VTest.
+
+To compile vtest:
+
+ $ cd VTest
+ $ make vtest
+
+Note that varnishtest may be also compiled but not without the varnish cache
+sources already compiled:
+
+ $ VARNISH_SRC=<...> make varnishtest
+
+After having compiled these sources, the vtest executable location is at the
+root of the vtest sources directory.
+
+
+vtest execution
+---------------------
+
+vtest is able to search for the haproxy executable file it is supposed to
+launch thanks to the PATH environment variable. To force the executable to be used by
+vtest, the HAPROXY_PROGRAM environment variable for vtest may be
+typically set as follows:
+
+ $ HAPROXY_PROGRAM=~/srcs/haproxy/haproxy vtest ...
+
+vtest program comes with interesting options. The most interesting are:
+
+ -t Timeout in seconds to abort the test if some launched program
+ -v By default, vtest does not dump the outputs of processus it launched
+ when the test passes. With this option the outputs are dumped even
+ when the test passes.
+ -L to always keep the temporary VTC directories.
+ -l to keep the temporary VTC directories only when the test fails.
+
+About haproxy, when launched by vtest, -d option is enabled by default.
+
+
+How to write VTC files
+----------------------
+
+A VTC file must start with a "varnishtest" or "vtest" command line followed by a
+descriptive line enclosed by double quotes. This is not specific to the VTC files
+for haproxy.
+
+The VTC files for haproxy must also contain a "feature ignore_unknown_macro" line
+if any macro is used for haproxy in this file. This is due to the fact that
+vtest parser code for haproxy commands generates macros the vtest
+parser code for varnish cache has no knowledge of. This line prevents vtest from
+failing in such cases. As a "cli" macro automatically generated, this
+"feature ignore_unknown_macro" is mandatory for each VTC file for haproxy.
+
+To make vtest capable of testing haproxy, two new VTC commands have been
+implemented: "haproxy" and "syslog". "haproxy" is used to start haproxy processus.
+"syslog" is used to start syslog servers (at this time, only used by haproxy).
+
+As haproxy cannot work without configuration file, a VTC file for haproxy must
+embed the configuration files contents for the haproxy instances it declares.
+This may be done using the following intuitive syntax construction: -conf {...}.
+Here -conf is an argument of "haproxy" VTC command to declare the configuration
+file of the haproxy instances it also declares (see "Basic HAProxy test" VTC file
+below).
+
+As for varnish VTC files, the parser of VTC files for haproxy automatically
+generates macros for the declared frontends to be reused by the clients later
+in the script, so after having written the "haproxy" command sections.
+The syntax "fd@${my_frontend_fd_name}" must be used to bind the frontend
+listeners to localhost address and random ports (see "Environment variables"
+section of haproxy documentation). This is mandatory.
+
+Each time the haproxy command parser finds a "fd@${xyz}" string in a 'ABC'
+"haproxy" command section, it generates three macros: 'ABC_xyz_addr', 'ABC_xyz_port'
+and 'ABC_xyz_sock', with 'ABC_xyz_sock' being resolved as 'ABC_xyz_addr
+ABC_xyz_port' typically used by clients -connect parameter.
+
+Each haproxy instance works in their own temporary working directories located
+at '/tmp/vtc.<varnitest PID>.XXXXXXXX/<haproxy_instance_name>' (with XXXXXXXX
+a random 8 digits hexadecimal integer. This is in this temporary directory that
+the configuration file is temporarily written.
+
+A 'stats.sock' UNIX socket is also created in this directory. There is no need
+to declare such stats sockets in the -conf {...} section. The name of the parent
+directory of the haproxy instances working directories is stored in 'tmpdir'. In
+fact this is the working directory of the current vtest processus.
+
+There also exists 'testdir' macro which is the parent directory of the VTC file.
+It may be useful to use other files located in the same directory than the current
+VTC file.
+
+
+
+VTC file examples
+-----------------
+
+The following first VTC file is a real regression test case file for a bug which has
+been fixed by 84c844e commit. We declare a basic configuration for a 'h1' haproxy
+instance.
+
+ varnishtest "SPOE bug: missing configuration file"
+
+ #commit 84c844eb12b250aa86f2aadaff77c42dfc3cb619
+ #Author: Christopher Faulet <cfaulet@haproxy.com>
+ #Date: Fri Mar 23 14:37:14 2018 +0100
+
+ # BUG/MINOR: spoe: Initialize variables used during conf parsing before any check
+
+ # Some initializations must be done at the beginning of parse_spoe_flt to avoid
+ # segmentaion fault when first errors are caught, when the "filter spoe" line is
+ # parsed.
+
+ haproxy h1 -conf-BAD {} {
+ defaults
+ timeout connect 5000ms
+ timeout client 50000ms
+ timeout server 50000ms
+
+ frontend my-front
+ filter spoe
+ }
+
+
+-conf-BAD haproxy command argument is used. Its role it to launch haproxy with
+-c option (configuration file checking) and check that 'h1' exit(3) with 1 as
+status. Here is the output when running this VTC file:
+
+
+ **** top 0.0 extmacro def pwd=/home/fred/src/haproxy
+ **** top 0.0 extmacro def localhost=127.0.0.1
+ **** top 0.0 extmacro def bad_backend=127.0.0.1 39564
+ **** top 0.0 extmacro def bad_ip=192.0.2.255
+ **** top 0.0 macro def testdir=//home/fred/src/varnish-cache-haproxy
+ **** top 0.0 macro def tmpdir=/tmp/vtc.6377.64329194
+ * top 0.0 TEST /home/fred/src/varnish-cache-haproxy/spoe_bug.vtc starting
+ ** top 0.0 === varnishtest "SPOE bug: missing configuration file"
+ * top 0.0 TEST SPOE bug: missing configuration file
+ ** top 0.0 === haproxy h1 -conf-BAD {} {
+ **** h1 0.0 conf| global
+ **** h1 0.0 conf|\tstats socket /tmp/vtc.6377.64329194/h1/stats.sock level admin mode 600
+ **** h1 0.0 conf|
+ **** h1 0.0 conf|\tdefaults
+ **** h1 0.0 conf| timeout connect 5000ms
+ **** h1 0.0 conf| timeout client 50000ms
+ **** h1 0.0 conf| timeout server 50000ms
+ **** h1 0.0 conf|
+ **** h1 0.0 conf|\tfrontend my-front
+ **** h1 0.0 conf|\t\tfilter spoe
+ **** h1 0.0 conf|
+ ** h1 0.0 haproxy_start
+ **** h1 0.0 opt_worker 0 opt_daemon 0 opt_check_mode 1
+ **** h1 0.0 argv|exec /home/fred/src/haproxy/haproxy -c -f /tmp/vtc.6377.64329194/h1/cfg
+ **** h1 0.0 XXX 5 @277
+ *** h1 0.0 PID: 6395
+ **** h1 0.0 macro def h1_pid=6395
+ **** h1 0.0 macro def h1_name=/tmp/vtc.6377.64329194/h1
+ ** h1 0.0 Wait
+ ** h1 0.0 Stop HAproxy pid=6395
+ **** h1 0.0 STDOUT poll 0x10
+ ** h1 0.0 WAIT4 pid=6395 status=0x008b (user 0.000000 sys 0.000000)
+ * h1 0.0 Expected exit: 0x1 signal: 0 core: 0
+ ---- h1 0.0 Bad exit status: 0x008b exit 0x0 signal 11 core 128
+ * top 0.0 RESETTING after /home/fred/src/varnish-cache-haproxy/spoe_bug.vtc
+ ** h1 0.0 Reset and free h1 haproxy 6395
+ ** h1 0.0 Wait
+ ---- h1 0.0 Assert error in haproxy_wait(), vtc_haproxy.c line 326: Condition(*(&h->fds[1]) >= 0) not true.
+
+ * top 0.0 failure during reset
+ # top TEST /home/fred/src/varnish-cache-haproxy/spoe_bug.vtc FAILED (0.008) exit=2
+
+
+'h1' exited with (128 + 11) status and a core file was produced in
+/tmp/vtc.6377.64329194/h1 directory.
+With the patch provided by 84c844e commit, varnishtest makes this VTC file pass
+as expected (verbose mode execution):
+
+ **** top 0.0 extmacro def pwd=/home/fred/src/haproxy
+ **** top 0.0 extmacro def localhost=127.0.0.1
+ **** top 0.0 extmacro def bad_backend=127.0.0.1 42264
+ **** top 0.0 extmacro def bad_ip=192.0.2.255
+ **** top 0.0 macro def testdir=//home/fred/src/varnish-cache-haproxy
+ **** top 0.0 macro def tmpdir=/tmp/vtc.25540.59b6ec5d
+ * top 0.0 TEST /home/fred/src/varnish-cache-haproxy/spoe_bug.vtc starting
+ ** top 0.0 === varnishtest "SPOE bug: missing configuration file"
+ * top 0.0 TEST SPOE bug: missing configuration file
+ ** top 0.0 === haproxy h1 -conf-BAD {} {
+ **** h1 0.0 conf| global
+ **** h1 0.0 conf|\tstats socket /tmp/vtc.25540.59b6ec5d/h1/stats.sock level admin mode 600
+ **** h1 0.0 conf|
+ **** h1 0.0 conf|\tdefaults
+ **** h1 0.0 conf| timeout connect 5000ms
+ **** h1 0.0 conf| timeout client 50000ms
+ **** h1 0.0 conf| timeout server 50000ms
+ **** h1 0.0 conf|
+ **** h1 0.0 conf|\tfrontend my-front
+ **** h1 0.0 conf|\t\tfilter spoe
+ **** h1 0.0 conf|
+ ** h1 0.0 haproxy_start
+ **** h1 0.0 opt_worker 0 opt_daemon 0 opt_check_mode 1
+ **** h1 0.0 argv|exec /home/fred/src/haproxy/haproxy -c -f /tmp/vtc.25540.59b6ec5d/h1/cfg
+ **** h1 0.0 XXX 5 @277
+ *** h1 0.0 PID: 25558
+ **** h1 0.0 macro def h1_pid=25558
+ **** h1 0.0 macro def h1_name=/tmp/vtc.25540.59b6ec5d/h1
+ ** h1 0.0 Wait
+ ** h1 0.0 Stop HAproxy pid=25558
+ *** h1 0.0 debug|[ALERT] 157/135318 (25558) : parsing [/tmp/vtc.25540.59b6ec5d/h1/cfg:10] : 'filter' : ''spoe' : missing config file'
+ *** h1 0.0 debug|[ALERT] 157/135318 (25558) : Error(s) found in configuration file : /tmp/vtc.25540.59b6ec5d/h1/cfg
+ *** h1 0.0 debug|[ALERT] 157/135318 (25558) : Fatal errors found in configuration.
+ **** h1 0.0 STDOUT poll 0x10
+ ** h1 0.0 WAIT4 pid=25558 status=0x0100 (user 0.000000 sys 0.000000)
+ ** h1 0.0 Found expected ''
+ * top 0.0 RESETTING after /home/fred/src/varnish-cache-haproxy/spoe_bug.vtc
+ ** h1 0.0 Reset and free h1 haproxy -1
+ * top 0.0 TEST /home/fred/src/varnish-cache-haproxy/spoe_bug.vtc completed
+ # top TEST /home/fred/src/varnish-cache-haproxy/spoe_bug.vtc passed (0.004)
+
+
+The following VTC file does almost nothing except running a shell to list
+the contents of 'tmpdir' directory after having launched a haproxy instance
+and 's1' HTTP server. This shell also prints the content of 'cfg' 'h1' configuration
+file.
+
+ varnishtest "List the contents of 'tmpdir'"
+ feature ignore_unknown_macro
+
+ server s1 {
+ } -start
+
+ haproxy h1 -conf {
+ defaults
+ mode http
+ timeout connect 5s
+ timeout server 30s
+ timeout client 30s
+
+ backend be1
+ server srv1 ${s1_addr}:${s1_port}
+
+ frontend http1
+ use_backend be1
+ bind "fd@${my_frontend_fd}"
+ } -start
+
+ shell {
+ echo "${tmpdir} working directory content:"
+ ls -lR ${tmpdir}
+ cat ${tmpdir}/h1/cfg
+ }
+
+We give only the output of the shell to illustrate this example:
+
+ .
+ .
+ .
+ ** top 0.0 === shell {
+ **** top 0.0 shell_cmd|exec 2>&1 ;
+ **** top 0.0 shell_cmd| echo "tmpdir: /tmp/vtc.32092.479d521e"
+ **** top 0.0 shell_cmd| ls -lR /tmp/vtc.32092.479d521e
+ **** top 0.0 shell_cmd| cat /tmp/vtc.32092.479d521e/h1/cfg
+ .
+ .
+ .
+ **** top 0.0 shell_out|/tmp/vtc.3808.448cbfe0 working directory content:
+ **** top 0.0 shell_out|/tmp/vtc.32092.479d521e:
+ **** top 0.0 shell_out|total 8
+ **** top 0.0 shell_out|drwxr-xr-x 2 users 4096 Jun 7 11:09 h1
+ **** top 0.0 shell_out|-rw-r--r-- 1 me users 84 Jun 7 11:09 INFO
+ **** top 0.0 shell_out|
+ **** top 0.0 shell_out|/tmp/vtc.32092.479d521e/h1:
+ **** top 0.0 shell_out|total 4
+ **** top 0.0 shell_out|-rw-r----- 1 fred users 339 Jun 7 11:09 cfg
+ **** top 0.0 shell_out|srw------- 1 fred users 0 Jun 7 11:09 stats.sock
+ **** top 0.0 shell_out| global
+ **** top 0.0 shell_out|\tstats socket /tmp/vtc.32092.479d521e/h1/stats.sock level admin mode 600
+ **** top 0.0 shell_out|
+ **** top 0.0 shell_out| defaults
+ **** top 0.0 shell_out| mode http
+ **** top 0.0 shell_out| timeout connect 5s
+ **** top 0.0 shell_out| timeout server 30s
+ **** top 0.0 shell_out| timeout client 30s
+ **** top 0.0 shell_out|
+ **** top 0.0 shell_out| backend be1
+ **** top 0.0 shell_out| server srv1 127.0.0.1:36984
+ **** top 0.0 shell_out|
+ **** top 0.0 shell_out| frontend http1
+ **** top 0.0 shell_out| use_backend be1
+ **** top 0.0 shell_out| bind "fd@${my_frontend_fd}"
+ **** top 0.0 shell_status = 0x0000
+
+
+The following example illustrate how to run a basic HTTP transaction between 'c1'
+client and 's1' server with 'http1' as haproxy frontend. This frontend listen
+on TCP socket with 'my_frontend_fd' as file descriptor.
+
+ # Mandatory line
+ varnishtest "Basic HAproxy test"
+
+ # As some macros for haproxy are used in this file, this line is mandatory.
+ feature ignore_unknown_macro
+
+ server s1 {
+ rxreq
+ txresp -body "s1 >>> Hello world!"
+ } -start
+
+ haproxy h1 -conf {
+ # Configuration file of 'h1' haproxy instance.
+ defaults
+ mode http
+ timeout connect 5s
+ timeout server 30s
+ timeout client 30s
+
+ backend be1
+ # declare 'srv1' server to point to 's1' server instance declare above.
+ server srv1 ${s1_addr}:${s1_port}
+
+ frontend http1
+ use_backend be1
+ bind "fd@${my_frontend_fd}"
+ } -start
+
+ client c1 -connect ${h1_my_frontend_fd_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.body == "s1 >>> Hello world!"
+ } -run
+
+
+It is possible to shorten the previous VTC file haproxy command section as follows:
+
+ haproxy h1 -conf {
+ # Configuration file of 'h1' haproxy instance.
+ defaults
+ mode http
+ timeout connect 5s
+ timeout server 30s
+ timeout client 30s
+ }
+
+In this latter example, "backend" and "frontend" sections are automatically
+generated depending on the declarations of server instances.
+
+
+Another interesting real regression test case is the following: we declare one
+server 's1', a syslog server 'Slg_1' and a basic haproxy configuration for 'h1'
+haproxy instance. Here we want to check that the syslog message are correctly
+formatted thanks to "expect" "syslog" command (see syslog Slg_1 {...} command)
+below.
+
+ varnishtest "Wrong ip/port logging"
+ feature ignore_unknown_macro
+
+ #commit d02286d6c866e5c0a7eb6fbb127fa57f3becaf16
+ #Author: Willy Tarreau <w@1wt.eu>
+ #Date: Fri Jun 23 11:23:43 2017 +0200
+ #
+ # BUG/MINOR: log: pin the front connection when front ip/ports are logged
+ #
+ # Mathias Weiersmueller reported an interesting issue with logs which Lukas
+ # diagnosed as dating back from commit 9b061e332 (1.5-dev9). When front
+ # connection information (ip, port) are logged in TCP mode and the log is
+ # emitted at the end of the connection (eg: because %B or any log tag
+ # requiring LW_BYTES is set), the log is emitted after the connection is
+ # closed, so the address and ports cannot be retrieved anymore.
+ #
+ # It could be argued that we'd make a special case of these to immediately
+ # retrieve the source and destination addresses from the connection, but it
+ # seems cleaner to simply pin the front connection, marking it "tracked" by
+ # adding the LW_XPRT flag to mention that we'll need some of these elements
+ # at the last moment. Only LW_FRTIP and LW_CLIP are affected. Note that after
+ # this change, LW_FRTIP could simply be removed as it's not used anywhere.
+
+ # Note that the problem doesn't happen when using %[src] or %[dst] since
+ # all sample expressions set LW_XPRT.
+
+
+ server s1 {
+ rxreq
+ txresp
+ } -start
+
+ syslog Slg_1 -level notice {
+ recv
+ recv
+ recv info
+ expect ~ \"dip\":\"${h1_fe_1_addr}\",\"dport\":\"${h1_fe_1_port}.*\"ts\":\"cD\",\"
+ } -start
+
+ haproxy h1 -conf {
+ global
+ log ${Slg_1_addr}:${Slg_1_port} local0
+
+ defaults
+ log global
+ timeout connect 3000
+ timeout client 5
+ timeout server 10000
+
+ frontend fe1
+ bind "fd@${fe_1}"
+ mode tcp
+ log-format {\"dip\":\"%fi\",\"dport\":\"%fp\",\"c_ip\":\"%ci\",\"c_port\":\"%cp\",\"fe_name\":\"%ft\",\"be_name\":\"%b\",\"s_name\":\"%s\",\"ts\":\"%ts\",\"bytes_read\":\"%B\"}
+ default_backend be_app
+
+ backend be_app
+ server app1 ${s1_addr}:${s1_port} check
+ } -start
+
+ client c1 -connect ${h1_fe_1_sock} {
+ txreq -url "/"
+ delay 0.02
+ } -run
+
+ syslog Slg_1 -wait
+
+
+Here is the output produced by varnishtest with the latter VTC file:
+
+ **** top 0.0 extmacro def pwd=/home/fred/src/haproxy
+ **** top 0.0 extmacro def localhost=127.0.0.1
+ **** top 0.0 extmacro def bad_backend=127.0.0.1 40386
+ **** top 0.0 extmacro def bad_ip=192.0.2.255
+ **** top 0.0 macro def testdir=//home/fred/src/varnish-cache-haproxy
+ **** top 0.0 macro def tmpdir=/tmp/vtc.15752.560ca66b
+ * top 0.0 TEST /home/fred/src/varnish-cache-haproxy/d02286d.vtc starting
+ ** top 0.0 === varnishtest "HAPEE bug 2788"
+ * top 0.0 TEST HAPEE bug 2788
+ ** top 0.0 === feature ignore_unknown_macro
+ ** top 0.0 === server s1 {
+ ** s1 0.0 Starting server
+ **** s1 0.0 macro def s1_addr=127.0.0.1
+ **** s1 0.0 macro def s1_port=35564
+ **** s1 0.0 macro def s1_sock=127.0.0.1 35564
+ * s1 0.0 Listen on 127.0.0.1 35564
+ ** top 0.0 === syslog Slg_1 -level notice {
+ ** Slg_1 0.0 Starting syslog server
+ ** s1 0.0 Started on 127.0.0.1 35564
+ **** Slg_1 0.0 macro def Slg_1_addr=127.0.0.1
+ **** Slg_1 0.0 macro def Slg_1_port=33012
+ **** Slg_1 0.0 macro def Slg_1_sock=127.0.0.1 33012
+ * Slg_1 0.0 Bound on 127.0.0.1 33012
+ ** top 0.0 === haproxy h1 -conf {
+ ** Slg_1 0.0 Started on 127.0.0.1 33012 (level: 5)
+ ** Slg_1 0.0 === recv
+ **** h1 0.0 macro def h1_fe_1_sock=::1 51782
+ **** h1 0.0 macro def h1_fe_1_addr=::1
+ **** h1 0.0 macro def h1_fe_1_port=51782
+ **** h1 0.0 setenv(fe_1, 7)
+ **** h1 0.0 conf| global
+ **** h1 0.0 conf|\tstats socket /tmp/vtc.15752.560ca66b/h1/stats.sock level admin mode 600
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| global
+ **** h1 0.0 conf| log 127.0.0.1:33012 local0
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| defaults
+ **** h1 0.0 conf| log global
+ **** h1 0.0 conf| timeout connect 3000
+ **** h1 0.0 conf| timeout client 5
+ **** h1 0.0 conf| timeout server 10000
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| frontend fe1
+ **** h1 0.0 conf| bind "fd@${fe_1}"
+ **** h1 0.0 conf| mode tcp
+ **** h1 0.0 conf| log-format {\"dip\":\"%fi\",\"dport\":\"%fp\",\"c_ip\":\"%ci\",\"c_port\":\"%cp\",\"fe_name\":\"%ft\",\"be_name\":\"%b\",\"s_name\":\"%s\",\"ts\":\"%ts\",\"bytes_read\":\"%B\"}
+ **** h1 0.0 conf| default_backend be_app
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| backend be_app
+ **** h1 0.0 conf| server app1 127.0.0.1:35564 check
+ ** h1 0.0 haproxy_start
+ **** h1 0.0 opt_worker 0 opt_daemon 0 opt_check_mode 0
+ **** h1 0.0 argv|exec /home/fred/src/haproxy/haproxy -d -f /tmp/vtc.15752.560ca66b/h1/cfg
+ **** h1 0.0 XXX 9 @277
+ *** h1 0.0 PID: 15787
+ **** h1 0.0 macro def h1_pid=15787
+ **** h1 0.0 macro def h1_name=/tmp/vtc.15752.560ca66b/h1
+ ** top 0.0 === client c1 -connect ${h1_fe_1_sock} {
+ ** c1 0.0 Starting client
+ ** c1 0.0 Waiting for client
+ *** c1 0.0 Connect to ::1 51782
+ *** c1 0.0 connected fd 8 from ::1 46962 to ::1 51782
+ ** c1 0.0 === txreq -url "/"
+ **** c1 0.0 txreq|GET / HTTP/1.1\r
+ **** c1 0.0 txreq|Host: 127.0.0.1\r
+ **** c1 0.0 txreq|\r
+ ** c1 0.0 === delay 0.02
+ *** c1 0.0 delaying 0.02 second(s)
+ *** h1 0.0 debug|Note: setting global.maxconn to 2000.
+ *** h1 0.0 debug|Available polling systems :
+ *** h1 0.0 debug| epoll :
+ *** h1 0.0 debug|pref=300,
+ *** h1 0.0 debug| test result OK
+ *** h1 0.0 debug| poll : pref=200, test result OK
+ *** h1 0.0 debug| select :
+ *** h1 0.0 debug|pref=150, test result FAILED
+ *** h1 0.0 debug|Total: 3 (2 usable), will use epoll.
+ *** h1 0.0 debug|
+ *** h1 0.0 debug|Available filters :
+ *** h1 0.0 debug|\t[SPOE] spoe
+ *** h1 0.0 debug|\t[COMP] compression
+ *** h1 0.0 debug|\t[TRACE] trace
+ **** Slg_1 0.0 syslog|<133>Jun 7 14:12:51 haproxy[15787]: Proxy fe1 started.
+ ** Slg_1 0.0 === recv
+ **** Slg_1 0.0 syslog|<133>Jun 7 14:12:51 haproxy[15787]: Proxy be_app started.
+ ** Slg_1 0.0 === recv info
+ *** h1 0.0 debug|00000000:fe1.accept(0007)=000a from [::1:46962]
+ *** s1 0.0 accepted fd 6 127.0.0.1 56770
+ ** s1 0.0 === rxreq
+ **** s1 0.0 rxhdr|GET / HTTP/1.1\r
+ **** s1 0.0 rxhdr|Host: 127.0.0.1\r
+ **** s1 0.0 rxhdr|\r
+ **** s1 0.0 rxhdrlen = 35
+ **** s1 0.0 http[ 0] |GET
+ **** s1 0.0 http[ 1] |/
+ **** s1 0.0 http[ 2] |HTTP/1.1
+ **** s1 0.0 http[ 3] |Host: 127.0.0.1
+ **** s1 0.0 bodylen = 0
+ ** s1 0.0 === txresp
+ **** s1 0.0 txresp|HTTP/1.1 200 OK\r
+ **** s1 0.0 txresp|Content-Length: 0\r
+ **** s1 0.0 txresp|\r
+ *** s1 0.0 shutting fd 6
+ ** s1 0.0 Ending
+ *** h1 0.0 debug|00000000:be_app.srvcls[000a:000c]
+ *** h1 0.0 debug|00000000:be_app.clicls[000a:000c]
+ *** h1 0.0 debug|00000000:be_app.closed[000a:000c]
+ **** Slg_1 0.0 syslog|<134>Jun 7 14:12:51 haproxy[15787]: {"dip":"","dport":"0","c_ip":"::1","c_port":"46962","fe_name":"fe1","be_name":"be_app","s_name":"app1","ts":"cD","bytes_read":"38"}
+ ** Slg_1 0.0 === expect ~ \"dip\":\"${h1_fe_1_addr}\",\"dport\":\"${h1_fe_1_p...
+ ---- Slg_1 0.0 EXPECT FAILED ~ "\"dip\":\"::1\",\"dport\":\"51782.*\"ts\":\"cD\",\""
+ *** c1 0.0 closing fd 8
+ ** c1 0.0 Ending
+ * top 0.0 RESETTING after /home/fred/src/varnish-cache-haproxy/d02286d.vtc
+ ** h1 0.0 Reset and free h1 haproxy 15787
+ ** h1 0.0 Wait
+ ** h1 0.0 Stop HAproxy pid=15787
+ **** h1 0.0 Kill(2)=0: Success
+ **** h1 0.0 STDOUT poll 0x10
+ ** h1 0.1 WAIT4 pid=15787 status=0x0002 (user 0.000000 sys 0.004000)
+ ** s1 0.1 Waiting for server (4/-1)
+ ** Slg_1 0.1 Waiting for syslog server (5)
+ * top 0.1 TEST /home/fred/src/varnish-cache-haproxy/d02286d.vtc FAILED
+ # top TEST /home/fred/src/varnish-cache-haproxy/d02286d.vtc FAILED (0.131) exit=2
+
+This test does not pass without the bug fix of d02286d commit. Indeed the third syslog
+message received by 'Slg_1' syslog server does not match the regular expression
+of the "syslog" "expect" command:
+
+ expect ~ \"dip\":\"${h1_fe_1_addr}\",\"dport\":\"${h1_fe_1_port}.*\"ts\":\"cD\",\"
+
+(the IP address and port are missing), contrary to what happens with the correct bug fix:
+
+ **** top 0.0 extmacro def pwd=/home/fred/src/haproxy
+ **** top 0.0 extmacro def localhost=127.0.0.1
+ **** top 0.0 extmacro def bad_backend=127.0.0.1 37284
+ **** top 0.0 extmacro def bad_ip=192.0.2.255
+ **** top 0.0 macro def testdir=//home/fred/src/varnish-cache-haproxy
+ **** top 0.0 macro def tmpdir=/tmp/vtc.12696.186b28b0
+ * top 0.0 TEST /home/fred/src/varnish-cache-haproxy/d02286d.vtc starting
+ ** top 0.0 === varnishtest "HAPEE bug 2788"
+ * top 0.0 TEST HAPEE bug 2788
+ ** top 0.0 === feature ignore_unknown_macro
+ ** top 0.0 === server s1 {
+ ** s1 0.0 Starting server
+ **** s1 0.0 macro def s1_addr=127.0.0.1
+ **** s1 0.0 macro def s1_port=53384
+ **** s1 0.0 macro def s1_sock=127.0.0.1 53384
+ * s1 0.0 Listen on 127.0.0.1 53384
+ ** top 0.0 === syslog Slg_1 -level notice {
+ ** Slg_1 0.0 Starting syslog server
+ **** Slg_1 0.0 macro def Slg_1_addr=127.0.0.1
+ ** s1 0.0 Started on 127.0.0.1 53384
+ **** Slg_1 0.0 macro def Slg_1_port=36195
+ **** Slg_1 0.0 macro def Slg_1_sock=127.0.0.1 36195
+ * Slg_1 0.0 Bound on 127.0.0.1 36195
+ ** top 0.0 === haproxy h1 -conf {
+ ** Slg_1 0.0 Started on 127.0.0.1 36195 (level: 5)
+ ** Slg_1 0.0 === recv
+ **** h1 0.0 macro def h1_fe_1_sock=::1 39264
+ **** h1 0.0 macro def h1_fe_1_addr=::1
+ **** h1 0.0 macro def h1_fe_1_port=39264
+ **** h1 0.0 setenv(fe_1, 7)
+ **** h1 0.0 conf| global
+ **** h1 0.0 conf|\tstats socket /tmp/vtc.12696.186b28b0/h1/stats.sock level admin mode 600
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| global
+ **** h1 0.0 conf| log 127.0.0.1:36195 local0
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| defaults
+ **** h1 0.0 conf| log global
+ **** h1 0.0 conf| timeout connect 3000
+ **** h1 0.0 conf| timeout client 5
+ **** h1 0.0 conf| timeout server 10000
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| frontend fe1
+ **** h1 0.0 conf| bind "fd@${fe_1}"
+ **** h1 0.0 conf| mode tcp
+ **** h1 0.0 conf| log-format {\"dip\":\"%fi\",\"dport\":\"%fp\",\"c_ip\":\"%ci\",\"c_port\":\"%cp\",\"fe_name\":\"%ft\",\"be_name\":\"%b\",\"s_name\":\"%s\",\"ts\":\"%ts\",\"bytes_read\":\"%B\"}
+ **** h1 0.0 conf| default_backend be_app
+ **** h1 0.0 conf|
+ **** h1 0.0 conf| backend be_app
+ **** h1 0.0 conf| server app1 127.0.0.1:53384 check
+ ** h1 0.0 haproxy_start
+ **** h1 0.0 opt_worker 0 opt_daemon 0 opt_check_mode 0
+ **** h1 0.0 argv|exec /home/fred/src/haproxy/haproxy -d -f /tmp/vtc.12696.186b28b0/h1/cfg
+ **** h1 0.0 XXX 9 @277
+ *** h1 0.0 PID: 12728
+ **** h1 0.0 macro def h1_pid=12728
+ **** h1 0.0 macro def h1_name=/tmp/vtc.12696.186b28b0/h1
+ ** top 0.0 === client c1 -connect ${h1_fe_1_sock} {
+ ** c1 0.0 Starting client
+ ** c1 0.0 Waiting for client
+ *** c1 0.0 Connect to ::1 39264
+ *** c1 0.0 connected fd 8 from ::1 41245 to ::1 39264
+ ** c1 0.0 === txreq -url "/"
+ **** c1 0.0 txreq|GET / HTTP/1.1\r
+ **** c1 0.0 txreq|Host: 127.0.0.1\r
+ **** c1 0.0 txreq|\r
+ ** c1 0.0 === delay 0.02
+ *** c1 0.0 delaying 0.02 second(s)
+ *** h1 0.0 debug|Note: setting global.maxconn to 2000.
+ *** h1 0.0 debug|Available polling systems :
+ *** h1 0.0 debug| epoll : pref=300,
+ *** h1 0.0 debug| test result OK
+ *** h1 0.0 debug| poll : pref=200, test result OK
+ *** h1 0.0 debug| select : pref=150, test result FAILED
+ *** h1 0.0 debug|Total: 3 (2 usable), will use epoll.
+ *** h1 0.0 debug|
+ *** h1 0.0 debug|Available filters :
+ *** h1 0.0 debug|\t[SPOE] spoe
+ *** h1 0.0 debug|\t[COMP] compression
+ *** h1 0.0 debug|\t[TRACE] trace
+ *** h1 0.0 debug|Using epoll() as the polling mechanism.
+ **** Slg_1 0.0 syslog|<133>Jun 7 14:10:18 haproxy[12728]: Proxy fe1 started.
+ ** Slg_1 0.0 === recv
+ **** Slg_1 0.0 syslog|<133>Jun 7 14:10:18 haproxy[12728]: Proxy be_app started.
+ ** Slg_1 0.0 === recv info
+ *** h1 0.0 debug|00000000:fe1.accept(0007)=000c from [::1:41245] ALPN=<none>
+ *** s1 0.0 accepted fd 6 127.0.0.1 49946
+ ** s1 0.0 === rxreq
+ **** s1 0.0 rxhdr|GET / HTTP/1.1\r
+ **** s1 0.0 rxhdr|Host: 127.0.0.1\r
+ **** s1 0.0 rxhdr|\r
+ **** s1 0.0 rxhdrlen = 35
+ **** s1 0.0 http[ 0] |GET
+ **** s1 0.0 http[ 1] |/
+ **** s1 0.0 http[ 2] |HTTP/1.1
+ **** s1 0.0 http[ 3] |Host: 127.0.0.1
+ **** s1 0.0 bodylen = 0
+ ** s1 0.0 === txresp
+ **** s1 0.0 txresp|HTTP/1.1 200 OK\r
+ **** s1 0.0 txresp|Content-Length: 0\r
+ **** s1 0.0 txresp|\r
+ *** s1 0.0 shutting fd 6
+ ** s1 0.0 Ending
+ *** h1 0.0 debug|00000000:be_app.srvcls[000c:adfd]
+ *** h1 0.0 debug|00000000:be_app.clicls[000c:adfd]
+ *** h1 0.0 debug|00000000:be_app.closed[000c:adfd]
+ **** Slg_1 0.0 syslog|<134>Jun 7 14:10:18 haproxy[12728]: {"dip":"::1","dport":"39264","c_ip":"::1","c_port":"41245","fe_name":"fe1","be_name":"be_app","s_name":"app1","ts":"cD","bytes_read":"38"}
+ ** Slg_1 0.0 === expect ~ \"dip\":\"${h1_fe_1_addr}\",\"dport\":\"${h1_fe_1_p...
+ **** Slg_1 0.0 EXPECT MATCH ~ "\"dip\":\"::1\",\"dport\":\"39264.*\"ts\":\"cD\",\""
+ *** Slg_1 0.0 shutting fd 5
+ ** Slg_1 0.0 Ending
+ *** c1 0.0 closing fd 8
+ ** c1 0.0 Ending
+ ** top 0.0 === syslog Slg_1 -wait
+ ** Slg_1 0.0 Waiting for syslog server (-1)
+ * top 0.0 RESETTING after /home/fred/src/varnish-cache-haproxy/d02286d.vtc
+ ** h1 0.0 Reset and free h1 haproxy 12728
+ ** h1 0.0 Wait
+ ** h1 0.0 Stop HAproxy pid=12728
+ **** h1 0.0 Kill(2)=0: Success
+ **** h1 0.0 STDOUT poll 0x10
+ ** h1 0.1 WAIT4 pid=12728 status=0x0002 (user 0.000000 sys 0.004000)
+ ** s1 0.1 Waiting for server (4/-1)
+ * top 0.1 TEST /home/fred/src/varnish-cache-haproxy/d02286d.vtc completed
+ # top TEST /home/fred/src/varnish-cache-haproxy/d02286d.vtc passed (0.128)
+
+In this latter execution the third syslog message is correct:
+
+ **** Slg_1 0.0 syslog|<134>Jun 7 14:10:18 haproxy[12728]: {"dip":"::1","dport":"39264","c_ip":"::1","c_port":"41245","fe_name":"fe1","be_name":"be_app","s_name":"app1","ts":"cD","bytes_read":"38"}

View file

@ -0,0 +1,53 @@
commit b3dae591a4fb47922d4235cf05ac66bcce443cf4
Author: Ilya Shipitsin <chipitsine@gmail.com>
Date: Sat May 25 03:38:14 2019 +0500
BUG/MINOR: ssl_sock: Fix memory leak when disabling compression
according to manpage:
sk_TYPE_zero() sets the number of elements in sk to zero. It
does not free sk so after this call sk is still valid.
so we need to free all elements
[wt: seems like it has been there forever and should be backported
to all stable branches]
(cherry picked from commit e242f3dfb8ae2f27de9d10d90a783df05d5c849b)
[wt: adjusted context due to libressl detection]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 273c61dfb45d968eb8b677616130cda6586977f0)
[wt: minor adjustments due to version detection and local variables]
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index fbb7cf2b..3ab63342 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -8959,11 +8959,10 @@ static void ssl_sock_capture_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *
__attribute__((constructor))
static void __ssl_sock_init(void)
{
+ STACK_OF(SSL_COMP)* cm;
char *ptr;
int i;
- STACK_OF(SSL_COMP)* cm;
-
if (global_ssl.listen_default_ciphers)
global_ssl.listen_default_ciphers = strdup(global_ssl.listen_default_ciphers);
if (global_ssl.connect_default_ciphers)
@@ -8978,7 +8977,11 @@ static void __ssl_sock_init(void)
xprt_register(XPRT_SSL, &ssl_sock);
SSL_library_init();
cm = SSL_COMP_get_compression_methods();
- sk_SSL_COMP_zero(cm);
+ i = sk_SSL_COMP_num(cm);
+ while (i--) {
+ (void) sk_SSL_COMP_pop(cm);
+ }
+
#ifdef USE_THREAD
ssl_locking_init();
#endif

View file

@ -0,0 +1,46 @@
commit e834ca5c6914c71a86138362423b9d6e7ce15a78
Author: Ilya Shipitsin <chipitsine@gmail.com>
Date: Sat May 25 19:30:50 2019 +0500
BUILD: ssl: fix latest LibreSSL reg-test error
starting with OpenSSL 1.0.0 recommended way to disable compression is
using SSL_OP_NO_COMPRESSION when creating context.
manipulations with SSL_COMP_get_compression_methods, sk_SSL_COMP_num
are only required for OpenSSL < 1.0.0
(cherry picked from commit 0590f44254da9203a3c9d239836f4703aa6d543b)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit be086d6dc7029aec9349e54ab5cccc499892bd61)
[wt: context adjustments]
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 3ab63342..3436459f 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -8959,7 +8959,9 @@ static void ssl_sock_capture_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *
__attribute__((constructor))
static void __ssl_sock_init(void)
{
+#if (!defined(OPENSSL_NO_COMP) && !defined(SSL_OP_NO_COMPRESSION))
STACK_OF(SSL_COMP)* cm;
+#endif
char *ptr;
int i;
@@ -8976,11 +8978,13 @@ static void __ssl_sock_init(void)
xprt_register(XPRT_SSL, &ssl_sock);
SSL_library_init();
+#if (!defined(OPENSSL_NO_COMP) && !defined(SSL_OP_NO_COMPRESSION))
cm = SSL_COMP_get_compression_methods();
i = sk_SSL_COMP_num(cm);
while (i--) {
(void) sk_SSL_COMP_pop(cm);
}
+#endif
#ifdef USE_THREAD
ssl_locking_init();

View file

@ -0,0 +1,54 @@
commit 5d60902ec7883bb05927c648638a009eb930acc7
Author: Willy Tarreau <w@1wt.eu>
Date: Mon May 27 10:17:05 2019 +0200
BUG/MAJOR: lb/threads: make sure the avoided server is not full on second pass
In fwrr_get_next_server(), we optionally pass a server to avoid. It
usually points to the current server during a redispatch operation. If
this server is usable, an "avoided" pointer is set and we continue to
look for another server. If in the end no other server is found, then
we fall back to this avoided one, which is still better than nothing.
The problem that may arise with threads is that in the mean time, this
avoided server might have received extra connections and might not be
usable anymore. This causes it to be queued a second time in the "full"
list and the loop to search for a server again, ending up on this one
again and so on.
This patch makes sure that we break out of the loop when we have to
pick the avoided server. It's probably what the code intended to do
as the current break statement causes fwrr_update_position() and
fwrr_dequeue_srv() to be called again on the avoided server.
It must be backported to 1.9 and 1.8, and seems appropriate for older
versions though it's unclear what the impact of this bug might be
there since the race doesn't exist and we're left with the double
update of the server's position.
(cherry picked from commit b6195ef2a6b0cb3f68bc34735313daa640ab3e92)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 7df15f275af90110e6ab4ec75065cbfabdf764ec)
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/lb_fwrr.c b/src/lb_fwrr.c
index cba7db5f..33181b46 100644
--- a/src/lb_fwrr.c
+++ b/src/lb_fwrr.c
@@ -504,7 +504,7 @@ struct server *fwrr_get_next_server(struct proxy *p, struct server *srvtoavoid)
if (switched) {
if (avoided) {
srv = avoided;
- break;
+ goto take_this_one;
}
goto requeue_servers;
}
@@ -534,6 +534,7 @@ struct server *fwrr_get_next_server(struct proxy *p, struct server *srvtoavoid)
full = srv;
}
+ take_this_one:
/* OK, we got the best server, let's update it */
fwrr_queue_srv(srv);

View file

@ -0,0 +1,43 @@
commit 339e69400f87c34e28c67eaab6e503dc41b29d48
Author: Willy Tarreau <w@1wt.eu>
Date: Tue May 28 08:26:17 2019 +0200
BUG/MEDIUM: http: fix "http-request reject" when not final
When "http-request reject" was introduced in 1.8 with commit 53275e8b0
("MINOR: http: implement the "http-request reject" rule"), it was already
broken. The code mentions "it always returns ACT_RET_STOP" and obviously
a gross copy-paste made it ACT_RET_CONT. If the rule is the last one it
properly blocks, but if not the last one it gets ignored, as can be seen
with this simple configuration :
frontend f1
bind :8011
mode http
http-request reject
http-request redirect location /
This trivial fix must be backported to 1.9 and 1.8. It is tracked by
github issue #107.
(cherry picked from commit 11c90fbd92cfaa5695e328481402d62d536456ef)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit f6d5f2b27634cf3f8591016985b9fb81a1caf01c)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
[Cf: The fix was applied in the file src/proto_http.c because, in 1.8, the file
src/http_act.c does not exist.]
diff --git a/src/proto_http.c b/src/proto_http.c
index 32aeef2d..e5792f8c 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -12157,7 +12157,7 @@ enum act_return http_action_reject(struct act_rule *rule, struct proxy *px,
if (!(s->flags & SF_FINST_MASK))
s->flags |= SF_FINST_R;
- return ACT_RET_CONT;
+ return ACT_RET_STOP;
}
/* parse the "reject" action:

View file

@ -0,0 +1,71 @@
commit ee60daed3071426242fcacbb1cd9603a825b359e
Author: Willy Tarreau <w@1wt.eu>
Date: Sun Jun 2 11:11:29 2019 +0200
BUG/MINOR: deinit/threads: make hard-stop-after perform a clean exit
As reported in GH issue #99, when hard-stop-after triggers and threads
are in use, the chance that any thread releases the resources in use by
the other ones is non-null. Thus no thread should be allowed to deinit()
nor exit by itself.
Here we take a different approach. We simply use a 3rd possible value
for the "killed" variable so that all threads know they must break out
of the run-poll-loop and immediately stop.
This patch was tested by commenting the stream_shutdown() calls in
hard_stop() to increase the chances to see a stream use released
resources. With this fix applied, it never crashes anymore.
This fix should be backported to 1.9 and 1.8.
(cherry picked from commit 7067b3a92efc9c9a7c3239245fdc96ea7310f46a)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit daf91d3a5c8d2b47878580a0e0bde654d92ad42a)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/include/types/global.h b/include/types/global.h
index bd7761cd..d3721d03 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -213,7 +213,7 @@ extern const int zero;
extern const int one;
extern const struct linger nolinger;
extern int stopping; /* non zero means stopping in progress */
-extern int killed; /* non zero means a hard-stop is triggered */
+extern int killed; /* >0 means a hard-stop is triggered, >1 means hard-stop immediately */
extern char hostname[MAX_HOSTNAME_LEN];
extern char localpeer[MAX_HOSTNAME_LEN];
extern struct list global_listener_queue; /* list of the temporarily limited listeners */
diff --git a/src/haproxy.c b/src/haproxy.c
index 105cde6f..6ea17a0c 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -2431,6 +2431,10 @@ static void run_poll_loop()
if (tid == 0 && jobs == 0)
THREAD_WANT_SYNC();
+ /* also stop if we failed to cleanly stop all tasks */
+ if (killed > 1)
+ break;
+
/* expire immediately if events are pending */
exp = now_ms;
if (fd_cache_mask & tid_bit)
diff --git a/src/proxy.c b/src/proxy.c
index cff6c0aa..ef491aed 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -942,9 +942,9 @@ struct task *hard_stop(struct task *t)
if (killed) {
ha_warning("Some tasks resisted to hard-stop, exiting now.\n");
send_log(NULL, LOG_WARNING, "Some tasks resisted to hard-stop, exiting now.\n");
- /* Do some cleanup and explicitely quit */
- deinit();
- exit(0);
+ killed = 2;
+ t->expire = TICK_ETERNITY;
+ return t;
}
ha_warning("soft-stop running for too long, performing a hard-stop.\n");

View file

@ -0,0 +1,133 @@
commit 30e228120ebc1caad1086d0d90383f472f710375
Author: Willy Tarreau <w@1wt.eu>
Date: Mon Jun 3 08:17:30 2019 +0200
BUG/MEDIUM: connection: fix multiple handshake polling issues
Connection handshakes were rarely stacked on top of each other, but the
recent experiments consisting in sending PROXY over SOCKS4 revealed a
number of issues in these lower layers. First, each handler waiting for
data MUST subscribe to recv events with __conn_sock_want_recv() and MUST
unsubscribe from send events using __conn_sock_stop_send() to avoid any
wake-up loop in case a previous sender has set this. Second, each handler
waiting for sending MUST subscribe to send events with __conn_sock_want_send()
and MUST unsubscribe from recv events using __conn_sock_stop_recv() to
avoid any wake-up loop in case some data are available on the connection.
Till now this was done at various random places, and in particular the
cases where the FD was not ready for recv forgot to re-enable reading.
Second, while senders can happily use conn_sock_send() which automatically
handles EINTR, loops, and marks the FD as not ready with fd_cant_send(),
there is no equivalent for recv so receivers facing EAGAIN MUST call
fd_cant_send() to enable polling. It could be argued that implementing
an equivalent conn_sock_recv() function could be useful and more
long-term proof than the current situation.
Third, both types of handlers MUST unsubscribe from their respective
events once they managed to do their job, and none may even play with
__conn_xprt_*(). Here again this was lacking, and one surprizing call
to __conn_xprt_stop_recv() was present in the proxy protocol parser
for TCP6 messages!
Thanks to Alexander Liu for his help on this issue.
This patch must be backported to 1.9 and possibly some older versions,
though the SOCKS parts should be dropped.
(cherry picked from commit 6499b9d996ea8f57749021f56ed635c4688a727e)
[wt: dropped SOCKS4]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit a34d37966ed87ffd9924e2b11f8003dc773e1853)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/connection.c b/src/connection.c
index f57ef60a..e00c2f81 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -404,7 +404,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
goto fail;
if (!fd_recv_ready(conn->handle.fd))
- return 0;
+ goto not_ready;
do {
trash.len = recv(conn->handle.fd, trash.str, trash.size, MSG_PEEK);
@@ -413,7 +413,7 @@ int conn_recv_proxy(struct connection *conn, int flag)
continue;
if (errno == EAGAIN) {
fd_cant_recv(conn->handle.fd);
- return 0;
+ goto not_ready;
}
goto recv_abort;
}
@@ -653,8 +653,14 @@ int conn_recv_proxy(struct connection *conn, int flag)
conn->flags &= ~flag;
conn->flags |= CO_FL_RCVD_PROXY;
+ __conn_sock_stop_recv(conn);
return 1;
+ not_ready:
+ __conn_sock_want_recv(conn);
+ __conn_sock_stop_send(conn);
+ return 0;
+
missing:
/* Missing data. Since we're using MSG_PEEK, we can only poll again if
* we have not read anything. Otherwise we need to fail because we won't
@@ -709,7 +715,7 @@ int conn_recv_netscaler_cip(struct connection *conn, int flag)
goto fail;
if (!fd_recv_ready(conn->handle.fd))
- return 0;
+ goto not_ready;
do {
trash.len = recv(conn->handle.fd, trash.str, trash.size, MSG_PEEK);
@@ -718,7 +724,7 @@ int conn_recv_netscaler_cip(struct connection *conn, int flag)
continue;
if (errno == EAGAIN) {
fd_cant_recv(conn->handle.fd);
- return 0;
+ goto not_ready;
}
goto recv_abort;
}
@@ -849,8 +855,14 @@ int conn_recv_netscaler_cip(struct connection *conn, int flag)
} while (0);
conn->flags &= ~flag;
+ __conn_sock_stop_recv(conn);
return 1;
+ not_ready:
+ __conn_sock_want_recv(conn);
+ __conn_sock_stop_send(conn);
+ return 0;
+
missing:
/* Missing data. Since we're using MSG_PEEK, we can only poll again if
* we have not read anything. Otherwise we need to fail because we won't
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 47a100d7..8e4df70a 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -400,6 +400,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
if (conn->flags & CO_FL_WAIT_L4_CONN)
conn->flags &= ~CO_FL_WAIT_L4_CONN;
conn->flags &= ~flag;
+ __conn_sock_stop_send(conn);
return 1;
out_error:
@@ -409,6 +410,7 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
out_wait:
__conn_sock_stop_recv(conn);
+ __conn_sock_want_send(conn);
return 0;
}

View file

@ -0,0 +1,193 @@
commit 47133bd99225554519c1d32293e0e5c3db83db30
Author: Willy Tarreau <w@1wt.eu>
Date: Tue Jun 4 16:27:36 2019 +0200
BUG/MEDIUM: vars: make sure the scope is always valid when accessing vars
Patrick Hemmer reported that a simple tcp rule involving a variable like
this is enough to crash haproxy :
frontend foo
bind :8001
tcp-request session set-var(txn.foo) src
The tests on the variables scopes is not strict enough, it needs to always
verify if the stream is valid when accessing a req/res/txn variable. This
patch does this by adding a new get_vars() function which does the job
instead of open-coding all the lookups everywhere.
It must be backported to all versions supporting set-var and
"tcp-request session" so at least 1.9 and 1.8.
(cherry picked from commit f37b140b06b9963dea8adaf5e13b5b57cd219c74)
[wt: s/_HA_/HA_/]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 5dcf8515592602ed0d962e365cbb74a3646727c1)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/vars.c b/src/vars.c
index 566ead6e..c86f612f 100644
--- a/src/vars.c
+++ b/src/vars.c
@@ -34,6 +34,25 @@ static unsigned int var_reqres_limit = 0;
__decl_hathreads(HA_RWLOCK_T var_names_rwlock);
+/* returns the struct vars pointer for a session, stream and scope, or NULL if
+ * it does not exist.
+ */
+static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
+{
+ switch (scope) {
+ case SCOPE_PROC:
+ return &global.vars;
+ case SCOPE_SESS:
+ return &sess->vars;
+ case SCOPE_TXN:
+ return strm ? &strm->vars_txn : NULL;
+ case SCOPE_REQ:
+ case SCOPE_RES:
+ default:
+ return strm ? &strm->vars_reqres : NULL;
+ }
+}
+
/* This function adds or remove memory size from the accounting. The inner
* pointers may be null when setting the outer ones only.
*/
@@ -42,10 +61,12 @@ static void var_accounting_diff(struct vars *vars, struct session *sess, struct
switch (vars->scope) {
case SCOPE_REQ:
case SCOPE_RES:
- HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
+ if (strm)
+ HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
/* fall through */
case SCOPE_TXN:
- HA_ATOMIC_ADD(&strm->vars_txn.size, size);
+ if (strm)
+ HA_ATOMIC_ADD(&strm->vars_txn.size, size);
/* fall through */
case SCOPE_SESS:
HA_ATOMIC_ADD(&sess->vars.size, size);
@@ -68,11 +89,11 @@ static int var_accounting_add(struct vars *vars, struct session *sess, struct st
switch (vars->scope) {
case SCOPE_REQ:
case SCOPE_RES:
- if (var_reqres_limit && strm->vars_reqres.size + size > var_reqres_limit)
+ if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
return 0;
/* fall through */
case SCOPE_TXN:
- if (var_txn_limit && strm->vars_txn.size + size > var_txn_limit)
+ if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
return 0;
/* fall through */
case SCOPE_SESS:
@@ -285,27 +306,8 @@ static int smp_fetch_var(const struct arg *args, struct sample *smp, const char
struct vars *vars;
/* Check the availibity of the variable. */
- switch (var_desc->scope) {
- case SCOPE_PROC:
- vars = &global.vars;
- break;
- case SCOPE_SESS:
- vars = &smp->sess->vars;
- break;
- case SCOPE_TXN:
- if (!smp->strm)
- return 0;
- vars = &smp->strm->vars_txn;
- break;
- case SCOPE_REQ:
- case SCOPE_RES:
- default:
- if (!smp->strm)
- return 0;
- vars = &smp->strm->vars_reqres;
- break;
- }
- if (vars->scope != var_desc->scope)
+ vars = get_vars(smp->sess, smp->strm, var_desc->scope);
+ if (!vars || vars->scope != var_desc->scope)
return 0;
HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
@@ -423,15 +425,8 @@ static inline int sample_store_stream(const char *name, enum vars_scope scope, s
struct vars *vars;
int ret;
- switch (scope) {
- case SCOPE_PROC: vars = &global.vars; break;
- case SCOPE_SESS: vars = &smp->sess->vars; break;
- case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
- case SCOPE_REQ:
- case SCOPE_RES:
- default: vars = &smp->strm->vars_reqres; break;
- }
- if (vars->scope != scope)
+ vars = get_vars(smp->sess, smp->strm, scope);
+ if (!vars || vars->scope != scope)
return 0;
HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
@@ -447,15 +442,8 @@ static inline int sample_clear_stream(const char *name, enum vars_scope scope, s
struct var *var;
unsigned int size = 0;
- switch (scope) {
- case SCOPE_PROC: vars = &global.vars; break;
- case SCOPE_SESS: vars = &smp->sess->vars; break;
- case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
- case SCOPE_REQ:
- case SCOPE_RES:
- default: vars = &smp->strm->vars_reqres; break;
- }
- if (vars->scope != scope)
+ vars = get_vars(smp->sess, smp->strm, scope);
+ if (!vars || vars->scope != scope)
return 0;
/* Look for existing variable name. */
@@ -586,17 +574,8 @@ int vars_get_by_name(const char *name, size_t len, struct sample *smp)
return 0;
/* Select "vars" pool according with the scope. */
- switch (scope) {
- case SCOPE_PROC: vars = &global.vars; break;
- case SCOPE_SESS: vars = &smp->sess->vars; break;
- case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
- case SCOPE_REQ:
- case SCOPE_RES:
- default: vars = &smp->strm->vars_reqres; break;
- }
-
- /* Check if the scope is avalaible a this point of processing. */
- if (vars->scope != scope)
+ vars = get_vars(smp->sess, smp->strm, scope);
+ if (!vars || vars->scope != scope)
return 0;
/* Get the variable entry. */
@@ -620,17 +599,10 @@ int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp)
struct var *var;
/* Select "vars" pool according with the scope. */
- switch (var_desc->scope) {
- case SCOPE_PROC: vars = &global.vars; break;
- case SCOPE_SESS: vars = &smp->sess->vars; break;
- case SCOPE_TXN: vars = &smp->strm->vars_txn; break;
- case SCOPE_REQ:
- case SCOPE_RES:
- default: vars = &smp->strm->vars_reqres; break;
- }
+ vars = get_vars(smp->sess, smp->strm, var_desc->scope);
- /* Check if the scope is avalaible a this point of processing. */
- if (vars->scope != var_desc->scope)
+ /* Check if the scope is available a this point of processing. */
+ if (!vars || vars->scope != var_desc->scope)
return 0;
/* Get the variable entry. */

View file

@ -0,0 +1,44 @@
commit a41ac2d710711f3ab91d92415278a73c358aedca
Author: Willy Tarreau <w@1wt.eu>
Date: Tue Jun 4 16:43:29 2019 +0200
BUG/MEDIUM: vars: make the tcp/http unset-var() action support conditions
Patrick Hemmer reported that http-request unset-var(foo) if ... fails to
parse. The reason is that it reuses the same parser as "set-var(foo)" which
makes a special case of the arguments, supposed to be a sample expression
for set-var, but which must not exist for unset-var. Unfortunately the
parser finds "if" or "unless" and believes it's an expression. Let's simply
drop the test so that the outer rule parser deals with potential extraneous
keywords.
This should be backported to all versions supporting unset-var().
(cherry picked from commit 4b7531f48b5aa66d11fcee2836c201644bfb6a71)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit a47e745276662db361637914b8558984f091306b)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/vars.c b/src/vars.c
index c86f612f..d69fb838 100644
--- a/src/vars.c
+++ b/src/vars.c
@@ -684,6 +684,7 @@ static int conv_check_var(struct arg *args, struct sample_conv *conv,
* the format:
*
* set-var(<variable-name>) <expression>
+ * unset-var(<variable-name>)
*
* It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
* message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
@@ -727,10 +728,6 @@ static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy
/* There is no fetch method when variable is unset. Just set the right
* action and return. */
if (!set_var) {
- if (*args[*arg]) {
- memprintf(err, "fetch method not supported");
- return ACT_RET_PRS_ERR;
- }
rule->action = ACT_CUSTOM;
rule->action_ptr = action_clear;
return ACT_RET_PRS_OK;

View file

@ -0,0 +1,73 @@
commit e7c30a33646f4ec73041ea57bd9fdcdc3e4a80ab
Author: Willy Tarreau <w@1wt.eu>
Date: Fri Jun 7 08:20:46 2019 +0200
BUG/MEDIUM: mux-h2: make sure the connection timeout is always set
There seems to be a tricky case in the H2 mux related to stream flow
control versus buffer a full situation : is a large response cannot
be entirely sent to the client due to the stream window being too
small, the stream is paused with the SFCTL flag. Then the upper
layer stream might get bored and expire this stream. It will then
shut it down first. But the shutdown operation might fail if the
mux buffer is full, resulting in the h2s being subscribed to the
deferred_shut event with the stream *not* added to the send_list
since it's blocked in SFCTL. In the mean time the upper layer completely
closes, calling h2_detach(). There we have a send_wait (the pending
shutw), the stream is marked with SFCTL so we orphan it.
Then if the client finally reads all the data that were clogging the
buffer, the send_list is run again, but our stream is not there. From
this point, the connection's stream list is not empty, the mux buffer
is empty, so the connection's timeout is not set. If the client
disappears without updating the stream's window, nothing will expire
the connection.
This patch makes sure we always keep the connection timeout updated.
There might be finer solutions, such as checking that there are still
living streams in the connection (i.e. streams not blocked in SFCTL
state), though this is not necessarily trivial nor useful, since the
client timeout is the same for the upper level stream and the connection
anyway.
This patch needs to be backported to 1.9 and 1.8 after some observation.
(cherry picked from commit 7348119fb22bf761c33e06e8a092bd006660cc81)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit e76090f78b6b8c519abf20061bfc5a4423816ea5)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/mux_h2.c b/src/mux_h2.c
index b2f3096e..985b9742 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -2406,12 +2406,8 @@ static int h2_wake(struct connection *conn)
}
if (h2c->task) {
- if (eb_is_empty(&h2c->streams_by_id) || h2c->mbuf->o) {
- h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
- task_queue(h2c->task);
- }
- else
- h2c->task->expire = TICK_ETERNITY;
+ h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
+ task_queue(h2c->task);
}
return 0;
}
@@ -2587,12 +2583,8 @@ static void h2_detach(struct conn_stream *cs)
h2_release(h2c->conn);
}
else if (h2c->task) {
- if (eb_is_empty(&h2c->streams_by_id) || h2c->mbuf->o) {
- h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
- task_queue(h2c->task);
- }
- else
- h2c->task->expire = TICK_ETERNITY;
+ h2c->task->expire = tick_add(now_ms, h2c->last_sid < 0 ? h2c->timeout : h2c->shut_timeout);
+ task_queue(h2c->task);
}
}

View file

@ -0,0 +1,34 @@
commit 8a74cad9b7fe8b9e1f5b140d90360ece838a878e
Author: Willy Tarreau <w@1wt.eu>
Date: Tue Jun 11 16:01:56 2019 +0200
BUG/MINOR: http-rules: mention "deny_status" for "deny" in the error message
The error message indicating an unknown keyword on an http-request rule
doesn't mention the "deny_status" option which comes with the "deny" rule,
this is particularly confusing.
This can be backported to all versions supporting this option.
(cherry picked from commit 5abdc760c99a0011607f2cc97e199ef6ce0e8486)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 4e66beaf2a32bd835db9de61a60318648258f649)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
[Cf: The fix was applied on src/proto_http.c because, in 1.8, the file
src/http_rules.c does not exist.]
diff --git a/src/proto_http.c b/src/proto_http.c
index e5792f8c..689e0e31 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -8830,7 +8830,8 @@ struct act_rule *parse_http_req_cond(const char **args, const char *file, int li
rule->cond = cond;
}
else if (*args[cur_arg]) {
- ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or"
+ ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth',"
+ " 'deny_status' for 'deny', or"
" either 'if' or 'unless' followed by a condition but found '%s'.\n",
file, linenum, args[0], args[cur_arg]);
goto out_err;

View file

@ -0,0 +1,30 @@
commit 8f2772f5c603168ad3f79adc9e17569f510274ca
Author: Kazuo Yagi <kazuo.yagi@gmail.com>
Date: Thu Jun 13 17:14:57 2019 +0900
MINOR: doc: Remove -Ds option in man page
Remove -Ds option in man page.
Should be backported in every version since 1.8.
(cherry picked from commit 971c3943be1283e9d377d68f95ea467304b3a8da)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit a5e78ea5150f31190e4c1cd38fa7c1cadbf1ae8a)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/doc/haproxy.1 b/doc/haproxy.1
index 91f58a3b..cfed2cf7 100644
--- a/doc/haproxy.1
+++ b/doc/haproxy.1
@@ -77,10 +77,6 @@ starting up.
\fB\-D\fP
Start in daemon mode.
-.TP
-\fB\-Ds\fP
-Start in systemd daemon mode, keeping a process in foreground.
-
.TP
\fB\-q\fP
Disable messages on output.

View file

@ -0,0 +1,55 @@
commit 4fb0b5fb585e7f697914e935fe1001f752da8470
Author: William Lallemand <wlallemand@haproxy.org>
Date: Thu Jun 13 11:51:09 2019 +0200
MINOR: doc: add master-worker in the man page
Add some information about the master-worker in the man page.
Should be backported in every version since 1.8.
(cherry picked from commit 95635ddac8c29859d297e8ba33174e71efd5fc47)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit d7a0c4695f00765dc03d7d6f6e17058726e84951)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/doc/haproxy.1 b/doc/haproxy.1
index cfed2cf7..2a23674f 100644
--- a/doc/haproxy.1
+++ b/doc/haproxy.1
@@ -6,7 +6,7 @@ HAProxy \- fast and reliable http reverse proxy and load balancer
.SH SYNOPSIS
-haproxy \-f <configuration\ file|dir> [\-L\ <name>] [\-n\ maxconn] [\-N\ maxconn] [\-C\ <dir>] [\-v|\-vv] [\-d] [\-D] [\-q] [\-V] [\-c] [\-p\ <pidfile>] [\-dk] [\-ds] [\-de] [\-dp] [\-db] [\-dM[<byte>]] [\-m\ <megs>] [\-x <unix_socket>] [{\-sf|\-st}\ pidlist...]
+haproxy \-f <configuration\ file|dir> [\-L\ <name>] [\-n\ maxconn] [\-N\ maxconn] [\-C\ <dir>] [\-v|\-vv] [\-d] [\-D] [\-W] [\-Ws] [\-q] [\-V] [\-c] [\-p\ <pidfile>] [\-dk] [\-ds] [\-de] [\-dp] [\-db] [\-dM[<byte>]] [\-m\ <megs>] [\-x <unix_socket>] [{\-sf|\-st}\ pidlist...]
.SH DESCRIPTION
@@ -77,6 +77,16 @@ starting up.
\fB\-D\fP
Start in daemon mode.
+.TP
+\fB\-W\fP
+Start in master-worker mode. Could be used either with foreground or daemon
+mode.
+
+.TP
+\fB\-Ws\fP
+Start in master-worker mode with systemd notify support. It tells systemd when
+the process is ready. This mode forces foreground.
+
.TP
\fB\-q\fP
Disable messages on output.
@@ -172,6 +182,9 @@ Some signals have a special meaning for the haproxy daemon. Generally, they are
\- \fBSIGUSR1\fP
Tells the daemon to stop all proxies and exit once all sessions are closed. It is often referred to as the "soft-stop" signal.
.TP
+\- \fBSIGUSR2\fP
+In master-worker mode, reloads the configuration and sends a soft-stop signal to old processes.
+.TP
\- \fBSIGTTOU\fP
Tells the daemon to stop listening to all sockets. Used internally by \fB\-sf\fP and \fB\-st\fP.
.TP

View file

@ -0,0 +1,249 @@
commit a27131f6e1e6c3e16e056915ba5ec2c560051296
Author: Tim Duesterhus <tim@bastelstu.be>
Date: Mon Jun 17 16:10:07 2019 +0200
BUG/MEDIUM: compression: Set Vary: Accept-Encoding for compressed responses
Make HAProxy set the `Vary: Accept-Encoding` response header if it compressed
the server response.
Technically the `Vary` header SHOULD also be set for responses that would
normally be compressed based off the current configuration, but are not due
to a missing or invalid `Accept-Encoding` request header or due to the
maximum compression rate being exceeded.
Not setting the header in these cases does no real harm, though: An
uncompressed response might be returned by a Cache, even if a compressed
one could be retrieved from HAProxy. This increases the traffic to the end
user if the cache is unable to compress itself, but it saves another
roundtrip to HAProxy.
see the discussion on the mailing list: https://www.mail-archive.com/haproxy@formilux.org/msg34221.html
Message-ID: 20190617121708.GA2964@1wt.eu
A small issue remains: The User-Agent is not added to the `Vary` header,
despite being relevant to the response. Adding the User-Agent header would
make responses effectively uncacheable and it's unlikely to see a Mozilla/4
in the wild in 2019.
Add a reg-test to ensure the behaviour as described in this commit message.
see issue #121
Should be backported to all branches with compression (i.e. 1.6+).
(cherry picked from commit 721d686bd10dc6993859f9026ad907753d1d2064)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit eaf650083924a697cde3379703984c5e7a5ebd41)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 96942d657ec7f29a328a5759558dbaa26d8e3e53)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
[Cf: The patch was updated because there is no HTX in 1.8]
diff --git a/reg-tests/compression/vary.vtc b/reg-tests/compression/vary.vtc
new file mode 100644
index 00000000..0a060e4b
--- /dev/null
+++ b/reg-tests/compression/vary.vtc
@@ -0,0 +1,187 @@
+varnishtest "Compression sets Vary header"
+
+#REQUIRE_VERSION=1.9
+#REQUIRE_OPTION=ZLIB|SLZ
+
+feature ignore_unknown_macro
+
+server s1 {
+ rxreq
+ expect req.url == "/plain/accept-encoding-gzip"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/plain/accept-encoding-invalid"
+ expect req.http.accept-encoding == "invalid"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/plain/accept-encoding-null"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/html/accept-encoding-gzip"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/html" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/html/accept-encoding-invalid"
+ expect req.http.accept-encoding == "invalid"
+ txresp \
+ -hdr "Content-Type: text/html" \
+ -bodylen 100
+
+
+ rxreq
+ expect req.url == "/html/accept-encoding-null"
+ expect req.http.accept-encoding == "<undef>"
+ txresp \
+ -hdr "Content-Type: text/html" \
+ -bodylen 100
+
+ rxreq
+ expect req.url == "/dup-etag/accept-encoding-gzip"
+ expect req.http.accept-encoding == "gzip"
+ txresp \
+ -hdr "Content-Type: text/plain" \
+ -hdr "ETag: \"123\"" \
+ -hdr "ETag: \"123\"" \
+ -bodylen 100
+} -repeat 2 -start
+
+
+haproxy h1 -conf {
+ defaults
+ mode http
+ ${no-htx} option http-use-htx
+ timeout connect 1s
+ timeout client 1s
+ timeout server 1s
+
+ frontend fe-gzip
+ bind "fd@${fe_gzip}"
+ default_backend be-gzip
+
+ backend be-gzip
+ compression algo gzip
+ compression type text/plain
+ server www ${s1_addr}:${s1_port}
+
+ frontend fe-nothing
+ bind "fd@${fe_nothing}"
+ default_backend be-nothing
+
+ backend be-nothing
+ server www ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_gzip_sock} {
+ txreq -url "/plain/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.content-encoding == "gzip"
+ expect resp.http.vary == "Accept-Encoding"
+ gunzip
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/dup-etag/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+} -run
+
+# This Client duplicates c1, against the "nothing" frontend, ensuring no Vary header is ever set.
+client c2 -connect ${h1_fe_nothing_sock} {
+ txreq -url "/plain/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/plain/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-invalid" \
+ -hdr "Accept-Encoding: invalid"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/html/accept-encoding-null"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+
+ txreq -url "/dup-etag/accept-encoding-gzip" \
+ -hdr "Accept-Encoding: gzip"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.vary == "<undef>"
+ expect resp.bodylen == 100
+} -run
diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c
index b93ff69e..b48a4491 100644
--- a/src/flt_http_comp.c
+++ b/src/flt_http_comp.c
@@ -555,6 +555,9 @@ select_compression_response_header(struct comp_state *st, struct stream *s, stru
if (!(msg->flags & HTTP_MSGF_TE_CHNK))
http_header_add_tail2(&txn->rsp, &txn->hdr_idx, "Transfer-Encoding: chunked", 26);
+ /* add Vary header */
+ if (http_header_add_tail2(&txn->rsp, &txn->hdr_idx, "Vary: Accept-Encoding", 21) < 0)
+ goto fail;
/*
* Add Content-Encoding header when it's not identity encoding.
* RFC 2616 : Identity encoding: This content-coding is used only in the

View file

@ -8,7 +8,7 @@
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
@@ -61,6 +62,17 @@
@@ -60,6 +61,17 @@
#include <openssl/async.h>
#endif
@ -26,7 +26,7 @@
#include <import/lru.h>
#include <import/xxhash.h>
@@ -206,7 +218,7 @@ static struct {
@@ -217,7 +229,7 @@ static struct {
.capture_cipherlist = 0,
};
@ -35,7 +35,7 @@
static HA_RWLOCK_T *ssl_rwlocks;
@@ -1675,8 +1687,8 @@ ssl_sock_do_create_cert(const char *servername, struct bind_conf *bind_conf, SSL
@@ -1716,8 +1728,8 @@ ssl_sock_do_create_cert(const char *servername, struct bind_conf *bind_conf, SSL
ASN1_INTEGER_set(X509_get_serialNumber(newcrt), HA_ATOMIC_ADD(&ssl_ctx_serial, 1));
/* Set duration for the certificate */
@ -46,7 +46,7 @@
goto mkcert_error;
/* set public key in the certificate */
@@ -6226,7 +6238,7 @@ smp_fetch_ssl_x_notafter(const struct arg *args, struct sample *smp, const char
@@ -6299,7 +6311,7 @@ smp_fetch_ssl_x_notafter(const struct arg *args, struct sample *smp, const char
goto out;
smp_trash = get_trash_chunk();
@ -55,7 +55,7 @@
goto out;
smp->data.u.str = *smp_trash;
@@ -6326,7 +6338,7 @@ smp_fetch_ssl_x_notbefore(const struct arg *args, struct sample *smp, const char
@@ -6399,7 +6411,7 @@ smp_fetch_ssl_x_notbefore(const struct a
goto out;
smp_trash = get_trash_chunk();
@ -64,21 +64,26 @@
goto out;
smp->data.u.str = *smp_trash;
@@ -8777,10 +8789,12 @@ static void __ssl_sock_init(void)
global_ssl.connect_default_ciphers = strdup(global_ssl.connect_default_ciphers);
@@ -8977,7 +8989,9 @@ static void __ssl_sock_init(void)
#endif
xprt_register(XPRT_SSL, &ssl_sock);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
+#endif
#if (!defined(OPENSSL_NO_COMP) && !defined(SSL_OP_NO_COMPRESSION))
cm = SSL_COMP_get_compression_methods();
sk_SSL_COMP_zero(cm);
i = sk_SSL_COMP_num(cm);
@@ -8986,7 +9000,7 @@ static void __ssl_sock_init(void)
}
#endif
-#ifdef USE_THREAD
+#if defined(USE_THREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
ssl_locking_init();
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
@@ -8808,8 +8822,8 @@ static void __ssl_sock_init(void)
@@ -9015,8 +9029,8 @@ static void __ssl_sock_init(void)
#else /* OPENSSL_IS_BORINGSSL */
OPENSSL_VERSION_TEXT
"\nRunning on OpenSSL version : %s%s",
@ -89,7 +94,7 @@
#endif
memprintf(&ptr, "%s\nOpenSSL library supports TLS extensions : "
#if OPENSSL_VERSION_NUMBER < 0x00907000L
@@ -8900,12 +8914,14 @@ static void __ssl_sock_deinit(void)
@@ -9107,12 +9121,14 @@ static void __ssl_sock_deinit(void)
}
#endif