]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: backend: handle reuse for conns with no server as target
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 2 Mar 2021 13:38:53 +0000 (14:38 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 3 Mar 2021 10:31:19 +0000 (11:31 +0100)
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
include/haproxy/connection.h
src/backend.c
src/connection.c

index 592f32ac0c1eacb6ee39d9d39560d7cffe90fad6..033f76143b4d0aa7cba9dc7d7b07b4ab6fa40cc5 100644 (file)
@@ -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;
index f96f78c796911526f08c63e31534bb4d09f58a44..622e77243ef238057bc4afef44550d2badb40fb4 100644 (file)
@@ -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.
index 6d0fb8632ad3743e5d658298f01e7ba8f744c4a6..4c1839d388716450e3ef0a0339dbe0a6dc1f90fa 100644 (file)
@@ -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 @@ skip_reuse:
 
                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 @@ skip_reuse:
                }
        }
 
-       if (srv)
-               srv_conn->hash_node->hash = hash;
+       srv_conn->hash_node->hash = hash;
 
        return SF_ERR_NONE;  /* connection is OK */
 }
index a9edf0184a8cbb4ad36ee607e1cc929016bae3b1..535fb6b15e9afcbc502f9317cf3d46415dfc8d92 100644 (file)
@@ -1465,7 +1465,7 @@ XXH64_hash_t conn_calculate_hash(const struct conn_hash_params *params)
 
        buf = trash.area;
 
-       conn_hash_update(buf, &idx, &params->srv, sizeof(params->srv), &hash_flags, 0);
+       conn_hash_update(buf, &idx, &params->target, sizeof(params->target), &hash_flags, 0);
 
        if (params->sni_prehash) {
                conn_hash_update(buf, &idx,