- fixes reload issue with hanging process Signed-off-by: Thomas Heil <heil@terminal-consulting.de>lilik-openwrt-22.03
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||
@ -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 | |||||