2021-12-16 17:27:21 +08:00
|
|
|
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
|
|
|
|
|
2020-09-26 08:07:07 +08:00
|
|
|
|
package nodes
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"bufio"
|
|
|
|
|
|
"bytes"
|
2021-10-13 11:56:40 +08:00
|
|
|
|
"compress/flate"
|
|
|
|
|
|
"compress/gzip"
|
2020-09-26 08:07:07 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
2020-10-05 16:55:14 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
2021-09-29 19:37:07 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
2020-12-17 17:36:10 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
2020-10-05 16:55:14 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
2021-10-13 11:56:40 +08:00
|
|
|
|
"github.com/andybalholm/brotli"
|
2021-10-11 14:53:23 +08:00
|
|
|
|
_ "github.com/biessek/golang-ico"
|
2020-10-05 16:55:14 +08:00
|
|
|
|
"github.com/iwind/TeaGo/lists"
|
2021-07-20 19:48:08 +08:00
|
|
|
|
"github.com/iwind/TeaGo/types"
|
2021-10-29 12:19:26 +08:00
|
|
|
|
"github.com/iwind/gowebp"
|
2021-10-01 16:24:17 +08:00
|
|
|
|
_ "golang.org/x/image/bmp"
|
|
|
|
|
|
_ "golang.org/x/image/webp"
|
|
|
|
|
|
"image"
|
2021-10-29 12:19:26 +08:00
|
|
|
|
"image/gif"
|
2021-10-01 16:24:17 +08:00
|
|
|
|
_ "image/gif"
|
|
|
|
|
|
_ "image/jpeg"
|
|
|
|
|
|
_ "image/png"
|
2021-10-13 11:56:40 +08:00
|
|
|
|
"io"
|
2020-09-26 08:07:07 +08:00
|
|
|
|
"net"
|
|
|
|
|
|
"net/http"
|
2021-09-29 19:37:07 +08:00
|
|
|
|
"path/filepath"
|
2020-09-29 17:21:46 +08:00
|
|
|
|
"strings"
|
2021-10-01 18:59:44 +08:00
|
|
|
|
"sync/atomic"
|
2020-09-26 08:07:07 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2021-10-01 18:59:44 +08:00
|
|
|
|
// 限制WebP能够同时使用的Buffer内存使用量
|
|
|
|
|
|
const webpMaxBufferSize int64 = 1_000_000_000
|
2021-10-03 18:00:57 +08:00
|
|
|
|
const webpSuffix = "@GOEDGE_WEBP"
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
|
|
|
|
|
var webpTotalBufferSize int64 = 0
|
|
|
|
|
|
var webpBufferPool = utils.NewBufferPool(1024)
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// HTTPWriter 响应Writer
|
2020-09-26 08:07:07 +08:00
|
|
|
|
type HTTPWriter struct {
|
2020-09-29 17:21:46 +08:00
|
|
|
|
req *HTTPRequest
|
2020-09-26 08:07:07 +08:00
|
|
|
|
writer http.ResponseWriter
|
|
|
|
|
|
|
2021-10-01 18:59:44 +08:00
|
|
|
|
size int64
|
|
|
|
|
|
|
2021-10-13 11:11:57 +08:00
|
|
|
|
webpIsEncoding bool
|
|
|
|
|
|
webpBuffer *bytes.Buffer
|
|
|
|
|
|
webpIsWriting bool
|
|
|
|
|
|
webpOriginContentType string
|
2021-10-13 11:56:40 +08:00
|
|
|
|
webpOriginEncoding string // gzip
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
compressionConfig *serverconfigs.HTTPCompressionConfig
|
|
|
|
|
|
compressionWriter compressions.Writer
|
|
|
|
|
|
compressionType serverconfigs.HTTPCompressionType
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
|
|
|
|
|
statusCode int
|
|
|
|
|
|
sentBodyBytes int64
|
|
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
bodyCopying bool
|
|
|
|
|
|
body []byte
|
|
|
|
|
|
compressionBodyBuffer *bytes.Buffer // 当使用压缩时使用
|
|
|
|
|
|
compressionBodyWriter compressions.Writer // 当使用压缩时使用
|
2020-10-05 16:55:14 +08:00
|
|
|
|
|
|
|
|
|
|
cacheWriter caches.Writer // 缓存写入
|
|
|
|
|
|
cacheStorage caches.StorageInterface
|
2021-06-06 23:42:11 +08:00
|
|
|
|
|
2021-12-30 11:19:11 +08:00
|
|
|
|
isOk bool // 是否完全成功
|
|
|
|
|
|
isFinished bool // 是否已完成
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// NewHTTPWriter 包装对象
|
2020-09-29 17:21:46 +08:00
|
|
|
|
func NewHTTPWriter(req *HTTPRequest, httpResponseWriter http.ResponseWriter) *HTTPWriter {
|
2020-09-26 08:07:07 +08:00
|
|
|
|
return &HTTPWriter{
|
2020-09-29 17:21:46 +08:00
|
|
|
|
req: req,
|
2020-09-26 08:07:07 +08:00
|
|
|
|
writer: httpResponseWriter,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
// SetCompression 设置内容压缩配置
|
|
|
|
|
|
func (this *HTTPWriter) SetCompression(config *serverconfigs.HTTPCompressionConfig) {
|
|
|
|
|
|
this.compressionConfig = config
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Prepare 准备输出
|
2021-10-01 16:24:17 +08:00
|
|
|
|
// 缓存不调用此函数
|
2021-10-13 11:11:57 +08:00
|
|
|
|
func (this *HTTPWriter) Prepare(size int64, status int) (delayHeaders bool) {
|
2021-10-01 18:59:44 +08:00
|
|
|
|
this.size = size
|
2021-05-23 14:29:56 +08:00
|
|
|
|
this.statusCode = status
|
|
|
|
|
|
|
2021-10-01 16:24:17 +08:00
|
|
|
|
if status == http.StatusOK {
|
|
|
|
|
|
this.prepareWebP(size)
|
2021-10-13 11:11:57 +08:00
|
|
|
|
|
|
|
|
|
|
if this.webpIsEncoding {
|
|
|
|
|
|
delayHeaders = true
|
|
|
|
|
|
}
|
2021-10-01 16:24:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 16:55:14 +08:00
|
|
|
|
this.prepareCache(size)
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
|
|
|
|
|
// 在WebP模式下,压缩暂不可用
|
|
|
|
|
|
if !this.webpIsEncoding {
|
|
|
|
|
|
this.PrepareCompression(size)
|
|
|
|
|
|
}
|
2021-10-13 11:11:57 +08:00
|
|
|
|
|
2021-12-12 11:48:01 +08:00
|
|
|
|
// 是否限速写入
|
|
|
|
|
|
if this.req.web != nil &&
|
|
|
|
|
|
this.req.web.RequestLimit != nil &&
|
|
|
|
|
|
this.req.web.RequestLimit.IsOn &&
|
|
|
|
|
|
this.req.web.RequestLimit.OutBandwidthPerConnBytes() > 0 {
|
|
|
|
|
|
this.writer = NewHTTPRateWriter(this.writer, this.req.web.RequestLimit.OutBandwidthPerConnBytes())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-13 11:11:57 +08:00
|
|
|
|
return
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Raw 包装前的原始的Writer
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) Raw() http.ResponseWriter {
|
|
|
|
|
|
return this.writer
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Header 获取Header
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) Header() http.Header {
|
|
|
|
|
|
if this.writer == nil {
|
|
|
|
|
|
return http.Header{}
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.writer.Header()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-30 11:19:11 +08:00
|
|
|
|
// DeleteHeader 删除Header
|
|
|
|
|
|
func (this *HTTPWriter) DeleteHeader(name string) {
|
|
|
|
|
|
this.writer.Header().Del(name)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SetHeader 设置Header
|
|
|
|
|
|
func (this *HTTPWriter) SetHeader(name string, values []string) {
|
|
|
|
|
|
this.writer.Header()[name] = values
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// AddHeaders 添加一组Header
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) AddHeaders(header http.Header) {
|
|
|
|
|
|
if this.writer == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
for key, value := range header {
|
2020-09-27 15:26:06 +08:00
|
|
|
|
if key == "Connection" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
for _, v := range value {
|
|
|
|
|
|
this.writer.Header().Add(key, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Write 写入数据
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) Write(data []byte) (n int, err error) {
|
2021-10-01 16:24:17 +08:00
|
|
|
|
n = len(data)
|
|
|
|
|
|
|
2020-09-26 08:07:07 +08:00
|
|
|
|
if this.writer != nil {
|
2021-10-01 16:24:17 +08:00
|
|
|
|
if this.webpIsEncoding && !this.webpIsWriting {
|
|
|
|
|
|
this.webpBuffer.Write(data)
|
2020-09-26 08:07:07 +08:00
|
|
|
|
} else {
|
2021-10-01 16:24:17 +08:00
|
|
|
|
// 写入压缩
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
|
2021-10-01 16:24:17 +08:00
|
|
|
|
// 写入缓存
|
|
|
|
|
|
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())
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
}
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
|
|
|
|
|
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...)
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
2020-09-26 08:07:07 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// WriteString 写入字符串
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) WriteString(s string) (n int, err error) {
|
|
|
|
|
|
return this.Write([]byte(s))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// SentBodyBytes 读取发送的字节数
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) SentBodyBytes() int64 {
|
|
|
|
|
|
return this.sentBodyBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// WriteHeader 写入状态码
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) WriteHeader(statusCode int) {
|
|
|
|
|
|
if this.writer != nil {
|
|
|
|
|
|
this.writer.WriteHeader(statusCode)
|
|
|
|
|
|
}
|
|
|
|
|
|
this.statusCode = statusCode
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-30 11:19:11 +08:00
|
|
|
|
// Send 发送响应
|
|
|
|
|
|
func (this *HTTPWriter) Send(status int, body string) {
|
|
|
|
|
|
this.WriteHeader(status)
|
|
|
|
|
|
_, _ = this.WriteString(body)
|
|
|
|
|
|
this.isFinished = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// StatusCode 读取状态码
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) StatusCode() int {
|
|
|
|
|
|
if this.statusCode == 0 {
|
|
|
|
|
|
return http.StatusOK
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.statusCode
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// SetBodyCopying 设置拷贝Body数据
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) SetBodyCopying(b bool) {
|
|
|
|
|
|
this.bodyCopying = b
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// BodyIsCopying 判断是否在拷贝Body数据
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) BodyIsCopying() bool {
|
|
|
|
|
|
return this.bodyCopying
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Body 读取拷贝的Body数据
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) Body() []byte {
|
|
|
|
|
|
return this.body
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// HeaderData 读取Header二进制数据
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) HeaderData() []byte {
|
|
|
|
|
|
if this.writer == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
resp := &http.Response{}
|
|
|
|
|
|
resp.Header = this.Header()
|
|
|
|
|
|
if this.statusCode == 0 {
|
|
|
|
|
|
this.statusCode = http.StatusOK
|
|
|
|
|
|
}
|
|
|
|
|
|
resp.StatusCode = this.statusCode
|
|
|
|
|
|
resp.ProtoMajor = 1
|
|
|
|
|
|
resp.ProtoMinor = 1
|
|
|
|
|
|
|
|
|
|
|
|
resp.ContentLength = 1 // Trick:这样可以屏蔽Content-Length
|
|
|
|
|
|
|
|
|
|
|
|
writer := bytes.NewBuffer([]byte{})
|
|
|
|
|
|
_ = resp.Write(writer)
|
|
|
|
|
|
return writer.Bytes()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// SetOk 设置成功
|
|
|
|
|
|
func (this *HTTPWriter) SetOk() {
|
|
|
|
|
|
this.isOk = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Close 关闭
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) Close() {
|
2021-10-01 18:59:44 +08:00
|
|
|
|
if this.webpIsEncoding {
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
atomic.AddInt64(&webpTotalBufferSize, -this.size*32)
|
|
|
|
|
|
webpBufferPool.Put(this.webpBuffer)
|
|
|
|
|
|
}()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-01 17:20:37 +08:00
|
|
|
|
// webp writer
|
|
|
|
|
|
if this.isOk && this.webpIsEncoding {
|
2021-10-01 18:59:44 +08:00
|
|
|
|
var bufferLen = int64(this.webpBuffer.Len())
|
2021-10-13 11:11:57 +08:00
|
|
|
|
atomic.AddInt64(&webpTotalBufferSize, bufferLen*4)
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
2021-10-13 11:11:57 +08:00
|
|
|
|
// 需要把字节读取出来做备份,防止在image.Decode()过程中丢失
|
|
|
|
|
|
var imageBytes = this.webpBuffer.Bytes()
|
2021-10-13 11:56:40 +08:00
|
|
|
|
var imageData image.Image
|
2021-10-29 12:19:26 +08:00
|
|
|
|
var gifImage *gif.GIF
|
|
|
|
|
|
var isGif = strings.Contains(this.webpOriginContentType, "image/gif")
|
|
|
|
|
|
|
2021-10-13 11:56:40 +08:00
|
|
|
|
var err error
|
|
|
|
|
|
if this.webpOriginEncoding == "gzip" {
|
|
|
|
|
|
this.Header().Del("Content-Encoding")
|
|
|
|
|
|
var reader *gzip.Reader
|
|
|
|
|
|
reader, err = gzip.NewReader(this.webpBuffer)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
_ = reader.Close()
|
|
|
|
|
|
}()
|
2021-10-29 12:19:26 +08:00
|
|
|
|
if isGif {
|
|
|
|
|
|
gifImage, err = gif.DecodeAll(reader)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
imageData, _, err = image.Decode(reader)
|
|
|
|
|
|
}
|
2021-10-13 11:56:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else if this.webpOriginEncoding == "deflate" {
|
|
|
|
|
|
this.Header().Del("Content-Encoding")
|
|
|
|
|
|
var reader io.ReadCloser
|
|
|
|
|
|
reader = flate.NewReader(this.webpBuffer)
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
_ = reader.Close()
|
|
|
|
|
|
}()
|
2021-10-29 12:19:26 +08:00
|
|
|
|
if isGif {
|
|
|
|
|
|
gifImage, err = gif.DecodeAll(reader)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
imageData, _, err = image.Decode(reader)
|
|
|
|
|
|
}
|
2021-10-13 11:56:40 +08:00
|
|
|
|
} else if this.webpOriginEncoding == "br" {
|
|
|
|
|
|
this.Header().Del("Content-Encoding")
|
|
|
|
|
|
var reader *brotli.Reader
|
|
|
|
|
|
reader = brotli.NewReader(this.webpBuffer)
|
2021-10-29 12:19:26 +08:00
|
|
|
|
if isGif {
|
|
|
|
|
|
gifImage, err = gif.DecodeAll(reader)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
imageData, _, err = image.Decode(reader)
|
|
|
|
|
|
}
|
2021-10-13 11:56:40 +08:00
|
|
|
|
} else {
|
2021-10-29 12:19:26 +08:00
|
|
|
|
if isGif {
|
|
|
|
|
|
gifImage, err = gif.DecodeAll(this.webpBuffer)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
imageData, _, err = image.Decode(this.webpBuffer)
|
|
|
|
|
|
}
|
2021-10-13 11:56:40 +08:00
|
|
|
|
}
|
2021-10-01 17:20:37 +08:00
|
|
|
|
if err != nil {
|
2021-10-13 11:11:57 +08:00
|
|
|
|
this.Header().Set("Content-Type", this.webpOriginContentType)
|
|
|
|
|
|
this.WriteHeader(http.StatusOK)
|
|
|
|
|
|
_, _ = this.writer.Write(imageBytes)
|
2021-10-01 17:20:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理缓存
|
|
|
|
|
|
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
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
2021-10-29 12:19:26 +08:00
|
|
|
|
if imageData != nil {
|
|
|
|
|
|
err = gowebp.Encode(this, imageData, &gowebp.Options{
|
|
|
|
|
|
Lossless: false,
|
|
|
|
|
|
Quality: f,
|
|
|
|
|
|
Exact: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
} else if gifImage != nil {
|
|
|
|
|
|
anim := gowebp.NewWebpAnimation(gifImage.Config.Width, gifImage.Config.Height, gifImage.LoopCount)
|
|
|
|
|
|
anim.WebPAnimEncoderOptions.SetKmin(9)
|
|
|
|
|
|
anim.WebPAnimEncoderOptions.SetKmax(17)
|
|
|
|
|
|
defer anim.ReleaseMemory()
|
|
|
|
|
|
webpConfig := gowebp.NewWebpConfig()
|
|
|
|
|
|
//webpConfig.SetLossless(1)
|
|
|
|
|
|
webpConfig.SetQuality(f)
|
|
|
|
|
|
|
|
|
|
|
|
timeline := 0
|
|
|
|
|
|
|
|
|
|
|
|
for i, img := range gifImage.Image {
|
|
|
|
|
|
err = anim.AddFrame(img, timeline, webpConfig)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
timeline += gifImage.Delay[i] * 10
|
|
|
|
|
|
}
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
err = anim.AddFrame(nil, timeline, webpConfig)
|
|
|
|
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
err = anim.Encode(this)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-10-01 17:20:37 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
if !this.req.canIgnore(err) {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "encode webp failed: "+err.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-13 11:11:57 +08:00
|
|
|
|
this.Header().Set("Content-Type", this.webpOriginContentType)
|
|
|
|
|
|
this.WriteHeader(http.StatusOK)
|
|
|
|
|
|
_, _ = this.writer.Write(imageBytes)
|
|
|
|
|
|
|
2021-10-01 17:20:37 +08:00
|
|
|
|
// 处理缓存
|
|
|
|
|
|
if this.cacheWriter != nil {
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
}
|
|
|
|
|
|
this.cacheWriter = nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
2021-10-13 11:11:57 +08:00
|
|
|
|
atomic.AddInt64(&webpTotalBufferSize, -bufferLen*4)
|
2021-10-01 18:59:44 +08:00
|
|
|
|
this.webpBuffer.Reset()
|
2021-10-01 17:20:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
// compression writer
|
|
|
|
|
|
if this.compressionWriter != nil {
|
|
|
|
|
|
if this.bodyCopying && this.compressionBodyWriter != nil {
|
|
|
|
|
|
_ = this.compressionBodyWriter.Close()
|
|
|
|
|
|
this.body = this.compressionBodyBuffer.Bytes()
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
2021-09-29 19:37:07 +08:00
|
|
|
|
_ = this.compressionWriter.Close()
|
|
|
|
|
|
this.compressionWriter = nil
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
|
|
|
|
|
|
// cache writer
|
|
|
|
|
|
if this.cacheWriter != nil {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
if this.isOk {
|
2021-07-20 19:48:08 +08:00
|
|
|
|
// 对比Content-Length
|
|
|
|
|
|
contentLengthString := this.Header().Get("Content-Length")
|
|
|
|
|
|
if len(contentLengthString) > 0 {
|
|
|
|
|
|
contentLength := types.Int64(contentLengthString)
|
|
|
|
|
|
if contentLength != this.cacheWriter.BodySize() {
|
|
|
|
|
|
this.isOk = false
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if this.isOk {
|
|
|
|
|
|
err := this.cacheWriter.Close()
|
|
|
|
|
|
if err == nil {
|
2021-12-16 17:27:21 +08:00
|
|
|
|
var expiredAt = this.cacheWriter.ExpiredAt()
|
2021-07-20 19:48:08 +08:00
|
|
|
|
this.cacheStorage.AddToList(&caches.Item{
|
|
|
|
|
|
Type: this.cacheWriter.ItemType(),
|
|
|
|
|
|
Key: this.cacheWriter.Key(),
|
2021-12-16 17:27:21 +08:00
|
|
|
|
ExpiredAt: expiredAt,
|
|
|
|
|
|
StaleAt: expiredAt + int64(this.calculateStaleLife()),
|
2021-07-20 19:48:08 +08:00
|
|
|
|
HeaderSize: this.cacheWriter.HeaderSize(),
|
|
|
|
|
|
BodySize: this.cacheWriter.BodySize(),
|
2021-12-30 11:19:11 +08:00
|
|
|
|
Host: this.req.host,
|
2021-07-20 19:48:08 +08:00
|
|
|
|
ServerId: this.req.Server.Id,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2021-06-06 23:42:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
2020-10-05 16:55:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Hijack Hijack
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) {
|
|
|
|
|
|
hijack, ok := this.writer.(http.Hijacker)
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
return hijack.Hijack()
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Flush Flush
|
2020-09-26 08:07:07 +08:00
|
|
|
|
func (this *HTTPWriter) Flush() {
|
|
|
|
|
|
flusher, ok := this.writer.(http.Flusher)
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
flusher.Flush()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
|
2021-10-01 16:24:17 +08:00
|
|
|
|
// 准备Webp
|
|
|
|
|
|
func (this *HTTPWriter) prepareWebP(size int64) {
|
|
|
|
|
|
if this.req.web != nil &&
|
|
|
|
|
|
this.req.web.WebP != nil &&
|
|
|
|
|
|
this.req.web.WebP.IsOn &&
|
2021-12-30 11:19:11 +08:00
|
|
|
|
this.req.web.WebP.MatchResponse(this.Header().Get("Content-Type"), size, filepath.Ext(this.req.Path()), this.req.Format) &&
|
2021-10-01 16:24:17 +08:00
|
|
|
|
this.req.web.WebP.MatchAccept(this.req.requestHeader("Accept")) &&
|
2021-10-01 18:59:44 +08:00
|
|
|
|
atomic.LoadInt64(&webpTotalBufferSize) < webpMaxBufferSize {
|
2021-10-13 11:56:40 +08:00
|
|
|
|
|
|
|
|
|
|
var contentEncoding = this.writer.Header().Get("Content-Encoding")
|
|
|
|
|
|
switch contentEncoding {
|
|
|
|
|
|
case "gzip", "deflate", "br":
|
|
|
|
|
|
this.webpOriginEncoding = contentEncoding
|
|
|
|
|
|
case "": // 空
|
|
|
|
|
|
default:
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-01 16:24:17 +08:00
|
|
|
|
this.webpIsEncoding = true
|
2021-10-13 11:11:57 +08:00
|
|
|
|
this.webpOriginContentType = this.Header().Get("Content-Type")
|
2021-10-13 11:56:40 +08:00
|
|
|
|
this.webpBuffer = webpBufferPool.Get()
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
|
|
|
|
|
this.Header().Del("Content-Length")
|
|
|
|
|
|
this.Header().Set("Content-Type", "image/webp")
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
|
|
|
|
|
atomic.AddInt64(&webpTotalBufferSize, size*32)
|
2021-10-01 16:24:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PrepareCompression 准备压缩
|
|
|
|
|
|
func (this *HTTPWriter) PrepareCompression(size int64) {
|
2021-09-29 19:37:07 +08:00
|
|
|
|
if this.compressionConfig == nil || !this.compressionConfig.IsOn || this.compressionConfig.Level <= 0 {
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
// 如果已经有编码则不处理
|
2021-10-18 16:50:06 +08:00
|
|
|
|
var contentEncoding = this.writer.Header().Get("Content-Encoding")
|
|
|
|
|
|
if len(contentEncoding) > 0 && (!this.compressionConfig.DecompressData || !lists.ContainsString([]string{"gzip", "deflate", "br"}, contentEncoding)) {
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尺寸和类型
|
2021-12-30 11:19:11 +08:00
|
|
|
|
if !this.compressionConfig.MatchResponse(this.Header().Get("Content-Type"), size, filepath.Ext(this.req.Path()), this.req.Format) {
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
// 判断Accept是否支持压缩
|
|
|
|
|
|
compressionType, compressionEncoding, ok := this.compressionConfig.MatchAcceptEncoding(this.req.RawReq.Header.Get("Accept-Encoding"))
|
|
|
|
|
|
if !ok {
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-10-18 16:50:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 压缩前后如果编码一致,则不处理
|
|
|
|
|
|
if compressionEncoding == contentEncoding {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
this.compressionType = compressionType
|
2020-10-05 16:55:14 +08:00
|
|
|
|
|
2021-09-29 19:37:07 +08:00
|
|
|
|
// compression writer
|
2020-10-05 16:55:14 +08:00
|
|
|
|
var err error = nil
|
2021-09-29 19:37:07 +08:00
|
|
|
|
this.compressionWriter, err = compressions.NewWriter(this.writer, compressionType, int(this.compressionConfig.Level))
|
2020-10-05 16:55:14 +08:00
|
|
|
|
if err != nil {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
remotelogs.Error("HTTP_WRITER", err.Error())
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-18 16:50:06 +08:00
|
|
|
|
// convert between encodings
|
|
|
|
|
|
if len(contentEncoding) > 0 {
|
|
|
|
|
|
this.compressionWriter, err = compressions.NewEncodingWriter(contentEncoding, this.compressionWriter)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", err.Error())
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 16:55:14 +08:00
|
|
|
|
// body copy
|
|
|
|
|
|
if this.bodyCopying {
|
2021-09-29 19:37:07 +08:00
|
|
|
|
this.compressionBodyBuffer = bytes.NewBuffer([]byte{})
|
|
|
|
|
|
this.compressionBodyWriter, err = compressions.NewWriter(this.compressionBodyBuffer, compressionType, int(this.compressionConfig.Level))
|
2020-10-05 16:55:14 +08:00
|
|
|
|
if err != nil {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
remotelogs.Error("HTTP_WRITER", err.Error())
|
2020-10-05 16:55:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
header := this.writer.Header()
|
2021-09-29 19:37:07 +08:00
|
|
|
|
header.Set("Content-Encoding", compressionEncoding)
|
2020-10-05 16:55:14 +08:00
|
|
|
|
header.Set("Vary", "Accept-Encoding")
|
|
|
|
|
|
header.Del("Content-Length")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 准备缓存
|
|
|
|
|
|
func (this *HTTPWriter) prepareCache(size int64) {
|
2021-04-18 22:17:17 +08:00
|
|
|
|
if this.writer == nil {
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-01 14:54:06 +08:00
|
|
|
|
cachePolicy := this.req.Server.HTTPCachePolicy
|
2020-12-17 17:36:10 +08:00
|
|
|
|
if cachePolicy == nil || !cachePolicy.IsOn {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 16:55:14 +08:00
|
|
|
|
cacheRef := this.req.cacheRef
|
2021-04-18 22:17:17 +08:00
|
|
|
|
if cacheRef == nil || !cacheRef.IsOn {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-05 14:15:21 +08:00
|
|
|
|
var addStatusHeader = this.req.web != nil && this.req.web.Cache != nil && this.req.web.Cache.AddStatusHeader
|
|
|
|
|
|
|
|
|
|
|
|
// 不支持Range
|
|
|
|
|
|
if len(this.Header().Get("Content-Range")) > 0 {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, not supported Content-Range")
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-18 22:17:17 +08:00
|
|
|
|
// 如果允许 ChunkedEncoding,就无需尺寸的判断,因为此时的 size 为 -1
|
|
|
|
|
|
if !cacheRef.AllowChunkedEncoding && size < 0 {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, ChunkedEncoding")
|
|
|
|
|
|
}
|
2021-04-18 22:17:17 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if size >= 0 && ((cacheRef.MaxSizeBytes() > 0 && size > cacheRef.MaxSizeBytes()) ||
|
2021-09-26 15:01:46 +08:00
|
|
|
|
(cachePolicy.MaxSizeBytes() > 0 && size > cachePolicy.MaxSizeBytes()) || (cacheRef.MinSizeBytes() > size)) {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Content-Length")
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查状态
|
|
|
|
|
|
if len(cacheRef.Status) > 0 && !lists.ContainsInt(cacheRef.Status, this.StatusCode()) {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Status: "+types.String(this.StatusCode()))
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Cache-Control
|
|
|
|
|
|
if len(cacheRef.SkipResponseCacheControlValues) > 0 {
|
|
|
|
|
|
cacheControl := this.writer.Header().Get("Cache-Control")
|
|
|
|
|
|
if len(cacheControl) > 0 {
|
|
|
|
|
|
values := strings.Split(cacheControl, ",")
|
|
|
|
|
|
for _, value := range values {
|
|
|
|
|
|
if cacheRef.ContainsCacheControl(strings.TrimSpace(value)) {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Cache-Control: "+cacheControl)
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set-Cookie
|
|
|
|
|
|
if cacheRef.SkipResponseSetCookie && len(this.writer.Header().Get("Set-Cookie")) > 0 {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Set-Cookie")
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 校验其他条件
|
|
|
|
|
|
if cacheRef.Conds != nil && cacheRef.Conds.HasResponseConds() && !cacheRef.Conds.MatchResponse(this.req.Format) {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, ResponseConds")
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打开缓存写入
|
2020-12-17 17:36:10 +08:00
|
|
|
|
storage := caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
2020-10-05 16:55:14 +08:00
|
|
|
|
if storage == nil {
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
2021-11-05 14:15:21 +08:00
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Storage")
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-11-05 14:15:21 +08:00
|
|
|
|
|
2021-12-05 17:10:06 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "UPDATING"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "UPDATING")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 16:55:14 +08:00
|
|
|
|
this.cacheStorage = storage
|
|
|
|
|
|
life := cacheRef.LifeSeconds()
|
2021-12-02 10:19:02 +08:00
|
|
|
|
|
|
|
|
|
|
if life <= 0 {
|
2020-10-05 16:55:14 +08:00
|
|
|
|
life = 60
|
|
|
|
|
|
}
|
2021-12-02 10:19:02 +08:00
|
|
|
|
|
|
|
|
|
|
// 支持源站设置的max-age
|
|
|
|
|
|
if this.req.web.Cache != nil && this.req.web.Cache.EnableCacheControlMaxAge {
|
|
|
|
|
|
var cacheControl = this.Header().Get("Cache-Control")
|
|
|
|
|
|
var pieces = strings.Split(cacheControl, ";")
|
|
|
|
|
|
for _, piece := range pieces {
|
|
|
|
|
|
var eqIndex = strings.Index(piece, "=")
|
|
|
|
|
|
if eqIndex > 0 && piece[:eqIndex] == "max-age" {
|
|
|
|
|
|
var maxAge = types.Int64(piece[eqIndex+1:])
|
|
|
|
|
|
if maxAge > 0 {
|
|
|
|
|
|
life = maxAge
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-05 16:55:14 +08:00
|
|
|
|
expiredAt := utils.UnixTime() + life
|
2021-10-03 18:00:57 +08:00
|
|
|
|
var cacheKey = this.req.cacheKey
|
|
|
|
|
|
if this.webpIsEncoding {
|
|
|
|
|
|
cacheKey += webpSuffix
|
|
|
|
|
|
}
|
|
|
|
|
|
cacheWriter, err := storage.OpenWriter(cacheKey, expiredAt, this.StatusCode())
|
2020-10-05 16:55:14 +08:00
|
|
|
|
if err != nil {
|
2021-06-08 11:24:41 +08:00
|
|
|
|
if !caches.CanIgnoreErr(err) {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
|
2021-01-11 23:06:50 +08:00
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.cacheWriter = cacheWriter
|
|
|
|
|
|
|
|
|
|
|
|
// 写入Header
|
2021-01-13 12:02:50 +08:00
|
|
|
|
for k, v := range this.Header() {
|
|
|
|
|
|
for _, v1 := range v {
|
|
|
|
|
|
_, err = cacheWriter.WriteHeader([]byte(k + ":" + v1 + "\n"))
|
|
|
|
|
|
if err != nil {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
2021-01-13 12:02:50 +08:00
|
|
|
|
this.cacheWriter = nil
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-12-16 17:27:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 计算stale时长
|
|
|
|
|
|
func (this *HTTPWriter) calculateStaleLife() int {
|
|
|
|
|
|
var staleLife = 600 // TODO 可以在缓存策略里设置此时间
|
|
|
|
|
|
var staleConfig = this.req.web.Cache.Stale
|
|
|
|
|
|
if staleConfig != nil && staleConfig.IsOn {
|
|
|
|
|
|
// 从Header中读取stale-if-error
|
|
|
|
|
|
var isDefinedInHeader = false
|
|
|
|
|
|
if staleConfig.SupportStaleIfErrorHeader {
|
|
|
|
|
|
var cacheControl = this.Header().Get("Cache-Control")
|
|
|
|
|
|
var pieces = strings.Split(cacheControl, ",")
|
|
|
|
|
|
for _, piece := range pieces {
|
|
|
|
|
|
var eqIndex = strings.Index(piece, "=")
|
|
|
|
|
|
if eqIndex > 0 && strings.TrimSpace(piece[:eqIndex]) == "stale-if-error" {
|
|
|
|
|
|
// 这里预示着如果stale-if-error=0,可以关闭stale功能
|
|
|
|
|
|
staleLife = types.Int(strings.TrimSpace(piece[eqIndex+1:]))
|
|
|
|
|
|
isDefinedInHeader = true
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 自定义
|
|
|
|
|
|
if !isDefinedInHeader && staleConfig.Life != nil {
|
|
|
|
|
|
staleLife = types.Int(staleConfig.Life.Duration().Seconds())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return staleLife
|
|
|
|
|
|
}
|