diff options
author | 2023-12-18 14:18:25 +0000 | |
---|---|---|
committer | 2023-12-18 14:18:25 +0000 | |
commit | 8ebb7775a35b632d49a8f294d83ac786666631f3 (patch) | |
tree | 02ac5475274125170132b0a4d9f69bd67491a32c /internal/cache | |
parent | fix poll total vote double count (#2464) (diff) | |
download | gotosocial-8ebb7775a35b632d49a8f294d83ac786666631f3.tar.xz |
[feature] request blocking by http headers (#2409)
Diffstat (limited to 'internal/cache')
-rw-r--r-- | internal/cache/cache.go | 22 | ||||
-rw-r--r-- | internal/cache/domain/domain.go | 32 | ||||
-rw-r--r-- | internal/cache/headerfilter/filter.go | 105 |
3 files changed, 131 insertions, 28 deletions
diff --git a/internal/cache/cache.go b/internal/cache/cache.go index dabf151ff..73e3ad6f0 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -18,21 +18,26 @@ package cache import ( + "github.com/superseriousbusiness/gotosocial/internal/cache/headerfilter" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" ) type Caches struct { - // GTS provides access to the collection of gtsmodel object caches. - // (used by the database). + // GTS provides access to the collection of + // gtsmodel object caches. (used by the database). GTS GTSCaches - // AP provides access to the collection of ActivityPub object caches. - // (planned to be used by the typeconverter). - AP APCaches + // AllowHeaderFilters provides access to + // the allow []headerfilter.Filter cache. + AllowHeaderFilters headerfilter.Cache - // Visibility provides access to the item visibility cache. - // (used by the visibility filter). + // BlockHeaderFilters provides access to + // the block []headerfilter.Filter cache. + BlockHeaderFilters headerfilter.Cache + + // Visibility provides access to the item visibility + // cache. (used by the visibility filter). Visibility VisibilityCache // prevent pass-by-value. @@ -45,7 +50,6 @@ func (c *Caches) Init() { log.Infof(nil, "init: %p", c) c.GTS.Init() - c.AP.Init() c.Visibility.Init() // Setup cache invalidate hooks. @@ -58,7 +62,6 @@ func (c *Caches) Start() { log.Infof(nil, "start: %p", c) c.GTS.Start() - c.AP.Start() c.Visibility.Start() } @@ -67,7 +70,6 @@ func (c *Caches) Stop() { log.Infof(nil, "stop: %p", c) c.GTS.Stop() - c.AP.Stop() c.Visibility.Stop() } diff --git a/internal/cache/domain/domain.go b/internal/cache/domain/domain.go index 051ec5c1b..1b836ed28 100644 --- a/internal/cache/domain/domain.go +++ b/internal/cache/domain/domain.go @@ -21,7 +21,6 @@ import ( "fmt" "strings" "sync/atomic" - "unsafe" "golang.org/x/exp/slices" ) @@ -37,17 +36,17 @@ import ( // The .Clear() function can be used to invalidate the cache, // e.g. when an entry is added / deleted from the database. type Cache struct { - // atomically updated ptr value to the // current domain cache radix trie. - rootptr unsafe.Pointer + rootptr atomic.Pointer[root] } // Matches checks whether domain matches an entry in the cache. // If the cache is not currently loaded, then the provided load // function is used to hydrate it. func (c *Cache) Matches(domain string, load func() ([]string, error)) (bool, error) { - // Load the current root pointer value. - ptr := atomic.LoadPointer(&c.rootptr) + // Load the current + // root pointer value. + ptr := c.rootptr.Load() if ptr == nil { // Cache is not hydrated. @@ -60,35 +59,32 @@ func (c *Cache) Matches(domain string, load func() ([]string, error)) (bool, err // Allocate new radix trie // node to store matches. - root := new(root) + ptr = new(root) // Add each domain to the trie. for _, domain := range domains { - root.Add(domain) + ptr.Add(domain) } // Sort the trie. - root.Sort() + ptr.Sort() - // Store the new node ptr. - ptr = unsafe.Pointer(root) - atomic.StorePointer(&c.rootptr, ptr) + // Store new node ptr. + c.rootptr.Store(ptr) } - // Look for a match in the trie node. - return (*root)(ptr).Match(domain), nil + // Look for match in trie node. + return ptr.Match(domain), nil } // Clear will drop the currently loaded domain list, // triggering a reload on next call to .Matches(). -func (c *Cache) Clear() { - atomic.StorePointer(&c.rootptr, nil) -} +func (c *Cache) Clear() { c.rootptr.Store(nil) } // String returns a string representation of stored domains in cache. func (c *Cache) String() string { - if ptr := atomic.LoadPointer(&c.rootptr); ptr != nil { - return (*root)(ptr).String() + if ptr := c.rootptr.Load(); ptr != nil { + return ptr.String() } return "<empty>" } diff --git a/internal/cache/headerfilter/filter.go b/internal/cache/headerfilter/filter.go new file mode 100644 index 000000000..96b6e757f --- /dev/null +++ b/internal/cache/headerfilter/filter.go @@ -0,0 +1,105 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package headerfilter + +import ( + "fmt" + "net/http" + "sync/atomic" + + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/internal/headerfilter" +) + +// Cache provides a means of caching headerfilter.Filters in +// memory to reduce load on an underlying storage mechanism. +type Cache struct { + // current cached header filters slice. + ptr atomic.Pointer[headerfilter.Filters] +} + +// RegularMatch performs .RegularMatch() on cached headerfilter.Filters, loading using callback if necessary. +func (c *Cache) RegularMatch(h http.Header, load func() ([]*gtsmodel.HeaderFilter, error)) (string, string, error) { + // Load ptr value. + ptr := c.ptr.Load() + + if ptr == nil { + // Cache is not hydrated. + // Load filters from callback. + filters, err := loadFilters(load) + if err != nil { + return "", "", err + } + + // Store the new + // header filters. + ptr = &filters + c.ptr.Store(ptr) + } + + // Deref and perform match. + return ptr.RegularMatch(h) +} + +// InverseMatch performs .InverseMatch() on cached headerfilter.Filters, loading using callback if necessary. +func (c *Cache) InverseMatch(h http.Header, load func() ([]*gtsmodel.HeaderFilter, error)) (string, string, error) { + // Load ptr value. + ptr := c.ptr.Load() + + if ptr == nil { + // Cache is not hydrated. + // Load filters from callback. + filters, err := loadFilters(load) + if err != nil { + return "", "", err + } + + // Store the new + // header filters. + ptr = &filters + c.ptr.Store(ptr) + } + + // Deref and perform match. + return ptr.InverseMatch(h) +} + +// Clear will drop the currently loaded filters, +// triggering a reload on next call to ._Match(). +func (c *Cache) Clear() { c.ptr.Store(nil) } + +// loadFilters will load filters from given load callback, creating and parsing raw filters. +func loadFilters(load func() ([]*gtsmodel.HeaderFilter, error)) (headerfilter.Filters, error) { + // Load filters from callback. + hdrFilters, err := load() + if err != nil { + return nil, fmt.Errorf("error reloading cache: %w", err) + } + + // Allocate new header filter slice to store expressions. + filters := make(headerfilter.Filters, 0, len(hdrFilters)) + + // Add all raw expression to filter slice. + for _, filter := range hdrFilters { + if err := filters.Append(filter.Header, filter.Regex); err != nil { + return nil, fmt.Errorf("error appending exprs: %w", err) + } + } + + return filters, nil +} |