summaryrefslogtreecommitdiff
path: root/internal/cache
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2023-12-18 14:18:25 +0000
committerLibravatar GitHub <noreply@github.com>2023-12-18 14:18:25 +0000
commit8ebb7775a35b632d49a8f294d83ac786666631f3 (patch)
tree02ac5475274125170132b0a4d9f69bd67491a32c /internal/cache
parentfix poll total vote double count (#2464) (diff)
downloadgotosocial-8ebb7775a35b632d49a8f294d83ac786666631f3.tar.xz
[feature] request blocking by http headers (#2409)
Diffstat (limited to 'internal/cache')
-rw-r--r--internal/cache/cache.go22
-rw-r--r--internal/cache/domain/domain.go32
-rw-r--r--internal/cache/headerfilter/filter.go105
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
+}