removed basically all the panics

This commit is contained in:
dtookey 2023-07-31 16:57:21 -04:00
parent e4d6348fc3
commit 465266c36d
8 changed files with 217 additions and 145 deletions

View File

@ -58,40 +58,40 @@ func CreateBlankConfig() *Config {
return &conf return &conf
} }
func LoadConfig(pathlike string) *VinegarHttpServlet { func LoadConfig(pathlike string) (*VinegarHttpServlet, error) {
contents, exists := vinegarUtil.GetDiskContent(pathlike) contents, err := vinegarUtil.GetDiskContent(pathlike)
if exists {
conf := Config{}
err := json.Unmarshal(*contents, &conf)
if err != nil { if err != nil {
panic(err) CreateBlankConfig()
return nil return nil, err
}
conf := Config{}
err = json.Unmarshal(*contents, &conf)
if err != nil {
return nil, err
} }
servlet := NewServlet(conf.ListeningAddress) servlet := NewServlet(conf.ListeningAddress)
for _, v := range conf.Routes { for _, v := range conf.Routes {
constructor, err := getConstructorFunction(v.ConfigType) constructor, err := getConstructorFunction(v.ConfigType)
if err != nil { if err != nil {
panic(err)
return nil return nil, err
} }
//we don't have to invoke servlet.AddRoute because the static routes already do that for us //we don't have to invoke servlet.AddRoute because the static routes already do that for us
constructor(servlet, v.UrlPattern, v.FileLocation, v.UseBuiltinCache) constructor(servlet, v.UrlPattern, v.FileLocation, v.UseBuiltinCache)
} }
return servlet return servlet, nil
} else {
CreateBlankConfig()
panic("Could not find config file at" + pathlike)
return nil
}
} }
func (e ConfigEntry) toRoute(serv *VinegarHttpServlet) { func (e ConfigEntry) toRoute(serv *VinegarHttpServlet) error {
constructor, err := getConstructorFunction(e.ConfigType) constructor, err := getConstructorFunction(e.ConfigType)
if err != nil { if err != nil {
panic(err) return err
} }
constructor(serv, e.UrlPattern, e.FileLocation, e.UseBuiltinCache) constructor(serv, e.UrlPattern, e.FileLocation, e.UseBuiltinCache)
return nil
} }
func getConstructorFunction(t ConfigType) (RouteConstructor, error) { func getConstructorFunction(t ConfigType) (RouteConstructor, error) {
@ -106,13 +106,14 @@ func getConstructorFunction(t ConfigType) (RouteConstructor, error) {
return nil, errors.New(fmt.Sprintf("could not determine constructor for invalid ConfigType: %s", t)) return nil, errors.New(fmt.Sprintf("could not determine constructor for invalid ConfigType: %s", t))
} }
func GenerateBlankConfig() { func GenerateBlankConfig() error {
fileName := "servlet.json.template.tmpl" fileName := "servlet.json.template.tmpl"
conf := CreateBlankConfig() conf := CreateBlankConfig()
content, err := json.Marshal(&conf) content, err := json.Marshal(&conf)
if err != nil { if err != nil {
panic(err) return err
} }
fmt.Println("Generating a blank configuration file at ") fmt.Println("Generating a blank configuration file at ")
ioutil.WriteFile(fileName, content, 0755) ioutil.WriteFile(fileName, content, 0755)
return nil
} }

View File

@ -2,6 +2,7 @@ package servlet
import ( import (
"fmt" "fmt"
"log"
"net/http" "net/http"
) )
@ -60,7 +61,7 @@ func (api *ApiRoute) RegisterHttpMethodHandler(method HttpMethod, handler Vinega
func getHttpMethod(req *http.Request) HttpMethod { func getHttpMethod(req *http.Request) HttpMethod {
switch req.Method { switch req.Method {
case "GET": case http.MethodGet:
return GET return GET
case "POST": case "POST":
return POST return POST
@ -79,10 +80,10 @@ func getHttpMethod(req *http.Request) HttpMethod {
func SendApiError(w http.ResponseWriter, httpCode int, messageCode int, message string) { func SendApiError(w http.ResponseWriter, httpCode int, messageCode int, message string) {
respMessage := fmt.Sprintf("{\"code\":%d, \"message\":\"%s\"}", messageCode, message) respMessage := fmt.Sprintf("{\"code\":%d, \"message\":\"%s\"}", messageCode, message)
w.WriteHeader(httpCode) log.Printf("[ERROR]\t%s\n", respMessage)
_, err := w.Write([]byte(respMessage)) _, err := w.Write([]byte(respMessage))
if err != nil { if err != nil {
panic(err) log.Println(err)
} }
w.WriteHeader(httpCode)
} }

View File

@ -7,7 +7,6 @@ import (
"geniuscartel.xyz/vinegar/vinegarUtil" "geniuscartel.xyz/vinegar/vinegarUtil"
"log" "log"
"net/http" "net/http"
"os"
"regexp" "regexp"
"strconv" "strconv"
) )
@ -83,10 +82,9 @@ func (s *VinegarHttpServlet) ServeHTTP(w http.ResponseWriter, req *http.Request)
s.SendError(w, req, 404, "Couldn't find your content.", errors.New("failed to match route for ["+path+"]")) s.SendError(w, req, 404, "Couldn't find your content.", errors.New("failed to match route for ["+path+"]"))
} }
func (s *VinegarHttpServlet) Start() { func (s *VinegarHttpServlet) Start() error {
if len(s.Routes) < 1 { if len(s.Routes) < 1 {
log.Fatal("No routes found for server. Nothing to listen and serve.") log.Fatal("No routes found for server. Nothing to listen and serve.")
os.Exit(1)
} }
log.Printf("Listening on [%s]\n", s.Port) log.Printf("Listening on [%s]\n", s.Port)
@ -94,9 +92,9 @@ func (s *VinegarHttpServlet) Start() {
err := http.ListenAndServe(s.Port, s) err := http.ListenAndServe(s.Port, s)
if err != nil { if err != nil {
panic(err) return err
} }
return nil
} }
func (r *VinegarWebRoute) Announce() { func (r *VinegarWebRoute) Announce() {
@ -105,28 +103,36 @@ func (r *VinegarWebRoute) Announce() {
func (s *VinegarHttpServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) { func (s *VinegarHttpServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) {
fmt.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg) fmt.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg)
fmt.Println(aErr) out, _ := json.Marshal(aErr)
fmt.Println(string(out))
tmpl, exists := s.ErrorRoutes[code] tmpl, exists := s.ErrorRoutes[code]
if exists { if exists {
tmpl.TemplateManager.AddMixin("code", strconv.Itoa(code)) err := tmpl.TemplateManager.AddMixin("code", strconv.Itoa(code))
tmpl.TemplateManager.AddMixin("msg", msg)
w.WriteHeader(code)
_, err := w.Write([]byte(tmpl.TemplateManager.RenderTemplate(fmt.Sprintf("%d.html", code))))
if err != nil { if err != nil {
panic(err) writeGeneric(w, code, msg)
return
}
err = tmpl.TemplateManager.AddMixin("msg", msg)
if err != nil {
writeGeneric(w, code, msg)
return
}
w.WriteHeader(code)
bitties, err := tmpl.TemplateManager.RenderTemplate(fmt.Sprintf("%d.html", code))
_, err = w.Write([]byte(bitties))
if err != nil {
writeGeneric(w, code, msg)
return
} }
return return
} else { }
writeGeneric(w, code, msg)
return
}
func writeGeneric(w http.ResponseWriter, code int, msg string) {
w.WriteHeader(code) w.WriteHeader(code)
errorPayload := ErrorResponse{Code: code, Message: msg} errorPayload := ErrorResponse{Code: code, Message: msg}
genericError, sErr := json.Marshal(errorPayload) genericError, _ := json.Marshal(errorPayload)
if sErr != nil { w.Write(genericError)
panic(sErr)
}
_, err := w.Write(genericError)
if err != nil {
panic(err)
}
return
}
} }

24
servlet/server_test.go Normal file
View File

@ -0,0 +1,24 @@
package servlet
import (
"testing"
)
func TestNewServlet(t *testing.T) {
port := ":8080"
srv := NewServlet(port)
if srv.Port != port {
t.Errorf("Expected port %s, got %s", port, srv.Port)
}
if srv.Routes != nil {
t.Error("Expected Routes to be nil")
}
if srv.ErrorRoutes == nil {
t.Error("Expected ErrorRoutes to be initialized")
}
}

View File

@ -3,6 +3,7 @@ package servlet
import ( import (
"errors" "errors"
util "geniuscartel.xyz/vinegar/vinegarUtil" util "geniuscartel.xyz/vinegar/vinegarUtil"
"log"
"net/http" "net/http"
"path" "path"
"path/filepath" "path/filepath"
@ -45,7 +46,7 @@ type (
// //
// This function signature allows encapsulating the creation of different types of FileRoutes. It is used to define // This function signature allows encapsulating the creation of different types of FileRoutes. It is used to define
// constructor functions for each file type, like NewTextRoute or NewImageRoute. // constructor functions for each file type, like NewTextRoute or NewImageRoute.
RouteConstructor func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute RouteConstructor func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error)
) )
// NewTextRoute creates a new FileRoute for serving text files. // NewTextRoute creates a new FileRoute for serving text files.
@ -66,10 +67,10 @@ type (
// //
// A FileRoute instance configured for serving text files, added to // A FileRoute instance configured for serving text files, added to
// the provided VinegarHttpServlet. // the provided VinegarHttpServlet.
var NewTextRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute { var NewTextRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error) {
fileRoot := filepath.Clean(pathlike) fileRoot := filepath.Clean(pathlike)
if strings.Contains(fileRoot, "../") { if strings.Contains(fileRoot, "../") {
panic("Traversing the directory is not allowed, use an absolute filepath instead") return nil, errors.New("Traversing the directory is not allowed, use an absolute filepath instead")
} }
defaultPrune := strings.Replace(urlPattern, ".*", "", -1) defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache} route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache}
@ -79,13 +80,13 @@ var NewTextRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern
servlet.AddRoute(route.VinegarRoute) servlet.AddRoute(route.VinegarRoute)
return &route return &route, nil
} }
var NewImageRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute { var NewImageRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error) {
fileRoot := filepath.Clean(pathlike) fileRoot := filepath.Clean(pathlike)
if strings.Contains(fileRoot, "../") { if strings.Contains(fileRoot, "../") {
panic("Traversing the directory is not allowed, use an absolute filepath instead") return nil, errors.New("Traversing the directory is not allowed, use an absolute filepath instead")
} }
defaultPrune := strings.Replace(urlPattern, ".*", "", -1) defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache} route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache}
@ -93,17 +94,20 @@ var NewImageRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPatter
route.VinegarRoute = rootRoute //i *kinda* don't like this pattern route.VinegarRoute = rootRoute //i *kinda* don't like this pattern
servlet.AddRoute(route.VinegarRoute) servlet.AddRoute(route.VinegarRoute)
return &route return &route, nil
} }
var NewSingleFileRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute { var NewSingleFileRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error) {
route := FileRoute{ route := FileRoute{
srv: servlet, srv: servlet,
fileRoot: pathlike, fileRoot: pathlike,
UseCache: useCache, UseCache: useCache,
} }
singleFileServletHandler := createSingleFileServletFunction(&route) singleFileServletHandler := createSingleFileServletFunction(&route)
sfCache := util.NewSingleFileCache(pathlike) sfCache, err := util.NewSingleFileCache(pathlike)
if err != nil {
return nil, err
}
parentRoute := NewServletRoute(urlPattern, singleFileServletHandler) parentRoute := NewServletRoute(urlPattern, singleFileServletHandler)
parentRoute.Handler = singleFileServletHandler parentRoute.Handler = singleFileServletHandler
@ -113,7 +117,7 @@ var NewSingleFileRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlP
servlet.AddRoute(route.VinegarRoute) servlet.AddRoute(route.VinegarRoute)
return &route return &route, nil
} }
// createSingleFileServletFunction creates a handler function for serving a single file. // createSingleFileServletFunction creates a handler function for serving a single file.
@ -167,7 +171,7 @@ func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction {
w.Header().Add(ContentTypeHeaderKey, cache.MimeType) w.Header().Add(ContentTypeHeaderKey, cache.MimeType)
_, err := w.Write(content) _, err := w.Write(content)
if err != nil { if err != nil {
panic(err) log.Println(err)
} }
} }
@ -189,20 +193,23 @@ func createCompressibleFileServletFunction(route *FileRoute, basePattern string,
resourcePath := path.Join(pathRoot, filePath) resourcePath := path.Join(pathRoot, filePath)
if !exists { if !exists {
content, fileExists := util.GetDiskContent(resourcePath) content, err := util.GetDiskContent(resourcePath)
if fileExists { if err != nil {
route.srv.SendError(w, req, 404, "Couldn't find your content.", errors.New("could not find valid file at ["+resourcePath+"]"))
return
}
if route.UseCache { if route.UseCache {
route.VinegarRoute.Cache.Put(stub, resourcePath) err := route.VinegarRoute.Cache.Put(stub, resourcePath)
if err != nil {
route.srv.SendError(w, req, 500, "Internal Server Error", err)
}
cachedContent, _ = route.VinegarRoute.Cache.Get(stub) cachedContent, _ = route.VinegarRoute.Cache.Get(stub)
} else { } else {
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub)) w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
w.Write(*content) w.Write(*content)
return return
} }
} else {
route.srv.SendError(w, req, 404, "Couldn't find your content.", errors.New("could not find valid file at ["+resourcePath+"]"))
return
}
} }
w.Header().Add(ContentTypeHeaderKey, cachedContent.MimeType) w.Header().Add(ContentTypeHeaderKey, cachedContent.MimeType)
var err error = nil var err error = nil
@ -214,7 +221,7 @@ func createCompressibleFileServletFunction(route *FileRoute, basePattern string,
_, err = w.Write(cachedContent.Content) _, err = w.Write(cachedContent.Content)
} }
if err != nil { if err != nil {
panic(err) log.Println(err)
} }
} }
@ -241,7 +248,7 @@ func createUncompressedFileServletFunction(route *FileRoute, basePattern string,
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub)) w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
_, err := w.Write(entry.Content) _, err := w.Write(entry.Content)
if err != nil { if err != nil {
panic(err) log.Println(err)
} }
return return

View File

@ -31,17 +31,17 @@ func NewTemplateManager(templatePath string, componentPath string) *TemplateMana
return &tm return &tm
} }
func (tm *TemplateManager) loadAssets() { func (tm *TemplateManager) loadAssets() error {
templateFiles, err := os.ReadDir(tm.templatePath) templateFiles, err := os.ReadDir(tm.templatePath)
if err != nil { if err != nil {
panic(err) return err
} }
for _, templateFile := range templateFiles { for _, templateFile := range templateFiles {
if !templateFile.IsDir() { if !templateFile.IsDir() {
filePath := path.Join(tm.templatePath, templateFile.Name()) filePath := path.Join(tm.templatePath, templateFile.Name())
dataContent, exists := util.GetDiskContent(filePath) dataContent, err := util.GetDiskContent(filePath)
if !exists { if err != nil {
panic("Could not read file contents for " + filePath) return err
} }
tmpl := template.New(templateFile.Name()) tmpl := template.New(templateFile.Name())
tmpl.Parse(string(*dataContent)) tmpl.Parse(string(*dataContent))
@ -52,44 +52,43 @@ func (tm *TemplateManager) loadAssets() {
componentFiles, err := os.ReadDir(tm.componentPath) componentFiles, err := os.ReadDir(tm.componentPath)
if err != nil { if err != nil {
panic(err) return err
} }
for _, componentFile := range componentFiles { for _, componentFile := range componentFiles {
if !componentFile.IsDir() { if !componentFile.IsDir() {
filePath := path.Join(tm.componentPath, componentFile.Name()) filePath := path.Join(tm.componentPath, componentFile.Name())
contentBytes, exists := util.GetDiskContent(filePath) contentBytes, err := util.GetDiskContent(filePath)
if !exists { if err != nil {
panic("could not find file for " + filePath) return err
return
} }
content := string(*contentBytes) content := string(*contentBytes)
tm.components[componentFile.Name()] = template.HTML(content) tm.components[componentFile.Name()] = template.HTML(content)
} }
} }
return nil
} }
func (tm *TemplateManager) RenderTemplate(key string) string { func (tm *TemplateManager) RenderTemplate(key string) (string, error) {
tmpl, exists := tm.templates[key] tmpl, exists := tm.templates[key]
if !exists { if !exists {
panic("Could not load template for key: " + key) return "", errors.New("Could not load template for key: " + key)
} }
buff := strings.Builder{} buff := strings.Builder{}
err := tmpl.Execute(&buff, tm) err := tmpl.Execute(&buff, tm)
if err != nil { if err != nil {
panic(err) return "", err
} }
return buff.String() return buff.String(), nil
} }
func (tm *TemplateManager) GetComponent(key string) template.HTML { func (tm *TemplateManager) GetComponent(key string) (template.HTML, error) {
content, exists := tm.components[key] content, exists := tm.components[key]
if !exists { if !exists {
panic("Could not load data for key: " + key) return "", errors.New("Could not load data for key: " + key)
return ""
} }
return content return content, nil
} }
func (tm *TemplateManager) GetMixin(key string) template.HTML { func (tm *TemplateManager) GetMixin(key string) template.HTML {
@ -107,50 +106,54 @@ func (tm *TemplateManager) AddMixin(key string, value string) error {
} }
func (tm *TemplateManager) AddMixinFromFile(key string, pathlike string) error { func (tm *TemplateManager) AddMixinFromFile(key string, pathlike string) error {
content, exists := util.GetDiskContent(pathlike) content, err := util.GetDiskContent(pathlike)
if exists { if err != nil {
tm.AddMixin(key, string(*content)) return err
return nil
} else { } else {
return errors.New("mixin source file not found at " + pathlike) err2 := tm.AddMixin(key, string(*content))
if err2 != nil {
return err2
}
return nil
} }
} }
func (tm *TemplateManager) RenderAllToFile(pathlike string) { func (tm *TemplateManager) RenderAllToFile(pathlike string) error {
for _, v := range tm.Templates { for _, v := range tm.Templates {
path := path.Join(pathlike, v) p := path.Join(pathlike, v)
tmpl := tm.templates[v] tmpl := tm.templates[v]
buff := strings.Builder{} buff := strings.Builder{}
err := tmpl.Execute(&buff, tm) err := tmpl.Execute(&buff, tm)
if err != nil { if err != nil {
panic(err) return err
} }
err = ioutil.WriteFile(path, []byte(buff.String()), 0755) err = ioutil.WriteFile(p, []byte(buff.String()), 0755)
if err != nil { if err != nil {
panic(err) return err
} }
} }
return nil
} }
func RenderTemplate(w http.ResponseWriter, pathlike string, data any) { func RenderTemplate(w http.ResponseWriter, pathlike string, data any) error {
templateHelper := template.New(pathlike) templateHelper := template.New(pathlike)
f, err := os.OpenFile(pathlike, os.O_RDONLY, 0777) f, err := os.OpenFile(pathlike, os.O_RDONLY, 0777)
if err != nil { if err != nil {
panic(err) return err
} }
defer f.Close() defer f.Close()
content, err := ioutil.ReadAll(f) content, err := ioutil.ReadAll(f)
if err != nil { if err != nil {
panic(err) return err
} }
templ, err := templateHelper.Parse(string(content)) templ, err := templateHelper.Parse(string(content))
err = templ.Execute(w, data) err = templ.Execute(w, data)
if err != nil { if err != nil {
panic(err) return err
} }
return nil
} }

View File

@ -7,36 +7,36 @@ import (
"os" "os"
) )
func GetDiskContent(filePath string) (*[]byte, bool) { func GetDiskContent(filePath string) (*[]byte, error) {
_, ferr := os.Stat(filePath) _, ferr := os.Stat(filePath)
if os.IsNotExist(ferr) { if os.IsNotExist(ferr) {
return nil, false return nil, ferr
} else { } else {
f, err := os.OpenFile(filePath, os.O_RDONLY, 0755) f, err := os.OpenFile(filePath, os.O_RDONLY, 0755)
if err != nil { if err != nil {
panic(err) return nil, err
} }
defer f.Close() defer f.Close()
content, err := ioutil.ReadAll(f) content, err := ioutil.ReadAll(f)
if err != nil { if err != nil {
panic(err) return nil, err
} }
return &content, true return &content, nil
} }
} }
func GZipBytes(uncompressed *[]byte) *[]byte { func GZipBytes(uncompressed *[]byte) (*[]byte, error) {
buff := bytes.Buffer{} buff := bytes.Buffer{}
gzip := gzip2.NewWriter(&buff) gzip := gzip2.NewWriter(&buff)
_, err := gzip.Write(*uncompressed) _, err := gzip.Write(*uncompressed)
if err != nil { if err != nil {
panic(err) return nil, err
} }
err = gzip.Flush() err = gzip.Flush()
if err != nil { if err != nil {
panic(err) return nil, err
} }
compressed := buff.Bytes() compressed := buff.Bytes()
return &compressed return &compressed, nil
} }

View File

@ -1,7 +1,6 @@
package vinegarUtil package vinegarUtil
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"strings" "strings"
@ -50,17 +49,23 @@ func NewLRU(size int64) *Lru {
return &Lru{&cLru, size, 0} return &Lru{&cLru, size, 0}
} }
func NewSingleFileCache(pathlike string) *SingleFileCache { func NewSingleFileCache(pathlike string) (*SingleFileCache, error) {
entry := newLRUEntry(pathlike) entry, err := newLRUEntry(pathlike)
if err != nil {
return nil, err
}
sfc := SingleFileCache{path: pathlike, entry: entry} sfc := SingleFileCache{path: pathlike, entry: entry}
return &sfc return &sfc, nil
} }
func (l *Lru) Get(key string) (*LruEntry, bool) { func (l *Lru) Get(key string) (*LruEntry, bool) {
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")
entry.Reload() err := entry.Reload()
if err != nil {
return nil, false
}
return entry, true return entry, true
} else { } else {
if exists { if exists {
@ -73,7 +78,10 @@ func (l *Lru) Get(key string) (*LruEntry, bool) {
func (l *Lru) GetFresh(key string) (*LruEntry, bool) { func (l *Lru) GetFresh(key string) (*LruEntry, bool) {
entry, exists := l.Get(key) entry, exists := l.Get(key)
if exists { if exists {
entry.Reload() err := entry.Reload()
if err != nil {
return nil, false
}
} }
return l.Get(key) return l.Get(key)
} }
@ -84,11 +92,13 @@ func (l *Lru) Put(key string, pathlike string) error {
var size int64 var size int64
if exists { if exists {
content, fExists := GetDiskContent(pathlike) content, err := GetDiskContent(pathlike)
if !fExists { if err != nil {
return errors.New("attempted to refresh cache with file that no longer exists on disk") return err
} }
zippedContent := *GZipBytes(content) zippedBytes, err := GZipBytes(content)
zippedContent := *zippedBytes
size = int64(len(*content)) + int64(len(zippedContent)) size = int64(len(*content)) + int64(len(zippedContent))
l.ensureVacancy(size) l.ensureVacancy(size)
@ -99,8 +109,11 @@ func (l *Lru) Put(key string, pathlike string) error {
entry.mostRecentAccess = time.Now() entry.mostRecentAccess = time.Now()
entry.totalSize = size entry.totalSize = size
} else { } else {
entry = newLRUEntry(pathlike) nEntry, err := newLRUEntry(pathlike)
if err != nil {
return err
}
entry = nEntry
} }
(*l.entries)[key] = entry (*l.entries)[key] = entry
@ -143,9 +156,7 @@ func (l *Lru) getLeastRecentlyUsedKey() string {
benchmark = entry benchmark = entry
} }
} }
if leastUsedKey == "" {
panic("Invalid key for LRU: [" + leastUsedKey + "]")
}
return leastUsedKey return leastUsedKey
} }
@ -157,13 +168,23 @@ func (l *Lru) recalcSize() {
l.currentSize = total l.currentSize = total
} }
func (le *LruEntry) Reload() { func (le *LruEntry) Reload() error {
content, _ := GetDiskContent(le.path)
content, err := GetDiskContent(le.path)
if err != nil {
return err
}
zippedBytes, err := GZipBytes(content)
if err != nil {
return err
}
le.Content = *content le.Content = *content
le.CompressedContent = *GZipBytes(content) le.CompressedContent = *zippedBytes
le.created = time.Now() le.created = time.Now()
le.mostRecentAccess = le.created le.mostRecentAccess = le.created
le.expires = le.created.Add(time.Minute * DefaultCacheTimeInMinutes) le.expires = le.created.Add(time.Minute * DefaultCacheTimeInMinutes)
return nil
} }
func (sfc *SingleFileCache) Get(key string) (*LruEntry, bool) { func (sfc *SingleFileCache) Get(key string) (*LruEntry, bool) {
@ -171,14 +192,20 @@ func (sfc *SingleFileCache) Get(key string) (*LruEntry, bool) {
} }
func (sfc *SingleFileCache) GetFresh(key string) (*LruEntry, bool) { func (sfc *SingleFileCache) GetFresh(key string) (*LruEntry, bool) {
sfc.entry.Reload() err := sfc.entry.Reload()
if err != nil {
return nil, false
}
return sfc.entry, true return sfc.entry, true
} }
// Put THIS WILL DELETE ANYTHING YOU HAVE STORED IN THIS CACHE. // Put THIS WILL DELETE ANYTHING YOU HAVE STORED IN THIS CACHE.
func (sfc *SingleFileCache) Put(key string, pathlike string) error { 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 //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) entry, err := newLRUEntry(pathlike)
if err != nil {
return err
}
sfc.entry = entry sfc.entry = entry
return nil return nil
} }
@ -187,13 +214,16 @@ 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? //i'm actually not sure what to do here. why would you ever remove from a single-file cache?
} }
func newLRUEntry(pathlike string) *LruEntry { func newLRUEntry(pathlike string) (*LruEntry, error) {
bits, exists := GetDiskContent(pathlike) bits, err := GetDiskContent(pathlike)
if !exists { if err != nil {
panic("Could not load single file for single file path: " + pathlike) return nil, err
} }
compressedBits := GZipBytes(bits) compressedBits, err := GZipBytes(bits)
if err != nil {
return nil, err
}
size := int64(len(*bits)) + int64(len(*compressedBits)) size := int64(len(*bits)) + int64(len(*compressedBits))
@ -207,7 +237,7 @@ func newLRUEntry(pathlike string) *LruEntry {
totalSize: size, totalSize: size,
MimeType: GuessMimetype(pathlike), MimeType: GuessMimetype(pathlike),
} }
return &entry return &entry, nil
} }
func GuessMimetype(filePath string) string { func GuessMimetype(filePath string) string {