package servlet import ( "errors" util "geniuscartel.xyz/vinegar/vinegarUtil" "net/http" "path" "strings" ) type ( FileRoute struct { *VinegarRoute srv *VinegarServlet fileRoot string UseCache bool } RouteConstructor func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute ) var NewTextRoute RouteConstructor = func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute { defaultPrune := strings.Replace(urlPattern, ".*", "", -1) route := FileRoute{srv: servlet, fileRoot: pathlike, UseCache: useCache} textRouteHandler := createCompressibleFileServletFunction(&route, defaultPrune, pathlike) rootRoute := NewServletRoute(urlPattern, textRouteHandler) //i *still* kinda don't like this pattern route.VinegarRoute = rootRoute servlet.AddRoute(route.VinegarRoute) return &route } var NewImageRoute RouteConstructor = func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute { defaultPrune := strings.Replace(urlPattern, ".*", "", -1) route := FileRoute{srv: servlet, fileRoot: pathlike, UseCache: useCache} rootRoute := NewServletRoute(urlPattern, createUncompressedFileServletFunction(&route, defaultPrune, pathlike)) route.VinegarRoute = rootRoute //i *kinda* don't like this pattern servlet.AddRoute(route.VinegarRoute) return &route } var NewSingleFileRoute RouteConstructor = func(servlet *VinegarServlet, urlPattern string, pathlike string, useCache bool) *FileRoute { route := FileRoute{ srv: servlet, fileRoot: pathlike, UseCache: useCache, } singleFileServletHandler := createSingleFileServletFunction(&route) sfCache := util.NewSingleFileCache(pathlike) parentRoute := NewServletRoute(urlPattern, singleFileServletHandler) parentRoute.Handler = singleFileServletHandler parentRoute.Cache = sfCache route.VinegarRoute = parentRoute servlet.AddRoute(route.VinegarRoute) return &route } func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction { var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) { var cache *util.LruEntry var exists bool if route.UseCache { cache, exists = route.Cache.Get("") } else { cache, exists = route.Cache.GetFresh("") } if !exists { route.srv.SendError(w, req, 404, "File not found.", errors.New("could not find file: "+route.fileRoot)) return } var content []byte if clientAcceptsGzip(req) { content = cache.CompressedContent w.Header().Add(ContentEncodingHeaderKey, "gzip") } else { content = cache.Content } w.Header().Add(ContentTypeHeaderKey, cache.MimeType) _, err := w.Write(content) if err != nil { panic(err) } } return fun } func createCompressibleFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction { var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) { stub := strings.Replace(req.URL.Path, basePattern, "", 1) 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 resourcePath := path.Join(pathlike, stub) if !exists { content, fileExists := util.GetDiskContent(resourcePath) if fileExists { if route.UseCache { route.Cache.Put(stub, resourcePath) cachedContent, _ = route.Cache.Get(stub) } else { w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub)) w.Write(*content) 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) var err error = nil if clientAcceptsGzip(req) { w.Header().Add(ContentEncodingHeaderKey, "gzip") _, err = w.Write(cachedContent.CompressedContent) } else { _, err = w.Write(cachedContent.Content) } if err != nil { panic(err) } } return fun } func createUncompressedFileServletFunction(route *FileRoute, basePattern string, pathlike string) VinegarHandlerFunction { var fun VinegarHandlerFunction = func(w http.ResponseWriter, req *http.Request) { stub := strings.Replace(req.URL.Path, basePattern, "", 1) resourcePath := path.Join(pathlike, stub) entry, exists := route.Cache.Get(stub) if !exists { route.Cache.Put(stub, resourcePath) entry, exists = route.Cache.Get(stub) } if exists { w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub)) _, err := w.Write(entry.Content) if err != nil { panic(err) } return } else { route.srv.SendError(w, req, 404, "Couldn't find your content.", errors.New("could not find file for ["+stub+"]")) } } return fun } func clientAcceptsGzip(req *http.Request) bool { encodings := req.Header.Get(AcceptEncodingHeaderKey) return strings.Contains(encodings, "gzip") }