fs.go 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. package fs
  2. import (
  3. "crypto/md5"
  4. "encoding/base64"
  5. "fmt"
  6. "io/fs"
  7. "net/http"
  8. "github.com/imgproxy/imgproxy/v3/config"
  9. )
  10. type transport struct {
  11. fs http.Dir
  12. }
  13. func New() transport {
  14. return transport{fs: http.Dir(config.LocalFileSystemRoot)}
  15. }
  16. func (t transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
  17. f, err := t.fs.Open(req.URL.Path)
  18. if err != nil {
  19. return nil, err
  20. }
  21. fi, err := f.Stat()
  22. if err != nil {
  23. return nil, err
  24. }
  25. if fi.IsDir() {
  26. return nil, fmt.Errorf("%s is a directory", req.URL.Path)
  27. }
  28. header := make(http.Header)
  29. if config.ETagEnabled {
  30. etag := BuildEtag(req.URL.Path, fi)
  31. header.Set("ETag", etag)
  32. if etag == req.Header.Get("If-None-Match") {
  33. return &http.Response{
  34. StatusCode: http.StatusNotModified,
  35. Proto: "HTTP/1.0",
  36. ProtoMajor: 1,
  37. ProtoMinor: 0,
  38. Header: header,
  39. ContentLength: 0,
  40. Body: nil,
  41. Close: false,
  42. Request: req,
  43. }, nil
  44. }
  45. }
  46. return &http.Response{
  47. Status: "200 OK",
  48. StatusCode: 200,
  49. Proto: "HTTP/1.0",
  50. ProtoMajor: 1,
  51. ProtoMinor: 0,
  52. Header: header,
  53. ContentLength: fi.Size(),
  54. Body: f,
  55. Close: true,
  56. Request: req,
  57. }, nil
  58. }
  59. func BuildEtag(path string, fi fs.FileInfo) string {
  60. tag := fmt.Sprintf("%s__%d__%d", path, fi.Size(), fi.ModTime().UnixNano())
  61. hash := md5.Sum([]byte(tag))
  62. return `"` + string(base64.RawURLEncoding.EncodeToString(hash[:])) + `"`
  63. }