]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: cfgparse/server: try to fix spelling mistakes on server lines
authorWilly Tarreau <w@1wt.eu>
Fri, 12 Mar 2021 08:58:04 +0000 (09:58 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 12 Mar 2021 13:13:21 +0000 (14:13 +0100)
Let's apply the fuzzy match to server keywords so that we can avoid
dumping the huge list of supported keywords each time there is a spelling
mistake, and suggest proper spelling instead:

  $ printf "listen f\nserver s 0 sendpx-v2\n" | ./haproxy -c -f /dev/stdin
  [NOTICE] 070/095718 (24152) : haproxy version is 2.4-dev11-caa6e3-25
  [NOTICE] 070/095718 (24152) : path to executable is ./haproxy
  [ALERT] 070/095718 (24152) : parsing [/dev/stdin:2] : 'server s' unknown keyword 'sendpx-v2'; did you mean 'send-proxy-v2' maybe ?
  [ALERT] 070/095718 (24152) : Error(s) found in configuration file : /dev/stdin
  [ALERT] 070/095718 (24152) : Fatal errors found in configuration.

src/server.c

index 8eb659d335be84afb650362b3d18734278b5b797..8b2dd4f25781f145acc1ce7483527e7b926c0991 100644 (file)
@@ -50,6 +50,19 @@ static void srv_update_status(struct server *s);
 static int srv_apply_lastaddr(struct server *srv, int *err_code);
 static void srv_cleanup_connections(struct server *srv);
 
+/* some keywords that are still being parsed using strcmp() and are not
+ * registered anywhere. They are used as suggestions for mistyped words.
+ */
+static const char *common_kw_list[] = {
+       "init-addr", "resolvers", "resolve-opts", "resolve-prefer", "ipv4",
+       "ipv6", "resolve-net", "weight", "log-proto", "legacy", "octet-count",
+       "minconn", "maxconn", "maxqueue", "slowstart", "on-error", "fastinter",
+       "fail-check", "sudden-death", "mark-down", "on-marked-down",
+       "shutdown-sessions", "on-marked-up", "shutdown-backup-sessions",
+       "error-limit", "usesrc",
+       NULL /* must be last */
+};
+
 /* List head of all known server keywords */
 static struct srv_kw_list srv_keywords = {
        .list = LIST_HEAD_INIT(srv_keywords.list)
@@ -287,6 +300,50 @@ void srv_dump_kws(char **out)
        }
 }
 
+/* Try to find in srv_keyword the word 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.
+ */
+static const char *srv_find_best_kw(const char *word)
+{
+       uint8_t word_sig[1024];
+       uint8_t list_sig[1024];
+       const struct srv_kw_list *kwl;
+       const char *best_ptr = NULL;
+       int dist, best_dist = INT_MAX;
+       const char **extra;
+       int index;
+
+       make_word_fingerprint(word_sig, word);
+       list_for_each_entry(kwl, &srv_keywords.list, list) {
+               for (index = 0; kwl->kw[index].kw != NULL; index++) {
+                       make_word_fingerprint(list_sig, kwl->kw[index].kw);
+                       dist = word_fingerprint_distance(word_sig, list_sig);
+                       if (dist < best_dist) {
+                               best_dist = dist;
+                               best_ptr = kwl->kw[index].kw;
+                       }
+               }
+       }
+
+       for (extra = common_kw_list; *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;
+}
+
 /* Parse the "backup" server keyword */
 static int srv_parse_backup(char **args, int *cur_arg,
                             struct proxy *curproxy, struct server *newsrv, char **err)
@@ -2394,9 +2451,8 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
                                goto out;
                        }
                        else {
-                               static int srv_dumped;
                                struct srv_kw *kw;
-                               char *err;
+                               const char *best;
 
                                kw = srv_find_kw(args[cur_arg]);
                                if (kw) {
@@ -2439,17 +2495,13 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
                                        continue;
                                }
 
-                               err = NULL;
-                               if (!srv_dumped) {
-                                       srv_dump_kws(&err);
-                                       indent_msg(&err, 4);
-                                       srv_dumped = 1;
-                               }
-
-                               ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.%s%s\n",
-                                     file, linenum, args[0], args[1], args[cur_arg],
-                                     err ? " Registered keywords :" : "", err ? err : "");
-                               free(err);
+                               best = srv_find_best_kw(args[cur_arg]);
+                               if (best)
+                                       ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'; did you mean '%s' maybe ?\n",
+                                                file, linenum, args[0], args[1], args[cur_arg], best);
+                               else
+                                       ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.\n",
+                                                file, linenum, args[0], args[1], args[cur_arg]);
 
                                err_code |= ERR_ALERT | ERR_FATAL;
                                goto out;