From 8ede3db080d7ed0ef8d4c2ae9aab382a109459f4 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Tue, 2 Mar 2021 14:38:53 +0100 Subject: [PATCH] MINOR: backend: handle reuse for conns with no server as target If dispatch mode or transparent backend is used, the backend connection target is a proxy instead of a server. In these cases, the reuse of backend connections is not consistent. With the default behavior, no reuse is done and every new request uses a new connection. However, if http-reuse is set to never, the connection are stored by the mux in the session and can be reused for future requests in the same session. As no server is used for these connections, no reuse can be made outside of the session, similarly to http-reuse never mode. A different http-reuse config value should not have an impact. To achieve this, mark these connections as private to have a defined behavior. For this feature to properly work, the connection hash has been slightly adjusted. The server pointer as an input as been replaced by a generic target pointer to refer to the server or proxy instance. The hash is always calculated on connect_server even if the connection target is not a server. This also requires to allocate the connection hash node for every backend connections, not just the one with a server target. --- include/haproxy/connection-t.h | 2 +- include/haproxy/connection.h | 23 ++++++++++++----------- src/backend.c | 28 +++++++++++++++++----------- src/connection.c | 2 +- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h index 592f32ac0..033f76143 100644 --- a/include/haproxy/connection-t.h +++ b/include/haproxy/connection-t.h @@ -492,7 +492,7 @@ enum conn_hash_params_t { * connection hash. */ struct conn_hash_params { - struct server *srv; + void *target; XXH64_hash_t sni_prehash; struct sockaddr_storage *src_addr; struct sockaddr_storage *dst_addr; diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h index f96f78c79..622e77243 100644 --- a/include/haproxy/connection.h +++ b/include/haproxy/connection.h @@ -429,6 +429,15 @@ static inline void sockaddr_free(struct sockaddr_storage **sap) *sap = NULL; } +/* returns 0 if the connection is valid and is a frontend connection, otherwise + * returns 1 indicating it's a backend connection. And uninitialized connection + * also returns 1 to better handle the usage in the middle of initialization. + */ +static inline int conn_is_back(const struct connection *conn) +{ + return !objt_listener(conn->target); +} + /* Tries to allocate a new connection and initialized its main fields. The * connection is returned on success, NULL on failure. The connection must * be released using pool_free() or conn_free(). @@ -444,8 +453,9 @@ static inline struct connection *conn_new(void *target) conn_init(conn, target); - if (obj_type(target) == OBJ_TYPE_SERVER) { - srv_use_conn(__objt_server(target), conn); + if (conn_is_back(conn)) { + if (obj_type(target) == OBJ_TYPE_SERVER) + srv_use_conn(__objt_server(target), conn); hash_node = conn_alloc_hash_node(conn); if (unlikely(!hash_node)) { @@ -968,15 +978,6 @@ static inline const struct mux_ops *conn_get_best_mux(struct connection *conn, return item ? item->mux : NULL; } -/* returns 0 if the connection is valid and is a frontend connection, otherwise - * returns 1 indicating it's a backend connection. And uninitialized connection - * also returns 1 to better handle the usage in the middle of initialization. - */ -static inline int conn_is_back(const struct connection *conn) -{ - return !objt_listener(conn->target); -} - /* returns a pointer to the proxy associated with this connection. For a front * connection it returns a pointer to the frontend ; for a back connection, it * returns a pointer to the backend. diff --git a/src/backend.c b/src/backend.c index 6d0fb8632..4c1839d38 100644 --- a/src/backend.c +++ b/src/backend.c @@ -1277,14 +1277,18 @@ int connect_server(struct stream *s) int64_t hash = 0; struct conn_hash_params hash_params; + /* in standard configuration, srv will be valid + * it can be NULL for dispatch mode or transparent backend */ + srv = objt_server(s->target); + /* first, set unique connection parameters and then calculate hash */ memset(&hash_params, 0, sizeof(hash_params)); - srv = objt_server(s->target); - hash_params.srv = srv; + /* 1. target */ + hash_params.target = s->target; #ifdef USE_OPENSSL - /* 1. sni */ + /* 2. sni */ if (srv && srv->ssl_ctx.sni) { sni_smp = sample_fetch_as_type(s->be, s->sess, s, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, @@ -1302,7 +1306,7 @@ int connect_server(struct stream *s) } #endif /* USE_OPENSSL */ - /* 2. destination address */ + /* 3. destination address */ if (!(s->flags & SF_ADDR_SET)) { err = alloc_dst_address(&s->target_addr, srv, s); if (err != SRV_STATUS_OK) @@ -1314,14 +1318,14 @@ int connect_server(struct stream *s) if (srv && (!is_addr(&srv->addr) || srv->flags & SRV_F_MAPPORTS)) hash_params.dst_addr = s->target_addr; - /* 3. source address */ + /* 4. source address */ err = alloc_bind_address(&bind_addr, srv, s); if (err != SRV_STATUS_OK) return SF_ERR_INTERNAL; hash_params.src_addr = bind_addr; - /* 4. proxy protocol */ + /* 5. proxy protocol */ if (srv && srv->pp_opts) { proxy_line_ret = make_proxy_line(trash.area, trash.size, srv, cli_conn, s); if (proxy_line_ret) { @@ -1330,8 +1334,7 @@ int connect_server(struct stream *s) } } - if (srv) - hash = conn_calculate_hash(&hash_params); + hash = conn_calculate_hash(&hash_params); /* This will catch some corner cases such as lying connections resulting from * retries or connect timeouts but will rarely trigger. @@ -1503,7 +1506,11 @@ int connect_server(struct stream *s) if (srv_conn) { srv_conn->owner = s->sess; - if (reuse_mode == PR_O_REUSE_NEVR) + + /* connection will be attached to the session if + * http-reuse mode is never or it is not targeted to a + * server */ + if (reuse_mode == PR_O_REUSE_NEVR || !srv) conn_set_private(srv_conn); /* assign bind_addr to srv_conn */ @@ -1729,8 +1736,7 @@ int connect_server(struct stream *s) } } - if (srv) - srv_conn->hash_node->hash = hash; + srv_conn->hash_node->hash = hash; return SF_ERR_NONE; /* connection is OK */ } diff --git a/src/connection.c b/src/connection.c index a9edf0184..535fb6b15 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1465,7 +1465,7 @@ XXH64_hash_t conn_calculate_hash(const struct conn_hash_params *params) buf = trash.area; - conn_hash_update(buf, &idx, ¶ms->srv, sizeof(params->srv), &hash_flags, 0); + conn_hash_update(buf, &idx, ¶ms->target, sizeof(params->target), &hash_flags, 0); if (params->sni_prehash) { conn_hash_update(buf, &idx,