urlmatch: include host in urlmatch ranking

In order to be able to rank positive matches by `urlmatch`, we inspect the path length and user part to decide whether a match is better than another match. As all other parts are matched exactly between both URLs, this is the correct thing to do right now. In the future, though, we want to introduce wild cards for the domain part. When doing this, it does not make sense anymore to only compare the path lengths. Instead, we also want to compare the domain lengths to determine which of both URLs matches the host part more closely. Signed-off-by: Patrick Steinhardt <patrick.steinhardt@elego.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jan 31, 2017 at 10:01 UTC af99049ca92f5a5d16d8cce9727b859ac5c9ee00
3 files changed +69 -25
t/t1300-repo-config.sh
+33
@@ -1177,6 +1177,39 @@ test_expect_success 'urlmatch' '
1177 test_cmp expect actual
1178 '
1179
1180 +test_expect_success 'urlmatch favors more specific URLs' '
1181 + cat >.git/config <<-\EOF &&
1182 + [http "https://example.com/"]
1183 + cookieFile = /tmp/root.txt
1184 + [http "https://example.com/subdirectory"]
1185 + cookieFile = /tmp/subdirectory.txt
1186 + [http "https://user@example.com/"]
1187 + cookieFile = /tmp/user.txt
1188 + [http "https://averylonguser@example.com/"]
1189 + cookieFile = /tmp/averylonguser.txt
1190 + EOF
1191 +
1192 + echo http.cookiefile /tmp/root.txt >expect &&
1193 + git config --get-urlmatch HTTP https://example.com >actual &&
1194 + test_cmp expect actual &&
1195 +
1196 + echo http.cookiefile /tmp/subdirectory.txt >expect &&
1197 + git config --get-urlmatch HTTP https://example.com/subdirectory >actual &&
1198 + test_cmp expect actual &&
1199 +
1200 + echo http.cookiefile /tmp/subdirectory.txt >expect &&
1201 + git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
1202 + test_cmp expect actual &&
1203 +
1204 + echo http.cookiefile /tmp/user.txt >expect &&
1205 + git config --get-urlmatch HTTP https://user@example.com/ >actual &&
1206 + test_cmp expect actual &&
1207 +
1208 + echo http.cookiefile /tmp/subdirectory.txt >expect &&
1209 + git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
1210 + test_cmp expect actual
1211 +'
1212 +
1213 # good section hygiene
1214 test_expect_failure 'unsetting the last key in a section removes header' '
1215 cat >.git/config <<-\EOF &&
urlmatch.c
+34 -24
@@ -426,7 +426,7 @@ static size_t url_match_prefix(const char *url,
426
427 static int match_urls(const struct url_info *url,
428 const struct url_info *url_prefix,
429 - int *exactusermatch)
429 + struct urlmatch_item *match)
430 {
431 /*
432 * url_prefix matches url if the scheme, host and port of url_prefix
@@ -445,8 +445,8 @@ static int match_urls(const struct url_info *url,
445 * contained a user name or false if url_prefix did not have a
446 * user name. If there is no match *exactusermatch is left untouched.
447 */
448 - int usermatched = 0;
449 - int pathmatchlen;
448 + char usermatched = 0;
449 + size_t pathmatchlen;
450
451 if (!url || !url_prefix || !url->url || !url_prefix->url)
452 return 0;
@@ -483,22 +483,38 @@ static int match_urls(const struct url_info *url,
483 url->url + url->path_off,
484 url_prefix->url + url_prefix->path_off,
485 url_prefix->url_len - url_prefix->path_off);
486 + if (!pathmatchlen)
487 + return 0; /* paths do not match */
488
487 - if (pathmatchlen && exactusermatch)
488 - *exactusermatch = usermatched;
489 - return pathmatchlen;
489 + if (match) {
490 + match->hostmatch_len = url_prefix->host_len;
491 + match->pathmatch_len = pathmatchlen;
492 + match->user_matched = usermatched;
493 + }
494 +
495 + return 1;
496 +}
497 +
498 +static int cmp_matches(const struct urlmatch_item *a,
499 + const struct urlmatch_item *b)
500 +{
501 + if (a->hostmatch_len != b->hostmatch_len)
502 + return a->hostmatch_len < b->hostmatch_len ? -1 : 1;
503 + if (a->pathmatch_len != b->pathmatch_len)
504 + return a->pathmatch_len < b->pathmatch_len ? -1 : 1;
505 + if (a->user_matched != b->user_matched)
506 + return b->user_matched ? -1 : 1;
507 + return 0;
508 }
509
510 int urlmatch_config_entry(const char *var, const char *value, void *cb)
511 {
512 struct string_list_item *item;
513 struct urlmatch_config *collect = cb;
496 - struct urlmatch_item *matched;
514 + struct urlmatch_item matched = {0};
515 struct url_info *url = &collect->url;
516 const char *key, *dot;
517 struct strbuf synthkey = STRBUF_INIT;
500 - size_t matched_len = 0;
501 - int user_matched = 0;
518 int retval;
519
520 if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
@@ -516,9 +532,9 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
532 free(config_url);
533 if (!norm_url)
534 return 0;
519 - matched_len = match_urls(url, &norm_info, &user_matched);
535 + retval = match_urls(url, &norm_info, &matched);
536 free(norm_url);
521 - if (!matched_len)
537 + if (!retval)
538 return 0;
539 key = dot + 1;
540 }
@@ -528,24 +544,18 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
544
545 item = string_list_insert(&collect->vars, key);
546 if (!item->util) {
531 - matched = xcalloc(1, sizeof(*matched));
532 - item->util = matched;
547 + item->util = xcalloc(1, sizeof(matched));
548 } else {
534 - matched = item->util;
535 - /*
536 - * Is our match shorter? Is our match the same
537 - * length, and without user while the current
538 - * candidate is with user? Then we cannot use it.
539 - */
540 - if (matched_len < matched->matched_len ||
541 - ((matched_len == matched->matched_len) &&
542 - (!user_matched && matched->user_matched)))
549 + if (cmp_matches(&matched, item->util) < 0)
550 + /*
551 + * Our match is worse than the old one,
552 + * we cannot use it.
553 + */
554 return 0;
555 /* Otherwise, replace it with this one. */
556 }
557
547 - matched->matched_len = matched_len;
548 - matched->user_matched = user_matched;
558 + memcpy(item->util, &matched, sizeof(matched));
559 strbuf_addstr(&synthkey, collect->section);
560 strbuf_addch(&synthkey, '.');
561 strbuf_addstr(&synthkey, key);
urlmatch.h
+2 -1
@@ -34,7 +34,8 @@ struct url_info {
34 extern char *url_normalize(const char *, struct url_info *);
35
36 struct urlmatch_item {
37 - size_t matched_len;
37 + size_t hostmatch_len;
38 + size_t pathmatch_len;
39 char user_matched;
40 };
41