Package: release.debian.org Severity: normal User: release.debian....@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.