mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 16:32:31 +01:00
improve (performance): increase performance as per the benchmark observation
This commit is contained in:
parent
7f820b8cd4
commit
36f937da80
8 changed files with 69 additions and 57 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue