--- Begin Message ---
- To: Debian Bug Tracking System <submit@bugs.debian.org>
- Subject: unblock: haproxy/1.7.4-1
- From: Vincent Bernat <bernat@debian.org>
- Date: Tue, 28 Mar 2017 11:58:05 +0200
- Message-id: <149069508581.18361.18091480581694957595.reportbug@zoro.exoscale.ch>
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Hey!
We would like to upload HAProxy 1.7.4 to unstable. This is mostly a
bugfix only release (1.7 is the current stable branch).
Upstream says:
> The most important ones concern a regression unveiled by a fix introduced
> in 1.7.3 (which itself allowed to spot another one), another issue where
> clients could occasionally get a 503 when compression was enabled, and a
> risk of memory leak if a redirect is enabled on http-response with compression
> enabled. [...]
>
> All users of 1.7 should definitely upgrade.
Changelog is:
- BUG/MAJOR: connection: update CO_FL_CONNECTED before calling the data layer
- BUG/MAJOR: http: fix typo in http_apply_redirect_rule
- BUG/MAJOR: stream-int: do not depend on connection flags to detect connection
- BUG/MEDIUM: cli: Prevent double free in CLI ACL lookup
- BUG/MEDIUM: connection: ensure to always report the end of handshakes
- BUG/MEDIUM: filters: Fix channels synchronization in flt_end_analyze
- BUG/MEDIUM: listener: do not try to rebind another process' socket
- BUG/MEDIUM: ssl: Clear OpenSSL error stack after trying to parse OCSP file
- BUG/MEDIUM: ssl: switchctx should not return SSL_TLSEXT_ERR_ALERT_WARNING
- BUG/MEDIUM: stream: fix client-fin/server-fin handling
- BUG/MEDIUM: tcp: don't require privileges to bind to device
- BUG/MINOR: Fix "get map <map> <value>" CLI command
- BUG/MINOR: cfgparse: loop in tracked servers lists not detected by check_config_validity().
- BUG/MINOR: checks: attempt clean shutw for SSL check
- BUG/MINOR: raw_sock: always perfom the last recv if RDHUP is not available
- BUG/MINOR: spoe: Fix parsing of arguments in spoe-message section
- BUG/MINOR: spoe: Fix soft stop handler using a specific id for spoe filters
- BUG: payload: fix payload not retrieving arbitrary lengths
- BUILD: make the release script use shortlog for the final changelog
- BUILD: scripts: fix typo in announce-release error message
- CONTRIB: tcploop: add limits.h to fix build issue with some compilers
- CONTRIB: tcploop: fix connect's address length
- CONTRIB: tcploop: fix time format to silence build warnings
- CONTRIB: tcploop: make it build on FreeBSD
- CONTRIB: tcploop: report action 'K' (kill) in usage message
- CONTRIB: tcploop: use the trash instead of NULL for recv()
- DOC/MINOR: Fix typos in proxy protocol doc
- DOC: Protocol doc: add SSL TLVs, rename CHECKSUM
- DOC: Protocol doc: add checksum, TLV type ranges
- DOC: Protocol doc: add noop TLV
- MEDIUM: global: add a 'hard-stop-after' option to cap the soft-stop time
- MINOR: config: warn when some HTTP rules are used in a TCP proxy
- MINOR: doc: 2.4. Examples should be 2.5. Examples
- MINOR: doc: fix use-server example (imap vs mail)
- MINOR: fd: add a new flag HAP_POLL_F_RDHUP to struct poller
- MINOR: server: irrelevant error message with 'default-server' config file keyword.
And diffstat:
CHANGELOG | 38 ++++++++++
README | 2 +-
VERDATE | 2 +-
VERSION | 2 +-
contrib/tcploop/tcploop.c | 25 ++++++-
doc/configuration.txt | 23 +++++-
doc/proxy-protocol.txt | 157 +++++++++++++++++++++++++++++++++------
examples/haproxy.spec | 5 +-
include/types/channel.h | 3 +-
include/types/connection.h | 10 +--
include/types/fd.h | 5 ++
include/types/global.h | 4 +
include/types/proto_http.h | 7 +-
include/types/stream_interface.h | 1 +
scripts/announce-release | 4 +-
src/cfgparse.c | 35 ++++++++-
src/checks.c | 13 ++--
src/connection.c | 44 +++++++----
src/ev_epoll.c | 5 +-
src/ev_kqueue.c | 1 +
src/ev_poll.c | 1 +
src/ev_select.c | 1 +
src/filters.c | 52 ++++++++-----
src/flt_spoe.c | 19 ++++-
src/haproxy.c | 3 +
src/listener.c | 10 +--
src/map.c | 3 +-
src/payload.c | 2 +-
src/proto_http.c | 22 +++---
src/proto_tcp.c | 1 -
src/proxy.c | 67 +++++++++++++++++
src/raw_sock.c | 14 +++-
src/server.c | 2 +-
src/ssl_sock.c | 4 +-
src/stream.c | 10 +--
src/stream_interface.c | 18 ++++-
36 files changed, 488 insertions(+), 127 deletions(-)
The attached diff is for "src/" only (21 files changed, 244
insertions, 83 deletions).
Would it be OK?
unblock haproxy/1.7.4-1
- -- System Information:
Debian Release: 9.0
APT prefers unstable-debug
APT policy: (500, 'unstable-debug'), (500, 'unstable'), (101, 'experimental-debug'), (101, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386
Kernel: Linux 4.9.0-2-amd64 (SMP w/4 CPU cores)
Locale: LANG=fr_FR.utf8, LC_CTYPE=fr_FR.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
-----BEGIN PGP SIGNATURE-----
iQJGBAEBCAAwFiEErvI0h2bzccaJpzYAlaQv6DU1JfkFAljaM6oSHGJlcm5hdEBk
ZWJpYW4ub3JnAAoJEJWkL+g1NSX5q2EP/jCKPhduEXGz3DGW/vvdCjD48CsdzyxT
IuuVgNxzIt4FQ6B95o4YcfN/eQU8aG03rIytBlwYoVFZLK3oy/F4wG8hNmLFk6MN
i7EFA6EA/cQkP+5K0JFCYYehkHRxEY4E9vwf0j0kLS2fNu+r+N7ZEJ7nlLZFfLWA
6dOyUpGHnJJYspVLOm0DqSL40SqJJV1HagJUx1GeTqo0z12hojnrRTtRUnjmB8Nk
bbMu5OqRcSD4+Zqg8iPzAazlaiB7dYfb0yVKiIMhwJxB+Xo3u427BaMH9YUb36GZ
1K+gyQgY5X2aT69uJOR0W9Wmn5tT5adGXWgy+aE/0wCRS2xKKT401/h/v7yiP4Uu
qG5w6WHjFehTG08KH53X9SARnWlYVk+PmgAv9TxgWB1GxQkJCSfKnbwTrMg7AQvj
RvKO0ETkCQVo2Zc3umNwPDKbMdfUIWOg3Z8ynMcXPb6zVZFbp0jf9s/S01eev6sq
vRDsOaEml9lz7/04/3pdpmv8SkjwMaQuJVq9RAMoxz1EKzr6Q/IOV7xCQb2LQAC1
qe20RX8iDs8ecZT+wKQaaObGk7tcOty9G/BhuDte0Py9hv9SbsLTMqYqQWrujyza
tz7b+mmiEBxAkQ3zahE3v6EviTQ1GTX6A/E5GFFvqC/dC8ohIVVjQ5jpJyHXf8Tt
R6C+7pcQ6hu1
=1GeK
-----END PGP SIGNATURE-----
diff --git a/src/cfgparse.c b/src/cfgparse.c
index fc6e1497f129..074d5e672a3a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -8552,11 +8552,12 @@ out_uri_auth_compat:
for (loop = srv->track; loop && loop != newsrv; loop = loop->track);
- if (loop) {
+ if (newsrv == srv || loop) {
Alert("config : %s '%s', server '%s': unable to track %s/%s as it "
"belongs to a tracking chain looping back to %s/%s.\n",
proxy_type_str(curproxy), curproxy->id,
- newsrv->id, px->id, srv->id, px->id, loop->id);
+ newsrv->id, px->id, srv->id, px->id,
+ newsrv == srv ? srv->id : loop->id);
cfgerr++;
goto next_srv;
}
@@ -8681,6 +8682,36 @@ out_uri_auth_compat:
curproxy->uri_auth = NULL;
}
+ if (curproxy->capture_name) {
+ Warning("config : 'capture' statement ignored for %s '%s' as it requires HTTP mode.\n",
+ proxy_type_str(curproxy), curproxy->id);
+ err_code |= ERR_WARN;
+ }
+
+ if (!LIST_ISEMPTY(&curproxy->http_req_rules)) {
+ Warning("config : 'http-request' rules ignored for %s '%s' as they require HTTP mode.\n",
+ proxy_type_str(curproxy), curproxy->id);
+ err_code |= ERR_WARN;
+ }
+
+ if (!LIST_ISEMPTY(&curproxy->http_res_rules)) {
+ Warning("config : 'http-response' rules ignored for %s '%s' as they require HTTP mode.\n",
+ proxy_type_str(curproxy), curproxy->id);
+ err_code |= ERR_WARN;
+ }
+
+ if (!LIST_ISEMPTY(&curproxy->block_rules)) {
+ Warning("config : 'block' rules ignored for %s '%s' as they require HTTP mode.\n",
+ proxy_type_str(curproxy), curproxy->id);
+ err_code |= ERR_WARN;
+ }
+
+ if (!LIST_ISEMPTY(&curproxy->redirect_rules)) {
+ Warning("config : 'redirect' rules ignored for %s '%s' as they require HTTP mode.\n",
+ proxy_type_str(curproxy), curproxy->id);
+ err_code |= ERR_WARN;
+ }
+
if (curproxy->options & (PR_O_FWDFOR | PR_O_FF_ALWAYS)) {
Warning("config : 'option %s' ignored for %s '%s' as it requires HTTP mode.\n",
"forwardfor", proxy_type_str(curproxy), curproxy->id);
diff --git a/src/checks.c b/src/checks.c
index 1e43dec3f02a..2d1447c71a36 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -1355,14 +1355,15 @@ static void event_srv_chk_r(struct connection *conn)
*check->bi->data = '\0';
check->bi->i = 0;
- /* Close the connection... We absolutely want to perform a hard close
- * and reset the connection if some data are pending, otherwise we end
- * up with many TIME_WAITs and eat all the source port range quickly.
- * To avoid sending RSTs all the time, we first try to drain pending
- * data.
+ /* Close the connection... We still attempt to nicely close if,
+ * for instance, SSL needs to send a "close notify." Later, we perform
+ * a hard close and reset the connection if some data are pending,
+ * otherwise we end up with many TIME_WAITs and eat all the source port
+ * range quickly. To avoid sending RSTs all the time, we first try to
+ * drain pending data.
*/
__conn_data_stop_both(conn);
- conn_data_shutw_hard(conn);
+ conn_data_shutw(conn);
/* OK, let's not stay here forever */
if (check->result == CHK_RES_FAILED)
diff --git a/src/connection.c b/src/connection.c
index 26fc5f6839fc..362909464257 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -99,19 +99,21 @@ void conn_fd_handler(int fd)
*/
if (conn->xprt && fd_recv_ready(fd) &&
((conn->flags & (CO_FL_DATA_RD_ENA|CO_FL_WAIT_ROOM|CO_FL_ERROR|CO_FL_HANDSHAKE)) == CO_FL_DATA_RD_ENA)) {
- /* force detection of a flag change : it's impossible to have both
- * CONNECTED and WAIT_CONN so we're certain to trigger a change.
+ /* force reporting of activity by clearing the previous flags :
+ * we'll have at least ERROR or CONNECTED at the end of an I/O,
+ * both of which will be detected below.
*/
- flags = CO_FL_WAIT_L4_CONN | CO_FL_CONNECTED;
+ flags = 0;
conn->data->recv(conn);
}
if (conn->xprt && fd_send_ready(fd) &&
((conn->flags & (CO_FL_DATA_WR_ENA|CO_FL_WAIT_DATA|CO_FL_ERROR|CO_FL_HANDSHAKE)) == CO_FL_DATA_WR_ENA)) {
- /* force detection of a flag change : it's impossible to have both
- * CONNECTED and WAIT_CONN so we're certain to trigger a change.
+ /* force reporting of activity by clearing the previous flags :
+ * we'll have at least ERROR or CONNECTED at the end of an I/O,
+ * both of which will be detected below.
*/
- flags = CO_FL_WAIT_L4_CONN | CO_FL_CONNECTED;
+ flags = 0;
conn->data->send(conn);
}
@@ -129,21 +131,33 @@ void conn_fd_handler(int fd)
if (!tcp_connect_probe(conn))
goto leave;
}
-
leave:
- /* The wake callback may be used to process a critical error and abort the
- * connection. If so, we don't want to go further as the connection will
- * have been released and the FD destroyed.
+ /* Verify if the connection just established. */
+ if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED))))
+ conn->flags |= CO_FL_CONNECTED;
+
+ /* The wake callback is normally used to notify the data layer about
+ * data layer activity (successful send/recv), connection establishment,
+ * shutdown and fatal errors. We need to consider the following
+ * situations to wake up the data layer :
+ * - change among the CO_FL_NOTIFY_DATA flags :
+ * {DATA,SOCK}_{RD,WR}_SH, ERROR,
+ * - absence of any of {L4,L6}_CONN and CONNECTED, indicating the
+ * end of handshake and transition to CONNECTED
+ * - raise of CONNECTED with HANDSHAKE down
+ * - end of HANDSHAKE with CONNECTED set
+ * - regular data layer activity
+ *
+ * Note that the wake callback is allowed to release the connection and
+ * the fd (and return < 0 in this case).
*/
if ((conn->flags & CO_FL_WAKE_DATA) &&
- ((conn->flags ^ flags) & CO_FL_CONN_STATE) &&
+ (((conn->flags ^ flags) & CO_FL_NOTIFY_DATA) ||
+ ((flags & (CO_FL_CONNECTED|CO_FL_HANDSHAKE)) != CO_FL_CONNECTED &&
+ (conn->flags & (CO_FL_CONNECTED|CO_FL_HANDSHAKE)) == CO_FL_CONNECTED)) &&
conn->data->wake(conn) < 0)
return;
- /* Last check, verify if the connection just established */
- if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED))))
- conn->flags |= CO_FL_CONNECTED;
-
/* remove the events before leaving */
fdtab[fd].ev &= FD_POLL_STICKY;
diff --git a/src/ev_epoll.c b/src/ev_epoll.c
index ccb7c33772ab..eb2bceb0383f 100644
--- a/src/ev_epoll.c
+++ b/src/ev_epoll.c
@@ -154,8 +154,10 @@ REGPRM2 static void _do_poll(struct poller *p, int exp)
}
/* always remap RDHUP to HUP as they're used similarly */
- if (e & EPOLLRDHUP)
+ if (e & EPOLLRDHUP) {
+ cur_poller.flags |= HAP_POLL_F_RDHUP;
n |= FD_POLL_HUP;
+ }
fdtab[fd].ev |= n;
if (n & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
@@ -263,6 +265,7 @@ static void _do_register(void)
p->name = "epoll";
p->pref = 300;
+ p->flags = 0;
p->private = NULL;
p->clo = __fd_clo;
diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c
index 081e78aa7757..de7da655b26c 100644
--- a/src/ev_kqueue.c
+++ b/src/ev_kqueue.c
@@ -234,6 +234,7 @@ static void _do_register(void)
p->name = "kqueue";
p->pref = 300;
+ p->flags = 0;
p->private = NULL;
p->clo = NULL;
diff --git a/src/ev_poll.c b/src/ev_poll.c
index 80d88eb91e48..9a6faa9bf718 100644
--- a/src/ev_poll.c
+++ b/src/ev_poll.c
@@ -235,6 +235,7 @@ static void _do_register(void)
p->name = "poll";
p->pref = 200;
+ p->flags = 0;
p->private = NULL;
p->clo = __fd_clo;
diff --git a/src/ev_select.c b/src/ev_select.c
index 35d3c77db7a6..1b40ea1028a1 100644
--- a/src/ev_select.c
+++ b/src/ev_select.c
@@ -235,6 +235,7 @@ static void _do_register(void)
p->name = "select";
p->pref = 150;
+ p->flags = 0;
p->private = NULL;
p->clo = __fd_clo;
diff --git a/src/filters.c b/src/filters.c
index aa6e8bb94a9f..3d5564543dab 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -675,6 +675,9 @@ flt_start_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
/* If this function is called, this means there is at least one filter,
* so we do not need to check the filter list's emptiness. */
+ /* Set flag on channel to tell that the channel is filtered */
+ chn->flags |= CF_FLT_ANALYZE;
+
RESUME_FILTER_LOOP(s, chn) {
if (!(chn->flags & CF_ISRESP)) {
if (an_bit == AN_REQ_FLT_START_BE &&
@@ -801,6 +804,11 @@ flt_end_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
{
int ret = 1;
+ /* Check if all filters attached on the stream have finished their
+ * processing on this channel. */
+ if (!(chn->flags & CF_FLT_ANALYZE))
+ goto sync;
+
RESUME_FILTER_LOOP(s, chn) {
FLT_NXT(filter, chn) = 0;
FLT_FWD(filter, chn) = 0;
@@ -813,27 +821,31 @@ flt_end_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
}
} RESUME_FILTER_END;
-end:
- ret = handle_analyzer_result(s, chn, an_bit, ret);
-
- /* Check if 'channel_end_analyze' callback has been called for the
- * request and the response. */
- if (!(s->req.analysers & AN_REQ_FLT_END) && !(s->res.analysers & AN_RES_FLT_END)) {
- /* When we are waiting for a new request, so we must reset
- * stream analyzers. The input must not be closed the request
- * channel, else it is useless to wait. */
- if (s->txn && (s->txn->flags & TX_WAIT_NEXT_RQ) && !channel_input_closed(&s->req)) {
- s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
- s->res.analysers = 0;
-
- /* Remove backend filters from the list */
- flt_stream_release(s, 1);
- }
-
+ end:
+ /* We don't remove yet this analyzer because we need to synchronize the
+ * both channels. So here, we just remove the flag CF_FLT_ANALYZE. */
+ ret = handle_analyzer_result(s, chn, 0, ret);
+ if (ret)
+ chn->flags &= ~CF_FLT_ANALYZE;
+
+ sync:
+ /* Now we can check if filters have finished their work on the both
+ * channels */
+ if (!(s->req.flags & CF_FLT_ANALYZE) && !(s->res.flags & CF_FLT_ANALYZE)) {
+ /* Sync channels by removing this analyzer for the both channels */
+ s->req.analysers &= ~AN_REQ_FLT_END;
+ s->res.analysers &= ~AN_RES_FLT_END;
+
+ /* Clean up the HTTP transaction if needed */
+ if (s->txn && (s->txn->flags & TX_WAIT_CLEANUP))
+ http_end_txn_clean_session(s);
+
+ /* Remove backend filters from the list */
+ flt_stream_release(s, 1);
}
- else if (ret) {
- /* Analyzer ends only for one channel. So wake up the stream to
- * be sure to process it for the other side as soon as
+ else {
+ /* This analyzer ends only for one channel. So wake up the
+ * stream to be sure to process it for the other side as soon as
* possible. */
task_wakeup(s->task, TASK_WOKEN_MSG);
}
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index aa6414abfdf4..57fb1da12e4f 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -235,6 +235,9 @@ struct spoe_context {
unsigned int process_exp; /* expiration date to process an event */
};
+/* SPOE filter id. Used to identify SPOE filters */
+const char *spoe_filter_id = "SPOE filter";
+
/* Set if the handle on SIGUSR1 is registered */
static int sighandler_registered = 0;
@@ -2286,10 +2289,16 @@ sig_stop_spoe(struct sig_handler *sh)
struct flt_conf *fconf;
list_for_each_entry(fconf, &p->filter_configs, list) {
- struct spoe_config *conf = fconf->conf;
- struct spoe_agent *agent = conf->agent;
+ struct spoe_config *conf;
+ struct spoe_agent *agent;
struct appctx *appctx;
+ if (fconf->id != spoe_filter_id)
+ continue;
+
+ conf = fconf->conf;
+ agent = conf->agent;
+
list_for_each_entry(appctx, &agent->cache, ctx.spoe.list) {
si_applet_want_get(appctx->owner);
si_applet_want_put(appctx->owner);
@@ -2957,8 +2966,9 @@ cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
arg->name_len = delim - args[cur_arg];
delim++;
}
-
- arg->expr = sample_parse_expr(&delim, &idx, file, linenum, &errmsg, &curproxy->conf.args);
+ arg->expr = sample_parse_expr((char*[]){delim, NULL},
+ &idx, file, linenum, &errmsg,
+ &curproxy->conf.args);
if (arg->expr == NULL) {
Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
err_code |= ERR_ALERT | ERR_FATAL;
@@ -3178,6 +3188,7 @@ parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
}
*cur_arg = pos;
+ fconf->id = spoe_filter_id;
fconf->ops = &spoe_ops;
fconf->conf = conf;
return 0;
diff --git a/src/haproxy.c b/src/haproxy.c
index 449ab054f48a..a58008e44aac 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -140,6 +140,7 @@ int relative_pid = 1; /* process id starting at 1 */
/* global options */
struct global global = {
+ .hard_stop_after = TICK_ETERNITY,
.nbproc = 1,
.req_count = 0,
.logsrvs = LIST_HEAD_INIT(global.logsrvs),
@@ -228,6 +229,7 @@ struct global global = {
/*********************************************************************/
int stopping; /* non zero means stopping in progress */
+int killed; /* non zero means a hard-stop is triggered */
int jobs = 0; /* number of active jobs (conns, listeners, active tasks, ...) */
/* Here we store informations about the pids of the processes we may pause
@@ -708,6 +710,7 @@ void init(int argc, char **argv)
*/
totalconn = actconn = maxfd = listeners = stopping = 0;
+ killed = 0;
#ifdef HAPROXY_MEMMAX
diff --git a/src/listener.c b/src/listener.c
index c2ce41329ce7..3c043baad339 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -128,6 +128,11 @@ int pause_listener(struct listener *l)
*/
int resume_listener(struct listener *l)
{
+ if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
+ l->bind_conf->bind_proc &&
+ !(l->bind_conf->bind_proc & (1UL << (relative_pid - 1))))
+ return 1;
+
if (l->state == LI_ASSIGNED) {
char msg[100];
int err;
@@ -145,11 +150,6 @@ int resume_listener(struct listener *l)
if (l->state < LI_PAUSED)
return 0;
- if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
- l->bind_conf->bind_proc &&
- !(l->bind_conf->bind_proc & (1UL << (relative_pid - 1))))
- return 1;
-
if (l->proto->sock_prot == IPPROTO_TCP &&
l->state == LI_PAUSED &&
listen(l->fd, l->backlog ? l->backlog : l->maxconn) != 0)
diff --git a/src/map.c b/src/map.c
index b6fce4df84b3..e8c6a2acbf05 100644
--- a/src/map.c
+++ b/src/map.c
@@ -524,7 +524,6 @@ static int cli_io_handler_map_lookup(struct appctx *appctx)
default:
appctx->st2 = STAT_ST_FIN;
- free(appctx->ctx.map.chunk.str);
return 1;
}
}
@@ -907,7 +906,7 @@ static struct cli_kw_list cli_kws = {{ },{
{ { "add", "map", NULL }, "add map : add map entry", cli_parse_add_map, NULL },
{ { "clear", "map", NULL }, "clear map <id> : clear the content of this map", cli_parse_clear_map, NULL },
{ { "del", "map", NULL }, "del map : delete map entry", cli_parse_del_map, NULL },
- { { "get", "map", NULL }, "get map : report the keys and values matching a sample for a map", cli_parse_get_map, NULL },
+ { { "get", "map", NULL }, "get map : report the keys and values matching a sample for a map", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook },
{ { "set", "map", NULL }, "set map : modify map entry", cli_parse_set_map, NULL },
{ { "show", "map", NULL }, "show map [id] : report available maps or dump a map's contents", cli_parse_show_map, NULL },
{ { NULL }, NULL, NULL, NULL }
diff --git a/src/payload.c b/src/payload.c
index a02a86966051..b80a19c91909 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -838,7 +838,7 @@ smp_fetch_payload(const struct arg *arg_p, struct sample *smp, const char *kw, v
return 0;
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
- if (!buf_size || buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
+ if (buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
/* will never match */
smp->flags = 0;
return 0;
diff --git a/src/proto_http.c b/src/proto_http.c
index eb53b7235a3a..f4a57e975c7c 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4253,7 +4253,7 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
req->next -= req->sov;
req->sov = 0;
s->req.analysers = AN_REQ_HTTP_XFER_BODY | (s->req.analysers & AN_REQ_FLT_END);
- s->res.analysers = AN_RES_HTTP_XFER_BODY | (s->req.analysers & AN_RES_FLT_END);
+ s->res.analysers = AN_RES_HTTP_XFER_BODY | (s->res.analysers & AN_RES_FLT_END);
req->msg_state = HTTP_MSG_CLOSED;
res->msg_state = HTTP_MSG_DONE;
/* Trim any possible response */
@@ -5310,15 +5310,8 @@ void http_end_txn_clean_session(struct stream *s)
else
si_idle_conn(&s->si[1], &srv->idle_conns);
}
-
- if (HAS_FILTERS(s)) {
- s->req.analysers &= AN_REQ_FLT_END;
- s->res.analysers &= AN_RES_FLT_END;
- }
- else {
- s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
- s->res.analysers = 0;
- }
+ s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
+ s->res.analysers = 0;
}
@@ -5674,8 +5667,12 @@ int http_resync_states(struct stream *s)
s->req.flags |= CF_WAKE_WRITE;
else if (channel_congested(&s->res))
s->res.flags |= CF_WAKE_WRITE;
- else
- http_end_txn_clean_session(s);
+ else {
+ s->req.analysers = AN_REQ_FLT_END;
+ s->res.analysers = AN_RES_FLT_END;
+ txn->flags |= TX_WAIT_CLEANUP;
+ return 1;
+ }
}
return txn->req.msg_state != old_req_state ||
@@ -9007,6 +9004,7 @@ void http_reset_txn(struct stream *s)
s->res.rex = TICK_ETERNITY;
s->res.wex = TICK_ETERNITY;
s->res.analyse_exp = TICK_ETERNITY;
+ s->si[1].hcto = TICK_ETERNITY;
}
void free_http_res_rules(struct list *r)
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index c04f2767f0ef..a2bb9d71cab8 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1698,7 +1698,6 @@ static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, stru
l->interface = strdup(args[cur_arg + 1]);
}
- global.last_checks |= LSTCHK_NETADM;
return 0;
}
#endif
diff --git a/src/proxy.c b/src/proxy.c
index a84a08fee23f..78120d9bc813 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -913,6 +913,58 @@ struct task *manage_proxy(struct task *t)
}
+static int proxy_parse_hard_stop_after(char **args, int section_type, struct proxy *curpx,
+ struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ const char *res;
+
+ if (!*args[1]) {
+ memprintf(err, "'%s' expects <time> as argument.\n", args[0]);
+ return -1;
+ }
+ res = parse_time_err(args[1], &global.hard_stop_after, TIME_UNIT_MS);
+ if (res) {
+ memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]);
+ return -1;
+ }
+ return 0;
+}
+
+struct task *hard_stop(struct task *t)
+{
+ struct proxy *p;
+ struct stream *s;
+
+ if (killed) {
+ Warning("Some tasks resisted to hard-stop, exiting now.\n");
+ send_log(NULL, LOG_WARNING, "Some tasks resisted to hard-stop, exiting now.\n");
+ /* Do some cleanup and explicitely quit */
+ deinit();
+ exit(0);
+ }
+
+ Warning("soft-stop running for too long, performing a hard-stop.\n");
+ send_log(NULL, LOG_WARNING, "soft-stop running for too long, performing a hard-stop.\n");
+ p = proxy;
+ while (p) {
+ if ((p->cap & PR_CAP_FE) && (p->feconn > 0)) {
+ Warning("Proxy %s hard-stopped (%d remaining conns will be closed).\n",
+ p->id, p->feconn);
+ send_log(p, LOG_WARNING, "Proxy %s hard-stopped (%d remaining conns will be closed).\n",
+ p->id, p->feconn);
+ }
+ p = p->next;
+ }
+ list_for_each_entry(s, &streams, list) {
+ stream_shutdown(s, SF_ERR_KILLED);
+ }
+
+ killed = 1;
+ t->expire = tick_add(now_ms, MS_TO_TICKS(1000));
+ return t;
+}
+
/*
* this function disables health-check servers so that the process will quickly be ignored
* by load balancers. Note that if a proxy was already in the PAUSED state, then its grace
@@ -922,8 +974,19 @@ void soft_stop(void)
{
struct proxy *p;
struct peers *prs;
+ struct task *task;
stopping = 1;
+ if (tick_isset(global.hard_stop_after)) {
+ task = task_new();
+ if (task) {
+ task->process = hard_stop;
+ task_schedule(task, tick_add(now_ms, global.hard_stop_after));
+ }
+ else {
+ Alert("out of memory trying to allocate the hard-stop task.\n");
+ }
+ }
p = proxy;
tv_update_date(0,1); /* else, the old time before select will be used */
while (p) {
@@ -1151,6 +1214,9 @@ int stream_set_backend(struct stream *s, struct proxy *be)
if (be->options2 & PR_O2_INDEPSTR)
s->si[1].flags |= SI_FL_INDEP_STR;
+ if (tick_isset(be->timeout.serverfin))
+ s->si[1].hcto = be->timeout.serverfin;
+
/* We want to enable the backend-specific analysers except those which
* were already run as part of the frontend/listener. Note that it would
* be more reliable to store the list of analysers that have been run,
@@ -1211,6 +1277,7 @@ int stream_set_backend(struct stream *s, struct proxy *be)
}
static struct cfg_kw_list cfg_kws = {ILH, {
+ { CFG_GLOBAL, "hard-stop-after", proxy_parse_hard_stop_after },
{ CFG_LISTEN, "timeout", proxy_parse_timeout },
{ CFG_LISTEN, "clitimeout", proxy_parse_timeout },
{ CFG_LISTEN, "contimeout", proxy_parse_timeout },
diff --git a/src/raw_sock.c b/src/raw_sock.c
index 0883c57a6a45..5f171c7a6f23 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -296,13 +296,21 @@ static int raw_sock_to_buf(struct connection *conn, struct buffer *buf, int coun
if (ret < try) {
/* unfortunately, on level-triggered events, POLL_HUP
* is generally delivered AFTER the system buffer is
- * empty, so this one might never match.
+ * empty, unless the poller supports POLL_RDHUP. If
+ * we know this is the case, we don't try to read more
+ * as we know there's no more available. Similarly, if
+ * there's no problem with lingering we don't even try
+ * to read an unlikely close from the client since we'll
+ * close first anyway.
*/
if (fdtab[conn->t.sock.fd].ev & FD_POLL_HUP)
goto read0;
- fd_done_recv(conn->t.sock.fd);
- break;
+ if ((!fdtab[conn->t.sock.fd].linger_risk) ||
+ (cur_poller.flags & HAP_POLL_F_RDHUP)) {
+ fd_done_recv(conn->t.sock.fd);
+ break;
+ }
}
count -= ret;
}
diff --git a/src/server.c b/src/server.c
index bae9709aa34f..38619b7431a5 100644
--- a/src/server.c
+++ b/src/server.c
@@ -951,7 +951,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
err_code |= ERR_ALERT | ERR_FATAL;
- if (!*args[2]) {
+ if (!defsrv && !*args[2]) {
Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
file, linenum, args[0]);
err_code |= ERR_ALERT | ERR_FATAL;
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index cc2dc1283fa2..a9a8f06e4b04 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -433,6 +433,8 @@ static int ssl_sock_load_ocsp_response(struct chunk *ocsp_response, struct certi
ret = 0;
out:
+ ERR_clear_error();
+
if (bs)
OCSP_BASICRESP_free(bs);
@@ -1464,7 +1466,7 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
}
return (s->strict_sni ?
SSL_TLSEXT_ERR_ALERT_FATAL :
- SSL_TLSEXT_ERR_ALERT_WARNING);
+ SSL_TLSEXT_ERR_OK);
}
/* switch ctx */
diff --git a/src/stream.c b/src/stream.c
index 01a2de093f81..dd74aed1d12d 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -168,6 +168,7 @@ struct stream *stream_new(struct session *sess, struct task *t, enum obj_type *o
/* this part should be common with other protocols */
si_reset(&s->si[0]);
si_set_state(&s->si[0], SI_ST_EST);
+ s->si[0].hcto = sess->fe->timeout.clientfin;
/* attach the incoming connection to the stream interface now. */
if (conn)
@@ -182,6 +183,7 @@ struct stream *stream_new(struct session *sess, struct task *t, enum obj_type *o
* callbacks will be initialized before attempting to connect.
*/
si_reset(&s->si[1]);
+ s->si[1].hcto = TICK_ETERNITY;
if (likely(sess->fe->options2 & PR_O2_INDEPSTR))
s->si[1].flags |= SI_FL_INDEP_STR;
@@ -2056,10 +2058,6 @@ struct task *process_stream(struct task *t)
if (req->flags & CF_READ_ERROR)
si_b->flags |= SI_FL_NOLINGER;
si_shutw(si_b);
- if (tick_isset(s->be->timeout.serverfin)) {
- res->rto = s->be->timeout.serverfin;
- res->rex = tick_add(now_ms, res->rto);
- }
}
/* shutdown(write) done on server side, we must stop the client too */
@@ -2239,10 +2237,6 @@ struct task *process_stream(struct task *t)
if (unlikely((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW &&
channel_is_empty(res))) {
si_shutw(si_f);
- if (tick_isset(sess->fe->timeout.clientfin)) {
- req->rto = sess->fe->timeout.clientfin;
- req->rex = tick_add(now_ms, req->rto);
- }
}
/* shutdown(write) done on the client side, we must stop the server too */
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 758aec7c7309..836487bdcc06 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -205,6 +205,11 @@ static void stream_int_shutw(struct stream_interface *si)
oc->wex = TICK_ETERNITY;
si->flags &= ~SI_FL_WAIT_DATA;
+ if (tick_isset(si->hcto)) {
+ ic->rto = si->hcto;
+ ic->rex = tick_add(now_ms, ic->rto);
+ }
+
switch (si->state) {
case SI_ST_EST:
/* we have to shut before closing, otherwise some short messages
@@ -563,7 +568,8 @@ static int si_conn_wake_cb(struct connection *conn)
if (conn->flags & CO_FL_ERROR)
si->flags |= SI_FL_ERR;
- if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED)))) {
+ if ((si->state < SI_ST_EST) &&
+ (conn->flags & (CO_FL_CONNECTED | CO_FL_HANDSHAKE)) == CO_FL_CONNECTED) {
si->exp = TICK_ETERNITY;
oc->flags |= CF_WRITE_NULL;
}
@@ -825,6 +831,11 @@ static void stream_int_shutw_conn(struct stream_interface *si)
oc->wex = TICK_ETERNITY;
si->flags &= ~SI_FL_WAIT_DATA;
+ if (tick_isset(si->hcto)) {
+ ic->rto = si->hcto;
+ ic->rex = tick_add(now_ms, ic->rto);
+ }
+
switch (si->state) {
case SI_ST_EST:
/* we have to shut before closing, otherwise some short messages
@@ -1440,6 +1451,11 @@ static void stream_int_shutw_applet(struct stream_interface *si)
oc->wex = TICK_ETERNITY;
si->flags &= ~SI_FL_WAIT_DATA;
+ if (tick_isset(si->hcto)) {
+ ic->rto = si->hcto;
+ ic->rex = tick_add(now_ms, ic->rto);
+ }
+
/* on shutw we always wake the applet up */
appctx_wakeup(si_appctx(si));
--- End Message ---