improve (performance): increase performance as per the benchmark observation

This commit is contained in:
Mickael KERJEAN 2019-02-19 02:34:33 +11:00
parent 7f820b8cd4
commit 36f937da80
8 changed files with 69 additions and 57 deletions

View file

@ -8,6 +8,7 @@ import (
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"hash/fnv"
"io" "io"
"io/ioutil" "io/ioutil"
mathrand "math/rand" mathrand "math/rand"
@ -62,6 +63,24 @@ func Hash(str string, n int) string {
return h return h
} }
func QuickHash(str string, n int) string {
hash := fnv.New32()
hash.Write([]byte(str))
d := string(hash.Sum(nil))
h := ""
for i:=0; i<len(d); i++ {
if n > 0 && len(h) >= n {
break
}
h += ReversedBaseChange(Letters, int(d[i]))
}
if len(h) > n {
return h[0:len(h) - 1]
}
return h
}
func ReversedBaseChange(alphabet []rune, i int) string { func ReversedBaseChange(alphabet []rune, i int) string {
str := "" str := ""
for { for {

View file

@ -6,7 +6,12 @@ import (
"strings" "strings"
) )
var MOCK_CURRENT_DIR string
func GetCurrentDir() string { func GetCurrentDir() string {
if MOCK_CURRENT_DIR != "" {
return MOCK_CURRENT_DIR
}
ex, _ := os.Executable() ex, _ := os.Executable()
return filepath.Dir(ex) return filepath.Dir(ex)
} }

View file

@ -33,6 +33,17 @@ func SendSuccessResult(res http.ResponseWriter, data interface{}) {
encoder.Encode(APISuccessResult{"ok", data}) encoder.Encode(APISuccessResult{"ok", data})
} }
func SendSuccessResultWithEtag(res http.ResponseWriter, req *http.Request, data interface{}) {
json, _ := json.Marshal(APISuccessResult{"ok", data})
hash := QuickHash(string(json), 20)
if req.Header.Get("If-None-Match") == hash {
res.WriteHeader(http.StatusNotModified)
return
}
res.Header().Set("Etag", hash)
res.Write(json)
}
func SendSuccessResults(res http.ResponseWriter, data interface{}) { func SendSuccessResults(res http.ResponseWriter, data interface{}) {
encoder := json.NewEncoder(res) encoder := json.NewEncoder(res)
encoder.SetEscapeHTML(false) encoder.SetEscapeHTML(false)

View file

@ -76,21 +76,12 @@ func AdminSessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Reques
SendSuccessResult(res, true) SendSuccessResult(res, true)
} }
func AdminBackend(ctx App, res http.ResponseWriter, req *http.Request) { func AdminBackend(ctx App, res http.ResponseWriter, req *http.Request) {
backends := make(map[string]Form)
drivers := Backend.Drivers() drivers := Backend.Drivers()
backends := make(map[string]Form, len(drivers))
for key := range drivers { for key := range drivers {
backends[key] = drivers[key].LoginForm() backends[key] = drivers[key].LoginForm()
} }
SendSuccessResultWithEtag(res, req, backends)
if c, err := json.Marshal(backends); err == nil {
hash := Hash(string(c), 20)
if req.Header.Get("If-None-Match") == hash {
res.WriteHeader(http.StatusNotModified)
return return
}
res.Header().Set("Etag", hash)
}
SendSuccessResult(res, backends)
} }

View file

@ -1,7 +1,6 @@
package ctrl package ctrl
import ( import (
"encoding/json"
. "github.com/mickael-kerjean/filestash/server/common" . "github.com/mickael-kerjean/filestash/server/common"
"io" "io"
"io/ioutil" "io/ioutil"
@ -14,10 +13,11 @@ import (
var ( var (
logpath = filepath.Join(GetCurrentDir(), LOG_PATH, "access.log") logpath = filepath.Join(GetCurrentDir(), LOG_PATH, "access.log")
cachepath = filepath.Join(GetCurrentDir(), CONFIG_PATH, "config.json") cachepath = filepath.Join(GetCurrentDir(), CONFIG_PATH, "config.json")
pluginpath = filepath.Join(GetCurrentDir(), PLUGIN_PATH)
) )
func FetchPluginsHandler(ctx App, res http.ResponseWriter, req *http.Request) { func FetchPluginsHandler(ctx App, res http.ResponseWriter, req *http.Request) {
f, err := os.OpenFile(filepath.Join(GetCurrentDir(), PLUGIN_PATH), os.O_RDONLY, os.ModePerm) f, err := os.OpenFile(pluginpath, os.O_RDONLY, os.ModePerm)
if err != nil { if err != nil {
SendErrorResult(res, err) SendErrorResult(res, err)
return return
@ -90,14 +90,5 @@ func PrivateConfigUpdateHandler(ctx App, res http.ResponseWriter, req *http.Requ
func PublicConfigHandler(ctx App, res http.ResponseWriter, req *http.Request) { func PublicConfigHandler(ctx App, res http.ResponseWriter, req *http.Request) {
cfg := Config.Export() cfg := Config.Export()
SendSuccessResultWithEtag(res, req, cfg)
if c, err := json.Marshal(cfg); err == nil {
hash := Hash(string(c), 20)
if req.Header.Get("If-None-Match") == hash {
res.WriteHeader(http.StatusNotModified)
return
}
res.Header().Set("Etag", hash)
}
SendSuccessResult(res, cfg)
} }

View file

@ -1,7 +1,7 @@
package ctrl package ctrl
import ( import (
"encoding/json" "fmt"
. "github.com/mickael-kerjean/filestash/server/common" . "github.com/mickael-kerjean/filestash/server/common"
"github.com/mickael-kerjean/filestash/server/model" "github.com/mickael-kerjean/filestash/server/model"
"io" "io"
@ -19,13 +19,12 @@ type FileInfo struct {
} }
func FileLs(ctx App, res http.ResponseWriter, req *http.Request) { func FileLs(ctx App, res http.ResponseWriter, req *http.Request) {
var files []FileInfo = make([]FileInfo, 0)
if model.CanRead(&ctx) == false { if model.CanRead(&ctx) == false {
if model.CanUpload(&ctx) == false { if model.CanUpload(&ctx) == false {
SendErrorResult(res, NewError("Permission denied", 403)) SendErrorResult(res, NewError("Permission denied", 403))
return return
} }
SendSuccessResults(res, files) SendSuccessResults(res, make([]FileInfo, 0))
return return
} }
path, err := pathBuilder(ctx, req.URL.Query().Get("path")) path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
@ -40,21 +39,23 @@ func FileLs(ctx App, res http.ResponseWriter, req *http.Request) {
return return
} }
for _, entry := range entries { files := make([]FileInfo, len(entries))
f := FileInfo{ etag := fmt.Sprintf("%d", len(entries)) + path
Name: entry.Name(), for i:=0; i<len(entries); i++ {
Size: entry.Size(), etag += files[i].Name
files[i] = FileInfo{
Name: entries[i].Name(),
Size: entries[i].Size(),
Time: func(t time.Time) int64 { Time: func(t time.Time) int64 {
return t.UnixNano() / int64(time.Millisecond) return t.UnixNano() / int64(time.Millisecond)
}(entry.ModTime()), }(entries[i].ModTime()),
Type: func(isDir bool) string { Type: func(isDir bool) string {
if isDir == true { if isDir == true {
return "directory" return "directory"
} }
return "file" return "file"
}(entry.IsDir()), }(entries[i].IsDir()),
} }
files = append(files, f)
} }
var perms Metadata = Metadata{} var perms Metadata = Metadata{}
@ -79,22 +80,12 @@ func FileLs(ctx App, res http.ResponseWriter, req *http.Request) {
perms.CanShare = NewBool(false) perms.CanShare = NewBool(false)
} }
etag := func() string { etag = QuickHash(etag, 20)
tmp := struct { res.Header().Set("Etag", etag)
Tmp0 interface{}
Tmp1 interface{}
}{ files, perms }
if j, err := json.Marshal(tmp); err == nil {
return Hash(string(j), 20)
}
return ""
}()
if etag != "" && req.Header.Get("If-None-Match") == etag { if etag != "" && req.Header.Get("If-None-Match") == etag {
res.WriteHeader(http.StatusNotModified) res.WriteHeader(http.StatusNotModified)
return return
} }
res.Header().Set("Etag", etag)
SendSuccessResultsWithMetadata(res, files, perms) SendSuccessResultsWithMetadata(res, files, perms)
} }

View file

@ -1,9 +1,8 @@
package ctrl package ctrl
import ( import (
"crypto/md5"
"encoding/base32"
. "github.com/mickael-kerjean/filestash/server/common" . "github.com/mickael-kerjean/filestash/server/common"
"fmt"
"io" "io"
"text/template" "text/template"
"net/http" "net/http"
@ -16,8 +15,8 @@ var ETAGS SafeMapStringString = NewSafeMapStringString()
func StaticHandler(_path string) func(App, http.ResponseWriter, *http.Request) { func StaticHandler(_path string) func(App, http.ResponseWriter, *http.Request) {
return func(ctx App, res http.ResponseWriter, req *http.Request) { return func(ctx App, res http.ResponseWriter, req *http.Request) {
var base string = GetAbsolutePath(_path)
var srcPath string var srcPath string
base := GetAbsolutePath(_path)
if srcPath = JoinPath(base, req.URL.Path); srcPath == base { if srcPath = JoinPath(base, req.URL.Path); srcPath == base {
http.NotFound(res, req) http.NotFound(res, req)
return return
@ -93,11 +92,12 @@ func hashFile (path string, n int) string {
return "" return ""
} }
defer f.Close() defer f.Close()
h := md5.New()
if _, err := io.Copy(h, f); err != nil { stat, err := f.Stat()
return "" if err != nil {
return "UNKNOWN"
} }
return base32.HexEncoding.EncodeToString(h.Sum(nil))[:n] return QuickHash(fmt.Sprintf("%s %d %d %s", path, stat.Size(), stat.Mode(), stat.ModTime()), n)
} }
func ServeFile(res http.ResponseWriter, req *http.Request, filePath string) { func ServeFile(res http.ResponseWriter, req *http.Request, filePath string) {
@ -128,6 +128,7 @@ func ServeFile(res http.ResponseWriter, req *http.Request, filePath string) {
head.Set("Etag", etagGzip) head.Set("Etag", etagGzip)
} }
io.Copy(res, file) io.Copy(res, file)
file.Close()
return return
} }
} }
@ -145,4 +146,5 @@ func ServeFile(res http.ResponseWriter, req *http.Request, filePath string) {
head.Set("Etag", etagNormal) head.Set("Etag", etagNormal)
} }
io.Copy(res, file) io.Copy(res, file)
file.Close()
} }

View file

@ -20,7 +20,9 @@ func NewMiddlewareChain(fn func(App, http.ResponseWriter, *http.Request), m []Mi
f = m[i](f) f = m[i](f)
} }
f(app, &resw, req) f(app, &resw, req)
if req.Body != nil {
req.Body.Close() req.Body.Close()
}
go Logger(app, &resw, req) go Logger(app, &resw, req)
} }
} }