Feature: Add Compression rate on header (#59)

* Add X-Compression-Rate on response header

* add test case for compression rate, change logging and other improvements

* move config to config.go, add header for safari and proxyMode, logging improvement

* update gitignore for remote-raw and coverage.txt

* Refine log level
This commit is contained in:
Nova Kwok 2020-11-27 15:41:03 +08:00 committed by GitHub
parent 1881ae83cc
commit 9eda9ee799
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 142 additions and 73 deletions

2
.gitignore vendored
View File

@ -19,3 +19,5 @@ exhaust/
/.idea/modules.xml /.idea/modules.xml
/.idea/vcs.xml /.idea/vcs.xml
/.idea/webp_server_go.iml /.idea/webp_server_go.iml
remote-raw/
coverage.txt

59
config.go Normal file
View File

@ -0,0 +1,59 @@
// webp_server_go - config
// 2020-11-27 13:05
// Benny <benny.think@gmail.com>
package main
type Config struct {
Host string `json:"HOST"`
Port string `json:"PORT"`
ImgPath string `json:"IMG_PATH"`
Quality string `json:"QUALITY"`
AllowedTypes []string `json:"ALLOWED_TYPES"`
ExhaustPath string `json:"EXHAUST_PATH"`
}
var (
configPath string
jobs int
dumpConfig, dumpSystemd bool
verboseMode, showVersion bool
prefetch, proxyMode bool
config Config
version = "0.2.2"
)
const (
NotCompressed = "not_compressed"
WebpBigger = "webp_bigger"
)
const (
sampleConfig = `
{
"HOST": "127.0.0.1",
"PORT": "3333",
"QUALITY": "80",
"IMG_PATH": "./pics",
"EXHAUST_PATH": "./exhaust",
"ALLOWED_TYPES": ["jpg","png","jpeg","bmp"]
}`
sampleSystemd = `
[Unit]
Description=WebP Server Go
Documentation=https://github.com/webp-sh/webp_server_go
After=nginx.target
[Service]
Type=simple
StandardError=journal
WorkingDirectory=/opt/webps
ExecStart=/opt/webps/webp-server --config /opt/webps/config.json
Restart=always
RestartSec=3s
[Install]
WantedBy=multi-user.target`
)

View File

@ -91,7 +91,7 @@ func cleanProxyCache(cacheImagePath string) {
// Delete /node.png* // Delete /node.png*
files, err := filepath.Glob(cacheImagePath + "*") files, err := filepath.Glob(cacheImagePath + "*")
if err != nil { if err != nil {
fmt.Println(err) log.Infoln(err)
} }
for _, f := range files { for _, f := range files {
if err := os.Remove(f); err != nil { if err := os.Remove(f); err != nil {
@ -108,7 +108,7 @@ func genWebpAbs(RawImagePath string, ExhaustPath string, ImgFilename string, req
return "", "" return "", ""
} }
ModifiedTime := STAT.ModTime().Unix() ModifiedTime := STAT.ModTime().Unix()
// webpFilename: abc.jpg.png -> abc.jpg.png1582558990.webp // webpFilename: abc.jpg.png -> abc.jpg.png.1582558990.webp
WebpFilename := fmt.Sprintf("%s.%d.webp", ImgFilename, ModifiedTime) WebpFilename := fmt.Sprintf("%s.%d.webp", ImgFilename, ModifiedTime)
cwd, _ := os.Getwd() cwd, _ := os.Getwd()
@ -127,6 +127,22 @@ func genEtag(ImgAbsPath string) string {
return fmt.Sprintf(`W/"%d-%08X"`, len(data), crc) return fmt.Sprintf(`W/"%d-%08X"`, len(data), crc)
} }
func getCompressionRate(RawImagePath string, webpAbsPath string) string {
originFileInfo, err := os.Stat(RawImagePath)
if err != nil {
log.Warnf("fail to get raw image %v", err)
return ""
}
webpFileInfo, err := os.Stat(webpAbsPath)
if err != nil {
log.Warnf("fail to get webp image %v", err)
return ""
}
compressionRate := float64(webpFileInfo.Size()) / float64(originFileInfo.Size())
log.Debugf("The compress rate is %d/%d=%.2f", originFileInfo.Size(), webpFileInfo.Size(), compressionRate)
return fmt.Sprintf(`%.2f`, compressionRate)
}
func goOrigin(UA string) bool { func goOrigin(UA string) bool {
// for more information, please check test case // for more information, please check test case
if strings.Contains(UA, "Firefox") || strings.Contains(UA, "Chrome") { if strings.Contains(UA, "Firefox") || strings.Contains(UA, "Chrome") {

View File

@ -157,3 +157,18 @@ func TestCleanProxyCache(t *testing.T) {
// test bad dir // test bad dir
cleanProxyCache("/aasdyg/dhj2/dagh") cleanProxyCache("/aasdyg/dhj2/dagh")
} }
func TestGetCompressionRate(t *testing.T) {
pic1 := "pics/webp_server.bmp"
pic2 := "pics/webp_server.jpg"
var ratio string
ratio = getCompressionRate(pic1, pic2)
assert.Equal(t, "0.16", ratio)
ratio = getCompressionRate(pic1, "pic2")
assert.Equal(t, "", ratio)
ratio = getCompressionRate("pic1", pic2)
assert.Equal(t, "", ratio)
}

View File

@ -20,16 +20,17 @@ func convert(c *fiber.Ctx) error {
var imgFilename = path.Base(reqURI) // pure filename, 123.jpg var imgFilename = path.Base(reqURI) // pure filename, 123.jpg
var finalFile string // We'll only need one c.sendFile() var finalFile string // We'll only need one c.sendFile()
var UA = c.Get("User-Agent") var UA = c.Get("User-Agent")
done := goOrigin(UA) log.Debugf("Incoming connection from %s@%s with %s", UA, c.IP(), imgFilename)
if done {
needOrigin := goOrigin(UA)
if needOrigin {
log.Infof("A Safari/IE/whatever user has arrived...%s", UA) log.Infof("A Safari/IE/whatever user has arrived...%s", UA)
// Check for Safari users. If they're Safari, just simply ignore everything. // Check for Safari users. If they're Safari, just simply ignore everything.
etag := genEtag(rawImageAbs) etag := genEtag(rawImageAbs)
c.Set("ETag", etag) c.Set("ETag", etag)
c.Set("X-Compression-Rate", NotCompressed)
return c.SendFile(rawImageAbs) return c.SendFile(rawImageAbs)
} }
log.Debugf("Incoming connection from %s@%s with %s", UA, c.IP(), imgFilename)
// check ext // check ext
var allowed = false var allowed = false
@ -43,6 +44,7 @@ func convert(c *fiber.Ctx) error {
allowed = false allowed = false
} }
} }
if !allowed { if !allowed {
msg := "File extension not allowed! " + imgFilename msg := "File extension not allowed! " + imgFilename
log.Warn(msg) log.Warn(msg)
@ -60,7 +62,7 @@ func convert(c *fiber.Ctx) error {
// https://test.webp.sh/node.png // https://test.webp.sh/node.png
realRemoteAddr := config.ImgPath + reqURI realRemoteAddr := config.ImgPath + reqURI
// Ping Remote for status code and etag info // Ping Remote for status code and etag info
fmt.Println("Remote Addr is " + realRemoteAddr + ", fetching..") log.Infof("Remote Addr is %s fetching", realRemoteAddr)
statusCode, etagValue := getRemoteImageInfo(realRemoteAddr) statusCode, etagValue := getRemoteImageInfo(realRemoteAddr)
if statusCode == 200 { if statusCode == 200 {
// Check local path: /node.png-etag-<etagValue> // Check local path: /node.png-etag-<etagValue>
@ -77,7 +79,7 @@ func convert(c *fiber.Ctx) error {
_ = os.MkdirAll(path.Dir(localEtagImagePath), 0755) _ = os.MkdirAll(path.Dir(localEtagImagePath), 0755)
err := webpEncoder(localRemoteTmpPath, localEtagImagePath, float32(q), true, nil) err := webpEncoder(localRemoteTmpPath, localEtagImagePath, float32(q), true, nil)
if err != nil { if err != nil {
fmt.Println(err) log.Warning(err)
} }
return c.SendFile(localEtagImagePath) return c.SendFile(localEtagImagePath)
} }
@ -130,13 +132,14 @@ func convert(c *fiber.Ctx) error {
if err != nil { if err != nil {
log.Error(err) log.Error(err)
_ = c.SendStatus(400) _ = c.SendStatus(400)
_ = c.Send([]byte("Bad file!")) _ = c.Send([]byte("Bad file. " + err.Error()))
return err return err
} }
finalFile = webpAbsPath finalFile = webpAbsPath
} }
etag := genEtag(finalFile) etag := genEtag(finalFile)
c.Set("ETag", etag) c.Set("ETag", etag)
c.Set("X-Compression-Rate", getCompressionRate(rawImageAbs, webpAbsPath))
return c.SendFile(finalFile) return c.SendFile(finalFile)
} }
} }

View File

@ -18,6 +18,44 @@ var (
SafariUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15" SafariUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15"
) )
func setupParam() {
// setup parameters here...
config.ImgPath = "./pics"
config.ExhaustPath = "./exhaust"
config.AllowedTypes = []string{"jpg", "png", "jpeg", "bmp"}
}
func requestToServer(url string, app *fiber.App, ua string) (*http.Response, []byte) {
req := httptest.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", ua)
resp, _ := app.Test(req, 60000)
data, _ := ioutil.ReadAll(resp.Body)
return resp, data
}
func TestServerHeaders(t *testing.T) {
setupParam()
var app = fiber.New()
app.Get("/*", convert)
url := "http://127.0.0.1:3333/webp_server.bmp"
// test for chrome
response, _ := requestToServer(url, app, chromeUA)
ratio := response.Header.Get("X-Compression-Rate")
etag := response.Header.Get("Etag")
assert.NotEqual(t, "", ratio)
assert.NotEqual(t, "", etag)
// test for safari
response, _ = requestToServer(url, app, SafariUA)
ratio = response.Header.Get("X-Compression-Rate")
etag = response.Header.Get("Etag")
assert.Equal(t, NotCompressed, ratio)
assert.NotEqual(t, "", etag)
}
func TestConvert(t *testing.T) { func TestConvert(t *testing.T) {
setupParam() setupParam()
var testChromeLink = map[string]string{ var testChromeLink = map[string]string{
@ -108,20 +146,4 @@ func TestConvertProxyModeWork(t *testing.T) {
resp, data := requestToServer(url, app, chromeUA) resp, data := requestToServer(url, app, chromeUA)
assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "image/webp", getFileContentType(data)) assert.Equal(t, "image/webp", getFileContentType(data))
}
func setupParam() {
// setup parameters here...
config.ImgPath = "./pics"
config.ExhaustPath = "./exhaust"
config.AllowedTypes = []string{"jpg", "png", "jpeg", "bmp"}
}
func requestToServer(url string, app *fiber.App, ua string) (*http.Response, []byte) {
req := httptest.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", ua)
resp, _ := app.Test(req, 60000)
data, _ := ioutil.ReadAll(resp.Body)
return resp, data
} }

View File

@ -12,54 +12,6 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
type Config struct {
Host string `json:"HOST"`
Port string `json:"PORT"`
ImgPath string `json:"IMG_PATH"`
Quality string `json:"QUALITY"`
AllowedTypes []string `json:"ALLOWED_TYPES"`
ExhaustPath string `json:"EXHAUST_PATH"`
}
var (
configPath string
jobs int
dumpConfig, dumpSystemd, verboseMode, prefetch, showVersion bool
proxyMode bool
config Config
version = "0.2.2"
)
const (
sampleConfig = `
{
"HOST": "127.0.0.1",
"PORT": "3333",
"QUALITY": "80",
"IMG_PATH": "./pics",
"EXHAUST_PATH": "./exhaust",
"ALLOWED_TYPES": ["jpg","png","jpeg","bmp"]
}`
sampleSystemd = `
[Unit]
Description=WebP Server Go
Documentation=https://github.com/webp-sh/webp_server_go
After=nginx.target
[Service]
Type=simple
StandardError=journal
WorkingDirectory=/opt/webps
ExecStart=/opt/webps/webp-server --config /opt/webps/config.json
Restart=always
RestartSec=3s
[Install]
WantedBy=multi-user.target`
)
func loadConfig(path string) Config { func loadConfig(path string) Config {
jsonObject, err := os.Open(path) jsonObject, err := os.Open(path)
if err != nil { if err != nil {