massive refactor. StaticRoute.go is still a mess. needs a big overhaul still.
This commit is contained in:
parent
0e6bc71322
commit
9ebc43e072
@ -1,19 +1,19 @@
|
|||||||
package servlet
|
package servlet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"vinegar/cacheutil"
|
"vinegar/vinegarUtil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultLruSize = int64(1024 * 1024 * 50)
|
defaultLruSize = int64(1024 * 1024 * 50)
|
||||||
ContentTypeHeaderKey = "Content-Type"
|
ContentTypeHeaderKey = "Content-Type"
|
||||||
ContentEncodingHeaderKey = "Content-Encoding"
|
ContentEncodingHeaderKey = "Content-Encoding"
|
||||||
|
AcceptEncodingHeaderKey = "Accept-Encoding"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
@ -25,32 +25,28 @@ type (
|
|||||||
VinegarServlet struct {
|
VinegarServlet struct {
|
||||||
Port string
|
Port string
|
||||||
Routes []*VinegarRoute
|
Routes []*VinegarRoute
|
||||||
Cache *cacheutil.Lru
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VinegarRoute struct {
|
VinegarRoute struct {
|
||||||
Pattern *regexp.Regexp
|
Pattern *regexp.Regexp
|
||||||
Handler VinegarHandlerFunction
|
Handler VinegarHandlerFunction
|
||||||
|
Cache vinegarUtil.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
VinegarHandlerFunction func(w http.ResponseWriter, req *http.Request)
|
VinegarHandlerFunction func(w http.ResponseWriter, req *http.Request)
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewServlet(port string) *VinegarServlet {
|
func NewServlet(port string) *VinegarServlet {
|
||||||
lru := cacheutil.NewLRU(defaultLruSize)
|
srv := VinegarServlet{Port: port}
|
||||||
srv := VinegarServlet{Port: port, Cache: lru}
|
|
||||||
|
|
||||||
return &srv
|
return &srv
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *VinegarRoute {
|
func NewServletRoute(routePattern string, handleFunc VinegarHandlerFunction) *VinegarRoute {
|
||||||
route := VinegarRoute{}
|
|
||||||
|
|
||||||
pattern := regexp.MustCompile(routePattern)
|
pattern := regexp.MustCompile(routePattern)
|
||||||
|
|
||||||
route.Pattern = pattern
|
route := VinegarRoute{Pattern: pattern, Handler: handleFunc, Cache: vinegarUtil.NewLRU(defaultLruSize)}
|
||||||
route.Handler = handleFunc
|
|
||||||
|
|
||||||
return &route
|
return &route
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +56,6 @@ func (s *VinegarServlet) AddRoute(route *VinegarRoute) {
|
|||||||
|
|
||||||
func (s *VinegarServlet) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (s *VinegarServlet) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
path := req.URL.Path
|
path := req.URL.Path
|
||||||
fmt.Println("Attempting to match request for " + path)
|
|
||||||
for _, route := range s.Routes {
|
for _, route := range s.Routes {
|
||||||
if route.Pattern.MatchString(path) {
|
if route.Pattern.MatchString(path) {
|
||||||
route.Handler(w, req)
|
route.Handler(w, req)
|
||||||
@ -69,7 +64,11 @@ func (s *VinegarServlet) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartServerGood(s *VinegarServlet) {
|
func (s *VinegarServlet) Start() {
|
||||||
|
if len(s.Routes) < 1 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
err := http.ListenAndServe(s.Port, s)
|
err := http.ListenAndServe(s.Port, s)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -105,24 +104,3 @@ func SendError(w http.ResponseWriter, code int, msg string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTemplate(w http.ResponseWriter, pathlike string, data any) {
|
|
||||||
templateHelper := template.New(pathlike)
|
|
||||||
f, err := os.OpenFile(pathlike, os.O_RDONLY, 0777)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
content, err := ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
templ, err := templateHelper.Parse(string(content))
|
|
||||||
err = templ.Execute(w, data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,96 +1,107 @@
|
|||||||
package servlet
|
package servlet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"vinegar/cacheutil"
|
util "vinegar/vinegarUtil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
FileRoute struct {
|
FileRoute struct {
|
||||||
*VinegarRoute
|
*VinegarRoute
|
||||||
|
srv *VinegarServlet
|
||||||
fileRoot string
|
fileRoot string
|
||||||
|
UseCache bool
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewImageRoute(urlPattern string, pathlike string, servlet *VinegarServlet) *FileRoute {
|
func NewImageRoute(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
||||||
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
||||||
rootRoute := NewServletRoute(urlPattern, createUncompressedFileServletFunction(defaultPrune, pathlike, servlet.Cache))
|
imgRoute := FileRoute{srv: servlet, fileRoot: pathlike, UseCache: useCache}
|
||||||
imgRoute := FileRoute{rootRoute, pathlike}
|
rootRoute := NewServletRoute(urlPattern, createUncompressedFileServletFunction(&imgRoute, defaultPrune, pathlike))
|
||||||
|
imgRoute.VinegarRoute = rootRoute //i *kinda* don't like this pattern
|
||||||
return &imgRoute
|
return &imgRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTextRoute(urlPattern string, pathlike string, servlet *VinegarServlet) *FileRoute {
|
func NewTextRoute(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
||||||
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
||||||
rootRoute := NewServletRoute(urlPattern, createCompressibleFileServletFunction(defaultPrune, pathlike, servlet.Cache))
|
fr := FileRoute{srv: servlet, fileRoot: pathlike, UseCache: useCache}
|
||||||
fr := FileRoute{rootRoute, pathlike}
|
textRouteHandler := createCompressibleFileServletFunction(&fr, defaultPrune, pathlike)
|
||||||
|
rootRoute := NewServletRoute(urlPattern, textRouteHandler) //i *still* kinda don't like this pattern
|
||||||
|
fr.VinegarRoute = rootRoute
|
||||||
|
|
||||||
return &fr
|
return &fr
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSingleFileRoute(urlPattern string, pathlike string, servlet *VinegarServlet) *FileRoute {
|
func NewSingleFileRoute(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
||||||
fun := createSingleFileServletFunction(pathlike)
|
|
||||||
route := FileRoute{
|
route := FileRoute{
|
||||||
VinegarRoute: NewServletRoute("^"+urlPattern+"$", fun),
|
srv: servlet,
|
||||||
fileRoot: pathlike,
|
fileRoot: pathlike,
|
||||||
|
UseCache: useCache,
|
||||||
}
|
}
|
||||||
|
singleFileServletHandler := createSingleFileServletFunction(&route)
|
||||||
|
sfCache := util.NewSingleFileCache(pathlike)
|
||||||
|
|
||||||
|
parentRoute := NewServletRoute(urlPattern, singleFileServletHandler)
|
||||||
|
parentRoute.Handler = singleFileServletHandler
|
||||||
|
parentRoute.Cache = sfCache
|
||||||
|
|
||||||
|
route.VinegarRoute = parentRoute
|
||||||
|
|
||||||
return &route
|
return &route
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSingleFileServletFunction(pathlike string) VinegarHandlerFunction {
|
func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction {
|
||||||
cache := cacheutil.NewSingleFileCache(pathlike)
|
|
||||||
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
|
||||||
acceptsGzip := clientAcceptsGzip(req)
|
|
||||||
|
|
||||||
if cacheutil.UseCache {
|
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||||
var content []byte
|
var cache *util.LruEntry
|
||||||
if acceptsGzip {
|
var exists bool
|
||||||
w.Header().Add(ContentEncodingHeaderKey, "gzip")
|
|
||||||
content = *cache.CompressedContent
|
if route.UseCache {
|
||||||
} else {
|
cache, exists = route.Cache.Get("")
|
||||||
content = *cache.Content
|
|
||||||
}
|
|
||||||
w.Header().Add(ContentTypeHeaderKey, cache.Mimetype)
|
|
||||||
_, err := w.Write(content)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
contents, exists := cacheutil.GetDiskContent(pathlike)
|
cache, exists = route.Cache.GetFresh("")
|
||||||
if exists {
|
}
|
||||||
fmt.Printf("returning contents: len of %d ", len(*contents))
|
|
||||||
w.Header().Add(ContentTypeHeaderKey, cache.Mimetype)
|
if !exists {
|
||||||
_, err := w.Write(*contents)
|
SendError(w, 404, "File not found.")
|
||||||
if err != nil {
|
return
|
||||||
panic(err)
|
}
|
||||||
}
|
|
||||||
return
|
var content []byte
|
||||||
} else {
|
if clientAcceptsGzip(req) {
|
||||||
w.WriteHeader(404)
|
content = cache.CompressedContent
|
||||||
w.Write(nil)
|
w.Header().Add(ContentEncodingHeaderKey, "gzip")
|
||||||
fmt.Println("returning nothing")
|
} else {
|
||||||
return
|
content = cache.Content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err := w.Write(content)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fun
|
return fun
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCompressibleFileServletFunction(basePattern string, pathlike string, cache *cacheutil.Lru) VinegarHandlerFunction {
|
func createCompressibleFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction {
|
||||||
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||||
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
||||||
cachedContent, exists := cache.Get(stub)
|
cachedContent, exists := route.Cache.Get(stub)
|
||||||
//i don't like this logic below. we need to streamline this a lot better. it's a twisty jungle right now
|
//i don't like this logic below. we need to streamline this a lot better. it's a twisty jungle right now
|
||||||
|
|
||||||
|
resourcePath := path.Join(pathlike, stub)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
content, fileExists := cacheutil.GetDiskContent(path.Join(pathlike, stub))
|
content, fileExists := util.GetDiskContent(resourcePath)
|
||||||
if fileExists {
|
if fileExists {
|
||||||
if cacheutil.UseCache {
|
if route.UseCache {
|
||||||
cache.Put(stub, content)
|
route.Cache.Put(stub, resourcePath)
|
||||||
cachedContent, _ = cache.Get(stub)
|
cachedContent, _ = route.Cache.Get(stub)
|
||||||
} else {
|
} else {
|
||||||
w.Header().Add(ContentTypeHeaderKey, cacheutil.GuessMimetype(stub))
|
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
|
||||||
w.Write(*content)
|
w.Write(*content)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -116,20 +127,17 @@ func createCompressibleFileServletFunction(basePattern string, pathlike string,
|
|||||||
return fun
|
return fun
|
||||||
}
|
}
|
||||||
|
|
||||||
func createUncompressedFileServletFunction(basePattern string, pathlike string, cache *cacheutil.Lru) VinegarHandlerFunction {
|
func createUncompressedFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction {
|
||||||
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||||
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
||||||
|
resourcePath := path.Join(pathlike, stub)
|
||||||
entry, exists := cache.Get(stub)
|
entry, exists := route.Cache.Get(stub)
|
||||||
if !exists {
|
if !exists {
|
||||||
fileContent, fExists := cacheutil.GetDiskContent(path.Join(pathlike, stub))
|
route.Cache.Put(stub, resourcePath)
|
||||||
if fExists {
|
entry, exists = route.Cache.Get(stub)
|
||||||
cache.Put(stub, fileContent)
|
} else {
|
||||||
entry, exists = cache.Get(stub)
|
SendError(w, 404, "Oops! Something went wrong!")
|
||||||
} else {
|
return
|
||||||
SendError(w, 404, "Oops! Something went wrong!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@ -146,9 +154,6 @@ func createUncompressedFileServletFunction(basePattern string, pathlike string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func clientAcceptsGzip(req *http.Request) bool {
|
func clientAcceptsGzip(req *http.Request) bool {
|
||||||
encodings := req.Header.Get("Accept-Encoding")
|
encodings := req.Header.Get(AcceptEncodingHeaderKey)
|
||||||
if strings.Contains(encodings, "gzip") {
|
return strings.Contains(encodings, "gzip")
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|||||||
9
tmp.go
Normal file
9
tmp.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "vinegar/servlet"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
serv := servlet.NewServlet(":8080")
|
||||||
|
|
||||||
|
serv.Start()
|
||||||
|
}
|
||||||
@ -1,11 +1,8 @@
|
|||||||
package cacheutil
|
package vinegarUtil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"errors"
|
||||||
gzip2 "compress/gzip"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -14,10 +11,6 @@ const (
|
|||||||
DefaultCacheTimeInMinutes = 15
|
DefaultCacheTimeInMinutes = 15
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
UseCache = true
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
lru map[string]*LruEntry
|
lru map[string]*LruEntry
|
||||||
Lru struct {
|
Lru struct {
|
||||||
@ -27,6 +20,7 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
LruEntry struct {
|
LruEntry struct {
|
||||||
|
path string
|
||||||
Content []byte
|
Content []byte
|
||||||
CompressedContent []byte
|
CompressedContent []byte
|
||||||
created time.Time
|
created time.Time
|
||||||
@ -37,11 +31,15 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
SingleFileCache struct {
|
SingleFileCache struct {
|
||||||
Content *[]byte
|
path string
|
||||||
CompressedContent *[]byte
|
entry *LruEntry
|
||||||
timeCreated time.Time
|
}
|
||||||
timeExpires time.Time
|
|
||||||
Mimetype string
|
Cache interface {
|
||||||
|
Get(key string) (*LruEntry, bool)
|
||||||
|
GetFresh(key string) (*LruEntry, bool)
|
||||||
|
Put(key string, pathlike string) error
|
||||||
|
Remove(key string)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -51,59 +49,13 @@ func NewLRU(size int64) *Lru {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewSingleFileCache(pathlike string) *SingleFileCache {
|
func NewSingleFileCache(pathlike string) *SingleFileCache {
|
||||||
bits, exists := GetDiskContent(pathlike)
|
|
||||||
if !exists {
|
|
||||||
panic("Could not load single file for single file path: " + pathlike)
|
|
||||||
}
|
|
||||||
|
|
||||||
compressedBits := GZipBytes(bits)
|
entry := newLRUEntry(pathlike)
|
||||||
|
sfc := SingleFileCache{path: pathlike, entry: entry}
|
||||||
cache := SingleFileCache{
|
return &sfc
|
||||||
Content: bits,
|
|
||||||
CompressedContent: compressedBits,
|
|
||||||
timeCreated: time.Now(),
|
|
||||||
timeExpires: time.Now().Add(DefaultCacheTimeInMinutes),
|
|
||||||
Mimetype: GuessMimetype(pathlike),
|
|
||||||
}
|
|
||||||
return &cache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Lru) Put(key string, content *[]byte) {
|
|
||||||
entry, exists := (*l.entries)[key]
|
|
||||||
|
|
||||||
zippedContent := *GZipBytes(content)
|
|
||||||
size := int64(len(*content)) + int64(len(zippedContent))
|
|
||||||
|
|
||||||
l.ensureVacancy(size)
|
|
||||||
|
|
||||||
if exists {
|
|
||||||
entry.Content = *content
|
|
||||||
entry.CompressedContent = zippedContent
|
|
||||||
entry.created = time.Now()
|
|
||||||
entry.expires = time.Now().Add(DefaultCacheTimeInMinutes * time.Minute)
|
|
||||||
entry.mostRecentAccess = time.Now()
|
|
||||||
entry.totalSize = size
|
|
||||||
} else {
|
|
||||||
mimetype := GuessMimetype(key)
|
|
||||||
entry = &LruEntry{
|
|
||||||
Content: *content,
|
|
||||||
CompressedContent: zippedContent,
|
|
||||||
created: time.Now(),
|
|
||||||
expires: time.Now().Add(DefaultCacheTimeInMinutes * time.Minute),
|
|
||||||
mostRecentAccess: time.Now(),
|
|
||||||
totalSize: size,
|
|
||||||
MimeType: mimetype,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(*l.entries)[key] = entry
|
|
||||||
l.currentSize = l.currentSize + size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lru) Get(key string) (*LruEntry, bool) {
|
func (l *Lru) Get(key string) (*LruEntry, bool) {
|
||||||
if !UseCache {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
entry, exists := (*l.entries)[key]
|
entry, exists := (*l.entries)[key]
|
||||||
if exists && entry.expires.Before(time.Now()) {
|
if exists && entry.expires.Before(time.Now()) {
|
||||||
fmt.Println("Cache miss due to expired content")
|
fmt.Println("Cache miss due to expired content")
|
||||||
@ -113,12 +65,48 @@ func (l *Lru) Get(key string) (*LruEntry, bool) {
|
|||||||
if exists {
|
if exists {
|
||||||
entry.mostRecentAccess = time.Now()
|
entry.mostRecentAccess = time.Now()
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("cacheutil miss for '%s'\n", key)
|
fmt.Printf("vinegarUtil miss for '%s'\n", key)
|
||||||
}
|
}
|
||||||
return entry, exists
|
return entry, exists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Lru) GetFresh(key string) (*LruEntry, bool) {
|
||||||
|
l.Remove(key)
|
||||||
|
return l.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Lru) Put(key string, pathlike string) error {
|
||||||
|
entry, exists := (*l.entries)[key]
|
||||||
|
|
||||||
|
var size int64
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
content, fExists := GetDiskContent(pathlike)
|
||||||
|
if !fExists {
|
||||||
|
return errors.New("attempted to refresh cache with file that no longer exists on disk")
|
||||||
|
}
|
||||||
|
zippedContent := *GZipBytes(content)
|
||||||
|
size = int64(len(*content)) + int64(len(zippedContent))
|
||||||
|
l.ensureVacancy(size)
|
||||||
|
|
||||||
|
entry.Content = *content
|
||||||
|
entry.CompressedContent = zippedContent
|
||||||
|
entry.created = time.Now()
|
||||||
|
entry.expires = time.Now().Add(DefaultCacheTimeInMinutes * time.Minute)
|
||||||
|
entry.mostRecentAccess = time.Now()
|
||||||
|
entry.totalSize = size
|
||||||
|
} else {
|
||||||
|
entry = newLRUEntry(pathlike)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
(*l.entries)[key] = entry
|
||||||
|
|
||||||
|
l.recalcSize()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Lru) Remove(key string) {
|
func (l *Lru) Remove(key string) {
|
||||||
entry, exists := (*l.entries)[key]
|
entry, exists := (*l.entries)[key]
|
||||||
if exists {
|
if exists {
|
||||||
@ -159,19 +147,57 @@ func (l *Lru) getLeastRecentlyUsedKey() string {
|
|||||||
return leastUsedKey
|
return leastUsedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func GZipBytes(uncompressed *[]byte) *[]byte {
|
func (l *Lru) recalcSize() {
|
||||||
buff := bytes.Buffer{}
|
var total int64
|
||||||
gzip := gzip2.NewWriter(&buff)
|
for _, entry := range *l.entries {
|
||||||
_, err := gzip.Write(*uncompressed)
|
total += entry.totalSize
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
err = gzip.Flush()
|
l.currentSize = total
|
||||||
if err != nil {
|
}
|
||||||
panic(err)
|
|
||||||
|
func (sfc *SingleFileCache) Get(key string) (*LruEntry, bool) {
|
||||||
|
return sfc.entry, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sfc *SingleFileCache) GetFresh(key string) (*LruEntry, bool) {
|
||||||
|
entry := newLRUEntry(sfc.entry.path)
|
||||||
|
sfc.entry = entry
|
||||||
|
return entry, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put THIS WILL DELETE ANYTHING YOU HAVE STORED IN THIS CACHE.
|
||||||
|
func (sfc *SingleFileCache) Put(key string, pathlike string) error {
|
||||||
|
//there's a bug in this. we don't return an error from newLRUEntry, so if the file disappears, the server will die
|
||||||
|
entry := newLRUEntry(pathlike)
|
||||||
|
sfc.entry = entry
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sfc *SingleFileCache) Remove(key string) {
|
||||||
|
//i'm actually not sure what to do here. why would you ever remove from a single-file cache?
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLRUEntry(pathlike string) *LruEntry {
|
||||||
|
bits, exists := GetDiskContent(pathlike)
|
||||||
|
if !exists {
|
||||||
|
panic("Could not load single file for single file path: " + pathlike)
|
||||||
}
|
}
|
||||||
compressed := buff.Bytes()
|
|
||||||
return &compressed
|
compressedBits := GZipBytes(bits)
|
||||||
|
|
||||||
|
size := int64(len(*bits)) + int64(len(*compressedBits))
|
||||||
|
|
||||||
|
entry := LruEntry{
|
||||||
|
path: pathlike,
|
||||||
|
Content: *bits,
|
||||||
|
CompressedContent: *compressedBits,
|
||||||
|
created: time.Now(),
|
||||||
|
expires: time.Now().Add(DefaultCacheTimeInMinutes * time.Minute),
|
||||||
|
mostRecentAccess: time.Now(),
|
||||||
|
totalSize: size,
|
||||||
|
MimeType: GuessMimetype(pathlike),
|
||||||
|
}
|
||||||
|
return &entry
|
||||||
}
|
}
|
||||||
|
|
||||||
func GuessMimetype(filePath string) string {
|
func GuessMimetype(filePath string) string {
|
||||||
@ -197,22 +223,3 @@ func GuessMimetype(filePath string) string {
|
|||||||
}
|
}
|
||||||
return "application/octet-stream"
|
return "application/octet-stream"
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDiskContent(filePath string) (*[]byte, bool) {
|
|
||||||
_, ferr := os.Stat(filePath)
|
|
||||||
if os.IsNotExist(ferr) {
|
|
||||||
return nil, false
|
|
||||||
} else {
|
|
||||||
f, err := os.OpenFile(filePath, os.O_RDONLY, 0755)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
content, err := ioutil.ReadAll(f)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return &content, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
65
vinegarUtil/files.go
Normal file
65
vinegarUtil/files.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package vinegarUtil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
gzip2 "compress/gzip"
|
||||||
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RenderTemplate(w http.ResponseWriter, pathlike string, data any) {
|
||||||
|
templateHelper := template.New(pathlike)
|
||||||
|
f, err := os.OpenFile(pathlike, os.O_RDONLY, 0777)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
content, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
templ, err := templateHelper.Parse(string(content))
|
||||||
|
err = templ.Execute(w, data)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDiskContent(filePath string) (*[]byte, bool) {
|
||||||
|
_, ferr := os.Stat(filePath)
|
||||||
|
if os.IsNotExist(ferr) {
|
||||||
|
return nil, false
|
||||||
|
} else {
|
||||||
|
f, err := os.OpenFile(filePath, os.O_RDONLY, 0755)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
content, err := ioutil.ReadAll(f)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &content, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GZipBytes(uncompressed *[]byte) *[]byte {
|
||||||
|
buff := bytes.Buffer{}
|
||||||
|
gzip := gzip2.NewWriter(&buff)
|
||||||
|
_, err := gzip.Write(*uncompressed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = gzip.Flush()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
compressed := buff.Bytes()
|
||||||
|
return &compressed
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user