110 lines
2.3 KiB
Go
110 lines
2.3 KiB
Go
package handler
|
|
|
|
import (
|
|
"container/list"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
manifestCacheVODTTL = 45 * time.Second
|
|
manifestCacheLiveTTL = 3 * time.Second
|
|
manifestCacheMaxEntries = 256
|
|
manifestCacheMaxBodySize = 2 << 20
|
|
)
|
|
|
|
type manifestCacheEntry struct {
|
|
key string
|
|
body []byte
|
|
headers http.Header
|
|
status int
|
|
expiresAt time.Time
|
|
}
|
|
|
|
type manifestCache struct {
|
|
mu sync.Mutex
|
|
maxEntries int
|
|
entries map[string]*list.Element
|
|
lru *list.List
|
|
}
|
|
|
|
func newManifestCache(maxEntries int) *manifestCache {
|
|
if maxEntries <= 0 {
|
|
maxEntries = manifestCacheMaxEntries
|
|
}
|
|
return &manifestCache{
|
|
maxEntries: maxEntries,
|
|
entries: make(map[string]*list.Element, maxEntries),
|
|
lru: list.New(),
|
|
}
|
|
}
|
|
|
|
func manifestCacheKey(targetURL string, referer string) string {
|
|
return targetURL + "\x00" + referer
|
|
}
|
|
|
|
func (c *manifestCache) get(key string, now time.Time) (manifestCacheEntry, bool) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
el := c.entries[key]
|
|
if el == nil {
|
|
return manifestCacheEntry{}, false
|
|
}
|
|
entry, ok := el.Value.(manifestCacheEntry)
|
|
if !ok || !now.Before(entry.expiresAt) {
|
|
c.remove(el)
|
|
return manifestCacheEntry{}, false
|
|
}
|
|
c.lru.MoveToFront(el)
|
|
entry.body = append([]byte(nil), entry.body...)
|
|
entry.headers = entry.headers.Clone()
|
|
return entry, true
|
|
}
|
|
|
|
func (c *manifestCache) set(key string, status int, headers http.Header, body []byte, now time.Time) bool {
|
|
if status < http.StatusOK || status >= http.StatusMultipleChoices || len(body) > manifestCacheMaxBodySize {
|
|
return false
|
|
}
|
|
|
|
ttl := manifestCacheVODTTL
|
|
bodyText := string(body)
|
|
if strings.Contains(bodyText, "#EXTINF") && !strings.Contains(bodyText, "#EXT-X-ENDLIST") {
|
|
ttl = manifestCacheLiveTTL
|
|
}
|
|
entry := manifestCacheEntry{
|
|
key: key,
|
|
body: append([]byte(nil), body...),
|
|
headers: headers.Clone(),
|
|
status: status,
|
|
expiresAt: now.Add(ttl),
|
|
}
|
|
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if el := c.entries[key]; el != nil {
|
|
el.Value = entry
|
|
c.lru.MoveToFront(el)
|
|
return true
|
|
}
|
|
c.entries[key] = c.lru.PushFront(entry)
|
|
for len(c.entries) > c.maxEntries {
|
|
back := c.lru.Back()
|
|
if back == nil {
|
|
break
|
|
}
|
|
c.remove(back)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (c *manifestCache) remove(el *list.Element) {
|
|
entry, ok := el.Value.(manifestCacheEntry)
|
|
if ok {
|
|
delete(c.entries, entry.key)
|
|
}
|
|
c.lru.Remove(el)
|
|
}
|