mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-30 08:32:05 +00:00
MEDIUM: connection: Add private connections synchronously in session server list
When a connection is marked as private, it is now added in the session server list. We don't wait a stream is detached from the mux to do so. When the connection is created, this happens after the mux creation. Otherwise, it is performed when the connection is marked as private. To allow that, when a connection is created, the session is systematically set as the connectin owner. Thus, a backend connection has always a owner during its creation. And a private connection has always a owner until its death. Note that outside the detach() callback, if the call to session_add_conn() failed, the error is ignored. In this situation, we retry to add the connection into the session server list in the detach() callback. If this fails at this step, the multiplexer is destroyed and the connection is closed.
This commit is contained in:
parent
21ddc74e8a
commit
08016ab82d
@ -90,11 +90,20 @@ static inline void session_unown_conn(struct session *sess, struct connection *c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add the connection <conn> to the server list of the session <sess>. 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)
|
static inline int session_add_conn(struct session *sess, struct connection *conn, void *target)
|
||||||
{
|
{
|
||||||
struct sess_srv_list *srv_list = NULL;
|
struct sess_srv_list *srv_list = NULL;
|
||||||
int found = 0;
|
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) {
|
list_for_each_entry(srv_list, &sess->srv_list, srv_list) {
|
||||||
if (srv_list->target == target) {
|
if (srv_list->target == target) {
|
||||||
found = 1;
|
found = 1;
|
||||||
@ -114,11 +123,16 @@ static inline int session_add_conn(struct session *sess, struct connection *conn
|
|||||||
return 1;
|
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)
|
static inline int session_check_idle_conn(struct session *sess, struct connection *conn)
|
||||||
{
|
{
|
||||||
if (!(conn->flags & CO_FL_PRIVATE) ||
|
/* Another session owns this connection */
|
||||||
sess->idle_conns >= sess->fe->max_out_conns) {
|
if (conn->owner != sess)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (sess->idle_conns >= sess->fe->max_out_conns) {
|
||||||
session_unown_conn(sess, conn);
|
session_unown_conn(sess, conn);
|
||||||
conn->owner = NULL;
|
conn->owner = NULL;
|
||||||
conn->flags &= ~CO_FL_SESS_IDLE;
|
conn->flags &= ~CO_FL_SESS_IDLE;
|
||||||
|
@ -1402,6 +1402,7 @@ int connect_server(struct stream *s)
|
|||||||
srv_conn->target = s->target;
|
srv_conn->target = s->target;
|
||||||
srv_cs = NULL;
|
srv_cs = NULL;
|
||||||
|
|
||||||
|
srv_conn->owner = s->sess;
|
||||||
if ((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_NEVR)
|
if ((s->be->options & PR_O_REUSE_MASK) == PR_O_REUSE_NEVR)
|
||||||
conn_set_private(srv_conn);
|
conn_set_private(srv_conn);
|
||||||
}
|
}
|
||||||
@ -1466,10 +1467,7 @@ int connect_server(struct stream *s)
|
|||||||
srv->mux_proto || s->be->mode != PR_MODE_HTTP))
|
srv->mux_proto || s->be->mode != PR_MODE_HTTP))
|
||||||
#endif
|
#endif
|
||||||
init_mux = 1;
|
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 */
|
/* process the case where the server requires the PROXY protocol to be sent */
|
||||||
srv_conn->send_proxy_ofs = 0;
|
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
|
/* If we're doing http-reuse always, and the connection is not
|
||||||
* private with available streams (an http2 connection), add it
|
* 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) &&
|
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)
|
!(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));
|
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,
|
/* The CO_FL_SEND_PROXY flag may have been set by the connect method,
|
||||||
* if so, add our handshake pseudo-XPRT now.
|
* if so, add our handshake pseudo-XPRT now.
|
||||||
|
@ -60,9 +60,20 @@ int conn_create_mux(struct connection *conn)
|
|||||||
else if (conn_install_mux_be(conn, conn->ctx, conn->owner) < 0)
|
else if (conn_install_mux_be(conn, conn->ctx, conn->owner) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
srv = objt_server(conn->target);
|
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) &&
|
if (srv && ((srv->proxy->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS) &&
|
||||||
!(conn->flags & CO_FL_PRIVATE) && conn->mux->avail_streams(conn) > 0)
|
!(conn->flags & CO_FL_PRIVATE) && conn->mux->avail_streams(conn) > 0)
|
||||||
LIST_ADDQ(&srv->available_conns[tid], mt_list_to_list(&conn->list));
|
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;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
/* let the upper layer know the connection failed */
|
/* let the upper layer know the connection failed */
|
||||||
|
@ -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) ||
|
if ((ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "Nego", 4) == 0) ||
|
||||||
(ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "NTLM", 4) == 0)) {
|
(ctx.value.len >= 4 && strncasecmp(ctx.value.ptr, "NTLM", 4) == 0)) {
|
||||||
sess->flags |= SESS_FL_PREFER_LAST;
|
sess->flags |= SESS_FL_PREFER_LAST;
|
||||||
|
conn_set_owner(srv_conn, sess, NULL);
|
||||||
conn_set_private(srv_conn);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)) &&
|
if (!(fconn->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH)) &&
|
||||||
(fconn->flags & FCGI_CF_KEEP_CONN)) {
|
(fconn->flags & FCGI_CF_KEEP_CONN)) {
|
||||||
if (fconn->conn->flags & CO_FL_PRIVATE) {
|
if (fconn->conn->flags & CO_FL_PRIVATE) {
|
||||||
if (!fconn->conn->owner) {
|
/* Add the connection in the session serverlist, if not already done */
|
||||||
fconn->conn->owner = sess;
|
if (!session_add_conn(sess, fconn->conn, fconn->conn->target)) {
|
||||||
if (!session_add_conn(sess, fconn->conn, fconn->conn->target)) {
|
fconn->conn->owner = NULL;
|
||||||
fconn->conn->owner = NULL;
|
if (eb_is_empty(&fconn->streams_by_id)) {
|
||||||
if (eb_is_empty(&fconn->streams_by_id)) {
|
/* let's kill the connection right away */
|
||||||
/* let's kill the connection right away */
|
fconn->conn->mux->destroy(fconn);
|
||||||
fconn->conn->mux->destroy(fconn);
|
TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR);
|
||||||
TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR);
|
return;
|
||||||
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) {
|
if (session_check_idle_conn(fconn->conn->owner, fconn->conn) != 0) {
|
||||||
/* The connection is destroyed, let's leave */
|
/* The connection is destroyed, let's leave */
|
||||||
TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR);
|
TRACE_DEVEL("outgoing connection killed", FCGI_EV_STRM_END|FCGI_EV_FCONN_ERR);
|
||||||
|
@ -2453,20 +2453,21 @@ static void h1_detach(struct conn_stream *cs)
|
|||||||
goto release;
|
goto release;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(h1c->conn->owner) && (h1c->conn->flags & CO_FL_PRIVATE)) {
|
if (h1c->conn->flags & CO_FL_PRIVATE) {
|
||||||
h1c->conn->owner = sess;
|
/* Add the connection in the session server list, if not already done */
|
||||||
if (!session_add_conn(sess, h1c->conn, h1c->conn->target)) {
|
if (!session_add_conn(sess, h1c->conn, h1c->conn->target)) {
|
||||||
h1c->conn->owner = NULL;
|
h1c->conn->owner = NULL;
|
||||||
h1c->conn->mux->destroy(h1c);
|
h1c->conn->mux->destroy(h1c);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
/* Always idle at this step */
|
||||||
if (session_check_idle_conn(sess, h1c->conn)) {
|
if (session_check_idle_conn(sess, h1c->conn)) {
|
||||||
/* The connection got destroyed, let's leave */
|
/* The connection got destroyed, let's leave */
|
||||||
TRACE_DEVEL("outgoing connection killed", H1_EV_STRM_END|H1_EV_H1C_END);
|
TRACE_DEVEL("outgoing connection killed", H1_EV_STRM_END|H1_EV_H1C_END);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(h1c->conn->flags & CO_FL_PRIVATE)) {
|
else {
|
||||||
if (h1c->conn->owner == sess)
|
if (h1c->conn->owner == sess)
|
||||||
h1c->conn->owner = NULL;
|
h1c->conn->owner = NULL;
|
||||||
h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
|
h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
|
||||||
|
18
src/mux_h2.c
18
src/mux_h2.c
@ -3943,18 +3943,16 @@ static void h2_detach(struct conn_stream *cs)
|
|||||||
if (!(h2c->conn->flags &
|
if (!(h2c->conn->flags &
|
||||||
(CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) {
|
(CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) {
|
||||||
if (h2c->conn->flags & CO_FL_PRIVATE) {
|
if (h2c->conn->flags & CO_FL_PRIVATE) {
|
||||||
if (!h2c->conn->owner) {
|
/* Add the connection in the session server list, if not already done */
|
||||||
h2c->conn->owner = sess;
|
if (!session_add_conn(sess, h2c->conn, h2c->conn->target)) {
|
||||||
if (!session_add_conn(sess, h2c->conn, h2c->conn->target)) {
|
h2c->conn->owner = NULL;
|
||||||
h2c->conn->owner = NULL;
|
if (eb_is_empty(&h2c->streams_by_id)) {
|
||||||
if (eb_is_empty(&h2c->streams_by_id)) {
|
h2c->conn->mux->destroy(h2c);
|
||||||
h2c->conn->mux->destroy(h2c);
|
TRACE_DEVEL("leaving on error after killing outgoing connection", H2_EV_STRM_END|H2_EV_H2C_ERR);
|
||||||
TRACE_DEVEL("leaving on error after killing outgoing connection", H2_EV_STRM_END|H2_EV_H2C_ERR);
|
return;
|
||||||
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) {
|
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 */
|
/* 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);
|
TRACE_DEVEL("leaving without reusable idle connection", H2_EV_STRM_END);
|
||||||
|
Loading…
Reference in New Issue
Block a user