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.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));