[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#858892: marked as done (unblock: haproxy/1.7.4-1)



Your message dated Sat, 01 Apr 2017 09:49:00 +0000
with message-id <b42e6ccb-1045-0837-af9b-6d2322830578@thykier.net>
and subject line Re: Bug#858892: unblock: haproxy/1.7.4-1
has caused the Debian Bug report #858892,
regarding unblock: haproxy/1.7.4-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
858892: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=858892
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
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 ---
--- Begin Message ---
Vincent Bernat:
> Control: tags -1 - moreinfo
> 
> <#secure method=pgpmime mode=sign>
>  ❦ 30 mars 2017 20:27 GMT, Niels Thykier <niels@thykier.net> :
> 
>>> Would it be OK?
>>>
>>> unblock haproxy/1.7.4-1
>>>
>>> [...]
>>
>> Thanks, please go ahead and remove the moreinfo tag once it has been
>> uploaded and built on all relevant release architectures.
> 
> Done. Thanks!
> 

Unblocked, thanks.

~Niels

--- End Message ---

Reply to: