MINOR: cfgparse/proxy: also support spelling fixes on options

Some are not always easy to spot with "chk" vs "check" or hyphens at
some places and not at others. Now entering "option http-close" properly
suggests "httpclose" and "option tcp-chk" suggests "tcp-check". There's
no need to consider the proxy's capabilities, what matters is to figure
what related word the user tried to spell, and there are not that many
options anyway.
This commit is contained in:
Willy Tarreau 2021-03-15 11:11:55 +01:00
parent b12bc646d5
commit 31a3cea84f
3 changed files with 66 additions and 1 deletions

View File

@ -49,6 +49,7 @@ int stream_set_backend(struct stream *s, struct proxy *be);
const char *proxy_cap_str(int cap);
const char *proxy_mode_str(int mode);
const char *proxy_find_best_option(const char *word, const char **extra);
void proxy_store_name(struct proxy *px);
struct proxy *proxy_find_by_id(int id, int cap, int table);
struct proxy *proxy_find_by_name(const char *name, int cap, int table);

View File

@ -54,6 +54,15 @@ static const char *common_kw_list[] = {
NULL /* must be last */
};
static const char *common_options[] = {
"httpclose", "forceclose", "http-server-close", "http-keep-alive",
"http-tunnel", "redispatch", "httplog", "tcplog", "tcpka", "httpchk",
"ssl-hello-chk", "smtpchk", "pgsql-check", "redis-check",
"mysql-check", "ldap-check", "spop-check", "tcp-check",
"external-check", "forwardfor", "original-to",
NULL /* must be last */
};
/* Report a warning if a rule is placed after a 'tcp-request session' rule.
* Return 1 if the warning has been emitted, otherwise 0.
*/
@ -2238,7 +2247,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
} /* end while loop */
}
else {
ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
const char *best = proxy_find_best_option(args[1], common_options);
if (best)
ha_alert("parsing [%s:%d] : unknown option '%s'; did you mean '%s' maybe ?\n", file, linenum, args[1], best);
else
ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}

View File

@ -152,6 +152,55 @@ const char *proxy_mode_str(int mode) {
return "unknown";
}
/* try to find among known options the one that looks closest to <word> by
* counting transitions between letters, digits and other characters. Will
* return the best matching word if found, otherwise NULL. An optional array
* of extra words to compare may be passed in <extra>, but it must then be
* terminated by a NULL entry. If unused it may be NULL.
*/
const char *proxy_find_best_option(const char *word, const char **extra)
{
uint8_t word_sig[1024];
uint8_t list_sig[1024];
const char *best_ptr = NULL;
int dist, best_dist = INT_MAX;
int index;
make_word_fingerprint(word_sig, word);
for (index = 0; cfg_opts[index].name; index++) {
make_word_fingerprint(list_sig, cfg_opts[index].name);
dist = word_fingerprint_distance(word_sig, list_sig);
if (dist < best_dist) {
best_dist = dist;
best_ptr = cfg_opts[index].name;
}
}
for (index = 0; cfg_opts2[index].name; index++) {
make_word_fingerprint(list_sig, cfg_opts2[index].name);
dist = word_fingerprint_distance(word_sig, list_sig);
if (dist < best_dist) {
best_dist = dist;
best_ptr = cfg_opts2[index].name;
}
}
while (extra && *extra) {
make_word_fingerprint(list_sig, *extra);
dist = word_fingerprint_distance(word_sig, list_sig);
if (dist < best_dist) {
best_dist = dist;
best_ptr = *extra;
}
extra++;
}
if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr)))
best_ptr = NULL;
return best_ptr;
}
/*
* This function scans the list of backends and servers to retrieve the first
* backend and the first server with the given names, and sets them in both