Initial commit of library
This commit is contained in:
commit
35c87e82d4
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/.idea/
|
||||
/vinegar-server.iml
|
||||
6
go.mod
Normal file
6
go.mod
Normal file
@ -0,0 +1,6 @@
|
||||
module vinegar-server
|
||||
require (
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2
|
||||
google.golang.org/api v0.30.0
|
||||
)
|
||||
go 1.18
|
||||
211
vinegar/WebLRU.go
Normal file
211
vinegar/WebLRU.go
Normal file
@ -0,0 +1,211 @@
|
||||
package vinegar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
gzip2 "compress/gzip"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultCacheTimeInMinutes = 15
|
||||
ContentTypeHeaderKey = "Content-Type"
|
||||
ContentEncodingHeaderKey = "Content-Encoding"
|
||||
)
|
||||
|
||||
type (
|
||||
lru map[string]*LruEntry
|
||||
Lru struct {
|
||||
entries *lru
|
||||
limit int64
|
||||
currentSize int64
|
||||
}
|
||||
|
||||
LruEntry struct {
|
||||
Content []byte
|
||||
CompressedContent []byte
|
||||
created time.Time
|
||||
expires time.Time
|
||||
mostRecentAccess time.Time
|
||||
totalSize int64
|
||||
MimeType string
|
||||
}
|
||||
|
||||
SingleFileCache struct {
|
||||
Content *[]byte
|
||||
CompressedContent *[]byte
|
||||
timeCreated time.Time
|
||||
timeExpires time.Time
|
||||
Mimetype string
|
||||
}
|
||||
)
|
||||
|
||||
func NewLRU(size int64) *Lru {
|
||||
cLru := make(lru)
|
||||
return &Lru{&cLru, size, 0}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
cache := SingleFileCache{
|
||||
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) {
|
||||
entry, exists := (*l.entries)[key]
|
||||
if exists && entry.expires.Before(time.Now()) {
|
||||
fmt.Println("Cache miss due to expired content")
|
||||
l.Remove(key)
|
||||
return nil, false
|
||||
} else {
|
||||
if exists {
|
||||
entry.mostRecentAccess = time.Now()
|
||||
} else {
|
||||
fmt.Printf("cache miss for '%s'\n", key)
|
||||
}
|
||||
return entry, exists
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Lru) Remove(key string) {
|
||||
entry, exists := (*l.entries)[key]
|
||||
if exists {
|
||||
size := entry.totalSize
|
||||
delete(*l.entries, key)
|
||||
l.currentSize = l.currentSize - size
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Lru) ensureVacancy(required int64) {
|
||||
if l.currentSize == 0 {
|
||||
return
|
||||
}
|
||||
for l.currentSize+required > l.limit {
|
||||
leastKey := l.getLeastRecentlyUsedKey()
|
||||
l.Remove(leastKey)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Lru) getLeastRecentlyUsedKey() string {
|
||||
started := false
|
||||
var benchmark *LruEntry
|
||||
leastUsedKey := ""
|
||||
for key, entry := range *l.entries {
|
||||
if !started {
|
||||
started = true
|
||||
benchmark = entry
|
||||
leastUsedKey = key
|
||||
continue
|
||||
}
|
||||
if benchmark.mostRecentAccess.After(entry.mostRecentAccess) {
|
||||
benchmark = entry
|
||||
}
|
||||
}
|
||||
if leastUsedKey == "" {
|
||||
panic("Invalid key for LRU: [" + leastUsedKey + "]")
|
||||
}
|
||||
return leastUsedKey
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func GuessMimetype(filePath string) string {
|
||||
dotIndex := strings.LastIndex(filePath, ".")
|
||||
ext := filePath[dotIndex+1:]
|
||||
switch ext {
|
||||
case "htm":
|
||||
case "html":
|
||||
return "text/html"
|
||||
case "ico":
|
||||
return "image/vnd.microsoft.icon"
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
return "image/jpeg"
|
||||
case "png":
|
||||
return "image/png"
|
||||
case "svg":
|
||||
return "image/svg+xml"
|
||||
default:
|
||||
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
|
||||
}
|
||||
}
|
||||
9
vinegar/campaign.go
Normal file
9
vinegar/campaign.go
Normal file
@ -0,0 +1,9 @@
|
||||
package vinegar
|
||||
|
||||
type Campaign struct {
|
||||
Targets *[]string
|
||||
HostName string
|
||||
ListeningPort int
|
||||
WebRoot string
|
||||
EmailTemplate string
|
||||
}
|
||||
86
vinegar/dynamicRoute.go
Normal file
86
vinegar/dynamicRoute.go
Normal file
@ -0,0 +1,86 @@
|
||||
package vinegar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
ApiRoute struct {
|
||||
ServletRoute *ServletRoute
|
||||
HttpMethodRoutes *map[HttpMethod]ServletHandleFunction
|
||||
}
|
||||
)
|
||||
|
||||
type (
|
||||
HttpMethod int
|
||||
)
|
||||
|
||||
const (
|
||||
GET HttpMethod = iota
|
||||
POST
|
||||
PUT
|
||||
PATCH
|
||||
DELETE
|
||||
UNDEFINED
|
||||
)
|
||||
|
||||
func NewApiRoute(pattern string) *ApiRoute {
|
||||
functionMap := make(map[HttpMethod]ServletHandleFunction)
|
||||
ancestorRoute := NewServletRoute(pattern, createMethodHandler(&functionMap))
|
||||
route := ApiRoute{
|
||||
ancestorRoute,
|
||||
&functionMap,
|
||||
}
|
||||
return &route
|
||||
}
|
||||
|
||||
func createMethodHandler(m *map[HttpMethod]ServletHandleFunction) ServletHandleFunction {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
method := getHttpMethod(req)
|
||||
fn, exists := (*m)[method]
|
||||
if exists {
|
||||
fn(w, req)
|
||||
} else {
|
||||
SendApiError(
|
||||
w,
|
||||
200,
|
||||
404,
|
||||
"Method ["+req.Method+"] does not exist for endpoint["+req.URL.Path+"].",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (api *ApiRoute) RegisterHttpMethodHandler(method HttpMethod, handler ServletHandleFunction) {
|
||||
(*api.HttpMethodRoutes)[method] = handler
|
||||
}
|
||||
|
||||
func getHttpMethod(req *http.Request) HttpMethod {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
return GET
|
||||
case "POST":
|
||||
return POST
|
||||
case "PUT":
|
||||
return PUT
|
||||
case "PATCH":
|
||||
return PATCH
|
||||
case "DELETE":
|
||||
return DELETE
|
||||
case "UNDEFINED":
|
||||
default:
|
||||
return UNDEFINED
|
||||
}
|
||||
return UNDEFINED
|
||||
}
|
||||
|
||||
func SendApiError(w http.ResponseWriter, httpCode int, messageCode int, message string) {
|
||||
respMessage := fmt.Sprintf("{\"code\":%d, \"message\":\"%s\"}", messageCode, message)
|
||||
w.WriteHeader(httpCode)
|
||||
|
||||
_, err := w.Write([]byte(respMessage))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
161
vinegar/email.go
Normal file
161
vinegar/email.go
Normal file
@ -0,0 +1,161 @@
|
||||
package vinegar
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/gmail/v1"
|
||||
"google.golang.org/api/option"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
//<editor-fold name="secure gmail">
|
||||
|
||||
/*======================================================================================
|
||||
|
||||
|
||||
|
||||
secure gmail
|
||||
|
||||
|
||||
|
||||
======================================================================================*/
|
||||
|
||||
// Retrieve a token, saves the token, then returns the generated client.
|
||||
|
||||
func getClient(config *oauth2.Config) *http.Client {
|
||||
|
||||
// The file token.json stores the user's access and refresh tokens, and is
|
||||
|
||||
// created automatically when the authorization flow completes for the first
|
||||
|
||||
// time.
|
||||
|
||||
tokFile := "token.json"
|
||||
|
||||
tok, err := tokenFromFile(tokFile)
|
||||
|
||||
if err != nil {
|
||||
|
||||
tok = getTokenFromWeb(config)
|
||||
|
||||
saveToken(tokFile, tok)
|
||||
|
||||
}
|
||||
|
||||
return config.Client(context.Background(), tok)
|
||||
|
||||
}
|
||||
|
||||
// Request a token from the web, then returns the retrieved token.
|
||||
|
||||
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
|
||||
|
||||
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
|
||||
|
||||
fmt.Printf("Go to the following link in your browser then type the "+
|
||||
"authorization code: \n%v\n", authURL)
|
||||
|
||||
var authCode string
|
||||
|
||||
if _, err := fmt.Scan(&authCode); err != nil {
|
||||
|
||||
log.Fatalf("Unable to read authorization code: %v", err)
|
||||
|
||||
}
|
||||
|
||||
tok, err := config.Exchange(context.TODO(), authCode)
|
||||
|
||||
if err != nil {
|
||||
|
||||
log.Fatalf("Unable to retrieve token from web: %v", err)
|
||||
|
||||
}
|
||||
|
||||
return tok
|
||||
|
||||
}
|
||||
|
||||
// Retrieves a token from a local file.
|
||||
|
||||
func tokenFromFile(file string) (*oauth2.Token, error) {
|
||||
f, err := os.Open(file)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
tok := &oauth2.Token{}
|
||||
|
||||
err = json.NewDecoder(f).Decode(tok)
|
||||
|
||||
return tok, err
|
||||
}
|
||||
|
||||
// Saves a token to a file path.
|
||||
|
||||
func saveToken(path string, token *oauth2.Token) {
|
||||
fmt.Printf("Saving credential file to: %s\n", path)
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to cache oauth token: %v", err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
json.NewEncoder(f).Encode(token)
|
||||
}
|
||||
|
||||
func getMailService() *gmail.Service {
|
||||
ctx := context.Background()
|
||||
b, err := ioutil.ReadFile("gmail-creds.json")
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read client secret file: %v", err)
|
||||
}
|
||||
|
||||
// If modifying these scopes, delete your previously saved token.json.
|
||||
|
||||
config, err := google.ConfigFromJSON(b, gmail.GmailSendScope, gmail.GmailComposeScope)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to parse client secret file to config: %v", err)
|
||||
}
|
||||
|
||||
client := getClient(config)
|
||||
|
||||
srv, err := gmail.NewService(ctx, option.WithHTTPClient(client))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to retrieve Gmail client: %v", err)
|
||||
}
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
func TestSend() {
|
||||
srv := getMailService()
|
||||
|
||||
payload := []byte("testing")
|
||||
|
||||
msg := gmail.Message{Raw: base64.URLEncoding.EncodeToString(payload)}
|
||||
|
||||
msgResp, err := srv.Users.Messages.Send("me", &msg).Do()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%#v\n", msgResp)
|
||||
}
|
||||
|
||||
//</editor-fold>
|
||||
125
vinegar/server.go
Normal file
125
vinegar/server.go
Normal file
@ -0,0 +1,125 @@
|
||||
package vinegar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLruSize = int64(1024 * 1024 * 50)
|
||||
)
|
||||
|
||||
type ErrorResponse struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
|
||||
type (
|
||||
Servlet struct {
|
||||
Port string
|
||||
Routes []*ServletRoute
|
||||
Cache *Lru
|
||||
}
|
||||
|
||||
ServletRoute struct {
|
||||
Pattern *regexp.Regexp
|
||||
Handler ServletHandleFunction
|
||||
}
|
||||
|
||||
ServletHandleFunction func(w http.ResponseWriter, req *http.Request)
|
||||
)
|
||||
|
||||
func NewServlet(port string) *Servlet {
|
||||
lru := NewLRU(defaultLruSize)
|
||||
srv := Servlet{Port: port, Cache: lru}
|
||||
|
||||
return &srv
|
||||
}
|
||||
|
||||
func NewServletRoute(routePattern string, handleFunc ServletHandleFunction) *ServletRoute {
|
||||
route := ServletRoute{}
|
||||
|
||||
pattern := regexp.MustCompile(routePattern)
|
||||
|
||||
route.Pattern = pattern
|
||||
route.Handler = handleFunc
|
||||
|
||||
return &route
|
||||
}
|
||||
|
||||
func (s *Servlet) AddRoute(route *ServletRoute) {
|
||||
s.Routes = append(s.Routes, route)
|
||||
}
|
||||
|
||||
func (s *Servlet) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
path := req.URL.Path
|
||||
fmt.Println("Attempting to match request for " + path)
|
||||
for _, route := range s.Routes {
|
||||
if route.Pattern.MatchString(path) {
|
||||
route.Handler(w, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func StartServerGood(s *Servlet) {
|
||||
err := http.ListenAndServe(s.Port, s)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func SendError(w http.ResponseWriter, code int, msg string) {
|
||||
errorFile := "templates/error.html"
|
||||
errorResp := ErrorResponse{code, msg}
|
||||
tmplManager := template.New("error")
|
||||
|
||||
f, err := os.OpenFile(errorFile, os.O_RDONLY, 0777)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tmpl, err := tmplManager.Parse(string(content))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
w.WriteHeader(code)
|
||||
err = tmpl.Execute(w, errorResp)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
118
vinegar/staticRoute.go
Normal file
118
vinegar/staticRoute.go
Normal file
@ -0,0 +1,118 @@
|
||||
package vinegar
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
FileRoute struct {
|
||||
*ServletRoute
|
||||
fileRoot string
|
||||
}
|
||||
)
|
||||
|
||||
func NewImageRoute(urlPattern string, pathlike string, servlet *Servlet) *FileRoute {
|
||||
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
||||
rootRoute := NewServletRoute(urlPattern, createUncompressedFileServletFunction(defaultPrune, pathlike, servlet.Cache))
|
||||
imgRoute := FileRoute{rootRoute, pathlike}
|
||||
return &imgRoute
|
||||
}
|
||||
|
||||
func NewTextRoute(urlPattern string, pathlike string, servlet *Servlet) *FileRoute {
|
||||
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
||||
rootRoute := NewServletRoute(urlPattern, createCompressibleFileServletFunction(defaultPrune, pathlike, servlet.Cache))
|
||||
fr := FileRoute{rootRoute, pathlike}
|
||||
return &fr
|
||||
}
|
||||
|
||||
func NewSingleFileRoute(urlPattern string, pathlike string, servlet *Servlet) *FileRoute {
|
||||
fun := createSingleFileServletFunction(pathlike)
|
||||
route := FileRoute{
|
||||
ServletRoute: NewServletRoute("^"+urlPattern+"$", fun),
|
||||
fileRoot: pathlike,
|
||||
}
|
||||
return &route
|
||||
}
|
||||
|
||||
func createSingleFileServletFunction(pathlike string) ServletHandleFunction {
|
||||
cache := NewSingleFileCache(pathlike)
|
||||
var fun ServletHandleFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Add(ContentTypeHeaderKey, cache.Mimetype)
|
||||
if clientAcceptsGzip(req) {
|
||||
w.Header().Add(ContentEncodingHeaderKey, "gzip")
|
||||
w.Write(*cache.CompressedContent)
|
||||
}
|
||||
}
|
||||
|
||||
return fun
|
||||
}
|
||||
|
||||
func createCompressibleFileServletFunction(basePattern string, pathlike string, cache *Lru) ServletHandleFunction {
|
||||
var fun ServletHandleFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
||||
cachedContent, exists := cache.Get(stub)
|
||||
if !exists {
|
||||
content, fileExists := GetDiskContent(path.Join(pathlike, stub))
|
||||
if fileExists {
|
||||
cache.Put(stub, content)
|
||||
cachedContent, _ = cache.Get(stub)
|
||||
} else {
|
||||
SendError(w, 404, "Oops! Something went wrong.")
|
||||
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(basePattern string, pathlike string, cache *Lru) ServletHandleFunction {
|
||||
var fun ServletHandleFunction = func(w http.ResponseWriter, req *http.Request) {
|
||||
stub := strings.Replace(req.URL.Path, basePattern, "", 1)
|
||||
|
||||
entry, exists := cache.Get(stub)
|
||||
if !exists {
|
||||
fileContent, fExists := GetDiskContent(path.Join(pathlike, stub))
|
||||
if fExists {
|
||||
cache.Put(stub, fileContent)
|
||||
entry, exists = cache.Get(stub)
|
||||
} else {
|
||||
SendError(w, 404, "Oops! Something went wrong!")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
SendError(w, 404, "Oops! Something went wrong.")
|
||||
} else {
|
||||
_, err := w.Write(entry.Content)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return fun
|
||||
}
|
||||
|
||||
func clientAcceptsGzip(req *http.Request) bool {
|
||||
encodings := req.Header.Get("Accept-Encoding")
|
||||
if strings.Contains(encodings, "gzip") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user