Files
seaweedfs/weed/images/gateway/cache.go
T
8c67b75190 image: add an optional public image processing gateway (#11593)
* image: add an optional public image processing gateway

* image: fix representation metadata and processing bounds

* image: restrict passthrough to non-executable media types

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* image: tighten source media-type validation

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* image: write passthrough body on the inner response writer

CodeQL still flagged the passthrough write: the content type was set on
the wrapper while the body reached w.ResponseWriter, so the validated
header could not be associated with the write. Set headers and copy the
body on the same inner writer.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* image: serve processed output on the inner response writer

* image: reject XML source types and unsafe conditional metadata

---------

Co-authored-by: zhaoyuchen <yc.zhao@yinzon.com>
Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-05 19:49:02 +08:00

66 lines
1.6 KiB
Go

package gateway
import (
"container/list"
"sync"
)
type result struct {
data []byte
contentType, etag string
}
type cacheEntry struct {
key string
value *result
size int64
}
// cache is a byte-bounded LRU with an entry limit to bound metadata for small objects.
type cache struct {
mu sync.Mutex
entries map[string]*list.Element
order *list.List
capacity, used int64
}
// newCache creates a disposable in-process cache without creating storage objects.
func newCache(capacity int64) *cache {
return &cache{entries: make(map[string]*list.Element), order: list.New(), capacity: capacity}
}
// get returns immutable results and updates recency.
func (c *cache) get(key string) *result {
c.mu.Lock()
defer c.mu.Unlock()
if e := c.entries[key]; e != nil {
c.order.MoveToFront(e)
return e.Value.(*cacheEntry).value
}
return nil
}
// put accounts for data and index overhead, skipping results larger than capacity.
func (c *cache) put(key string, value *result) {
c.mu.Lock()
defer c.mu.Unlock()
size := int64(len(key) + len(value.data) + len(value.contentType) + len(value.etag) + 128)
if c.capacity == 0 || size > c.capacity {
return
}
if old := c.entries[key]; old != nil {
c.used -= old.Value.(*cacheEntry).size
c.order.Remove(old)
delete(c.entries, key)
}
for c.used+size > c.capacity || len(c.entries) >= 1024 {
old := c.order.Back()
entry := old.Value.(*cacheEntry)
delete(c.entries, entry.key)
c.used -= entry.size
c.order.Remove(old)
}
c.entries[key] = c.order.PushFront(&cacheEntry{key: key, value: value, size: size})
c.used += size
}