mirror of
https://github.com/woodchen-ink/webp_server_go.git
synced 2025-07-18 21:52:01 +08:00
choose webp or png feature (#70)
* support webp_bigger header, add test cases * fix test case
This commit is contained in:
parent
878a2ddd0c
commit
428192275b
@ -22,11 +22,7 @@ var (
|
|||||||
remoteRaw = "remote-raw"
|
remoteRaw = "remote-raw"
|
||||||
config Config
|
config Config
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
)
|
releaseUrl = "https://github.com/webp-sh/webp_server_go/releases/latest/download/"
|
||||||
|
|
||||||
const (
|
|
||||||
NotCompressed = "not_compressed"
|
|
||||||
WebpBigger = "webp_bigger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -8,7 +8,17 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go test -v -cover .
|
func walker() []string {
|
||||||
|
var list []string
|
||||||
|
_ = filepath.Walk("./pics", func(path string, info os.FileInfo, err error) error {
|
||||||
|
if !info.IsDir() {
|
||||||
|
list = append(list, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
func TestWebpEncoder(t *testing.T) {
|
func TestWebpEncoder(t *testing.T) {
|
||||||
var webp = "/tmp/test-result.webp"
|
var webp = "/tmp/test-result.webp"
|
||||||
var target = walker()
|
var target = walker()
|
||||||
@ -25,24 +35,18 @@ func TestWebpEncoder(t *testing.T) {
|
|||||||
_ = os.Remove(webp)
|
_ = os.Remove(webp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNonImage(t *testing.T) {
|
func TestNonExistImage(t *testing.T) {
|
||||||
var webp = "/tmp/test-result.webp"
|
var webp = "/tmp/test-result.webp"
|
||||||
// test error
|
// test error
|
||||||
var err = webpEncoder("./pics/empty.jpg", webp, 80, true, nil)
|
var err = webpEncoder("./pics/empty.jpg", webp, 80, true, nil)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
_ = os.Remove(webp)
|
_ = os.Remove(webp)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func walker() []string {
|
func TestConvertFail(t *testing.T) {
|
||||||
var list []string
|
var webp = "/tmp/test-result.webp"
|
||||||
_ = filepath.Walk("./pics", func(path string, info os.FileInfo, err error) error {
|
var err = webpEncoder("./pics/webp_server.jpg", webp, -1, true, nil)
|
||||||
if !info.IsDir() {
|
assert.NotNil(t, t, err)
|
||||||
list = append(list, path)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runEncoder(t *testing.T, file string, webp string) {
|
func runEncoder(t *testing.T, file string, webp string) {
|
||||||
|
36
helper.go
36
helper.go
@ -9,6 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -32,6 +33,9 @@ func fileCount(dir string) int {
|
|||||||
count := 0
|
count := 0
|
||||||
_ = filepath.Walk(dir,
|
_ = filepath.Walk(dir,
|
||||||
func(path string, info os.FileInfo, err error) error {
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if !info.IsDir() {
|
if !info.IsDir() {
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
@ -50,22 +54,24 @@ func imageExists(filename string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for remote filepath, e.g: https://test.webp.sh/node.png
|
// Check for remote filepath, e.g: https://test.webp.sh/node.png
|
||||||
// return StatusCode, etagValue
|
// return StatusCode, etagValue and length
|
||||||
func getRemoteImageInfo(fileUrl string) (int, string) {
|
func getRemoteImageInfo(fileUrl string) (int, string, string) {
|
||||||
res, err := http.Head(fileUrl)
|
res, err := http.Head(fileUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("Connection to remote error!")
|
log.Errorln("Connection to remote error!")
|
||||||
return http.StatusInternalServerError, ""
|
return http.StatusInternalServerError, "", ""
|
||||||
}
|
}
|
||||||
if res.StatusCode != 404 {
|
if res.StatusCode != 404 {
|
||||||
etagValue := res.Header.Get("etag")
|
etagValue := res.Header.Get("etag")
|
||||||
if etagValue == "" {
|
if etagValue == "" {
|
||||||
log.Info("Remote didn't return etag in header, please check.")
|
log.Info("Remote didn't return etag in header, please check.")
|
||||||
} else {
|
} else {
|
||||||
return 200, etagValue
|
return 200, etagValue, res.Header.Get("content-length")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res.StatusCode, ""
|
|
||||||
|
return res.StatusCode, "", res.Header.Get("content-length")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchRemoteImage(filepath string, url string) error {
|
func fetchRemoteImage(filepath string, url string) error {
|
||||||
@ -184,3 +190,23 @@ func headerOrigin(header string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func chooseProxy(proxyRawSize string, webpAbsPath string) bool {
|
||||||
|
var proxyRaw, _ = strconv.Atoi(proxyRawSize)
|
||||||
|
webp, _ := ioutil.ReadFile(webpAbsPath)
|
||||||
|
if len(webp) > proxyRaw {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func chooseLocalSmallerFile(rawImageAbs, webpAbsPath string) string {
|
||||||
|
raw, _ := ioutil.ReadFile(rawImageAbs)
|
||||||
|
webp, _ := ioutil.ReadFile(webpAbsPath)
|
||||||
|
if len(webp) > len(raw) {
|
||||||
|
return rawImageAbs
|
||||||
|
} else {
|
||||||
|
return webpAbsPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -177,14 +177,16 @@ func TestChanErr(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetRemoteImageInfo(t *testing.T) {
|
func TestGetRemoteImageInfo(t *testing.T) {
|
||||||
url := "https://github.com/favicon.ico"
|
url := "https://github.com/favicon.ico"
|
||||||
statusCode, etag := getRemoteImageInfo(url)
|
statusCode, etag, length := getRemoteImageInfo(url)
|
||||||
assert.NotEqual(t, "", etag)
|
assert.NotEqual(t, "", etag)
|
||||||
|
assert.NotEqual(t, "0", length)
|
||||||
assert.Equal(t, statusCode, http.StatusOK)
|
assert.Equal(t, statusCode, http.StatusOK)
|
||||||
|
|
||||||
// test non-exist url
|
// test non-exist url
|
||||||
url = "http://sdahjajda.com"
|
url = "http://sdahjajda.com"
|
||||||
statusCode, etag = getRemoteImageInfo(url)
|
statusCode, etag, length = getRemoteImageInfo(url)
|
||||||
assert.Equal(t, "", etag)
|
assert.Equal(t, "", etag)
|
||||||
|
assert.Equal(t, "", length)
|
||||||
assert.Equal(t, statusCode, http.StatusInternalServerError)
|
assert.Equal(t, statusCode, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
pics/big.jpg
Normal file
BIN
pics/big.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
@ -16,7 +16,7 @@ func TestPrefetchImages(t *testing.T) {
|
|||||||
_ = os.Mkdir(fp, 0755)
|
_ = os.Mkdir(fp, 0755)
|
||||||
prefetchImages("./pics", "./prefetch", "80")
|
prefetchImages("./pics", "./prefetch", "80")
|
||||||
count := fileCount("./prefetch")
|
count := fileCount("./prefetch")
|
||||||
assert.Equal(t, 7, count)
|
assert.Equal(t, 8, count)
|
||||||
_ = os.RemoveAll(fp)
|
_ = os.RemoveAll(fp)
|
||||||
|
|
||||||
// concurrency
|
// concurrency
|
||||||
@ -24,6 +24,11 @@ func TestPrefetchImages(t *testing.T) {
|
|||||||
_ = os.Mkdir(fp, 0755)
|
_ = os.Mkdir(fp, 0755)
|
||||||
prefetchImages("./pics", "./prefetch", "80")
|
prefetchImages("./pics", "./prefetch", "80")
|
||||||
count = fileCount("./prefetch")
|
count = fileCount("./prefetch")
|
||||||
assert.Equal(t, 5, count)
|
assert.Equal(t, 6, count)
|
||||||
_ = os.RemoveAll(fp)
|
_ = os.RemoveAll(fp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBadPrefetch(t *testing.T) {
|
||||||
|
jobs = 1
|
||||||
|
prefetchImages("./pics2", "./prefetch", "80")
|
||||||
|
}
|
||||||
|
73
router.go
73
router.go
@ -33,7 +33,6 @@ func convert(c *fiber.Ctx) error {
|
|||||||
if needOrigin {
|
if needOrigin {
|
||||||
log.Debugf("A Safari/IE/whatever user has arrived...%s", ua)
|
log.Debugf("A Safari/IE/whatever user has arrived...%s", ua)
|
||||||
c.Set("ETag", genEtag(rawImageAbs))
|
c.Set("ETag", genEtag(rawImageAbs))
|
||||||
c.Set("X-Compression-Rate", NotCompressed)
|
|
||||||
if proxyMode {
|
if proxyMode {
|
||||||
localRemoteTmpPath := remoteRaw + reqURI
|
localRemoteTmpPath := remoteRaw + reqURI
|
||||||
_ = fetchRemoteImage(localRemoteTmpPath, rawImageAbs)
|
_ = fetchRemoteImage(localRemoteTmpPath, rawImageAbs)
|
||||||
@ -69,41 +68,10 @@ func convert(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start Proxy Mode
|
|
||||||
if proxyMode {
|
if proxyMode {
|
||||||
// https://test.webp.sh/node.png
|
return proxyHandler(c, reqURI)
|
||||||
realRemoteAddr := config.ImgPath + reqURI
|
|
||||||
// Ping Remote for status code and etag info
|
|
||||||
log.Infof("Remote Addr is %s fetching", realRemoteAddr)
|
|
||||||
statusCode, etagValue := getRemoteImageInfo(realRemoteAddr)
|
|
||||||
if statusCode == 200 {
|
|
||||||
// Check local path: /node.png-etag-<etagValue>
|
|
||||||
localEtagImagePath := config.ExhaustPath + reqURI + "-etag-" + etagValue
|
|
||||||
if imageExists(localEtagImagePath) {
|
|
||||||
return c.SendFile(localEtagImagePath)
|
|
||||||
} else {
|
|
||||||
// Temporary store of remote file.
|
|
||||||
cleanProxyCache(config.ExhaustPath + reqURI + "*")
|
|
||||||
localRemoteTmpPath := remoteRaw + reqURI
|
|
||||||
_ = fetchRemoteImage(localRemoteTmpPath, realRemoteAddr)
|
|
||||||
q, _ := strconv.ParseFloat(config.Quality, 32)
|
|
||||||
_ = os.MkdirAll(path.Dir(localEtagImagePath), 0755)
|
|
||||||
err := webpEncoder(localRemoteTmpPath, localEtagImagePath, float32(q), true, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Warning(err)
|
|
||||||
}
|
}
|
||||||
return c.SendFile(localEtagImagePath)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
msg := fmt.Sprintf("Remote returned %d status code!", statusCode)
|
|
||||||
_ = c.Send([]byte(msg))
|
|
||||||
log.Warn(msg)
|
|
||||||
_ = c.SendStatus(statusCode)
|
|
||||||
cleanProxyCache(config.ExhaustPath + reqURI + "*")
|
|
||||||
return errors.New(msg)
|
|
||||||
}
|
|
||||||
// End Proxy Mode
|
|
||||||
} else {
|
|
||||||
// Check the original image for existence,
|
// Check the original image for existence,
|
||||||
if !imageExists(rawImageAbs) {
|
if !imageExists(rawImageAbs) {
|
||||||
msg := "image not found"
|
msg := "image not found"
|
||||||
@ -151,6 +119,43 @@ func convert(c *fiber.Ctx) error {
|
|||||||
etag := genEtag(finalFile)
|
etag := genEtag(finalFile)
|
||||||
c.Set("ETag", etag)
|
c.Set("ETag", etag)
|
||||||
c.Set("X-Compression-Rate", getCompressionRate(rawImageAbs, webpAbsPath))
|
c.Set("X-Compression-Rate", getCompressionRate(rawImageAbs, webpAbsPath))
|
||||||
|
finalFile = chooseLocalSmallerFile(rawImageAbs, webpAbsPath)
|
||||||
return c.SendFile(finalFile)
|
return c.SendFile(finalFile)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func proxyHandler(c *fiber.Ctx, reqURI string) error {
|
||||||
|
// https://test.webp.sh/node.png
|
||||||
|
realRemoteAddr := config.ImgPath + reqURI
|
||||||
|
// Ping Remote for status code and etag info
|
||||||
|
log.Infof("Remote Addr is %s fetching", realRemoteAddr)
|
||||||
|
statusCode, etagValue, remoteLength := getRemoteImageInfo(realRemoteAddr)
|
||||||
|
if statusCode == 200 {
|
||||||
|
// Check local path: /node.png-etag-<etagValue>
|
||||||
|
localEtagWebPPath := config.ExhaustPath + reqURI + "-etag-" + etagValue
|
||||||
|
if imageExists(localEtagWebPPath) {
|
||||||
|
chooseProxy(remoteLength, localEtagWebPPath)
|
||||||
|
return c.SendFile(localEtagWebPPath)
|
||||||
|
} else {
|
||||||
|
// Temporary store of remote file.
|
||||||
|
cleanProxyCache(config.ExhaustPath + reqURI + "*")
|
||||||
|
localRawImagePath := remoteRaw + reqURI
|
||||||
|
_ = fetchRemoteImage(localRawImagePath, realRemoteAddr)
|
||||||
|
q, _ := strconv.ParseFloat(config.Quality, 32)
|
||||||
|
_ = os.MkdirAll(path.Dir(localEtagWebPPath), 0755)
|
||||||
|
err := webpEncoder(localRawImagePath, localEtagWebPPath, float32(q), true, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Warning(err)
|
||||||
|
}
|
||||||
|
chooseProxy(remoteLength, localEtagWebPPath)
|
||||||
|
return c.SendFile(localEtagWebPPath)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg := fmt.Sprintf("Remote returned %d status code!", statusCode)
|
||||||
|
_ = c.Send([]byte(msg))
|
||||||
|
log.Warn(msg)
|
||||||
|
_ = c.SendStatus(statusCode)
|
||||||
|
cleanProxyCache(config.ExhaustPath + reqURI + "*")
|
||||||
|
return errors.New(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ var (
|
|||||||
func setupParam() {
|
func setupParam() {
|
||||||
// setup parameters here...
|
// setup parameters here...
|
||||||
config.ImgPath = "./pics"
|
config.ImgPath = "./pics"
|
||||||
config.ExhaustPath = "./exhaust"
|
config.ExhaustPath = "./exhaust_test"
|
||||||
config.AllowedTypes = []string{"jpg", "png", "jpeg", "bmp"}
|
config.AllowedTypes = []string{"jpg", "png", "jpeg", "bmp"}
|
||||||
|
|
||||||
proxyMode = false
|
proxyMode = false
|
||||||
@ -55,7 +56,6 @@ func TestServerHeaders(t *testing.T) {
|
|||||||
ratio = response.Header.Get("X-Compression-Rate")
|
ratio = response.Header.Get("X-Compression-Rate")
|
||||||
etag = response.Header.Get("Etag")
|
etag = response.Header.Get("Etag")
|
||||||
|
|
||||||
assert.Equal(t, NotCompressed, ratio)
|
|
||||||
assert.NotEqual(t, "", etag)
|
assert.NotEqual(t, "", etag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,3 +156,20 @@ func TestConvertProxyModeWork(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
assert.Equal(t, "image/jpeg", getFileContentType(data))
|
assert.Equal(t, "image/jpeg", getFileContentType(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvertBigger(t *testing.T) {
|
||||||
|
setupParam()
|
||||||
|
config.Quality = "100"
|
||||||
|
|
||||||
|
jpg, _ := ioutil.ReadFile("pics/big.jpg")
|
||||||
|
|
||||||
|
var app = fiber.New()
|
||||||
|
app.Get("/*", convert)
|
||||||
|
|
||||||
|
url := "http://127.0.0.1:3333/big.jpg"
|
||||||
|
response, data := requestToServer(url, app, chromeUA)
|
||||||
|
assert.Equal(t, "image/jpeg", response.Header.Get("content-type"))
|
||||||
|
assert.True(t, len(data) == len(jpg))
|
||||||
|
|
||||||
|
_ = os.RemoveAll(config.ExhaustPath)
|
||||||
|
}
|
||||||
|
@ -41,9 +41,8 @@ func autoUpdate() {
|
|||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
filename += ".exe"
|
filename += ".exe"
|
||||||
}
|
}
|
||||||
var releaseUrl = "https://github.com/webp-sh/webp_server_go/releases/latest/download/" + filename
|
|
||||||
log.Info("Downloading binary to update...")
|
log.Info("Downloading binary to update...")
|
||||||
resp, _ := http.Get(releaseUrl)
|
resp, _ := http.Get(releaseUrl + filename)
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
log.Debugf("%s-%s not found on release.", runtime.GOOS, runtime.GOARCH)
|
log.Debugf("%s-%s not found on release.", runtime.GOOS, runtime.GOARCH)
|
||||||
return
|
return
|
||||||
|
@ -18,6 +18,15 @@ func TestNormalAutoUpdate(t *testing.T) {
|
|||||||
_ = os.RemoveAll(dir)
|
_ = os.RemoveAll(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test404AutoUpdate(t *testing.T) {
|
||||||
|
version = "0.0.1"
|
||||||
|
dir := "./update"
|
||||||
|
releaseUrl = releaseUrl + "a"
|
||||||
|
autoUpdate()
|
||||||
|
assert.Equal(t, 0, fileCount(dir))
|
||||||
|
_ = os.RemoveAll(dir)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNoNeedAutoUpdate(t *testing.T) {
|
func TestNoNeedAutoUpdate(t *testing.T) {
|
||||||
version = "99.99"
|
version = "99.99"
|
||||||
dir := "./update"
|
dir := "./update"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user