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

Bug#856451: unblock: haproxy/1.7.3-1



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.3 to unstable. This is mostly a
bugfix only release (1.7 is the current stable branch). Here is the
changelog:

    - BUG/MINOR: stream: Fix how backend-specific analyzers are set on a stream
    - BUILD: ssl: fix build on OpenSSL 1.0.0
    - BUILD: ssl: silence a warning reported for ERR_remove_state()
    - BUILD: ssl: eliminate warning with OpenSSL 1.1.0 regarding RAND_pseudo_bytes()
    - BUG/MEDIUM: tcp: don't poll for write when connect() succeeds
    - BUG/MINOR: unix: fix connect's polling in case no data are scheduled
    - DOC: lua: improve links
    - BUG/MINOR: lua: Map.end are not reliable because "end" is a reserved keyword
    - MINOR: dns: give ability to dns_init_resolvers() to close a socket when requested
    - BUG/MAJOR: dns: restart sockets after fork()
    - MINOR: chunks: implement a simple dynamic allocator for trash buffers
    - BUG/MEDIUM: http: prevent redirect from overwriting a buffer
    - BUG/MEDIUM: filters: Do not truncate HTTP response when body length is undefined
    - BUG/MEDIUM: http: Prevent replace-header from overwriting a buffer
    - BUG/MINOR: http: Return an error when a replace-header rule failed on the response
    - BUG/MINOR: sendmail: The return of vsnprintf is not cleanly tested
    - BUG/MAJOR: lua segmentation fault when the request is like 'GET ?arg=val HTTP/1.1'
    - BUG/MEDIUM: config: reject anything but "if" or "unless" after a use-backend rule
    - MINOR: http: don't close when redirect location doesn't start with "/"

The diffstat:

 CHANGELOG                      |  21 +++++++++
 README                         |   2 +-
 VERDATE                        |   2 +-
 VERSION                        |   2 +-
 doc/configuration.txt          |   2 +-
 doc/lua-api/index.rst          | 141 +++++++++++++++++++++++++++++++++++----------------------
 examples/haproxy.spec          |   5 ++-
 include/common/chunk.h         |  13 ++++++
 include/proto/dns.h            |   2 +-
 include/proto/openssl-compat.h |  44 +++++++++++++-----
 src/cfgparse.c                 |   6 +++
 src/checks.c                   |   2 +-
 src/chunk.c                    |  25 ++++++++++-
 src/dns.c                      |  18 +++++++-
 src/haproxy.c                  |   7 ++-
 src/hlua.c                     |  34 ++++++++------
 src/proto_http.c               | 169 ++++++++++++++++++++++++++++++++++++---------------------------------
 src/proto_tcp.c                |  30 ++++++++++---
 src/proto_uxst.c               |  27 ++++++-----
 src/proxy.c                    |   2 +-
 20 files changed, 368 insertions(+), 186 deletions(-)

And attached is the diff for "src/" only. Does it sound reasonable?

unblock haproxy/1.7.3-1

- -- System Information:
Debian Release: 9.0
  APT prefers unstable-debug
  APT policy: (500, 'unstable-debug'), (500, 'unstable'), (500, 'testing'), (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-----

iQJGBAEBCAAwFiEErvI0h2bzccaJpzYAlaQv6DU1JfkFAli2ccwSHGJlcm5hdEBk
ZWJpYW4ub3JnAAoJEJWkL+g1NSX5izAP/34k9DBU01v7/NoQxrnToIZg4NCt79a/
OwiA2wjltYMcqFzuirA5GkDw+7U6GWfob72MbbMvsXaw6qW3bI2uJMTR+QPaObVQ
l8ItHH8hHMP9d+r/lXCPuwfgpHEEu1OOPStofC0l6bz0tPrW3r3ftNjIxVJfc0yf
nCm5iGM1zh2GlLPbXaSzOqvVkSRm+GG6zXDeH9nsv9DbvT/uZkESTX66eVwgl/FS
MM5g/bwQqdsR9sSpL635Syq/Mahe5VFMG9xZ53YJS+K+XGV++53d7vxQkVOBlJo8
DW4W0USVM3gB6WRNsKR0VthE45cEuwbtIe5CeN7xQO5j9HecuLQ/dCU5REJZ5O3J
Y5ks4RRspIFPbfLv70i+B1gnGzopM1HltXMVlx2AkHxr32sdrrWxGF2+gS6qH2fS
8XkD0e4QUgnLUNmoM+W6HHqYiU63qI/gLf2UdvtlxjS4c5UygnauWqgRoOmuVc9O
1esvWTsmr7LV3adysC3gaa4DUjVqP69VS2vdl+gVEBmbwzfTwLL51oNnEmMJXIpW
RukMFWktkh30KVMQQE7ZzHAL9/Zh3BxWBccGfyimG2JHV8PfRYPRHtAXqKhEbjAK
MFDNue551ZknmdunGhG3KeobyYpCtGzzkfRRgfUw50Ddt2WhF7uZKz14f8QZMvmE
aSB34fQPyX5S
=FdZa
-----END PGP SIGNATURE-----
diff --git a/src/cfgparse.c b/src/cfgparse.c
index db8feebbcd11..fc6e1497f129 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -3984,6 +3984,12 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
 
 			err_code |= warnif_cond_conflicts(cond, SMP_VAL_FE_SET_BCK, file, linenum);
 		}
+		else if (*args[2]) {
+			Alert("parsing [%s:%d] : unexpected keyword '%s' after switching rule, only 'if' and 'unless' are allowed.\n",
+			      file, linenum, args[2]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
 
 		rule = calloc(1, sizeof(*rule));
 		if (!rule) {
diff --git a/src/checks.c b/src/checks.c
index 53513caa411d..1e43dec3f02a 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -3407,7 +3407,7 @@ void send_email_alert(struct server *s, int level, const char *format, ...)
 	len = vsnprintf(buf, sizeof(buf), format, argp);
 	va_end(argp);
 
-	if (len < 0) {
+	if (len < 0 || len >= sizeof(buf)) {
 		Alert("Email alert [%s] could not format message\n", p->id);
 		return;
 	}
diff --git a/src/chunk.c b/src/chunk.c
index 7134fead3503..60902127c64c 100644
--- a/src/chunk.c
+++ b/src/chunk.c
@@ -29,6 +29,9 @@ static int trash_size;
 static char *trash_buf1;
 static char *trash_buf2;
 
+/* the trash pool for reentrant allocations */
+struct pool_head *pool2_trash = NULL;
+
 /*
 * Returns a pre-allocated and initialized trash chunk that can be used for any
 * type of conversion. Two chunks and their respective buffers are alternatively
@@ -63,7 +66,8 @@ int alloc_trash_buffers(int bufsize)
 	trash_size = bufsize;
 	trash_buf1 = (char *)my_realloc2(trash_buf1, bufsize);
 	trash_buf2 = (char *)my_realloc2(trash_buf2, bufsize);
-	return trash_buf1 && trash_buf2;
+	pool2_trash = create_pool("trash", sizeof(struct chunk) + bufsize, MEM_F_EXACT);
+	return trash_buf1 && trash_buf2 && pool2_trash;
 }
 
 /*
@@ -77,6 +81,25 @@ void free_trash_buffers(void)
 	trash_buf1 = NULL;
 }
 
+/*
+ * Allocate a trash chunk from the reentrant pool. The buffer starts at the
+ * end of the chunk. This chunk must be freed using free_trash_chunk(). This
+ * call may fail and the caller is responsible for checking that the returned
+ * pointer is not NULL.
+ */
+struct chunk *alloc_trash_chunk(void)
+{
+	struct chunk *chunk;
+
+	chunk = pool_alloc2(pool2_trash);
+	if (chunk) {
+		char *buf = (char *)chunk + sizeof(struct chunk);
+		*buf = 0;
+		chunk_init(chunk, buf, pool2_trash->size - sizeof(struct chunk));
+	}
+	return chunk;
+}
+
 /*
  * Does an snprintf() at the beginning of chunk <chk>, respecting the limit of
  * at most chk->size chars. If the chk->len is over, nothing is added. Returns
diff --git a/src/dns.c b/src/dns.c
index 012fcede4f9a..83a1ef4ffe73 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -919,11 +919,13 @@ unsigned short dns_response_get_query_id(unsigned char *resp)
  * parses resolvers sections and initializes:
  *  - task (time events) for each resolvers section
  *  - the datagram layer (network IO events) for each nameserver
+ * It takes one argument:
+ *  - close_first takes 2 values: 0 or 1. If 1, the connection is closed first.
  * returns:
  *  0 in case of error
  *  1 when no error
  */
-int dns_init_resolvers(void)
+int dns_init_resolvers(int close_socket)
 {
 	struct dns_resolvers *curr_resolvers;
 	struct dns_nameserver *curnameserver;
@@ -961,7 +963,19 @@ int dns_init_resolvers(void)
 		curr_resolvers->t = t;
 
 		list_for_each_entry(curnameserver, &curr_resolvers->nameserver_list, list) {
-			if ((dgram = calloc(1, sizeof(*dgram))) == NULL) {
+		        dgram = NULL;
+
+			if (close_socket == 1) {
+				if (curnameserver->dgram) {
+					close(curnameserver->dgram->t.sock.fd);
+					memset(curnameserver->dgram, '\0', sizeof(*dgram));
+					dgram = curnameserver->dgram;
+				}
+			}
+
+			/* allocate memory only if it has not already been allocated
+			 * by a previous call to this function */
+			if (!dgram && (dgram = calloc(1, sizeof(*dgram))) == NULL) {
 				Alert("Starting [%s/%s] nameserver: out of memory.\n", curr_resolvers->id,
 						curnameserver->id);
 				return 0;
diff --git a/src/haproxy.c b/src/haproxy.c
index 995b3a630b76..449ab054f48a 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1309,7 +1309,7 @@ void init(int argc, char **argv)
 		exit(1);
 
 	/* initialize structures for name resolution */
-	if (!dns_init_resolvers())
+	if (!dns_init_resolvers(0))
 		exit(1);
 
 	free(err_msg);
@@ -1685,6 +1685,7 @@ void deinit(void)
 	pool_destroy2(pool2_stream);
 	pool_destroy2(pool2_session);
 	pool_destroy2(pool2_connection);
+	pool_destroy2(pool2_trash);
 	pool_destroy2(pool2_buffer);
 	pool_destroy2(pool2_requri);
 	pool_destroy2(pool2_task);
@@ -2090,6 +2091,10 @@ int main(int argc, char **argv)
 		fork_poller();
 	}
 
+	/* initialize structures for name resolution */
+	if (!dns_init_resolvers(1))
+		exit(1);
+
 	protocol_enable_all();
 	/*
 	 * That's it : the central polling loop. Run until we stop.
diff --git a/src/hlua.c b/src/hlua.c
index 133756605093..2f9c8b7b8242 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -3551,22 +3551,24 @@ static int hlua_applet_http_new(lua_State *L, struct appctx *ctx)
 
 	/* Get path and qs */
 	path = http_get_path(txn);
-	end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
-	p = path;
-	while (p < end && *p != '?')
-		p++;
+	if (path) {
+		end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+		p = path;
+		while (p < end && *p != '?')
+			p++;
 
-	/* Stores the request path. */
-	lua_pushstring(L, "path");
-	lua_pushlstring(L, path, p - path);
-	lua_settable(L, -3);
+		/* Stores the request path. */
+		lua_pushstring(L, "path");
+		lua_pushlstring(L, path, p - path);
+		lua_settable(L, -3);
 
-	/* Stores the query string. */
-	lua_pushstring(L, "qs");
-	if (*p == '?')
-		p++;
-	lua_pushlstring(L, p, end - p);
-	lua_settable(L, -3);
+		/* Stores the query string. */
+		lua_pushstring(L, "qs");
+		if (*p == '?')
+			p++;
+		lua_pushlstring(L, p, end - p);
+		lua_settable(L, -3);
+	}
 
 	/* Stores the request path. */
 	lua_pushstring(L, "length");
@@ -7008,6 +7010,10 @@ void hlua_init(void)
 	/* register pattern types. */
 	for (i=0; i<PAT_MATCH_NUM; i++)
 		hlua_class_const_int(gL.T, pat_match_names[i], i);
+	for (i=0; i<PAT_MATCH_NUM; i++) {
+		snprintf(trash.str, trash.size, "_%s", pat_match_names[i]);
+		hlua_class_const_int(gL.T, trash.str, i);
+	}
 
 	/* register constructor. */
 	hlua_class_function(gL.T, "new", hlua_map_new);
diff --git a/src/proto_http.c b/src/proto_http.c
index dd5c731ca3a3..eb53b7235a3a 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3419,13 +3419,22 @@ static int http_transform_header(struct stream* s, struct http_msg *msg,
                                  struct list *fmt, struct my_regex *re,
                                  int action)
 {
-	struct chunk *replace = get_trash_chunk();
+	struct chunk *replace;
+	int ret = -1;
+
+	replace = alloc_trash_chunk();
+	if (!replace)
+		goto leave;
 
 	replace->len = build_logline(s, replace->str, replace->size, fmt);
 	if (replace->len >= replace->size - 1)
-		return -1;
+		goto leave;
+
+	ret = http_transform_header_str(s, msg, name, name_len, replace->str, re, action);
 
-	return http_transform_header_str(s, msg, name, name_len, replace->str, re, action);
+  leave:
+	free_trash_chunk(replace);
+	return ret;
 }
 
 /* Executes the http-request rules <rules> for stream <s>, proxy <px> and
@@ -3814,7 +3823,7 @@ resume_execution:
 			                          rule->arg.hdr_add.name_len,
 			                          &rule->arg.hdr_add.fmt,
 			                          &rule->arg.hdr_add.re, rule->action))
-				return HTTP_RULE_RES_STOP; /* note: we should report an error here */
+				return HTTP_RULE_RES_BADREQ;
 			break;
 
 		case ACT_HTTP_DEL_HDR:
@@ -4023,7 +4032,12 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 	struct http_msg *req = &txn->req;
 	struct http_msg *res = &txn->rsp;
 	const char *msg_fmt;
-	const char *location;
+	struct chunk *chunk;
+	int ret = 0;
+
+	chunk = alloc_trash_chunk();
+	if (!chunk)
+		goto leave;
 
 	/* build redirect message */
 	switch(rule->code) {
@@ -4045,10 +4059,8 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 		break;
 	}
 
-	if (unlikely(!chunk_strcpy(&trash, msg_fmt)))
-		return 0;
-
-	location = trash.str + trash.len;
+	if (unlikely(!chunk_strcpy(chunk, msg_fmt)))
+		goto leave;
 
 	switch(rule->type) {
 	case REDIRECT_TYPE_SCHEME: {
@@ -4087,40 +4099,40 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 
 		if (rule->rdr_str) { /* this is an old "redirect" rule */
 			/* check if we can add scheme + "://" + host + path */
-			if (trash.len + rule->rdr_len + 3 + hostlen + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + rule->rdr_len + 3 + hostlen + pathlen > chunk->size - 4)
+				goto leave;
 
 			/* add scheme */
-			memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
-			trash.len += rule->rdr_len;
+			memcpy(chunk->str + chunk->len, rule->rdr_str, rule->rdr_len);
+			chunk->len += rule->rdr_len;
 		}
 		else {
 			/* add scheme with executing log format */
-			trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
+			chunk->len += build_logline(s, chunk->str + chunk->len, chunk->size - chunk->len, &rule->rdr_fmt);
 
 			/* check if we can add scheme + "://" + host + path */
-			if (trash.len + 3 + hostlen + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + 3 + hostlen + pathlen > chunk->size - 4)
+				goto leave;
 		}
 		/* add "://" */
-		memcpy(trash.str + trash.len, "://", 3);
-		trash.len += 3;
+		memcpy(chunk->str + chunk->len, "://", 3);
+		chunk->len += 3;
 
 		/* add host */
-		memcpy(trash.str + trash.len, host, hostlen);
-		trash.len += hostlen;
+		memcpy(chunk->str + chunk->len, host, hostlen);
+		chunk->len += hostlen;
 
 		/* add path */
-		memcpy(trash.str + trash.len, path, pathlen);
-		trash.len += pathlen;
+		memcpy(chunk->str + chunk->len, path, pathlen);
+		chunk->len += pathlen;
 
 		/* append a slash at the end of the location if needed and missing */
-		if (trash.len && trash.str[trash.len - 1] != '/' &&
+		if (chunk->len && chunk->str[chunk->len - 1] != '/' &&
 		    (rule->flags & REDIRECT_FLAG_APPEND_SLASH)) {
-			if (trash.len > trash.size - 5)
-				return 0;
-			trash.str[trash.len] = '/';
-			trash.len++;
+			if (chunk->len > chunk->size - 5)
+				goto leave;
+			chunk->str[chunk->len] = '/';
+			chunk->len++;
 		}
 
 		break;
@@ -4149,38 +4161,38 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 		}
 
 		if (rule->rdr_str) { /* this is an old "redirect" rule */
-			if (trash.len + rule->rdr_len + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + rule->rdr_len + pathlen > chunk->size - 4)
+				goto leave;
 
 			/* add prefix. Note that if prefix == "/", we don't want to
 			 * add anything, otherwise it makes it hard for the user to
 			 * configure a self-redirection.
 			 */
 			if (rule->rdr_len != 1 || *rule->rdr_str != '/') {
-				memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
-				trash.len += rule->rdr_len;
+				memcpy(chunk->str + chunk->len, rule->rdr_str, rule->rdr_len);
+				chunk->len += rule->rdr_len;
 			}
 		}
 		else {
 			/* add prefix with executing log format */
-			trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
+			chunk->len += build_logline(s, chunk->str + chunk->len, chunk->size - chunk->len, &rule->rdr_fmt);
 
 			/* Check length */
-			if (trash.len + pathlen > trash.size - 4)
-				return 0;
+			if (chunk->len + pathlen > chunk->size - 4)
+				goto leave;
 		}
 
 		/* add path */
-		memcpy(trash.str + trash.len, path, pathlen);
-		trash.len += pathlen;
+		memcpy(chunk->str + chunk->len, path, pathlen);
+		chunk->len += pathlen;
 
 		/* append a slash at the end of the location if needed and missing */
-		if (trash.len && trash.str[trash.len - 1] != '/' &&
+		if (chunk->len && chunk->str[chunk->len - 1] != '/' &&
 		    (rule->flags & REDIRECT_FLAG_APPEND_SLASH)) {
-			if (trash.len > trash.size - 5)
-				return 0;
-			trash.str[trash.len] = '/';
-			trash.len++;
+			if (chunk->len > chunk->size - 5)
+				goto leave;
+			chunk->str[chunk->len] = '/';
+			chunk->len++;
 		}
 
 		break;
@@ -4188,59 +4200,54 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 	case REDIRECT_TYPE_LOCATION:
 	default:
 		if (rule->rdr_str) { /* this is an old "redirect" rule */
-			if (trash.len + rule->rdr_len > trash.size - 4)
-				return 0;
+			if (chunk->len + rule->rdr_len > chunk->size - 4)
+				goto leave;
 
 			/* add location */
-			memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
-			trash.len += rule->rdr_len;
+			memcpy(chunk->str + chunk->len, rule->rdr_str, rule->rdr_len);
+			chunk->len += rule->rdr_len;
 		}
 		else {
 			/* add location with executing log format */
-			trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
+			chunk->len += build_logline(s, chunk->str + chunk->len, chunk->size - chunk->len, &rule->rdr_fmt);
 
 			/* Check left length */
-			if (trash.len > trash.size - 4)
-				return 0;
+			if (chunk->len > chunk->size - 4)
+				goto leave;
 		}
 		break;
 	}
 
 	if (rule->cookie_len) {
-		memcpy(trash.str + trash.len, "\r\nSet-Cookie: ", 14);
-		trash.len += 14;
-		memcpy(trash.str + trash.len, rule->cookie_str, rule->cookie_len);
-		trash.len += rule->cookie_len;
+		memcpy(chunk->str + chunk->len, "\r\nSet-Cookie: ", 14);
+		chunk->len += 14;
+		memcpy(chunk->str + chunk->len, rule->cookie_str, rule->cookie_len);
+		chunk->len += rule->cookie_len;
 	}
 
-	/* add end of headers and the keep-alive/close status.
-	 * We may choose to set keep-alive if the Location begins
-	 * with a slash, because the client will come back to the
-	 * same server.
-	 */
+	/* add end of headers and the keep-alive/close status. */
 	txn->status = rule->code;
 	/* let's log the request time */
 	s->logs.tv_request = now;
 
-	if (*location == '/' &&
-	    (req->flags & HTTP_MSGF_XFER_LEN) &&
+	if ((req->flags & HTTP_MSGF_XFER_LEN) &&
 	    ((!(req->flags & HTTP_MSGF_TE_CHNK) && !req->body_len) || (req->msg_state == HTTP_MSG_DONE)) &&
 	    ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL ||
 	     (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL)) {
 		/* keep-alive possible */
 		if (!(req->flags & HTTP_MSGF_VER_11)) {
 			if (unlikely(txn->flags & TX_USE_PX_CONN)) {
-				memcpy(trash.str + trash.len, "\r\nProxy-Connection: keep-alive", 30);
-				trash.len += 30;
+				memcpy(chunk->str + chunk->len, "\r\nProxy-Connection: keep-alive", 30);
+				chunk->len += 30;
 			} else {
-				memcpy(trash.str + trash.len, "\r\nConnection: keep-alive", 24);
-				trash.len += 24;
+				memcpy(chunk->str + chunk->len, "\r\nConnection: keep-alive", 24);
+				chunk->len += 24;
 			}
 		}
-		memcpy(trash.str + trash.len, "\r\n\r\n", 4);
-		trash.len += 4;
-		FLT_STRM_CB(s, flt_http_reply(s, txn->status, &trash));
-		bo_inject(res->chn, trash.str, trash.len);
+		memcpy(chunk->str + chunk->len, "\r\n\r\n", 4);
+		chunk->len += 4;
+		FLT_STRM_CB(s, flt_http_reply(s, txn->status, chunk));
+		bo_inject(res->chn, chunk->str, chunk->len);
 		/* "eat" the request */
 		bi_fast_delete(req->chn->buf, req->sov);
 		req->next -= req->sov;
@@ -4255,13 +4262,13 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 	} else {
 		/* keep-alive not possible */
 		if (unlikely(txn->flags & TX_USE_PX_CONN)) {
-			memcpy(trash.str + trash.len, "\r\nProxy-Connection: close\r\n\r\n", 29);
-			trash.len += 29;
+			memcpy(chunk->str + chunk->len, "\r\nProxy-Connection: close\r\n\r\n", 29);
+			chunk->len += 29;
 		} else {
-			memcpy(trash.str + trash.len, "\r\nConnection: close\r\n\r\n", 23);
-			trash.len += 23;
+			memcpy(chunk->str + chunk->len, "\r\nConnection: close\r\n\r\n", 23);
+			chunk->len += 23;
 		}
-		http_reply_and_close(s, txn->status, &trash);
+		http_reply_and_close(s, txn->status, chunk);
 		req->chn->analysers &= AN_REQ_FLT_END;
 	}
 
@@ -4270,7 +4277,10 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 	if (!(s->flags & SF_FINST_MASK))
 		s->flags |= SF_FINST_R;
 
-	return 1;
+	ret = 1;
+ leave:
+	free_trash_chunk(chunk);
+	return ret;
 }
 
 /* This stream analyser runs all HTTP request processing which is common to
@@ -6820,7 +6830,7 @@ int http_process_res_common(struct stream *s, struct channel *rep, int an_bit, s
 	}
 
  skip_header_mangling:
-	if ((msg->flags & HTTP_MSGF_XFER_LEN) || HAS_FILTERS(s) ||
+	if ((msg->flags & HTTP_MSGF_XFER_LEN) || HAS_DATA_FILTERS(s, rep) ||
 	    (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_TUN) {
 		rep->analysers &= ~AN_RES_FLT_XFER_DATA;
 		rep->analysers |= AN_RES_HTTP_XFER_BODY;
@@ -6971,8 +6981,8 @@ int http_response_forward_body(struct stream *s, struct channel *res, int an_bit
 	 * keep-alive is set on the client side or if there are filters
 	 * registered on the stream, we don't want to forward a close
 	 */
-	if ((msg->flags & HTTP_MSGF_TE_CHNK) || !msg->body_len ||
-	    HAS_FILTERS(s) ||
+	if ((msg->flags & HTTP_MSGF_TE_CHNK) || !(msg->flags & HTTP_MSGF_XFER_LEN) ||
+	    HAS_DATA_FILTERS(s, res) ||
 	    (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
 	    (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
 		channel_dont_close(res);
@@ -7064,11 +7074,10 @@ http_msg_forward_body(struct stream *s, struct http_msg *msg)
 		goto missing_data_or_waiting;
 	}
 
-	if (!(msg->flags & HTTP_MSGF_XFER_LEN) && !(chn->flags & CF_SHUTR) &&
-	    HAS_DATA_FILTERS(s, chn)) {
-		/* The server still sending data that should be filtered */
+	/* 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;
-	}
+
 	msg->msg_state = HTTP_MSG_ENDING;
 
   ending:
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index f6d8ca13c7dc..c04f2767f0ef 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -474,10 +474,16 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 	if (global.tune.server_rcvbuf)
                 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
 
-	if ((connect(fd, (struct sockaddr *)&conn->addr.to, get_addr_len(&conn->addr.to)) == -1) &&
-	    (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) {
-
-		if (errno == EAGAIN || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
+	if (connect(fd, (struct sockaddr *)&conn->addr.to, get_addr_len(&conn->addr.to)) == -1) {
+		if (errno == EINPROGRESS || errno == EALREADY) {
+			/* common case, let's wait for connect status */
+			conn->flags |= CO_FL_WAIT_L4_CONN;
+		}
+		else if (errno == EISCONN) {
+			/* should normally not happen but if so, indicates that it's OK */
+			conn->flags &= ~CO_FL_WAIT_L4_CONN;
+		}
+		else if (errno == EAGAIN || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
 			char *msg;
 			if (errno == EAGAIN || errno == EADDRNOTAVAIL) {
 				msg = "no free ports";
@@ -514,6 +520,10 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 			return SF_ERR_SRVCL;
 		}
 	}
+	else {
+		/* connect() == 0, this is great! */
+		conn->flags &= ~CO_FL_WAIT_L4_CONN;
+	}
 
 	conn->flags |= CO_FL_ADDR_TO_SET;
 
@@ -523,7 +533,6 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 
 	conn_ctrl_init(conn);       /* registers the FD */
 	fdtab[fd].linger_risk = 1;  /* close hard if needed */
-	conn_sock_want_send(conn);  /* for connect status */
 
 	if (conn_xprt_init(conn) < 0) {
 		conn_force_close(conn);
@@ -531,6 +540,17 @@ int tcp_connect_server(struct connection *conn, int data, int delack)
 		return SF_ERR_RESOURCE;
 	}
 
+	if (conn->flags & (CO_FL_HANDSHAKE | CO_FL_WAIT_L4_CONN)) {
+		conn_sock_want_send(conn);  /* for connect status, proxy protocol or SSL */
+	}
+	else {
+		/* If there's no more handshake, we need to notify the data
+		 * layer when the connection is already OK otherwise we'll have
+		 * no other opportunity to do it later (eg: health checks).
+		 */
+		data = 1;
+	}
+
 	if (data)
 		conn_data_want_send(conn);  /* prepare to send data if any */
 
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 5c8f9d4b2770..27ff0fa40786 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -495,12 +495,12 @@ int uxst_connect_server(struct connection *conn, int data, int delack)
                 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
 
 	if (connect(fd, (struct sockaddr *)&conn->addr.to, get_addr_len(&conn->addr.to)) == -1) {
-		if (errno == EALREADY || errno == EISCONN) {
-			conn->flags &= ~CO_FL_WAIT_L4_CONN;
-		}
-		else if (errno == EINPROGRESS) {
+		if (errno == EINPROGRESS || errno == EALREADY) {
 			conn->flags |= CO_FL_WAIT_L4_CONN;
 		}
+		else if (errno == EISCONN) {
+			conn->flags &= ~CO_FL_WAIT_L4_CONN;
+		}
 		else if (errno == EAGAIN || errno == EADDRINUSE || errno == EADDRNOTAVAIL) {
 			char *msg;
 			if (errno == EAGAIN || errno == EADDRNOTAVAIL) {
@@ -533,13 +533,9 @@ int uxst_connect_server(struct connection *conn, int data, int delack)
 	}
 	else {
 		/* connect() already succeeded, which is quite usual for unix
-		 * sockets. Let's avoid a second connect() probe to complete it,
-		 * but we need to ensure we'll wake up if there's no more handshake
-		 * pending (eg: for health checks).
+		 * sockets. Let's avoid a second connect() probe to complete it.
 		 */
 		conn->flags &= ~CO_FL_WAIT_L4_CONN;
-		if (!(conn->flags & CO_FL_HANDSHAKE))
-			data = 1;
 	}
 
 	conn->flags |= CO_FL_ADDR_TO_SET;
@@ -550,8 +546,6 @@ int uxst_connect_server(struct connection *conn, int data, int delack)
 
 	conn_ctrl_init(conn);       /* registers the FD */
 	fdtab[fd].linger_risk = 0;  /* no need to disable lingering */
-	if (conn->flags & CO_FL_HANDSHAKE)
-		conn_sock_want_send(conn);  /* for connect status or proxy protocol */
 
 	if (conn_xprt_init(conn) < 0) {
 		conn_force_close(conn);
@@ -559,6 +553,17 @@ int uxst_connect_server(struct connection *conn, int data, int delack)
 		return SF_ERR_RESOURCE;
 	}
 
+	if (conn->flags & (CO_FL_HANDSHAKE | CO_FL_WAIT_L4_CONN)) {
+		conn_sock_want_send(conn);  /* for connect status, proxy protocol or SSL */
+	}
+	else {
+		/* If there's no more handshake, we need to notify the data
+		 * layer when the connection is already OK otherwise we'll have
+		 * no other opportunity to do it later (eg: health checks).
+		 */
+		data = 1;
+	}
+
 	if (data)
 		conn_data_want_send(conn);  /* prepare to send data if any */
 
diff --git a/src/proxy.c b/src/proxy.c
index fec25555e8c3..a84a08fee23f 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1156,7 +1156,7 @@ int stream_set_backend(struct stream *s, struct proxy *be)
 	 * be more reliable to store the list of analysers that have been run,
 	 * but what we do here is OK for now.
 	 */
-	s->req.analysers |= be->be_req_ana & (strm_li(s) ? ~strm_li(s)->analysers : 0);
+	s->req.analysers |= be->be_req_ana & ~(strm_li(s) ? strm_li(s)->analysers : 0);
 
 	/* If the target backend requires HTTP processing, we have to allocate
 	 * the HTTP transaction and hdr_idx if we did not have one.

Reply to: