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.
This commit is contained in:
Christopher Faulet 2018-04-10 15:01:45 +02:00
parent 8ed0a3e32a
commit 7ce0c891ab
3 changed files with 60 additions and 38 deletions

View File

@ -1008,56 +1008,78 @@ static inline void list_mux_proto(FILE *out)
} }
} }
/* returns the first mux in the list matching the exact same token and /* returns the first mux in the list matching the exact same <mux_proto> and
* compatible with the proxy's mode (http or tcp). Mode "health" has to be * compatible with the <proto_side> (FE or BE) and the <proto_mode> (TCP or
* considered as TCP here. Ie passing "px->mode == PR_MODE_HTTP" is fine. Will * HTTP). <mux_proto> can be empty. Will fall back to the first compatible mux
* fall back to the first compatible mux with empty ALPN name. May return null * with exactly the same <proto_mode> or with an empty name. May return
* if the code improperly registered the default mux to use as a fallback. * 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; struct mux_proto_list *item;
const struct mux_ops *fallback = NULL; struct mux_proto_list *fallback = NULL;
http_mode = 1 << !!http_mode;
list_for_each_entry(item, &mux_proto_list.list, list) { 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; continue;
if (isteq(token, item->token)) if (istlen(mux_proto) && isteq(mux_proto, item->token))
return item->mux; return item->mux;
if (!istlen(item->token)) else if (!istlen(item->token)) {
fallback = item->mux; 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 /* installs the best mux for incoming connection <conn> using the upper context
* the proxy. Null cannot be returned unless there's a serious bug somewhere * <ctx>. If the mux protocol is forced, we use it to find the best
* else (no fallback mux registered). * 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;
if (!conn_get_alpn(conn, &alpn_str, &alpn_len))
alpn_len = 0;
return alpn_get_mux(ist2(alpn_str, alpn_len), http_mode);
}
/* 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
* < 0 on error.
*/
static inline int conn_install_best_mux(struct connection *conn, int mode, void *ctx)
{ {
struct bind_conf *bind_conf = objt_listener(conn->target)->bind_conf;
const struct mux_ops *mux_ops; const struct mux_ops *mux_ops;
mux_ops = conn_find_best_mux(conn, mode); if (bind_conf->mux_proto)
if (!mux_ops) mux_ops = bind_conf->mux_proto->mux;
return -1; else {
struct ist mux_proto;
const char *alpn_str = NULL;
int alpn_len = 0;
int mode = (1 << (bind_conf->frontend->mode == PR_MODE_HTTP));
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);
}
/* 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_mux_be(struct connection *conn, void *ctx)
{
struct server *srv = objt_server(conn->target);
const struct mux_ops *mux_ops;
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); return conn_install_mux(conn, mux_ops, ctx);
} }

View File

@ -1189,7 +1189,7 @@ int connect_server(struct stream *s)
else else
return SF_ERR_INTERNAL; /* how did we get there ? */ 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 */ /* 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;

View File

@ -423,7 +423,7 @@ static int conn_complete_session(struct connection *conn)
goto fail; goto fail;
session_count_new(sess); 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; goto fail;
/* the embryonic session's task is not needed anymore */ /* the embryonic session's task is not needed anymore */