2021-04-26 13:52:50 +02:00
|
|
|
package fs
|
2019-02-04 16:04:19 +02:00
|
|
|
|
|
|
|
import (
|
2021-09-29 12:23:54 +02:00
|
|
|
"crypto/md5"
|
|
|
|
"encoding/base64"
|
2019-02-04 16:04:19 +02:00
|
|
|
"fmt"
|
2022-06-08 14:28:24 +02:00
|
|
|
"io"
|
2021-09-29 12:23:54 +02:00
|
|
|
"io/fs"
|
2019-02-04 16:04:19 +02:00
|
|
|
"net/http"
|
2022-06-08 14:28:24 +02:00
|
|
|
"os"
|
|
|
|
"strings"
|
2021-04-26 13:52:50 +02:00
|
|
|
|
2021-09-30 16:23:30 +02:00
|
|
|
"github.com/imgproxy/imgproxy/v3/config"
|
2019-02-04 16:04:19 +02:00
|
|
|
)
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
type transport struct {
|
2019-02-04 16:04:19 +02:00
|
|
|
fs http.Dir
|
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func New() transport {
|
|
|
|
return transport{fs: http.Dir(config.LocalFileSystemRoot)}
|
2019-02-04 16:04:19 +02:00
|
|
|
}
|
|
|
|
|
2021-04-26 13:52:50 +02:00
|
|
|
func (t transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
2022-06-08 14:28:24 +02:00
|
|
|
header := make(http.Header)
|
2019-02-04 16:04:19 +02:00
|
|
|
|
2022-06-08 14:28:24 +02:00
|
|
|
f, err := t.fs.Open(req.URL.Path)
|
2019-02-04 16:04:19 +02:00
|
|
|
if err != nil {
|
2022-06-08 14:28:24 +02:00
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return &http.Response{
|
|
|
|
StatusCode: http.StatusNotFound,
|
|
|
|
Proto: "HTTP/1.0",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 0,
|
|
|
|
Header: header,
|
|
|
|
ContentLength: 0,
|
|
|
|
Body: io.NopCloser(strings.NewReader(
|
|
|
|
fmt.Sprintf("%s doesn't exist", req.URL.Path),
|
|
|
|
)),
|
|
|
|
Close: false,
|
|
|
|
Request: req,
|
|
|
|
}, nil
|
|
|
|
}
|
2019-02-04 16:04:19 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
fi, err := f.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if fi.IsDir() {
|
2022-06-08 14:28:24 +02:00
|
|
|
return &http.Response{
|
|
|
|
StatusCode: http.StatusNotFound,
|
|
|
|
Proto: "HTTP/1.0",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 0,
|
|
|
|
Header: header,
|
|
|
|
ContentLength: 0,
|
|
|
|
Body: io.NopCloser(strings.NewReader(
|
|
|
|
fmt.Sprintf("%s is directory", req.URL.Path),
|
|
|
|
)),
|
|
|
|
Close: false,
|
|
|
|
Request: req,
|
|
|
|
}, nil
|
2019-02-04 16:04:19 +02:00
|
|
|
}
|
|
|
|
|
2021-09-29 12:23:54 +02:00
|
|
|
if config.ETagEnabled {
|
|
|
|
etag := BuildEtag(req.URL.Path, fi)
|
|
|
|
header.Set("ETag", etag)
|
|
|
|
|
|
|
|
if etag == req.Header.Get("If-None-Match") {
|
2022-04-07 19:32:51 +02:00
|
|
|
f.Close()
|
|
|
|
|
2021-09-29 12:23:54 +02:00
|
|
|
return &http.Response{
|
|
|
|
StatusCode: http.StatusNotModified,
|
|
|
|
Proto: "HTTP/1.0",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 0,
|
|
|
|
Header: header,
|
|
|
|
ContentLength: 0,
|
|
|
|
Body: nil,
|
|
|
|
Close: false,
|
|
|
|
Request: req,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-04 16:04:19 +02:00
|
|
|
return &http.Response{
|
|
|
|
Status: "200 OK",
|
|
|
|
StatusCode: 200,
|
|
|
|
Proto: "HTTP/1.0",
|
|
|
|
ProtoMajor: 1,
|
|
|
|
ProtoMinor: 0,
|
2021-09-29 12:23:54 +02:00
|
|
|
Header: header,
|
2019-02-04 16:04:19 +02:00
|
|
|
ContentLength: fi.Size(),
|
|
|
|
Body: f,
|
|
|
|
Close: true,
|
|
|
|
Request: req,
|
|
|
|
}, nil
|
|
|
|
}
|
2021-09-29 12:23:54 +02:00
|
|
|
|
|
|
|
func BuildEtag(path string, fi fs.FileInfo) string {
|
|
|
|
tag := fmt.Sprintf("%s__%d__%d", path, fi.Size(), fi.ModTime().UnixNano())
|
|
|
|
hash := md5.Sum([]byte(tag))
|
|
|
|
return `"` + string(base64.RawURLEncoding.EncodeToString(hash[:])) + `"`
|
|
|
|
}
|