From c54635e8a166f0c7f9d50d12f96f981c2a2a15f6 Mon Sep 17 00:00:00 2001 From: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri, 11 Aug 2023 13:55:49 +0300 Subject: [PATCH] Pull request: 6020-rulelist-name Updates #6020. Squashed commit of the following: commit fedb9415fb40d103261ca9b966c3d634692f899d Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Aug 11 12:45:06 2023 +0300 filtering: imp tests commit d85d193ca7808e9089fa8ac3b26652f9c88c44ad Merge: f1c1eddc1 94cf50a53 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Aug 11 11:07:39 2023 +0300 Merge remote-tracking branch 'origin/master' into 6020-rulelist-name commit f1c1eddc113d2659adb666d7849ce0830eaf71f0 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Aug 11 10:59:07 2023 +0300 filtering: imp tests commit 39e9d546dc2438409607ffebe414e9d656275504 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Aug 11 10:02:48 2023 +0300 filtering: imp code commit 230f15ddad95c670e93c58db6d9928c3d0e0b79b Merge: 1940fb397 111005b8d Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Aug 10 15:03:08 2023 +0300 Merge remote-tracking branch 'origin/master' into 6020-rulelist-name commit 1940fb3973344a7d1ab8acfdc9401ed41fe0e666 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Aug 10 15:01:57 2023 +0300 all: docs commit 810f6d17968873ce489b2e24f496d31179675e37 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Aug 10 15:00:59 2023 +0300 filtering: imp code commit f310dd2281dc81cd816701696cf1bb289b4fb708 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Aug 10 12:19:55 2023 +0300 client: flt name commit 9494771c57c464dbe5117315efdb3104b977bac4 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Thu Aug 10 12:18:57 2023 +0300 filtering: flt name --- CHANGELOG.md | 7 +- client/src/components/Filters/Form.js | 1 - internal/filtering/filter.go | 20 +++- internal/filtering/filter_test.go | 129 ++++++++++++++++++-------- 4 files changed, 114 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf360a41..82d58a69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,9 @@ NOTE: Add new changes BELOW THIS COMMENT. ### Added -- The ability to filter DNS HTTPS records including IPv4/v6 hints. ([#6053]). +- While adding or updating blocklists, the title can now be parsed from + `! Title:` definition of the blocklist's source ([#6020]). +- The ability to filter DNS HTTPS records including IPv4/v6 hints ([#6053]). - Two new metrics showing total number of responses from each upstream DNS server and their average processing time in the Web UI ([#1453]). - The ability to set the port for the `pprof` debug API, see configuration @@ -35,7 +37,7 @@ NOTE: Add new changes BELOW THIS COMMENT. - For non-A and non-AAAA requests, which has been filtered, the NODATA response is returned if the blocking mode isn't set to `Null IP`. In previous versions - it returned NXDOMAIN response in such cases. + it returned NXDOMAIN response in such cases. #### Configuration Changes @@ -68,6 +70,7 @@ In this release, the schema version has changed from 24 to 25. [#1453]: https://github.com/AdguardTeam/AdGuardHome/issues/1453 [#5948]: https://github.com/AdguardTeam/AdGuardHome/issues/5948 +[#6020]: https://github.com/AdguardTeam/AdGuardHome/issues/6020 [#6053]: https://github.com/AdguardTeam/AdGuardHome/issues/6053 <!-- diff --git a/client/src/components/Filters/Form.js b/client/src/components/Filters/Form.js index f4e902f5..5619580b 100644 --- a/client/src/components/Filters/Form.js +++ b/client/src/components/Filters/Form.js @@ -134,7 +134,6 @@ const Form = (props) => { component={renderInputField} className="form-control" placeholder={t('enter_name_hint')} - validate={[validateRequiredValue]} normalizeOnBlur={(data) => data.trim()} /> </div> diff --git a/internal/filtering/filter.go b/internal/filtering/filter.go index 88e8a0fc..0d476802 100644 --- a/internal/filtering/filter.go +++ b/internal/filtering/filter.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghrenameio" "github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist" "github.com/AdguardTeam/golibs/errors" @@ -54,6 +53,22 @@ func (filter *FilterYAML) Path(dataDir string) string { return filepath.Join(dataDir, filterDir, strconv.FormatInt(filter.ID, 10)+".txt") } +// ensureName sets provided title or default name for the filter if it doesn't +// have name already. +func (filter *FilterYAML) ensureName(title string) { + if filter.Name != "" { + return + } + + if title != "" { + filter.Name = title + + return + } + + filter.Name = fmt.Sprintf("List %d", filter.ID) +} + const ( // errFilterNotExist is returned from [filterSetProperties] when there are // no lists with the desired URL to update. @@ -527,7 +542,7 @@ func (d *DNSFilter) finalizeUpdate( rulesCount := res.RulesCount log.Info("filtering: updated filter %d: %d bytes, %d rules", id, res.BytesWritten, rulesCount) - flt.Name = aghalg.Coalesce(flt.Name, res.Title) + flt.ensureName(res.Title) flt.checksum = res.Checksum flt.RulesCount = rulesCount @@ -601,6 +616,7 @@ func (d *DNSFilter) load(flt *FilterYAML) (err error) { return fmt.Errorf("parsing filter file: %w", err) } + flt.ensureName(res.Title) flt.RulesCount, flt.checksum, flt.LastUpdated = res.RulesCount, res.Checksum, st.ModTime() return nil diff --git a/internal/filtering/filter_test.go b/internal/filtering/filter_test.go index 53e846fc..e613f1b3 100644 --- a/internal/filtering/filter_test.go +++ b/internal/filtering/filter_test.go @@ -1,7 +1,6 @@ package filtering import ( - "io/fs" "net" "net/http" "net/url" @@ -16,6 +15,9 @@ import ( "github.com/stretchr/testify/require" ) +// testTimeout is the common timeout for tests. +const testTimeout = 5 * time.Second + // serveHTTPLocally starts a new HTTP server, that handles its index with h. It // also gracefully closes the listener when the test under t finishes. func serveHTTPLocally(t *testing.T, h http.Handler) (urlStr string) { @@ -50,7 +52,49 @@ func serveFiltersLocally(t *testing.T, fltContent []byte) (urlStr string) { })) } -func TestFilters(t *testing.T) { +// updateAndAssert loads filter content from its URL and then asserts rules +// count. +func updateAndAssert( + t *testing.T, + dnsFilter *DNSFilter, + f *FilterYAML, + wantUpd require.BoolAssertionFunc, + wantRulesCount int, +) { + t.Helper() + + ok, err := dnsFilter.update(f) + require.NoError(t, err) + wantUpd(t, ok) + + assert.Equal(t, wantRulesCount, f.RulesCount) + + dir, err := os.ReadDir(filepath.Join(dnsFilter.DataDir, filterDir)) + require.NoError(t, err) + require.FileExists(t, f.Path(dnsFilter.DataDir)) + + assert.Len(t, dir, 1) + + err = dnsFilter.load(f) + require.NoError(t, err) +} + +// newDNSFilter returns a new properly initialized DNS filter instance. +func newDNSFilter(t *testing.T) (d *DNSFilter) { + t.Helper() + + dnsFilter, err := New(&Config{ + DataDir: t.TempDir(), + HTTPClient: &http.Client{ + Timeout: testTimeout, + }, + }, nil) + require.NoError(t, err) + + return dnsFilter +} + +func TestDNSFilter_Update(t *testing.T) { const content = `||example.org^$third-party # Inline comment example ||example.com^$third-party @@ -58,49 +102,20 @@ func TestFilters(t *testing.T) { ` fltContent := []byte(content) - addr := serveFiltersLocally(t, fltContent) - - tempDir := t.TempDir() - - filters, err := New(&Config{ - DataDir: tempDir, - HTTPClient: &http.Client{ - Timeout: 5 * time.Second, - }, - }, nil) - require.NoError(t, err) - f := &FilterYAML{ - URL: addr, + URL: addr, + Name: "test-filter", } - updateAndAssert := func(t *testing.T, want require.BoolAssertionFunc, wantRulesCount int) { - var ok bool - ok, err = filters.update(f) - require.NoError(t, err) - want(t, ok) - - assert.Equal(t, wantRulesCount, f.RulesCount) - - var dir []fs.DirEntry - dir, err = os.ReadDir(filepath.Join(tempDir, filterDir)) - require.NoError(t, err) - - assert.Len(t, dir, 1) - - require.FileExists(t, f.Path(tempDir)) - - err = filters.load(f) - require.NoError(t, err) - } + dnsFilter := newDNSFilter(t) t.Run("download", func(t *testing.T) { - updateAndAssert(t, require.True, 3) + updateAndAssert(t, dnsFilter, f, require.True, 3) }) t.Run("refresh_idle", func(t *testing.T) { - updateAndAssert(t, require.False, 3) + updateAndAssert(t, dnsFilter, f, require.False, 3) }) t.Run("refresh_actually", func(t *testing.T) { @@ -110,13 +125,51 @@ func TestFilters(t *testing.T) { f.URL = serveFiltersLocally(t, anotherContent) t.Cleanup(func() { f.URL = oldURL }) - updateAndAssert(t, require.True, 1) + updateAndAssert(t, dnsFilter, f, require.True, 1) }) t.Run("load_unload", func(t *testing.T) { - err = filters.load(f) + err := dnsFilter.load(f) require.NoError(t, err) f.unload() }) } + +func TestFilterYAML_EnsureName(t *testing.T) { + dnsFilter := newDNSFilter(t) + + t.Run("title_custom", func(t *testing.T) { + content := []byte("! Title: src-title\n||example.com^") + + f := &FilterYAML{ + URL: serveFiltersLocally(t, content), + Name: "user-custom", + } + + updateAndAssert(t, dnsFilter, f, require.True, 1) + assert.Equal(t, "user-custom", f.Name) + }) + + t.Run("title_from_src", func(t *testing.T) { + content := []byte("! Title: src-title\n||example.com^") + + f := &FilterYAML{ + URL: serveFiltersLocally(t, content), + } + + updateAndAssert(t, dnsFilter, f, require.True, 1) + assert.Equal(t, "src-title", f.Name) + }) + + t.Run("title_default", func(t *testing.T) { + content := []byte("||example.com^") + + f := &FilterYAML{ + URL: serveFiltersLocally(t, content), + } + + updateAndAssert(t, dnsFilter, f, require.True, 1) + assert.Equal(t, "List 0", f.Name) + }) +}