-
-
Notifications
You must be signed in to change notification settings - Fork 187
Expand file tree
/
Copy pathremote.go
More file actions
147 lines (127 loc) · 4.04 KB
/
remote.go
File metadata and controls
147 lines (127 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package handler
import (
"bytes"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"webp_server_go/config"
"webp_server_go/helper"
"github.com/gofiber/fiber/v2"
"github.com/h2non/filetype"
"github.com/patrickmn/go-cache"
log "github.com/sirupsen/logrus"
)
// Given /path/to/node.png
// Delete /path/to/node.png*
func cleanProxyCache(cacheImagePath string) {
// Delete /node.png*
files, err := filepath.Glob(cacheImagePath + "*")
if err != nil {
log.Infoln(err)
}
for _, f := range files {
if err := os.Remove(f); err != nil {
log.Info(err)
}
}
}
// Download file and return response header
func downloadFile(filepath string, url string) http.Header {
resp, err := http.Get(url)
if err != nil {
log.Errorln("Connection to remote error when downloadFile!")
return nil
}
defer resp.Body.Close()
if resp.StatusCode != fiber.StatusOK {
log.Errorf("remote returned %s when fetching remote image", resp.Status)
return resp.Header
}
// Copy bytes here
bodyBytes := new(bytes.Buffer)
_, err = bodyBytes.ReadFrom(resp.Body)
if err != nil {
return nil
}
// Check if remote content-type is image using check by filetype instead of content-type returned by origin
kind, _ := filetype.Match(bodyBytes.Bytes())
mime := kind.MIME.Value
if !strings.Contains(mime, "image") && !config.AllowAllExtensions {
log.Errorf("remote file %s is not image and AllowedTypes is not '*', remote content has MIME type of %s", url, mime)
return nil
}
_ = os.MkdirAll(path.Dir(filepath), 0755)
// Create Cache here as a lock, so we can prevent incomplete file from being read
// Key: filepath, Value: true
config.WriteLock.Set(filepath, true, -1)
err = os.WriteFile(filepath, bodyBytes.Bytes(), 0600)
if err != nil {
// not likely to happen
return nil
}
// Delete lock here
config.WriteLock.Delete(filepath)
return resp.Header
}
func fetchRemoteImg(url string, subdir string) (metaContent config.MetaFile) {
// url is https://test.webp.sh/mypic/123.jpg?someother=200&somebugs=200
// How do we know if the remote img is changed? we're using hash(etag+length)
var etag string
cacheKey := subdir + ":" + helper.HashString(url)
if val, found := config.RemoteCache.Get(cacheKey); found {
if etagVal, ok := val.(string); ok {
log.Infof("Using cache for remote addr: %s", url)
etag = etagVal
} else {
config.RemoteCache.Delete(cacheKey)
}
}
if etag == "" {
log.Infof("Remote Addr is %s, pinging for info...", url)
etag = pingURL(url)
if etag != "" {
config.RemoteCache.Set(cacheKey, etag, cache.DefaultExpiration)
}
}
metadata := helper.ReadMetadata(url, etag, subdir)
remoteFileExtension := path.Ext(url)
localRawImagePath := path.Join(config.Config.RemoteRawPath, subdir, metadata.Id) + remoteFileExtension
localExhaustImagePath := path.Join(config.Config.ExhaustPath, subdir, metadata.Id)
if !helper.ImageExists(localRawImagePath) || metadata.Checksum != helper.HashString(etag) {
cleanProxyCache(localExhaustImagePath)
if metadata.Checksum != helper.HashString(etag) {
// remote file has changed
log.Info("Remote file changed, updating metadata and fetching image source...")
helper.DeleteMetadata(url, subdir)
helper.WriteMetadata(url, etag, subdir)
} else {
// local file not exists
log.Info("Remote file not found in remote-raw, re-fetching...")
}
_ = downloadFile(localRawImagePath, url)
// Update metadata with newly downloaded file
helper.WriteMetadata(url, etag, subdir)
}
return metadata
}
func pingURL(url string) string {
// this function will try to return identifiable info, currently include etag, content-length as string
// anything goes wrong, will return ""
var etag, length string
resp, err := http.Head(url)
if err != nil {
log.Errorln("Connection to remote error when pingUrl:"+url, err)
return ""
}
defer resp.Body.Close()
if resp.StatusCode == fiber.StatusOK {
etag = resp.Header.Get("etag")
length = resp.Header.Get("content-length")
}
if etag == "" {
log.Info("Remote didn't return etag in header when getRemoteImageInfo, please check.")
}
return etag + length
}