MEDIUM: mux: Use the mux protocol specified on bind/server lines

To do so, mux choices are split to handle incoming and outgoing connections in a
different way. The protocol specified on the bind/server line is used in
priority. Then, for frontend connections, the ALPN is retrieved and used to
choose the best mux. For backend connection, there is no ALPN. Finaly, if no
protocol is specified and no protocol matches the ALPN, we fall back on a
default mux, choosing in priority the first mux with exactly the same mode.
diff --git a/include/proto/connection.h b/include/proto/connection.h
index fe4c070..3b25fc5 100644
--- a/include/proto/connection.h
+++ b/include/proto/connection.h
@@ -1008,56 +1008,78 @@
 	}
 }
 
-/* returns the first mux in the list matching the exact same token and
- * compatible with the proxy's mode (http or tcp). Mode "health" has to be
- * considered as TCP here. Ie passing "px->mode == PR_MODE_HTTP" is fine. Will
- * fall back to the first compatible mux with empty ALPN name. May return null
- * if the code improperly registered the default mux to use as a fallback.
+/* returns the first mux in the list matching the exact same <mux_proto> and
+ * compatible with the <proto_side> (FE or BE) and the <proto_mode> (TCP or
+ * HTTP). <mux_proto> can be empty. Will fall back to the first compatible mux
+ * with exactly the same <proto_mode> or with an empty name. May return
+ * null if the code improperly registered the default mux to use as a fallback.
  */
-static inline const struct mux_ops *alpn_get_mux(const struct ist token, int http_mode)
+static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
+						      const struct ist mux_proto,
+						      int proto_side, int proto_mode)
 {
 	struct mux_proto_list *item;
-	const struct mux_ops *fallback = NULL;
-
-	http_mode = 1 << !!http_mode;
+	struct mux_proto_list *fallback = NULL;
 
 	list_for_each_entry(item, &mux_proto_list.list, list) {
-		if (!(item->mode & http_mode))
+		if (!(item->side & proto_side) || !(item->mode & proto_mode))
 			continue;
-		if (isteq(token, item->token))
+		if (istlen(mux_proto) && isteq(mux_proto, item->token))
 			return item->mux;
-		if (!istlen(item->token))
-			fallback = item->mux;
+		else if (!istlen(item->token)) {
+			if (!fallback || (item->mode == proto_mode && fallback->mode != proto_mode))
+				fallback = item;
+		}
 	}
-	return fallback;
+	return (fallback ? fallback->mux : NULL);
+
 }
 
-/* finds the best mux for incoming connection <conn> and mode <http_mode> for
- * the proxy. Null cannot be returned unless there's a serious bug somewhere
- * else (no fallback mux registered).
+/* installs the best mux for incoming connection <conn> using the upper context
+ * <ctx>. If the mux protocol is forced, we use it to find the best
+ * mux. Otherwise we use the ALPN name, if any. Returns < 0 on error.
  */
-static inline const struct mux_ops *conn_find_best_mux(struct connection *conn, int http_mode)
+static inline int conn_install_mux_fe(struct connection *conn, void *ctx)
 {
-	const char *alpn_str;
-	int alpn_len;
+	struct bind_conf     *bind_conf = objt_listener(conn->target)->bind_conf;
+	const struct mux_ops *mux_ops;
 
-	if (!conn_get_alpn(conn, &alpn_str, &alpn_len))
-		alpn_len = 0;
+	if (bind_conf->mux_proto)
+		mux_ops = bind_conf->mux_proto->mux;
+	else {
+		struct ist mux_proto;
+		const char *alpn_str = NULL;
+		int alpn_len = 0;
+		int mode = (1 << (bind_conf->frontend->mode == PR_MODE_HTTP));
 
-	return alpn_get_mux(ist2(alpn_str, alpn_len), http_mode);
+		conn_get_alpn(conn, &alpn_str, &alpn_len);
+		mux_proto = ist2(alpn_str, alpn_len);
+		mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
+		if (!mux_ops)
+			return -1;
+	}
+	return conn_install_mux(conn, mux_ops, ctx);
 }
 
-/* finds the best mux for incoming connection <conn>, a proxy in and http mode
- * <mode>, and installs it on the connection for upper context <ctx>. Returns
+/* installs the best mux for outgoing connection <conn> using the upper context
+ * <ctx>. If the mux protocol is forced, we use it to find the best mux. Returns
  * < 0 on error.
  */
-static inline int conn_install_best_mux(struct connection *conn, int mode, void *ctx)
+static inline int conn_install_mux_be(struct connection *conn, void *ctx)
 {
+	struct server *srv = objt_server(conn->target);
 	const struct mux_ops *mux_ops;
 
-	mux_ops = conn_find_best_mux(conn, mode);
-	if (!mux_ops)
-		return -1;
+	if (srv->mux_proto)
+		mux_ops = srv->mux_proto->mux;
+	else {
+		int mode;
+
+		mode = (1 << (srv->proxy->mode == PR_MODE_HTTP));
+		mux_ops = conn_get_best_mux(conn, ist(NULL), PROTO_SIDE_BE, mode);
+		if (!mux_ops)
+			return -1;
+	}
 	return conn_install_mux(conn, mux_ops, ctx);
 }
 
diff --git a/src/backend.c b/src/backend.c
index 50ef3b7..1aadca5 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1189,7 +1189,7 @@
 		else
 			return SF_ERR_INTERNAL;  /* how did we get there ? */
 
-		conn_install_best_mux(srv_conn, s->be->mode == PR_MODE_HTTP, srv_cs);
+		conn_install_mux_be(srv_conn, srv_cs);
 
 		/* process the case where the server requires the PROXY protocol to be sent */
 		srv_conn->send_proxy_ofs = 0;
diff --git a/src/session.c b/src/session.c
index 635aee5..1d66b73 100644
--- a/src/session.c
+++ b/src/session.c
@@ -423,7 +423,7 @@
 		goto fail;
 
 	session_count_new(sess);
-	if (conn_install_best_mux(conn, sess->fe->mode == PR_MODE_HTTP, NULL) < 0)
+	if (conn_install_mux_fe(conn, NULL) < 0)
 		goto fail;
 
 	/* the embryonic session's task is not needed anymore */