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/vcs.xml
/.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*
files, err := filepath.Glob(cacheImagePath + "*")
if err != nil {
fmt.Println(err)
log.Infoln(err)
}
for _, f := range files {
if err := os.Remove(f); err != nil {
@ -108,7 +108,7 @@ func genWebpAbs(RawImagePath string, ExhaustPath string, ImgFilename string, req
return "", ""
}
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)
cwd, _ := os.Getwd()
@ -127,6 +127,22 @@ func genEtag(ImgAbsPath string) string {
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 {
// for more information, please check test case
if strings.Contains(UA, "Firefox") || strings.Contains(UA, "Chrome") {

View File

@ -157,3 +157,18 @@ func TestCleanProxyCache(t *testing.T) {
// test bad dir
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 finalFile string // We'll only need one c.sendFile()
var UA = c.Get("User-Agent")
done := goOrigin(UA)
if done {
log.Debugf("Incoming connection from %s@%s with %s", UA, c.IP(), imgFilename)
needOrigin := goOrigin(UA)
if needOrigin {
log.Infof("A Safari/IE/whatever user has arrived...%s", UA)
// Check for Safari users. If they're Safari, just simply ignore everything.
etag := genEtag(rawImageAbs)
c.Set("ETag", etag)
c.Set("X-Compression-Rate", NotCompressed)
return c.SendFile(rawImageAbs)
}
log.Debugf("Incoming connection from %s@%s with %s", UA, c.IP(), imgFilename)
// check ext
var allowed = false
@ -43,6 +44,7 @@ func convert(c *fiber.Ctx) error {
allowed = false
}
}
if !allowed {
msg := "File extension not allowed! " + imgFilename
log.Warn(msg)
@ -60,7 +62,7 @@ func convert(c *fiber.Ctx) error {
// https://test.webp.sh/node.png
realRemoteAddr := config.ImgPath + reqURI
// 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)
if statusCode == 200 {
// Check local path: /node.png-etag-<etagValue>
@ -77,7 +79,7 @@ func convert(c *fiber.Ctx) error {
_ = os.MkdirAll(path.Dir(localEtagImagePath), 0755)
err := webpEncoder(localRemoteTmpPath, localEtagImagePath, float32(q), true, nil)
if err != nil {
fmt.Println(err)
log.Warning(err)
}
return c.SendFile(localEtagImagePath)
}
@ -130,13 +132,14 @@ func convert(c *fiber.Ctx) error {
if err != nil {
log.Error(err)
_ = c.SendStatus(400)
_ = c.Send([]byte("Bad file!"))
_ = c.Send([]byte("Bad file. " + err.Error()))
return err
}
finalFile = webpAbsPath
}
etag := genEtag(finalFile)
c.Set("ETag", etag)
c.Set("X-Compression-Rate", getCompressionRate(rawImageAbs, webpAbsPath))
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"
)
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) {
setupParam()
var testChromeLink = map[string]string{
@ -108,20 +146,4 @@ func TestConvertProxyModeWork(t *testing.T) {
resp, data := requestToServer(url, app, chromeUA)
assert.Equal(t, http.StatusOK, resp.StatusCode)
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"
)
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 {
jsonObject, err := os.Open(path)
if err != nil {