mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-03 06:40:25 +08:00
支持自动转换图像文件为WebP
This commit is contained in:
2
go.mod
2
go.mod
@@ -9,6 +9,7 @@ require (
|
||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||
github.com/andybalholm/brotli v1.0.3
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/chai2010/webp v1.1.0
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/dop251/goja v0.0.0-20210804101310-32956a348b49
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
@@ -22,6 +23,7 @@ require (
|
||||
github.com/mssola/user_agent v0.5.2
|
||||
github.com/shirou/gopsutil v3.21.5+incompatible
|
||||
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
||||
golang.org/x/text v0.3.6
|
||||
|
||||
3
go.sum
3
go.sum
@@ -15,6 +15,8 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/webp v1.1.0 h1:4Ei0/BRroMF9FaXDG2e4OxwFcuW2vcXd+A6tyqTJUQQ=
|
||||
github.com/chai2010/webp v1.1.0/go.mod h1:LP12PG5IFmLGHUU26tBiCBKnghxx3toZFwDjOYvd3Ow=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
@@ -128,6 +130,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
|
||||
@@ -338,6 +338,11 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
||||
this.web.Compression = web.Compression
|
||||
}
|
||||
|
||||
// webp
|
||||
if web.WebP != nil && (web.WebP.IsPrior || isTop) {
|
||||
this.web.WebP = web.WebP
|
||||
}
|
||||
|
||||
// cache
|
||||
if web.Cache != nil && (web.Cache.IsPrior || isTop) {
|
||||
this.web.Cache = web.Cache
|
||||
|
||||
@@ -8,8 +8,16 @@ import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/chai2010/webp"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/webp"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
@@ -21,6 +29,10 @@ type HTTPWriter struct {
|
||||
req *HTTPRequest
|
||||
writer http.ResponseWriter
|
||||
|
||||
webpIsEncoding bool
|
||||
webpBuffer *bytes.Buffer
|
||||
webpIsWriting bool
|
||||
|
||||
compressionConfig *serverconfigs.HTTPCompressionConfig
|
||||
compressionWriter compressions.Writer
|
||||
compressionType serverconfigs.HTTPCompressionType
|
||||
@@ -69,11 +81,20 @@ func (this *HTTPWriter) SetCompression(config *serverconfigs.HTTPCompressionConf
|
||||
}
|
||||
|
||||
// Prepare 准备输出
|
||||
// 缓存不调用此函数
|
||||
func (this *HTTPWriter) Prepare(size int64, status int) {
|
||||
this.statusCode = status
|
||||
|
||||
this.prepareCompression(size)
|
||||
if status == http.StatusOK {
|
||||
this.prepareWebP(size)
|
||||
}
|
||||
|
||||
this.prepareCache(size)
|
||||
|
||||
// 在WebP模式下,压缩暂不可用
|
||||
if !this.webpIsEncoding {
|
||||
this.PrepareCompression(size)
|
||||
}
|
||||
}
|
||||
|
||||
// Raw 包装前的原始的Writer
|
||||
@@ -106,40 +127,46 @@ func (this *HTTPWriter) AddHeaders(header http.Header) {
|
||||
|
||||
// Write 写入数据
|
||||
func (this *HTTPWriter) Write(data []byte) (n int, err error) {
|
||||
if this.writer != nil {
|
||||
if this.compressionWriter != nil {
|
||||
n, err = this.compressionWriter.Write(data)
|
||||
} else {
|
||||
n, err = this.writer.Write(data)
|
||||
}
|
||||
if n > 0 {
|
||||
this.sentBodyBytes += int64(n)
|
||||
}
|
||||
n = len(data)
|
||||
|
||||
// 写入缓存
|
||||
if this.cacheWriter != nil {
|
||||
_, err = this.cacheWriter.Write(data)
|
||||
if err != nil {
|
||||
_ = this.cacheWriter.Discard()
|
||||
this.cacheWriter = nil
|
||||
remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if n == 0 {
|
||||
n = len(data) // 防止出现short write错误
|
||||
}
|
||||
}
|
||||
if this.bodyCopying {
|
||||
if this.compressionBodyWriter != nil {
|
||||
_, err := this.compressionBodyWriter.Write(data)
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_WRITER", err.Error())
|
||||
}
|
||||
if this.writer != nil {
|
||||
if this.webpIsEncoding && !this.webpIsWriting {
|
||||
this.webpBuffer.Write(data)
|
||||
} else {
|
||||
this.body = append(this.body, data...)
|
||||
// 写入压缩
|
||||
var n1 int
|
||||
if this.compressionWriter != nil {
|
||||
n1, err = this.compressionWriter.Write(data)
|
||||
} else {
|
||||
n1, err = this.writer.Write(data)
|
||||
}
|
||||
if n1 > 0 {
|
||||
this.sentBodyBytes += int64(n1)
|
||||
}
|
||||
|
||||
// 写入缓存
|
||||
if this.cacheWriter != nil {
|
||||
_, err = this.cacheWriter.Write(data)
|
||||
if err != nil {
|
||||
_ = this.cacheWriter.Discard()
|
||||
this.cacheWriter = nil
|
||||
remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if this.bodyCopying {
|
||||
if this.compressionBodyWriter != nil {
|
||||
_, err := this.compressionBodyWriter.Write(data)
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_WRITER", err.Error())
|
||||
}
|
||||
} else {
|
||||
this.body = append(this.body, data...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -254,6 +281,40 @@ func (this *HTTPWriter) Close() {
|
||||
_ = this.cacheWriter.Discard()
|
||||
}
|
||||
}
|
||||
|
||||
// webp writer
|
||||
if this.webpIsEncoding {
|
||||
imageData, _, err := image.Decode(this.webpBuffer)
|
||||
if err != nil {
|
||||
_, _ = io.Copy(this.writer, this.webpBuffer)
|
||||
|
||||
// 处理缓存
|
||||
if this.cacheWriter != nil {
|
||||
_ = this.cacheWriter.Discard()
|
||||
}
|
||||
this.cacheWriter = nil
|
||||
} else {
|
||||
var f = types.Float32(this.req.web.WebP.Quality)
|
||||
if f > 100 {
|
||||
f = 100
|
||||
}
|
||||
this.webpIsWriting = true
|
||||
err = webp.Encode(this, imageData, &webp.Options{
|
||||
Lossless: false,
|
||||
Quality: f,
|
||||
Exact: true,
|
||||
})
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_WRITER", "encode webp failed: "+err.Error())
|
||||
|
||||
// 处理缓存
|
||||
if this.cacheWriter != nil {
|
||||
_ = this.cacheWriter.Discard()
|
||||
}
|
||||
this.cacheWriter = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hijack Hijack
|
||||
@@ -273,8 +334,24 @@ func (this *HTTPWriter) Flush() {
|
||||
}
|
||||
}
|
||||
|
||||
// 准备压缩
|
||||
func (this *HTTPWriter) prepareCompression(size int64) {
|
||||
// 准备Webp
|
||||
func (this *HTTPWriter) prepareWebP(size int64) {
|
||||
if this.req.web != nil &&
|
||||
this.req.web.WebP != nil &&
|
||||
this.req.web.WebP.IsOn &&
|
||||
this.req.web.WebP.MatchResponse(this.Header().Get("Content-Type"), size, filepath.Ext(this.req.requestPath()), this.req.Format) &&
|
||||
this.req.web.WebP.MatchAccept(this.req.requestHeader("Accept")) &&
|
||||
len(this.writer.Header().Get("Content-Encoding")) == 0 {
|
||||
this.webpIsEncoding = true
|
||||
this.webpBuffer = &bytes.Buffer{}
|
||||
|
||||
this.Header().Del("Content-Length")
|
||||
this.Header().Set("Content-Type", "image/webp")
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareCompression 准备压缩
|
||||
func (this *HTTPWriter) PrepareCompression(size int64) {
|
||||
if this.compressionConfig == nil || !this.compressionConfig.IsOn || this.compressionConfig.Level <= 0 {
|
||||
return
|
||||
}
|
||||
@@ -396,14 +473,6 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
||||
return
|
||||
}
|
||||
this.cacheWriter = cacheWriter
|
||||
if this.compressionWriter != nil {
|
||||
cacheCompressionWriter, err := compressions.NewWriter(this.cacheWriter, this.compressionType, this.compressionWriter.Level())
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_WRITER", "create cache compression writer failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
this.cacheWriter = caches.NewCompressionWriter(this.cacheWriter, cacheCompressionWriter, this.req.cacheKey, expiredAt)
|
||||
}
|
||||
|
||||
// 写入Header
|
||||
for k, v := range this.Header() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -14,7 +13,7 @@ func TestTicker(t *testing.T) {
|
||||
ticker.Stop()
|
||||
}()
|
||||
for ticker.Next() {
|
||||
logs.Println("tick")
|
||||
t.Log("tick")
|
||||
}
|
||||
t.Log("finished")
|
||||
}
|
||||
@@ -26,10 +25,10 @@ func TestTicker2(t *testing.T) {
|
||||
ticker.Stop()
|
||||
}()
|
||||
for {
|
||||
logs.Println("loop")
|
||||
t.Log("loop")
|
||||
select {
|
||||
case <-ticker.C:
|
||||
logs.Println("tick")
|
||||
t.Log("tick")
|
||||
case <-ticker.S:
|
||||
return
|
||||
}
|
||||
@@ -42,7 +41,7 @@ func TestTickerEvery(t *testing.T) {
|
||||
wg.Add(1)
|
||||
Every(2*time.Second, func(ticker *Ticker) {
|
||||
i++
|
||||
logs.Println("TestTickerEvery i:", i)
|
||||
t.Log("TestTickerEvery i:", i)
|
||||
if i >= 4 {
|
||||
ticker.Stop()
|
||||
wg.Done()
|
||||
|
||||
Reference in New Issue
Block a user