added cache only access server
This commit is contained in:
parent
2a0801b015
commit
b5ae5504a7
6 changed files with 217 additions and 8 deletions
41
pkg/fs/fs.go
41
pkg/fs/fs.go
|
|
@ -128,11 +128,50 @@ func (fs *FS) RemoveDst(name string) error {
|
|||
return os.Remove(dp)
|
||||
}
|
||||
|
||||
func (fs *FS) statDst(name string) (fi stdfs.FileInfo, err error) {
|
||||
func (fs *FS) StatDst(name string) (fi stdfs.FileInfo, err error) {
|
||||
fi, err = fs.sc.Get(name)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
return fs.statDst(name)
|
||||
}
|
||||
|
||||
func (fs *FS) statDst(name string) (stdfs.FileInfo, error) {
|
||||
_, dp := fs.paths(name)
|
||||
return os.Stat(dp)
|
||||
}
|
||||
|
||||
func (fs *FS) OpenDst(name string) (f http.File, err error) {
|
||||
log := fs.log.WithValues("file", name)
|
||||
_, dp := fs.paths(name)
|
||||
fi, err := fs.StatDst(name)
|
||||
if err != nil {
|
||||
if !skipLog(name) {
|
||||
log.Error(err, "error stat cache file")
|
||||
}
|
||||
return
|
||||
}
|
||||
file, err := os.Open(dp)
|
||||
if err != nil {
|
||||
if !skipLog(name) {
|
||||
log.Error(err, "error opening cache file")
|
||||
}
|
||||
return
|
||||
}
|
||||
if fi.IsDir() {
|
||||
f = &Dir{log: log.WithName("dir"), f: file, dc: fs.dc}
|
||||
return
|
||||
}
|
||||
md := fs.metadata(name, fi.Size())
|
||||
f = &File{
|
||||
log: log,
|
||||
f: file,
|
||||
md: md,
|
||||
offline: true,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (fs *FS) CancelPreload(name string) {
|
||||
if cancel, ok := fs.pm[name]; ok {
|
||||
cancel()
|
||||
|
|
|
|||
102
pkg/srv/cache.go
Normal file
102
pkg/srv/cache.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
|
||||
package srv
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"cachefs/pkg/fs"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
type CacheFS struct {
|
||||
fs *fs.FS
|
||||
}
|
||||
|
||||
func (cfs *CacheFS) Open(name string) (http.File, error) {
|
||||
return cfs.fs.OpenDst(name)
|
||||
}
|
||||
|
||||
type CacheServer struct {
|
||||
log logr.Logger
|
||||
fs *fs.FS
|
||||
h http.Handler
|
||||
}
|
||||
|
||||
func NewCacheServer(fs *fs.FS, log logr.Logger) http.Handler {
|
||||
return &CacheServer{log, fs, http.FileServer(&CacheFS{fs})}
|
||||
}
|
||||
|
||||
func (cs *CacheServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s := &statusInterceptor{w: w}
|
||||
w = s
|
||||
if !skipLog(r.RequestURI) {
|
||||
defer func() {
|
||||
if s.Status() == http.StatusPartialContent {
|
||||
return
|
||||
}
|
||||
cs.log.Info("access",
|
||||
"client", r.RemoteAddr,
|
||||
"method", r.Method,
|
||||
"status", s.Status(),
|
||||
"uri", r.RequestURI,
|
||||
)
|
||||
}()
|
||||
}
|
||||
upath := r.URL.Path
|
||||
if !strings.HasPrefix(upath, "/") {
|
||||
upath = "/" + upath
|
||||
r.URL.Path = upath
|
||||
}
|
||||
p := path.Clean(upath)
|
||||
option := r.FormValue("o")
|
||||
d, err := cs.fs.StatDst(p)
|
||||
if err != nil {
|
||||
msg, code := toHTTPError(err)
|
||||
http.Error(w, msg, code)
|
||||
return
|
||||
}
|
||||
h := w.Header()
|
||||
if option == "v" {
|
||||
h.Set(csp, videoCSP)
|
||||
err = video.Execute(w, r.URL.Path)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !d.IsDir() {
|
||||
cs.h.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
i := &responseInterceptor{w: w}
|
||||
r.Header.Del("If-Modified-Since")
|
||||
r.Header.Del("Cache-Control")
|
||||
cs.h.ServeHTTP(i, r)
|
||||
|
||||
h.Del("Last-Modified")
|
||||
|
||||
paths, err := i.GetPaths(p, cs.fs, true)
|
||||
if err == io.EOF {
|
||||
w.WriteHeader(i.Status())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
h.Set(csp, indexCSP)
|
||||
|
||||
w.WriteHeader(i.Status())
|
||||
err = cache.Execute(w, paths)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ func (r *responseInterceptor) Status() int {
|
|||
return r.status
|
||||
}
|
||||
|
||||
func (r *responseInterceptor) GetPaths(path string, fs *fs.FS) (dc dirContents, err error) {
|
||||
func (r *responseInterceptor) GetPaths(path string, fs *fs.FS, relative bool) (dc dirContents, err error) {
|
||||
buf := r.buf.Bytes()
|
||||
buf = bytes.ReplaceAll(buf, []byte{'&'}, []byte("&"))
|
||||
err = xml.Unmarshal(buf, &dc)
|
||||
|
|
@ -106,6 +106,9 @@ func (r *responseInterceptor) GetPaths(path string, fs *fs.FS) (dc dirContents,
|
|||
name := template.HTML(p)
|
||||
full := path + "/" + p
|
||||
uri := template.HTML(full)
|
||||
if relative {
|
||||
uri = template.HTML(p)
|
||||
}
|
||||
if p[len(p)-1] == '/' {
|
||||
dc.Dirs = append(dc.Dirs, dir{
|
||||
Name: name,
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ func (fs *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
h.Del("Last-Modified")
|
||||
|
||||
paths, err := i.GetPaths(p, fs.fs)
|
||||
paths, err := i.GetPaths(p, fs.fs, false)
|
||||
if err == io.EOF {
|
||||
w.WriteHeader(i.Status())
|
||||
return
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ package srv
|
|||
import "html/template"
|
||||
|
||||
var (
|
||||
index = template.Must(template.New("index").Parse(`<!doctype html>
|
||||
indexHead = `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
|
@ -107,8 +107,9 @@ span {
|
|||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</head>`
|
||||
|
||||
index = template.Must(template.New("index").Parse(indexHead + `<body>
|
||||
<article>
|
||||
<pre>[v]: show video
|
||||
[n]: skip file caching
|
||||
|
|
@ -143,6 +144,39 @@ span {
|
|||
</table>
|
||||
</article>
|
||||
</body>
|
||||
</html>`))
|
||||
cache = template.Must(template.New("cache").Parse(indexHead + `<body>
|
||||
<article>
|
||||
<pre>[v]: show video</pre>
|
||||
<table>
|
||||
<tr class="listitem">
|
||||
<td class="listpath up"><a href="../">../</a></td>
|
||||
<td class="listoptions">[dir]</td>
|
||||
</tr>
|
||||
<tr class="spacer"><td colspan="2"></td></tr>
|
||||
{{range $s := .Dirs -}}
|
||||
<tr class="listitem">
|
||||
<td class="listpath"><a href="{{$s.URI}}">{{$s.Name}}</a></td>
|
||||
<td class="listoptions">[dir]</td>
|
||||
</tr>
|
||||
<tr class="spacer"><td colspan="2"></td></tr>
|
||||
{{end -}}
|
||||
{{range $s := .Files -}}
|
||||
<tr class="listitem">
|
||||
<td class="listpath"><a id="{{$s.Anchor}}" href="{{$s.URI}}">{{$s.Name}}</a></td>
|
||||
<td class="listoptions">
|
||||
{{if ge $s.Status 0}}<span>{{$s.Status}}%</span>{{end}}<a href="{{$s.URI}}?o=v">[v]</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="spacer"><td colspan="2">
|
||||
{{if ge $s.Status 0}}
|
||||
<div class="spacer {{if le $s.Status 35}}red{{else if le $s.Status 65}}yellow{{else}}green{{end}}" style="width: {{$s.Status}}%;"></div>
|
||||
{{end -}}
|
||||
</td></tr>
|
||||
{{end -}}
|
||||
</table>
|
||||
</article>
|
||||
</body>
|
||||
</html>`))
|
||||
video = template.Must(template.New("video").Parse(`<!doctype html>
|
||||
<html lang="en">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue