diff --git a/include/haproxy/session.h b/include/haproxy/session.h index c687c3907..24f8e5d68 100644 --- a/include/haproxy/session.h +++ b/include/haproxy/session.h @@ -90,11 +90,20 @@ static inline void session_unown_conn(struct session *sess, struct connection *c } } +/* Add the connection to the server list of the session . This + * function is called only if the connection is private. Nothing is performed if + * the connection is already in the session sever list or if the session does + * not own the connection. + */ static inline int session_add_conn(struct session *sess, struct connection *conn, void *target) { struct sess_srv_list *srv_list = NULL; int found = 0; + /* Already attach to the session or not the connection owner */ + if (!LIST_ISEMPTY(&conn->session_list) || conn->owner != sess) + return 1; + list_for_each_entry(srv_list, &sess->srv_list, srv_list) { if (srv_list->target == target) { found = 1; @@ -114,11 +123,16 @@ static inline int session_add_conn(struct session *sess, struct connection *conn return 1; } -/* Returns 0 if the session can keep the idle conn, -1 if it was destroyed, or 1 if it was added to the server list */ +/* Returns 0 if the session can keep the idle conn, -1 if it was destroyed. The + * connection must be private. + */ static inline int session_check_idle_conn(struct session *sess, struct connection *conn) { - if (!(conn->flags & CO_FL_PRIVATE) || - sess->idle_conns >= sess->fe->max_out_conns) { + /* Another session owns this connection */ + if (conn->owner != sess) + return 0; + + if (sess->idle_conns >= sess->fe->max_out_conns) { session_unown_conn(sess, conn); conn->owner = NULL; conn->flags &= ~CO_FL_SESS_IDLE; diff --git a/src/backend.c b/src/backend.c index c83371e0d..f346af558 100644 --- a/src/backend.c +++ b/src/backend.c @@ -1402,6 +1402,7 @@ int connect_server(struct stream *s) srv_conn->target = s->target; srv_cs = NULL; + srv_conn->owner = s->sess; if ((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_NEVR) conn_set_private(srv_conn); } @@ -1466,10 +1467,7 @@ int connect_server(struct stream *s) srv->mux_proto || s->be->mode != PR_MODE_HTTP)) #endif init_mux = 1; -#if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation) - else - srv_conn->owner = s->sess; -#endif + /* process the case where the server requires the PROXY protocol to be sent */ srv_conn->send_proxy_ofs = 0; @@ -1547,11 +1545,17 @@ int connect_server(struct stream *s) } /* If we're doing http-reuse always, and the connection is not * private with available streams (an http2 connection), add it - * to the available list, so that others can use it right away. + * to the available list, so that others can use it right + * away. If the connection is private, add it in the session + * server list. */ if (srv && ((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS) && !(srv_conn->flags & CO_FL_PRIVATE) && srv_conn->mux->avail_streams(srv_conn) > 0) LIST_ADDQ(&srv->available_conns[tid], mt_list_to_list(&srv_conn->list)); + else if (srv_conn->flags & CO_FL_PRIVATE) { + /* If it fail now, the same will be done in mux->detach() callack */ + session_add_conn(srv_conn->owner, srv_conn, srv_conn->target); + } } /* The CO_FL_SEND_PROXY flag may have been set by the connect method, * if so, add our handshake pseudo-XPRT now. diff --git a/src/connection.c b/src/connection.c index 11b174767..57160e711 100644 --- a/src/connection.c +++ b/src/connection.c @@ -60,9 +60,20 @@ int conn_create_mux(struct connection *conn) else if (conn_install_mux_be(conn, conn->ctx, conn->owner) < 0) goto fail; srv = objt_server(conn->target); + + /* If we're doing http-reuse always, and the connection is not + * private with available streams (an http2 connection), add it + * to the available list, so that others can use it right + * away. If the connection is private, add it in the session + * server list. + */ if (srv && ((srv->proxy->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS) && !(conn->flags & CO_FL_PRIVATE) && conn->mux->avail_streams(conn) > 0) LIST_ADDQ(&srv->available_conns[tid], mt_list_to_list(&conn->list)); + else if (conn->flags & CO_FL_PRIVATE) { + /* If it fail now, the same will be done in mux->detach() callack */ + session_add_conn(conn->owner, conn, conn->target); + } return 0; fail: /* let the upper layer know the connection failed */ diff --git a/src/http_ana.c b/src/http_ana.c index 71e723c38..b77bf2aec 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -1838,7 +1838,10 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit) if ((ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "Nego", 4) == 0) || (ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "NTLM", 4) == 0)) { sess->flags |= SESS_FL_PREFER_LAST; + conn_set_owner(srv_conn, sess, NULL); conn_set_private(srv_conn); + /* If it fail now, the same will be done in mux->detach() callack */ + session_add_conn(srv_conn->owner, srv_conn, srv_conn->target); break; } } diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index 64b91fdb4..88cc6b1e4 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -3541,19 +3541,17 @@ static void fcgi_detach(struct conn_stream *cs) if (!(fconn->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH)) && (fconn->flags & FCGI_CF_KEEP_CONN)) { if (fconn->conn->flags & CO_FL_PRIVATE) { - if (!fconn->conn->owner) { - fconn->conn->owner = sess; - if (!session_add_conn(sess, fconn->conn, fconn->conn->target)) { - fconn->conn->owner = NULL; - if (eb_is_empty(&fconn->streams_by_id)) { - /* let's kill the connection right away */ - fconn->conn->mux->destroy(fconn); - TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR); - return; - } + /* Add the connection in the session serverlist, if not already done */ + if (!session_add_conn(sess, fconn->conn, fconn->conn->target)) { + fconn->conn->owner = NULL; + if (eb_is_empty(&fconn->streams_by_id)) { + /* let's kill the connection right away */ + fconn->conn->mux->destroy(fconn); + TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR); + return; } } - if (eb_is_empty(&fconn->streams_by_id) && fconn->conn->owner == sess) { + if (eb_is_empty(&fconn->streams_by_id)) { if (session_check_idle_conn(fconn->conn->owner, fconn->conn) != 0) { /* The connection is destroyed, let's leave */ TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR); diff --git a/src/mux_h1.c b/src/mux_h1.c index 6f594410d..a27c2d0a3 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -2453,20 +2453,21 @@ static void h1_detach(struct conn_stream *cs) goto release; } - if (!(h1c->conn->owner) && (h1c->conn->flags & CO_FL_PRIVATE)) { - h1c->conn->owner = sess; + if (h1c->conn->flags & CO_FL_PRIVATE) { + /* Add the connection in the session server list, if not already done */ if (!session_add_conn(sess, h1c->conn, h1c->conn->target)) { h1c->conn->owner = NULL; h1c->conn->mux->destroy(h1c); goto end; } + /* Always idle at this step */ if (session_check_idle_conn(sess, h1c->conn)) { /* The connection got destroyed, let's leave */ TRACE_DEVEL("outgoing connection killed", H1_EV_STRM_END|H1_EV_H1C_END); goto end; } } - if (!(h1c->conn->flags & CO_FL_PRIVATE)) { + else { if (h1c->conn->owner == sess) h1c->conn->owner = NULL; h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event); diff --git a/src/mux_h2.c b/src/mux_h2.c index bb554258b..ea79371bb 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -3943,18 +3943,16 @@ static void h2_detach(struct conn_stream *cs) if (!(h2c->conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) { if (h2c->conn->flags & CO_FL_PRIVATE) { - if (!h2c->conn->owner) { - h2c->conn->owner = sess; - if (!session_add_conn(sess, h2c->conn, h2c->conn->target)) { - h2c->conn->owner = NULL; - if (eb_is_empty(&h2c->streams_by_id)) { - h2c->conn->mux->destroy(h2c); - TRACE_DEVEL("leaving on error after killing outgoing connection", H2_EV_STRM_END|H2_EV_H2C_ERR); - return; - } + /* Add the connection in the session server list, if not already done */ + if (!session_add_conn(sess, h2c->conn, h2c->conn->target)) { + h2c->conn->owner = NULL; + if (eb_is_empty(&h2c->streams_by_id)) { + h2c->conn->mux->destroy(h2c); + TRACE_DEVEL("leaving on error after killing outgoing connection", H2_EV_STRM_END|H2_EV_H2C_ERR); + return; } } - if (eb_is_empty(&h2c->streams_by_id) && h2c->conn->owner == sess) { + if (eb_is_empty(&h2c->streams_by_id)) { if (session_check_idle_conn(h2c->conn->owner, h2c->conn) != 0) { /* At this point either the connection is destroyed, or it's been added to the server idle list, just stop */ TRACE_DEVEL("leaving without reusable idle connection", H2_EV_STRM_END);