mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-05 19:18:07 -06:00
[feature] request blocking by http headers (#2409)
This commit is contained in:
parent
07bd848028
commit
8ebb7775a3
36 changed files with 2561 additions and 81 deletions
22
internal/cache/cache.go
vendored
22
internal/cache/cache.go
vendored
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
32
internal/cache/domain/domain.go
vendored
32
internal/cache/domain/domain.go
vendored
|
|
@ -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>"
|
||||
}
|
||||
|
|
|
|||
105
internal/cache/headerfilter/filter.go
vendored
Normal file
105
internal/cache/headerfilter/filter.go
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue