haproxy: update to 1.7.8 and pending patches

- fixes reload issue with hanging process

Signed-off-by: Thomas Heil <heil@terminal-consulting.de>
This commit is contained in:
Thomas Heil 2017-08-17 01:07:49 +02:00
parent a6a44f91f3
commit d61bf45c3c
19 changed files with 1410 additions and 3 deletions

View File

@ -9,12 +9,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=haproxy
PKG_VERSION:=1.7.5
PKG_RELEASE:=01
PKG_VERSION:=1.7.8
PKG_RELEASE:=18
PKG_SOURCE:=haproxy-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=http://www.haproxy.org/download/1.7/src/
PKG_MD5SUM:=ed84c80cb97852d2aa3161ed16c48a1c
PKG_MD5SUM:=7e94653cc5a1dba006bbe43736f53595
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)
PKG_LICENSE:=GPL-2.0

View File

@ -0,0 +1,299 @@
From fa73e6b0d5f64eb8a6fd8a1706d7ec03293a943e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= <flecaille@haproxy.com>
Date: Thu, 13 Jul 2017 09:07:09 +0200
Subject: [PATCH 01/18] BUG/MINOR: peers: peer synchronization issue (with
several peers sections).
When several stick-tables were configured with several peers sections,
only a part of them could be synchronized: the ones attached to the last
parsed 'peers' section. This was due to the fact that, at least, the peer I/O handler
refered to the wrong peer section list, in fact always the same: the last one parsed.
The fact that the global peer section list was named "struct peers *peers"
lead to this issue. This variable name is dangerous ;).
So this patch renames global 'peers' variable to 'cfg_peers' to ensure that
no such wrong references are still in use, then all the functions wich used
old 'peers' variable have been modified to refer to the correct peer list.
Must be backported to 1.6 and 1.7.
(cherry picked from commit ed2b4a6b793d062000518e51ed71e014c649c313)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
include/types/peers.h | 2 +-
src/cfgparse.c | 18 +++++++++---------
src/haproxy.c | 10 +++++-----
src/peers.c | 40 ++++++++++++++++++++--------------------
src/proxy.c | 6 +++---
5 files changed, 38 insertions(+), 38 deletions(-)
diff --git a/include/types/peers.h b/include/types/peers.h
index 105dffb0..a77a0942 100644
--- a/include/types/peers.h
+++ b/include/types/peers.h
@@ -91,7 +91,7 @@ struct peers {
};
-extern struct peers *peers;
+extern struct peers *cfg_peers;
#endif /* _TYPES_PEERS_H */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 8c0906bf..1b53006b 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2124,7 +2124,7 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
goto out;
}
- for (curpeers = peers; curpeers != NULL; curpeers = curpeers->next) {
+ for (curpeers = cfg_peers; curpeers != NULL; curpeers = curpeers->next) {
/*
* If there are two proxies with the same name only following
* combinations are allowed:
@@ -2142,8 +2142,8 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
goto out;
}
- curpeers->next = peers;
- peers = curpeers;
+ curpeers->next = cfg_peers;
+ cfg_peers = curpeers;
curpeers->conf.file = strdup(file);
curpeers->conf.line = linenum;
curpeers->last_change = now.tv_sec;
@@ -2223,7 +2223,7 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
if (strcmp(newpeer->id, localpeer) == 0) {
/* Current is local peer, it define a frontend */
newpeer->local = 1;
- peers->local = newpeer;
+ cfg_peers->local = newpeer;
if (!curpeers->peers_fe) {
if ((curpeers->peers_fe = calloc(1, sizeof(struct proxy))) == NULL) {
@@ -8189,9 +8189,9 @@ int check_config_validity()
}
if (curproxy->table.peers.name) {
- struct peers *curpeers = peers;
+ struct peers *curpeers;
- for (curpeers = peers; curpeers; curpeers = curpeers->next) {
+ for (curpeers = cfg_peers; curpeers; curpeers = curpeers->next) {
if (strcmp(curpeers->id, curproxy->table.peers.name) == 0) {
free((void *)curproxy->table.peers.name);
curproxy->table.peers.p = curpeers;
@@ -9279,15 +9279,15 @@ out_uri_auth_compat:
if (curproxy->table.peers.p)
curproxy->table.peers.p->peers_fe->bind_proc |= curproxy->bind_proc;
- if (peers) {
- struct peers *curpeers = peers, **last;
+ if (cfg_peers) {
+ struct peers *curpeers = cfg_peers, **last;
struct peer *p, *pb;
/* Remove all peers sections which don't have a valid listener,
* which are not used by any table, or which are bound to more
* than one process.
*/
- last = &peers;
+ last = &cfg_peers;
while (*last) {
curpeers = *last;
diff --git a/src/haproxy.c b/src/haproxy.c
index 6d09aed4..25cea0cd 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -988,7 +988,7 @@ void init(int argc, char **argv)
struct peers *pr;
struct proxy *px;
- for (pr = peers; pr; pr = pr->next)
+ for (pr = cfg_peers; pr; pr = pr->next)
if (pr->peers_fe)
break;
@@ -1217,11 +1217,11 @@ void init(int argc, char **argv)
if (global.stats_fe)
global.maxsock += global.stats_fe->maxconn;
- if (peers) {
+ if (cfg_peers) {
/* peers also need to bypass global maxconn */
- struct peers *p = peers;
+ struct peers *p = cfg_peers;
- for (p = peers; p; p = p->next)
+ for (p = cfg_peers; p; p = p->next)
if (p->peers_fe)
global.maxsock += p->peers_fe->maxconn;
}
@@ -2067,7 +2067,7 @@ int main(int argc, char **argv)
}
/* we might have to unbind some peers sections from some processes */
- for (curpeers = peers; curpeers; curpeers = curpeers->next) {
+ for (curpeers = cfg_peers; curpeers; curpeers = curpeers->next) {
if (!curpeers->peers_fe)
continue;
diff --git a/src/peers.c b/src/peers.c
index 543c84c1..5b8a287a 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -171,7 +171,7 @@ enum {
#define PEER_MINOR_VER 1
#define PEER_DWNGRD_MINOR_VER 0
-struct peers *peers = NULL;
+struct peers *cfg_peers = NULL;
static void peer_session_forceshutdown(struct appctx *appctx);
/* This function encode an uint64 to 'dynamic' length format.
@@ -727,19 +727,19 @@ switchstate:
/* if current peer is local */
if (curpeer->local) {
/* if current host need resyncfrom local and no process assined */
- if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL &&
- !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
+ if ((curpeers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMLOCAL &&
+ !(curpeers->flags & PEERS_F_RESYNC_ASSIGN)) {
/* assign local peer for a lesson, consider lesson already requested */
curpeer->flags |= PEER_F_LEARN_ASSIGN;
- peers->flags |= (PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
+ curpeers->flags |= (PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
}
}
- else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
- !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
+ else if ((curpeers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
+ !(curpeers->flags & PEERS_F_RESYNC_ASSIGN)) {
/* assign peer for a lesson */
curpeer->flags |= PEER_F_LEARN_ASSIGN;
- peers->flags |= PEERS_F_RESYNC_ASSIGN;
+ curpeers->flags |= PEERS_F_RESYNC_ASSIGN;
}
@@ -807,7 +807,7 @@ switchstate:
curpeer->statuscode = atoi(trash.str);
/* Awake main task */
- task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
+ task_wakeup(curpeers->sync_task, TASK_WOKEN_MSG);
/* If status code is success */
if (curpeer->statuscode == PEER_SESS_SC_SUCCESSCODE) {
@@ -830,14 +830,14 @@ switchstate:
curpeer->flags |= PEER_F_TEACH_PROCESS;
}
- else if ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
- !(peers->flags & PEERS_F_RESYNC_ASSIGN)) {
+ else if ((curpeers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FROMREMOTE &&
+ !(curpeers->flags & PEERS_F_RESYNC_ASSIGN)) {
/* If peer is remote and resync from remote is needed,
and no peer currently assigned */
/* assign peer for a lesson */
curpeer->flags |= PEER_F_LEARN_ASSIGN;
- peers->flags |= PEERS_F_RESYNC_ASSIGN;
+ curpeers->flags |= PEERS_F_RESYNC_ASSIGN;
}
}
@@ -950,8 +950,8 @@ switchstate:
if (curpeer->flags & PEER_F_LEARN_ASSIGN) {
curpeer->flags &= ~PEER_F_LEARN_ASSIGN;
- peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
- peers->flags |= (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE);
+ curpeers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
+ curpeers->flags |= (PEERS_F_RESYNC_LOCAL|PEERS_F_RESYNC_REMOTE);
}
curpeer->confirm++;
}
@@ -959,11 +959,11 @@ switchstate:
if (curpeer->flags & PEER_F_LEARN_ASSIGN) {
curpeer->flags &= ~PEER_F_LEARN_ASSIGN;
- peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
+ curpeers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS);
curpeer->flags |= PEER_F_LEARN_NOTUP2DATE;
- peers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
- task_wakeup(peers->sync_task, TASK_WOKEN_MSG);
+ curpeers->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
+ task_wakeup(curpeers->sync_task, TASK_WOKEN_MSG);
}
curpeer->confirm++;
}
@@ -1334,8 +1334,8 @@ incomplete:
/* Need to request a resync */
if ((curpeer->flags & PEER_F_LEARN_ASSIGN) &&
- (peers->flags & PEERS_F_RESYNC_ASSIGN) &&
- !(peers->flags & PEERS_F_RESYNC_PROCESS)) {
+ (curpeers->flags & PEERS_F_RESYNC_ASSIGN) &&
+ !(curpeers->flags & PEERS_F_RESYNC_PROCESS)) {
unsigned char msg[2];
/* Current peer was elected to request a resync */
@@ -1351,7 +1351,7 @@ incomplete:
appctx->st0 = PEER_SESS_ST_END;
goto switchstate;
}
- peers->flags |= PEERS_F_RESYNC_PROCESS;
+ curpeers->flags |= PEERS_F_RESYNC_PROCESS;
}
/* Nothing to read, now we start to write */
@@ -1624,7 +1624,7 @@ incomplete:
/* Current peer was elected to request a resync */
msg[0] = PEER_MSG_CLASS_CONTROL;
- msg[1] = ((peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED) ? PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL;
+ msg[1] = ((curpeers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED) ? PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL;
/* process final lesson message */
repl = bi_putblk(si_ic(si), (char *)msg, sizeof(msg));
if (repl <= 0) {
diff --git a/src/proxy.c b/src/proxy.c
index 78120d9b..bedc7ae0 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1007,7 +1007,7 @@ void soft_stop(void)
p = p->next;
}
- prs = peers;
+ prs = cfg_peers;
while (prs) {
if (prs->peers_fe)
stop_proxy(prs->peers_fe);
@@ -1142,7 +1142,7 @@ void pause_proxies(void)
p = p->next;
}
- prs = peers;
+ prs = cfg_peers;
while (prs) {
if (prs->peers_fe)
err |= !pause_proxy(prs->peers_fe);
@@ -1176,7 +1176,7 @@ void resume_proxies(void)
p = p->next;
}
- prs = peers;
+ prs = cfg_peers;
while (prs) {
if (prs->peers_fe)
err |= !resume_proxy(prs->peers_fe);
--
2.13.0

View File

@ -0,0 +1,36 @@
From bcc483a9edfeb8ab69d1af83886d9e1323cffd06 Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <thierry.fournier@ozon.io>
Date: Wed, 12 Jul 2017 11:18:00 +0200
Subject: [PATCH 02/18] BUG/MINOR: lua: In error case, the safe mode is not
removed
Just forgot of reset the safe mode. This have not consequences
the safe mode just set a pointer on fucntion which is called only
and initialises a longjmp.
Out of lua execution, this longjmp is never executed and the
function is never called.
This patch should be backported in 1.6 and 1.7
(cherry picked from commit 0a97620c080232a21ad7fce2c859a2edc9d7147e)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/hlua.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/hlua.c b/src/hlua.c
index c862102d..4c1c2d21 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -854,6 +854,7 @@ int hlua_ctx_init(struct hlua *lua, struct task *task)
lua->T = lua_newthread(gL.T);
if (!lua->T) {
lua->Tref = LUA_REFNIL;
+ RESET_SAFE_LJMP(gL.T);
return 0;
}
hlua_sethlua(lua);
--
2.13.0

View File

@ -0,0 +1,59 @@
From 49d319a677432b69c6a69ef5331ae2ed592075c9 Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <thierry.fournier@ozon.io>
Date: Wed, 12 Jul 2017 13:41:33 +0200
Subject: [PATCH 03/18] BUG/MINOR: lua: executes the function destroying the
Lua session in safe mode
When we destroy the Lua session, we manipulates Lua stack,
so errors can raises. It will be better to catch these errors.
This patch should be backported in 1.6 and 1.7
(cherry picked from commit 75d0208009c3189b5d10793e08f27dd62a76c3ae)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/hlua.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/hlua.c b/src/hlua.c
index 4c1c2d21..2d312804 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -876,9 +876,15 @@ void hlua_ctx_destroy(struct hlua *lua)
/* Purge all the pending signals. */
hlua_com_purge(lua);
+ if (!SET_SAFE_LJMP(lua->T))
+ return;
luaL_unref(lua->T, LUA_REGISTRYINDEX, lua->Mref);
- luaL_unref(gL.T, LUA_REGISTRYINDEX, lua->Tref);
+ RESET_SAFE_LJMP(lua->T);
+ if (!SET_SAFE_LJMP(gL.T))
+ return;
+ luaL_unref(gL.T, LUA_REGISTRYINDEX, lua->Tref);
+ RESET_SAFE_LJMP(gL.T);
/* Forces a garbage collecting process. If the Lua program is finished
* without error, we run the GC on the thread pointer. Its freed all
* the unused memory.
@@ -889,9 +895,16 @@ void hlua_ctx_destroy(struct hlua *lua)
* the garbage collection.
*/
if (lua->flags & HLUA_MUST_GC) {
+ if (!SET_SAFE_LJMP(lua->T))
+ return;
lua_gc(lua->T, LUA_GCCOLLECT, 0);
- if (lua_status(lua->T) != LUA_OK)
+ RESET_SAFE_LJMP(lua->T);
+ if (lua_status(lua->T) != LUA_OK) {
+ if (!SET_SAFE_LJMP(gL.T))
+ return;
lua_gc(gL.T, LUA_GCCOLLECT, 0);
+ RESET_SAFE_LJMP(gL.T);
+ }
}
lua->T = NULL;
--
2.13.0

View File

@ -0,0 +1,97 @@
From 2823f54f706f56304970313cb14a98a4ce20d5ab Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <thierry.fournier@ozon.io>
Date: Sun, 16 Jul 2017 20:48:54 +0200
Subject: [PATCH 04/18] BUG/MAJOR: lua/socket: resources not detroyed when the
socket is aborted
In some cases, the socket is misused. The user can open socket and never
close it, or open the socket and close it without sending data. This
causes resources leak on all resources associated to the stream (buffer,
spoe, ...)
This is caused by the stream_shutdown function which is called outside
of the stream execution process. Sometimes, the shtudown is required
while the stream is not started, so the cleanup is ignored.
This patch change the shutdown mode of the session. Now if the session is
no longer used and the Lua want to destroy it, it just set a destroy flag
and the session kill itself.
This patch should be backported in 1.6 and 1.7
(cherry picked from cmomit b13b20a19aacb039a33f886e38a181b00c9a6d41)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
include/types/applet.h | 1 +
src/hlua.c | 16 ++++++++++++++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/include/types/applet.h b/include/types/applet.h
index 46b2bc10..aee9167e 100644
--- a/include/types/applet.h
+++ b/include/types/applet.h
@@ -122,6 +122,7 @@ struct appctx {
struct hlua_socket *socket;
struct list wake_on_read;
struct list wake_on_write;
+ int die;
} hlua;
struct {
struct hlua hlua;
diff --git a/src/hlua.c b/src/hlua.c
index 2d312804..eb003558 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -1544,6 +1544,15 @@ static void hlua_socket_handler(struct appctx *appctx)
struct stream_interface *si = appctx->owner;
struct connection *c = objt_conn(si_opposite(si)->end);
+ if (appctx->ctx.hlua.die) {
+ si_shutw(si);
+ si_shutr(si);
+ si_ic(si)->flags |= CF_READ_NULL;
+ hlua_com_wake(&appctx->ctx.hlua.wake_on_read);
+ hlua_com_wake(&appctx->ctx.hlua.wake_on_write);
+ stream_shutdown(si_strm(si), SF_ERR_KILLED);
+ }
+
/* If the connection object is not avalaible, close all the
* streams and wakeup everithing waiting for.
*/
@@ -1619,9 +1628,10 @@ __LJMP static int hlua_socket_gc(lua_State *L)
/* Remove all reference between the Lua stack and the coroutine stream. */
appctx = objt_appctx(socket->s->si[0].end);
- stream_shutdown(socket->s, SF_ERR_KILLED);
socket->s = NULL;
appctx->ctx.hlua.socket = NULL;
+ appctx->ctx.hlua.die = 1;
+ appctx_wakeup(appctx);
return 0;
}
@@ -1641,10 +1651,11 @@ __LJMP static int hlua_socket_close(lua_State *L)
return 0;
/* Close the stream and remove the associated stop task. */
- stream_shutdown(socket->s, SF_ERR_KILLED);
appctx = objt_appctx(socket->s->si[0].end);
appctx->ctx.hlua.socket = NULL;
socket->s = NULL;
+ appctx->ctx.hlua.die = 1;
+ appctx_wakeup(appctx);
return 0;
}
@@ -2316,6 +2327,7 @@ __LJMP static int hlua_socket_new(lua_State *L)
appctx->ctx.hlua.socket = socket;
appctx->ctx.hlua.connected = 0;
+ appctx->ctx.hlua.die = 0;
LIST_INIT(&appctx->ctx.hlua.wake_on_write);
LIST_INIT(&appctx->ctx.hlua.wake_on_read);
--
2.13.0

View File

@ -0,0 +1,46 @@
From ea3b479be6cacb399a6541a00b1bdce17b0179d0 Mon Sep 17 00:00:00 2001
From: Thierry FOURNIER <thierry.fournier@ozon.io>
Date: Mon, 17 Jul 2017 00:44:40 +0200
Subject: [PATCH 05/18] BUG/MEDIUM: lua: bad memory access
We cannot perform garbage collection on unreferenced thread.
This memory is now free and another Lua process can use it for
other things.
HAProxy is monothread, so this bug doesn't cause crash.
This patch must be backported in 1.6 and 1.7
(cherry picked from commit 7bd10d58d3aecf7cf1e5ee7df01193e07128a52d)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/hlua.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/src/hlua.c b/src/hlua.c
index eb003558..a998860e 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -895,16 +895,10 @@ void hlua_ctx_destroy(struct hlua *lua)
* the garbage collection.
*/
if (lua->flags & HLUA_MUST_GC) {
- if (!SET_SAFE_LJMP(lua->T))
+ if (!SET_SAFE_LJMP(gL.T))
return;
- lua_gc(lua->T, LUA_GCCOLLECT, 0);
- RESET_SAFE_LJMP(lua->T);
- if (lua_status(lua->T) != LUA_OK) {
- if (!SET_SAFE_LJMP(gL.T))
- return;
- lua_gc(gL.T, LUA_GCCOLLECT, 0);
- RESET_SAFE_LJMP(gL.T);
- }
+ lua_gc(gL.T, LUA_GCCOLLECT, 0);
+ RESET_SAFE_LJMP(gL.T);
}
lua->T = NULL;
--
2.13.0

View File

@ -0,0 +1,64 @@
From 20850d19250eb530cab889bb9059a630b3f805a3 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w@1wt.eu>
Date: Tue, 18 Jul 2017 06:56:40 +0200
Subject: [PATCH 06/18] DOC: update CONTRIBUTING regarding optional parts and
message format
Make it clear that optional components must not break when disabled,
that openssl is the only officially supported library and its support
must not be broken, and that bug fixes must always be detailed.
(cherry picked from commit 9d84cd602f4adb3954209eb14c94eea9254d1b5b)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
CONTRIBUTING | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTING b/CONTRIBUTING
index 74a099bc..b2c2b493 100644
--- a/CONTRIBUTING
+++ b/CONTRIBUTING
@@ -69,6 +69,16 @@ code :
Since most of these restrictions are just a matter of coding style, it is
normally not a problem to comply.
+When modifying some optional subsystem (SSL, Lua, compression, device detection
+engines), please make sure the code continues to build (and to work) when these
+features are disabled. Similarly, when modifying the SSL stack, please always
+ensure that supported OpenSSL versions continue to build and to work, especially
+if you modify support for alternate libraries. Clean support for the legacy
+OpenSSL libraries is mandatory, support for its derivatives is a bonus and may
+occasionally break eventhough a great care is taken. In other words, if you
+provide a patch for OpenSSL you don't need to test its derivatives, but if you
+provide a patch for a derivative you also need to test with OpenSSL.
+
If your work is very confidential and you can't publicly discuss it, you can
also mail willy@haproxy.org directly about it, but your mail may be waiting
several days in the queue before you get a response, if you get a response at
@@ -441,13 +451,22 @@ do not think about them anymore after a few patches.
way the subject is built. Please see the section below for more information
regarding this formatting.
- As a rule of thumb, your patch must never be made only of a subject line,
+ As a rule of thumb, your patch MUST NEVER be made only of a subject line,
it *must* contain a description. Even one or two lines, or indicating
whether a backport is desired or not. It turns out that single-line commits
are so rare in the Git world that they require special manual (hence
painful) handling when they are backported, and at least for this reason
it's important to keep this in mind.
+ Each patch fixing a bug MUST be tagged with "BUG", a severity level, an
+ indication of the affected subsystem and a brief description of the nature
+ of the issue in the subject line, and a detailed analysis in the message
+ body. The explanation of the user-visible impact and the need for
+ backporting to stable branches or not are MANDATORY. Bug fixes with no
+ indication will simply be rejected as they are very likely to cause more
+ harm when nobody is able to tell whether or not the patch needs to be
+ backported or can be reverted in case of regression.
+
12) Discuss on the mailing list
When submitting changes, please always CC the mailing list address so that
--
2.13.0

View File

@ -0,0 +1,36 @@
From 8d99949c4c51d95c14fb2b09d18e1cff058f0c17 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w@1wt.eu>
Date: Tue, 18 Jul 2017 06:58:16 +0200
Subject: [PATCH 07/18] DOC: update the list of OpenSSL versions in the README
1.1.0 is also supported nowadays. Also mention the best effort support
for derivatives.
(cherry picked from commit 7ab16868bc6e9d5ef879e1046effa035789835cc)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
README | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/README b/README
index 839d06ec..8ad70e66 100644
--- a/README
+++ b/README
@@ -113,8 +113,12 @@ build fails due to missing symbols such as deflateInit(), then try again with
Your are strongly encouraged to always use an up-to-date version of OpenSSL, as
found on https://www.openssl.org/ as vulnerabilities are occasionally found and
you don't want them on your systems. HAProxy is known to build correctly on all
-currently supported branches (0.9.8, 1.0.0, 1.0.1 and 1.0.2 at the time of
-writing). Branch 1.0.2 is recommended for the richest features.
+currently supported branches (0.9.8, 1.0.0, 1.0.1, 1.0.2 and 1.1.0 at the time
+of writing). Branch 1.0.2 is currently recommended for the best combination of
+features and stability. Asynchronous engines require OpenSSL 1.1.0 though. It's
+worth mentionning that some OpenSSL derivatives are also reported to work but
+may occasionally break. Patches to fix them are welcome but please read the
+CONTRIBUTING file first.
To link OpenSSL statically against haproxy, build OpenSSL with the no-shared
keyword and install it to a local directory, so your system is not affected :
--
2.13.0

View File

@ -0,0 +1,129 @@
From 3e21b8d25ad148ef4e6544f28a8b2305f9484a7b Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w@1wt.eu>
Date: Wed, 19 Jul 2017 19:05:29 +0200
Subject: [PATCH 08/18] MINOR: tools: add a portable timegm() alternative
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
timegm() is not provided everywhere and the documentation on how to
replace it is bogus as it proposes an inefficient and non-thread safe
alternative.
Here we reimplement everything needed to compute the number of seconds
since Epoch based on the broken down fields in struct tm. It is only
guaranteed to return correct values for correct inputs. It was successfully
tested with all possible 32-bit values of time_t converted to struct tm
using gmtime() and back to time_t using the legacy timegm() and this
function, and both functions always produced the same result.
Thanks to Benoît Garnier for an instructive discussion and detailed
explanations of the various time functions, leading to this solution.
(cherry picked from commit cb1949b8b30b8db7e05546da2939eff2b5973321)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
include/common/standard.h | 21 ++++++++++++++++++
src/standard.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 75 insertions(+)
diff --git a/include/common/standard.h b/include/common/standard.h
index 87f90a65..c19c368b 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -624,6 +624,27 @@ static inline void get_gmtime(const time_t now, struct tm *tm)
gmtime_r(&now, tm);
}
+/* Counts a number of elapsed days since 01/01/0000 based solely on elapsed
+ * years and assuming the regular rule for leap years applies. It's fake but
+ * serves as a temporary origin. It's worth remembering that it's the first
+ * year of each period that is leap and not the last one, so for instance year
+ * 1 sees 366 days since year 0 was leap. For this reason we have to apply
+ * modular arithmetics which is why we offset the year by 399 before
+ * subtracting the excess at the end. No overflow here before ~11.7 million
+ * years.
+ */
+static inline unsigned int days_since_zero(unsigned int y)
+{
+ return y * 365 + (y + 399) / 4 - (y + 399) / 100 + (y + 399) / 400
+ - 399 / 4 + 399 / 100;
+}
+
+/* Returns the number of seconds since 01/01/1970 0:0:0 GMT for GMT date <tm>.
+ * It is meant as a portable replacement for timegm() for use with valid inputs.
+ * Returns undefined results for invalid dates (eg: months out of range 0..11).
+ */
+extern time_t my_timegm(const struct tm *tm);
+
/* This function parses a time value optionally followed by a unit suffix among
* "d", "h", "m", "s", "ms" or "us". It converts the value into the unit
* expected by the caller. The computation does its best to avoid overflows.
diff --git a/src/standard.c b/src/standard.c
index 8df1da6c..e1d414f3 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -2841,6 +2841,60 @@ char *localdate2str_log(char *dst, time_t t, struct tm *tm, size_t size)
return dst;
}
+/* Returns the number of seconds since 01/01/1970 0:0:0 GMT for GMT date <tm>.
+ * It is meant as a portable replacement for timegm() for use with valid inputs.
+ * Returns undefined results for invalid dates (eg: months out of range 0..11).
+ */
+time_t my_timegm(const struct tm *tm)
+{
+ /* Each month has 28, 29, 30 or 31 days, or 28+N. The date in the year
+ * is thus (current month - 1)*28 + cumulated_N[month] to count the
+ * sum of the extra N days for elapsed months. The sum of all these N
+ * days doesn't exceed 30 for a complete year (366-12*28) so it fits
+ * in a 5-bit word. This means that with 60 bits we can represent a
+ * matrix of all these values at once, which is fast and efficient to
+ * access. The extra February day for leap years is not counted here.
+ *
+ * Jan : none = 0 (0)
+ * Feb : Jan = 3 (3)
+ * Mar : Jan..Feb = 3 (3 + 0)
+ * Apr : Jan..Mar = 6 (3 + 0 + 3)
+ * May : Jan..Apr = 8 (3 + 0 + 3 + 2)
+ * Jun : Jan..May = 11 (3 + 0 + 3 + 2 + 3)
+ * Jul : Jan..Jun = 13 (3 + 0 + 3 + 2 + 3 + 2)
+ * Aug : Jan..Jul = 16 (3 + 0 + 3 + 2 + 3 + 2 + 3)
+ * Sep : Jan..Aug = 19 (3 + 0 + 3 + 2 + 3 + 2 + 3 + 3)
+ * Oct : Jan..Sep = 21 (3 + 0 + 3 + 2 + 3 + 2 + 3 + 3 + 2)
+ * Nov : Jan..Oct = 24 (3 + 0 + 3 + 2 + 3 + 2 + 3 + 3 + 2 + 3)
+ * Dec : Jan..Nov = 26 (3 + 0 + 3 + 2 + 3 + 2 + 3 + 3 + 2 + 3 + 2)
+ */
+ uint64_t extra =
+ ( 0ULL << 0*5) + ( 3ULL << 1*5) + ( 3ULL << 2*5) + /* Jan, Feb, Mar, */
+ ( 6ULL << 3*5) + ( 8ULL << 4*5) + (11ULL << 5*5) + /* Apr, May, Jun, */
+ (13ULL << 6*5) + (16ULL << 7*5) + (19ULL << 8*5) + /* Jul, Aug, Sep, */
+ (21ULL << 9*5) + (24ULL << 10*5) + (26ULL << 11*5); /* Oct, Nov, Dec, */
+
+ unsigned int y = tm->tm_year + 1900;
+ unsigned int m = tm->tm_mon;
+ unsigned long days = 0;
+
+ /* days since 1/1/1970 for full years */
+ days += days_since_zero(y) - days_since_zero(1970);
+
+ /* days for full months in the current year */
+ days += 28 * m + ((extra >> (m * 5)) & 0x1f);
+
+ /* count + 1 after March for leap years. A leap year is a year multiple
+ * of 4, unless it's multiple of 100 without being multiple of 400. 2000
+ * is leap, 1900 isn't, 1904 is.
+ */
+ if ((m > 1) && !(y & 3) && ((y % 100) || !(y % 400)))
+ days++;
+
+ days += tm->tm_mday - 1;
+ return days * 86400ULL + tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
+}
+
/* This function check a char. It returns true and updates
* <date> and <len> pointer to the new position if the
* character is found.
--
2.13.0

View File

@ -0,0 +1,38 @@
From df1655a6c0e4431317cc66c67693281092a952b0 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w@1wt.eu>
Date: Wed, 19 Jul 2017 19:08:48 +0200
Subject: [PATCH 09/18] BUILD: lua: replace timegm() with my_timegm() to fix
build on Solaris 10
Akhnin Nikita reported that Lua doesn't build on Solaris 10 because
the code uses timegm() to parse a date, which is not provided there.
The recommended way to implement timegm() is broken in the man page,
as it is based on a change of the TZ environment variable at run time
before calling the function (which is obviously not thread safe, and
terribly inefficient).
Here instead we rely on the new my_timegm() function, it should be
sufficient for all known use cases.
(cherry picked from commit abd9bb20b76818c9f461a82b72b10818736ff8b3)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
src/hlua_fcn.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c
index 58905d7d..fe899a4a 100644
--- a/src/hlua_fcn.c
+++ b/src/hlua_fcn.c
@@ -287,7 +287,7 @@ static int hlua_parse_date(lua_State *L, int (*fcn)(const char *, int, struct tm
* the timezone from the broken-down time, it must be fixed
* after the conversion.
*/
- time = timegm(&tm);
+ time = my_timegm(&tm);
if (time == -1) {
lua_pushnil(L);
return 1;
--
2.13.0

View File

@ -0,0 +1,32 @@
From e14ec1d816de60b648dd7cb6c55b665f5163156b Mon Sep 17 00:00:00 2001
From: ben51degrees <ben@51degrees.com>
Date: Wed, 19 Jul 2017 16:22:04 +0100
Subject: [PATCH 10/18] DOC: Updated 51Degrees git URL to point to a stable
version.
The previously documented location doesn't work anymore and must not be
used. Warning for backports, different branches are in use depending on
the version (v3.2.10 for 1.7, v3.2.5 for 1.6).
(cherry picked from commit ac752ff68cd3ac88a7a27ce17daa5c3f0c839694)
Signed-off-by: Willy Tarreau <w@1wt.eu>
---
doc/51Degrees-device-detection.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/51Degrees-device-detection.txt b/doc/51Degrees-device-detection.txt
index 1ee912d9..71b2eb76 100644
--- a/doc/51Degrees-device-detection.txt
+++ b/doc/51Degrees-device-detection.txt
@@ -14,7 +14,7 @@ headers as configurable parameters.
In order to enable 51Degrees download the 51Degrees source code from the
official github repository :
- git clone https://github.com/51Degrees/Device-Detection
+ git clone https://git.51Degrees.com/Device-Detection.git -b v3.2.10
then run 'make' with USE_51DEGREES and 51DEGREES_SRC set. Both 51DEGREES_INC
and 51DEGREES_LIB may additionally be used to force specific different paths
--
2.13.0

View File

@ -0,0 +1,33 @@
From 9304b76fb37a36f6249ec963093d74210bd237f6 Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue, 18 Jul 2017 10:35:55 +0200
Subject: [PATCH 11/18] BUG/MINOR: http: Set the response error state in
http_sync_res_state
This is just typo. It may only report a wrong response message state in
"show errors" on the CLI.
This patch must be backported in 1.7.
(cherry picked from commit a3992e06a6e74142d9784d18d8cb3527fadb64d6)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/proto_http.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/proto_http.c b/src/proto_http.c
index 94c8d639..796955f5 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5530,7 +5530,7 @@ int http_sync_res_state(struct stream *s)
goto http_msg_closed;
}
else if (chn->flags & CF_SHUTW) {
- txn->req.err_state = txn->req.msg_state;
+ txn->rsp.err_state = txn->rsp.msg_state;
txn->rsp.msg_state = HTTP_MSG_ERROR;
s->be->be_counters.cli_aborts++;
if (objt_server(s->target))
--
2.13.0

View File

@ -0,0 +1,100 @@
From a49007a187ab7fddfcec58e1d9fc8a707e4531c9 Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue, 18 Jul 2017 11:18:46 +0200
Subject: [PATCH 12/18] MINOR: http: Reorder/rewrite checks in
http_resync_states
The previous patch removed the forced symmetry of the TUNNEL mode during the
state synchronization. Here, we take care to remove body analyzer only on the
channel in TUNNEL mode. In fact, today, this change has no effect because both
sides are switched in same time. But this way, with some changes, it will be
possible to keep body analyzer on a side (to finish the states synchronization)
with the other one in TUNNEL mode.
WARNING: This patch will be used to fix a bug. The fix will be commited in a
very next commit. So if the fix is backported, this one must be backported too.
(cherry picked from commit f77bb539d4846ab278269b99a3165a5608ca0cf4)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/proto_http.c | 48 +++++++++++++++++++++++++++++-------------------
1 file changed, 29 insertions(+), 19 deletions(-)
diff --git a/src/proto_http.c b/src/proto_http.c
index 796955f5..aaf9f648 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5577,34 +5577,27 @@ int http_resync_states(struct stream *s)
/* OK, both state machines agree on a compatible state.
* There are a few cases we're interested in :
- * - HTTP_MSG_TUNNEL on either means we have to disable both analysers
* - HTTP_MSG_CLOSED on both sides means we've reached the end in both
* directions, so let's simply disable both analysers.
- * - HTTP_MSG_CLOSED on the response only means we must abort the
- * request.
- * - HTTP_MSG_CLOSED on the request and HTTP_MSG_DONE on the response
- * with server-close mode means we've completed one request and we
- * must re-initialize the server connection.
+ * - HTTP_MSG_CLOSED on the response only or HTTP_MSG_ERROR on either
+ * means we must abort the request.
+ * - HTTP_MSG_TUNNEL on either means we have to disable analyser on
+ * corresponding channel.
+ * - HTTP_MSG_DONE or HTTP_MSG_CLOSED on the request and HTTP_MSG_DONE
+ * on the response with server-close mode means we've completed one
+ * request and we must re-initialize the server connection.
*/
-
- if (txn->req.msg_state == HTTP_MSG_TUNNEL ||
- txn->rsp.msg_state == HTTP_MSG_TUNNEL ||
- (txn->req.msg_state == HTTP_MSG_CLOSED &&
- txn->rsp.msg_state == HTTP_MSG_CLOSED)) {
+ if (txn->req.msg_state == HTTP_MSG_CLOSED &&
+ txn->rsp.msg_state == HTTP_MSG_CLOSED) {
s->req.analysers &= AN_REQ_FLT_END;
channel_auto_close(&s->req);
channel_auto_read(&s->req);
s->res.analysers &= AN_RES_FLT_END;
channel_auto_close(&s->res);
channel_auto_read(&s->res);
- if (txn->req.msg_state == HTTP_MSG_TUNNEL && HAS_REQ_DATA_FILTERS(s))
- s->req.analysers |= AN_REQ_FLT_XFER_DATA;
- if (txn->rsp.msg_state == HTTP_MSG_TUNNEL && HAS_RSP_DATA_FILTERS(s))
- s->res.analysers |= AN_RES_FLT_XFER_DATA;
- }
- else if ((txn->req.msg_state >= HTTP_MSG_DONE &&
- (txn->rsp.msg_state == HTTP_MSG_CLOSED || (s->res.flags & CF_SHUTW))) ||
- txn->rsp.msg_state == HTTP_MSG_ERROR ||
+ }
+ else if (txn->rsp.msg_state == HTTP_MSG_CLOSED ||
+ txn->rsp.msg_state == HTTP_MSG_ERROR ||
txn->req.msg_state == HTTP_MSG_ERROR) {
s->res.analysers &= AN_RES_FLT_END;
channel_auto_close(&s->res);
@@ -5615,6 +5608,23 @@ int http_resync_states(struct stream *s)
channel_auto_read(&s->req);
channel_truncate(&s->req);
}
+ else if (txn->req.msg_state == HTTP_MSG_TUNNEL ||
+ txn->rsp.msg_state == HTTP_MSG_TUNNEL) {
+ if (txn->req.msg_state == HTTP_MSG_TUNNEL) {
+ s->req.analysers &= AN_REQ_FLT_END;
+ if (HAS_REQ_DATA_FILTERS(s))
+ s->req.analysers |= AN_REQ_FLT_XFER_DATA;
+ }
+ if (txn->rsp.msg_state == HTTP_MSG_TUNNEL) {
+ s->res.analysers &= AN_RES_FLT_END;
+ if (HAS_RSP_DATA_FILTERS(s))
+ s->res.analysers |= AN_RES_FLT_XFER_DATA;
+ }
+ channel_auto_close(&s->req);
+ channel_auto_read(&s->req);
+ channel_auto_close(&s->res);
+ channel_auto_read(&s->res);
+ }
else if ((txn->req.msg_state == HTTP_MSG_DONE ||
txn->req.msg_state == HTTP_MSG_CLOSED) &&
txn->rsp.msg_state == HTTP_MSG_DONE &&
--
2.13.0

View File

@ -0,0 +1,153 @@
From 1430a0c0f62fcff4303706f5baf2b544e00fcda3 Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue, 18 Jul 2017 10:48:24 +0200
Subject: [PATCH 13/18] MINOR: http: Switch requests/responses in TUNNEL mode
only by checking txn flags
Today, the only way to have a request or a response in HTTP_MSG_TUNNEL state is
to have the flag TX_CON_WANT_TUN set on the transaction. So this is a symmetric
state. Both the request and the response are switch in same time in this
state. This can be done only by checking transaction flags instead of relying on
the other side state. This is the purpose of this patch.
This way, if for any reason we need to switch only one side in TUNNEL mode, it
will be possible. And to prepare asymmetric cases, we check channel flags in
DONE _AND_ TUNNEL states.
WARNING: This patch will be used to fix a bug. The fix will be commited in a
very next commit. So if the fix is backported, this one must be backported too.
(cherry picked from commit 4be9803914ae7156109c915659aad216e4a3c6c1)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/proto_http.c | 65 +++++++++++++++++++-------------------------------------
1 file changed, 22 insertions(+), 43 deletions(-)
diff --git a/src/proto_http.c b/src/proto_http.c
index aaf9f648..00a92cdb 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5294,7 +5294,7 @@ int http_sync_req_state(struct stream *s)
unsigned int old_flags = chn->flags;
unsigned int old_state = txn->req.msg_state;
- if (unlikely(txn->req.msg_state < HTTP_MSG_BODY))
+ if (unlikely(txn->req.msg_state < HTTP_MSG_DONE))
return 0;
if (txn->req.msg_state == HTTP_MSG_DONE) {
@@ -5338,13 +5338,6 @@ int http_sync_req_state(struct stream *s)
goto wait_other_side;
}
- if (txn->rsp.msg_state == HTTP_MSG_TUNNEL) {
- /* if any side switches to tunnel mode, the other one does too */
- channel_auto_read(chn);
- txn->req.msg_state = HTTP_MSG_TUNNEL;
- goto wait_other_side;
- }
-
/* When we get here, it means that both the request and the
* response have finished receiving. Depending on the connection
* mode, we'll have to wait for the last bytes to leave in either
@@ -5377,20 +5370,7 @@ int http_sync_req_state(struct stream *s)
}
}
- if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
- /* if we've just closed an output, let's switch */
- s->si[1].flags |= SI_FL_NOLINGER; /* we want to close ASAP */
-
- if (!channel_is_empty(chn)) {
- txn->req.msg_state = HTTP_MSG_CLOSING;
- goto http_msg_closing;
- }
- else {
- txn->req.msg_state = HTTP_MSG_CLOSED;
- goto http_msg_closed;
- }
- }
- goto wait_other_side;
+ goto check_channel_flags;
}
if (txn->req.msg_state == HTTP_MSG_CLOSING) {
@@ -5419,6 +5399,16 @@ int http_sync_req_state(struct stream *s)
goto wait_other_side;
}
+ check_channel_flags:
+ /* Here, we are in HTTP_MSG_DONE or HTTP_MSG_TUNNEL */
+ if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
+ /* if we've just closed an output, let's switch */
+ s->si[1].flags |= SI_FL_NOLINGER; /* we want to close ASAP */
+ txn->req.msg_state = HTTP_MSG_CLOSING;
+ goto http_msg_closing;
+ }
+
+
wait_other_side:
return txn->req.msg_state != old_state || chn->flags != old_flags;
}
@@ -5438,7 +5428,7 @@ int http_sync_res_state(struct stream *s)
unsigned int old_flags = chn->flags;
unsigned int old_state = txn->rsp.msg_state;
- if (unlikely(txn->rsp.msg_state < HTTP_MSG_BODY))
+ if (unlikely(txn->rsp.msg_state < HTTP_MSG_DONE))
return 0;
if (txn->rsp.msg_state == HTTP_MSG_DONE) {
@@ -5461,14 +5451,6 @@ int http_sync_res_state(struct stream *s)
goto wait_other_side;
}
- if (txn->req.msg_state == HTTP_MSG_TUNNEL) {
- /* if any side switches to tunnel mode, the other one does too */
- channel_auto_read(chn);
- txn->rsp.msg_state = HTTP_MSG_TUNNEL;
- chn->flags |= CF_NEVER_WAIT;
- goto wait_other_side;
- }
-
/* When we get here, it means that both the request and the
* response have finished receiving. Depending on the connection
* mode, we'll have to wait for the last bytes to leave in either
@@ -5506,18 +5488,7 @@ int http_sync_res_state(struct stream *s)
txn->rsp.msg_state = HTTP_MSG_TUNNEL;
}
- if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
- /* if we've just closed an output, let's switch */
- if (!channel_is_empty(chn)) {
- txn->rsp.msg_state = HTTP_MSG_CLOSING;
- goto http_msg_closing;
- }
- else {
- txn->rsp.msg_state = HTTP_MSG_CLOSED;
- goto http_msg_closed;
- }
- }
- goto wait_other_side;
+ goto check_channel_flags;
}
if (txn->rsp.msg_state == HTTP_MSG_CLOSING) {
@@ -5548,6 +5519,14 @@ int http_sync_res_state(struct stream *s)
goto wait_other_side;
}
+ check_channel_flags:
+ /* Here, we are in HTTP_MSG_DONE or HTTP_MSG_TUNNEL */
+ if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
+ /* if we've just closed an output, let's switch */
+ txn->rsp.msg_state = HTTP_MSG_CLOSING;
+ goto http_msg_closing;
+ }
+
wait_other_side:
/* We force the response to leave immediately if we're waiting for the
* other side, since there is no pending shutdown to push it out.
--
2.13.0

View File

@ -0,0 +1,118 @@
From f82344c1cf20afcf77e8c3df8f9d341d659da93b Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Tue, 18 Jul 2017 11:42:08 +0200
Subject: [PATCH 14/18] BUG/MEDIUM: http: Switch HTTP responses in TUNNEL mode
when body length is undefined
When the body length of a HTTP response is undefined, the HTTP parser is blocked
in the body parsing. Before HAProxy 1.7, in this case, because
AN_RES_HTTP_XFER_BODY is never set, there is no visible effect. When the server
closes its connection to terminate the response, HAProxy catches it as a normal
closure. Since 1.7, we always set this analyzer to enter at least once in
http_response_forward_body. But, in the present case, when the server connection
is closed, http_response_forward_body is called one time too many. The response
is correctly sent to the client, but an error is catched and logged with "SD--"
flags.
To reproduce the bug, you can use the configuration "tests/test-fsm.cfg". The
tests 3 and 21 hit the bug.
Idea to fix the bug is to switch the response in TUNNEL mode without switching
the request. This is possible because of previous patches.
First, we need to detect responses with undefined body length during states
synchronization. Excluding tunnelled transactions, when the response length is
undefined, TX_CON_WANT_CLO is always set on the transaction. So, when states are
synchronized, if TX_CON_WANT_CLO is set, the response is switched in TUNNEL mode
and the request remains unchanged.
Then, in http_msg_forward_body, we add a specific check to switch the response
in DONE mode if the body length is undefined and if there is no data filter.
This patch depends on following previous commits:
* MINOR: http: Switch requests/responses in TUNNEL mode only by checking txn flags
* MINOR: http: Reorder/rewrite checks in http_resync_states
This patch must be backported in 1.7 with 2 previous ones.
(cherry picked from commit 1486b0ab6de744e14ae684af105951345534f9ec)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/proto_http.c | 37 +++++++++++++++++++++++++------------
1 file changed, 25 insertions(+), 12 deletions(-)
diff --git a/src/proto_http.c b/src/proto_http.c
index 00a92cdb..e776e4d5 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5354,7 +5354,16 @@ int http_sync_req_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 need to wait
+ * the close from the server. The response will be
+ * switched in TUNNEL mode until the end.
*/
+ if (!(txn->rsp.flags & HTTP_MSGF_XFER_LEN) &&
+ txn->rsp.msg_state != HTTP_MSG_CLOSED)
+ goto check_channel_flags;
+
if (!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
channel_shutr_now(chn);
channel_shutw_now(chn);
@@ -5471,8 +5480,16 @@ 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 (!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
+ 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))) {
channel_shutr_now(chn);
channel_shutw_now(chn);
}
@@ -6952,14 +6969,6 @@ int http_response_forward_body(struct stream *s, struct channel *res, int an_bit
if ((msg->flags & HTTP_MSGF_TE_CHNK) || (msg->flags & HTTP_MSGF_COMPRESSING))
res->flags |= CF_EXPECT_MORE;
- /* If there is neither content-length, nor transfer-encoding header
- * _AND_ there is no data filtering, we can safely forward all data
- * indefinitely. */
- if (!(msg->flags & HTTP_MSGF_XFER_LEN) && !HAS_DATA_FILTERS(s, res)) {
- buffer_flush(res->buf);
- channel_forward_forever(res);
- }
-
/* the stream handler will take care of timeouts and errors */
return 0;
@@ -7036,9 +7045,13 @@ http_msg_forward_body(struct stream *s, struct http_msg *msg)
goto missing_data_or_waiting;
}
- /* The server still sending data that should be filtered */
- if (!(msg->flags & HTTP_MSGF_XFER_LEN) && !(chn->flags & CF_SHUTR))
- goto missing_data_or_waiting;
+ /* This check can only be true for a response. HTTP_MSGF_XFER_LEN is
+ * always set for a request. */
+ if (!(msg->flags & HTTP_MSGF_XFER_LEN)) {
+ /* 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_ENDING;
--
2.13.0

View File

@ -0,0 +1,43 @@
From af9b52e92be8ca6a07f9156dcb0b08dd2ad8db75 Mon Sep 17 00:00:00 2001
From: Christopher Faulet <cfaulet@haproxy.com>
Date: Thu, 20 Jul 2017 11:05:10 +0200
Subject: [PATCH 15/18] BUG/MAJOR: http: Fix possible infinity loop in
http_sync_(req|res)_state
In commit "MINOR: http: Switch requests/responses in TUNNEL mode only by
checking txn flags", it is possible to have an infinite loop on HTTP_MSG_CLOSING
state.
(cherry picked from commit 56d260916f61e48c8b2f1fd2f9431afac776d160)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/proto_http.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/proto_http.c b/src/proto_http.c
index e776e4d5..4a030013 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5394,8 +5394,8 @@ int http_sync_req_state(struct stream *s)
else if (chn->flags & CF_SHUTW) {
txn->req.err_state = txn->req.msg_state;
txn->req.msg_state = HTTP_MSG_ERROR;
- goto wait_other_side;
}
+ goto wait_other_side;
}
if (txn->req.msg_state == HTTP_MSG_CLOSED) {
@@ -5523,8 +5523,8 @@ int http_sync_res_state(struct stream *s)
s->be->be_counters.cli_aborts++;
if (objt_server(s->target))
objt_server(s->target)->counters.cli_aborts++;
- goto wait_other_side;
}
+ goto wait_other_side;
}
if (txn->rsp.msg_state == HTTP_MSG_CLOSED) {
--
2.13.0

View File

@ -0,0 +1,45 @@
From c00347899e9f0c3420f98c53eab1469644e28e06 Mon Sep 17 00:00:00 2001
From: Nenad Merdanovic <nmerdan@haproxy.com>
Date: Sun, 23 Jul 2017 22:04:58 -0400
Subject: [PATCH 16/18] BUG/MINOR: lua: Fix Server.get_addr() port values
The get_addr() method of the Lua Server class was using the
'sockaddr_storage addr' member to get the port value. HAProxy does not
store ports in this member as it uses a separate member, called
'svc_port'.
This fix should be backported to 1.7.
(cherry picked from commit 3849473828f319829aff422d2fbbce0823e65d64)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/hlua_fcn.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c
index fe899a4a..0752220e 100644
--- a/src/hlua_fcn.c
+++ b/src/hlua_fcn.c
@@ -545,8 +545,7 @@ int hlua_server_get_addr(lua_State *L)
addr, INET_ADDRSTRLEN);
luaL_addstring(&b, addr);
luaL_addstring(&b, ":");
- snprintf(addr, INET_ADDRSTRLEN, "%d",
- ntohs(((struct sockaddr_in *)&srv->addr)->sin_port));
+ snprintf(addr, INET_ADDRSTRLEN, "%d", srv->svc_port);
luaL_addstring(&b, addr);
break;
case AF_INET6:
@@ -554,8 +553,7 @@ int hlua_server_get_addr(lua_State *L)
addr, INET_ADDRSTRLEN);
luaL_addstring(&b, addr);
luaL_addstring(&b, ":");
- snprintf(addr, INET_ADDRSTRLEN, "%d",
- ntohs(((struct sockaddr_in6 *)&srv->addr)->sin6_port));
+ snprintf(addr, INET_ADDRSTRLEN, "%d", srv->svc_port);
luaL_addstring(&b, addr);
break;
case AF_UNIX:
--
2.13.0

View File

@ -0,0 +1,34 @@
From e79fe9bc0ae363e91555f1ba64889e2ddf475b8e Mon Sep 17 00:00:00 2001
From: Nenad Merdanovic <nmerdan@haproxy.com>
Date: Sun, 23 Jul 2017 22:04:59 -0400
Subject: [PATCH 17/18] BUG/MINOR: lua: Correctly use INET6_ADDRSTRLEN in
Server.get_addr()
The get_addr() method of the Lua Server class incorrectly used
INET_ADDRSTRLEN for IPv6 addresses resulting in failing to convert
longer IPv6 addresses to strings.
This fix should be backported to 1.7.
(cherry picked from commit a9f040453acc09e888c3f2dc983f15dcf3fa66e3)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/hlua_fcn.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/hlua_fcn.c b/src/hlua_fcn.c
index 0752220e..f8b9b5ec 100644
--- a/src/hlua_fcn.c
+++ b/src/hlua_fcn.c
@@ -550,7 +550,7 @@ int hlua_server_get_addr(lua_State *L)
break;
case AF_INET6:
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&srv->addr)->sin6_addr,
- addr, INET_ADDRSTRLEN);
+ addr, INET6_ADDRSTRLEN);
luaL_addstring(&b, addr);
luaL_addstring(&b, ":");
snprintf(addr, INET_ADDRSTRLEN, "%d", srv->svc_port);
--
2.13.0

View File

@ -0,0 +1,45 @@
From dd18f945c26fc30872a52c66b06b5a0a86b10060 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w@1wt.eu>
Date: Mon, 24 Jul 2017 17:35:27 +0200
Subject: [PATCH 18/18] BUG/MINOR: lua: always detach the tcp/http tasks before
freeing them
In hlua_{http,tcp}_applet_release(), a call to task_free() is performed
to release the task, but no task_delete() is made on these tasks. Till
now it wasn't much of a problem because this was normally not done with
the task in the run queue, and the task was never put into the wait queue
since it doesn't have any timer. But with threading it will become an
issue. And not having this already prevents another bug from being fixed.
Thanks to Christopher for spotting this one. A backport to 1.7 and 1.6 is
preferred for safety.
(cherry picked from commit bd7fc95edbce821f1d7b745a7b75deef4d6b1e27)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
---
src/hlua.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/hlua.c b/src/hlua.c
index a998860e..67b9458c 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -5948,6 +5948,7 @@ error:
static void hlua_applet_tcp_release(struct appctx *ctx)
{
+ task_delete(ctx->ctx.hlua_apptcp.task);
task_free(ctx->ctx.hlua_apptcp.task);
ctx->ctx.hlua_apptcp.task = NULL;
hlua_ctx_destroy(&ctx->ctx.hlua_apptcp.hlua);
@@ -6226,6 +6227,7 @@ error:
static void hlua_applet_http_release(struct appctx *ctx)
{
+ task_delete(ctx->ctx.hlua_apphttp.task);
task_free(ctx->ctx.hlua_apphttp.task);
ctx->ctx.hlua_apphttp.task = NULL;
hlua_ctx_destroy(&ctx->ctx.hlua_apphttp.hlua);
--
2.13.0