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"
|
2022-01-02 22:45:37 +08:00
|
|
|
|
"errors"
|
2023-08-11 14:38:00 +08:00
|
|
|
|
"fmt"
|
2022-04-01 16:18:15 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
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"
|
2023-04-08 12:47:04 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
2022-02-15 14:55:49 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/readers"
|
2023-09-11 16:05:59 +08:00
|
|
|
|
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
|
2022-02-15 14:55:49 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/writers"
|
2021-10-11 14:53:23 +08:00
|
|
|
|
_ "github.com/biessek/golang-ico"
|
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/jpeg"
|
|
|
|
|
|
_ "image/png"
|
2021-10-13 11:56:40 +08:00
|
|
|
|
"io"
|
2020-09-26 08:07:07 +08:00
|
|
|
|
"net"
|
|
|
|
|
|
"net/http"
|
2022-03-03 19:36:28 +08:00
|
|
|
|
"net/textproto"
|
2022-01-02 22:45:37 +08:00
|
|
|
|
"os"
|
2021-09-29 19:37:07 +08:00
|
|
|
|
"path/filepath"
|
2023-12-12 09:55:18 +08:00
|
|
|
|
"runtime"
|
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
|
|
|
|
)
|
|
|
|
|
|
|
2023-12-12 09:55:18 +08:00
|
|
|
|
var webPThreads int32
|
|
|
|
|
|
var webPMaxThreads int32 = 1
|
|
|
|
|
|
var webPIgnoreURLSet = setutils.NewFixedSet(131072)
|
|
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
|
webPMaxThreads = int32(runtime.NumCPU() / 4)
|
|
|
|
|
|
if webPMaxThreads < 1 {
|
|
|
|
|
|
webPMaxThreads = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// HTTPWriter 响应Writer
|
2020-09-26 08:07:07 +08:00
|
|
|
|
type HTTPWriter struct {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
req *HTTPRequest
|
|
|
|
|
|
rawWriter http.ResponseWriter
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
rawReader io.ReadCloser
|
|
|
|
|
|
delayRead bool
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
counterWriter *writers.BytesCounterWriter
|
|
|
|
|
|
writer io.WriteCloser
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
size int64
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-03-26 12:29:34 +08:00
|
|
|
|
statusCode int
|
|
|
|
|
|
sentBodyBytes int64
|
|
|
|
|
|
sentHeaderBytes int64
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2021-12-30 11:19:11 +08:00
|
|
|
|
isOk bool // 是否完全成功
|
|
|
|
|
|
isFinished bool // 是否已完成
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
2022-02-22 19:29:27 +08:00
|
|
|
|
// Partial
|
2022-03-04 22:42:03 +08:00
|
|
|
|
isPartial bool
|
|
|
|
|
|
partialFileIsNew bool
|
2022-02-22 19:29:27 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// WebP
|
|
|
|
|
|
webpIsEncoding bool
|
|
|
|
|
|
webpOriginContentType string
|
2023-12-11 10:17:17 +08:00
|
|
|
|
webpQuality int
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
|
|
|
|
|
// Compression
|
2022-02-22 19:29:27 +08:00
|
|
|
|
compressionConfig *serverconfigs.HTTPCompressionConfig
|
|
|
|
|
|
compressionCacheWriter caches.Writer
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
|
|
|
|
|
// Cache
|
|
|
|
|
|
cacheStorage caches.StorageInterface
|
|
|
|
|
|
cacheWriter caches.Writer
|
|
|
|
|
|
cacheIsFinished bool
|
2022-02-22 19:29:27 +08:00
|
|
|
|
|
|
|
|
|
|
cacheReader caches.Reader
|
|
|
|
|
|
cacheReaderSuffix string
|
2024-05-07 16:20:22 +08:00
|
|
|
|
|
|
|
|
|
|
statusSent 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 {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
var counterWriter = writers.NewBytesCounterWriter(httpResponseWriter)
|
2020-09-26 08:07:07 +08:00
|
|
|
|
return &HTTPWriter{
|
2022-02-15 14:55:49 +08:00
|
|
|
|
req: req,
|
|
|
|
|
|
rawWriter: httpResponseWriter,
|
|
|
|
|
|
writer: counterWriter,
|
|
|
|
|
|
counterWriter: counterWriter,
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-06 23:42:11 +08:00
|
|
|
|
// Prepare 准备输出
|
2022-02-15 14:55:49 +08:00
|
|
|
|
func (this *HTTPWriter) Prepare(resp *http.Response, size int64, status int, enableCache bool) (delayHeaders bool) {
|
2023-03-12 16:09:06 +08:00
|
|
|
|
// 清理以前数据,防止重试时发生异常错误
|
|
|
|
|
|
if this.compressionCacheWriter != nil {
|
|
|
|
|
|
_ = this.compressionCacheWriter.Discard()
|
|
|
|
|
|
this.compressionCacheWriter = nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if this.cacheWriter != nil {
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
this.cacheWriter = nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 新的请求相关数据
|
2021-10-01 18:59:44 +08:00
|
|
|
|
this.size = size
|
2021-05-23 14:29:56 +08:00
|
|
|
|
this.statusCode = status
|
|
|
|
|
|
|
2022-03-04 17:00:48 +08:00
|
|
|
|
// 是否为区间请求
|
2022-03-04 17:09:12 +08:00
|
|
|
|
this.isPartial = status == http.StatusPartialContent
|
|
|
|
|
|
|
|
|
|
|
|
// 不支持对GET以外的方法返回的Partial内容的缓存
|
|
|
|
|
|
if this.isPartial && this.req.Method() != http.MethodGet {
|
|
|
|
|
|
enableCache = false
|
|
|
|
|
|
}
|
2022-02-22 19:29:27 +08:00
|
|
|
|
|
|
|
|
|
|
if resp != nil && resp.Body != nil {
|
|
|
|
|
|
cacheReader, ok := resp.Body.(caches.Reader)
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
this.cacheReader = cacheReader
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.rawReader = resp.Body
|
2021-10-13 11:11:57 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if enableCache {
|
|
|
|
|
|
this.PrepareCache(resp, size)
|
2021-10-13 11:11:57 +08:00
|
|
|
|
}
|
2022-02-22 19:29:27 +08:00
|
|
|
|
if !this.isPartial {
|
|
|
|
|
|
this.PrepareWebP(resp, size)
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.PrepareCompression(resp, size)
|
2021-10-01 16:24:17 +08:00
|
|
|
|
}
|
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 {
|
2022-11-29 19:14:46 +08:00
|
|
|
|
this.writer = writers.NewRateLimitWriter(this.req.RawReq.Context(), this.writer, this.req.web.RequestLimit.OutBandwidthPerConnBytes())
|
2021-12-12 11:48:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-13 11:11:57 +08:00
|
|
|
|
return
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// PrepareCache 准备缓存
|
|
|
|
|
|
func (this *HTTPWriter) PrepareCache(resp *http.Response, size int64) {
|
|
|
|
|
|
if resp == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
var cachePolicy = this.req.ReqServer.HTTPCachePolicy
|
|
|
|
|
|
if cachePolicy == nil || !cachePolicy.IsOn {
|
|
|
|
|
|
return
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
var cacheRef = this.req.cacheRef
|
|
|
|
|
|
if cacheRef == nil || !cacheRef.IsOn {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-12-30 11:19:11 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
var addStatusHeader = this.req.web != nil && this.req.web.Cache != nil && this.req.web.Cache.AddStatusHeader
|
2021-12-30 11:19:11 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 不支持Range
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if this.isPartial {
|
|
|
|
|
|
if !cacheRef.AllowPartialContent {
|
|
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, not supported partial content")
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if this.cacheStorage.Policy().Type != serverconfigs.CachePolicyStorageFile {
|
|
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, not supported partial content in memory storage")
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果允许 ChunkedEncoding,就无需尺寸的判断,因为此时的 size 为 -1
|
|
|
|
|
|
if !cacheRef.AllowChunkedEncoding && size < 0 {
|
|
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, ChunkedEncoding")
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-03-05 19:31:50 +08:00
|
|
|
|
|
|
|
|
|
|
var contentSize = size
|
|
|
|
|
|
if this.isPartial {
|
|
|
|
|
|
// 从Content-Range中读取内容总长度
|
|
|
|
|
|
var contentRange = this.Header().Get("Content-Range")
|
|
|
|
|
|
_, totalSize := httpRequestParseContentRangeHeader(contentRange)
|
|
|
|
|
|
if totalSize > 0 {
|
|
|
|
|
|
contentSize = totalSize
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if contentSize >= 0 && ((cacheRef.MaxSizeBytes() > 0 && contentSize > cacheRef.MaxSizeBytes()) ||
|
|
|
|
|
|
(cachePolicy.MaxSizeBytes() > 0 && contentSize > cachePolicy.MaxSizeBytes()) || (cacheRef.MinSizeBytes() > contentSize)) {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Content-Length")
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 检查状态
|
2022-03-04 17:00:48 +08:00
|
|
|
|
if !cacheRef.MatchStatus(this.StatusCode()) {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Status: "+types.String(this.StatusCode()))
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// Cache-Control
|
|
|
|
|
|
if len(cacheRef.SkipResponseCacheControlValues) > 0 {
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var cacheControl = this.GetHeader("Cache-Control")
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if len(cacheControl) > 0 {
|
|
|
|
|
|
values := strings.Split(cacheControl, ",")
|
|
|
|
|
|
for _, value := range values {
|
|
|
|
|
|
if cacheRef.ContainsCacheControl(strings.TrimSpace(value)) {
|
|
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Cache-Control: "+cacheControl)
|
2021-10-01 16:24:17 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
2021-10-01 16:24:17 +08:00
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-10-01 16:24:17 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// Set-Cookie
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if cacheRef.SkipResponseSetCookie && len(this.GetHeader("Set-Cookie")) > 0 {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Set-Cookie")
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 校验其他条件
|
|
|
|
|
|
if cacheRef.Conds != nil && cacheRef.Conds.HasResponseConds() && !cacheRef.Conds.MatchResponse(this.req.Format) {
|
|
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, ResponseConds")
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 打开缓存写入
|
|
|
|
|
|
var storage = caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
|
|
|
|
|
if storage == nil {
|
|
|
|
|
|
this.req.varMapping["cache.status"] = "BYPASS"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, Storage")
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.req.varMapping["cache.status"] = "UPDATING"
|
|
|
|
|
|
if addStatusHeader {
|
|
|
|
|
|
this.Header().Set("X-Cache", "UPDATING")
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.cacheStorage = storage
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var life = cacheRef.LifeSeconds()
|
2021-12-30 11:19:11 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if life <= 0 {
|
|
|
|
|
|
life = 60
|
|
|
|
|
|
}
|
2022-01-02 22:45:37 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 支持源站设置的max-age
|
|
|
|
|
|
if this.req.web.Cache != nil && this.req.web.Cache.EnableCacheControlMaxAge {
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var cacheControl = this.GetHeader("Cache-Control")
|
2022-02-15 14:55:49 +08:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-01-02 22:45:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-08 12:47:04 +08:00
|
|
|
|
var expiresAt = fasttime.Now().Unix() + life
|
2022-04-05 11:00:55 +08:00
|
|
|
|
|
|
|
|
|
|
if this.req.isLnRequest {
|
|
|
|
|
|
// 返回上级节点过期时间
|
|
|
|
|
|
this.SetHeader(LNExpiresHeader, []string{types.String(expiresAt)})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
var expiresHeader = this.Header().Get(LNExpiresHeader)
|
|
|
|
|
|
if len(expiresHeader) > 0 {
|
|
|
|
|
|
this.Header().Del(LNExpiresHeader)
|
|
|
|
|
|
|
|
|
|
|
|
var expiresHeaderInt64 = types.Int64(expiresHeader)
|
|
|
|
|
|
if expiresHeaderInt64 > 0 {
|
|
|
|
|
|
expiresAt = expiresHeaderInt64
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
var cacheKey = this.req.cacheKey
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if this.isPartial {
|
2022-03-04 11:51:59 +08:00
|
|
|
|
cacheKey += caches.SuffixPartial
|
2022-03-03 19:36:28 +08:00
|
|
|
|
}
|
2022-11-20 18:07:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 待写入尺寸
|
|
|
|
|
|
var totalSize = size
|
2023-07-26 14:48:07 +08:00
|
|
|
|
if this.isPartial {
|
2022-11-20 18:07:46 +08:00
|
|
|
|
var contentRange = resp.Header.Get("Content-Range")
|
|
|
|
|
|
if len(contentRange) > 0 {
|
|
|
|
|
|
_, partialTotalSize := httpRequestParseContentRangeHeader(contentRange)
|
2023-07-26 14:48:07 +08:00
|
|
|
|
if partialTotalSize > 0 && partialTotalSize > totalSize {
|
2022-11-20 18:07:46 +08:00
|
|
|
|
totalSize = partialTotalSize
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-03-12 20:32:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 先清理以前的
|
|
|
|
|
|
if this.cacheWriter != nil {
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-20 18:07:46 +08:00
|
|
|
|
cacheWriter, err := storage.OpenWriter(cacheKey, expiresAt, this.StatusCode(), this.calculateHeaderLength(), totalSize, cacheRef.MaxSizeBytes(), this.isPartial)
|
2022-01-02 22:45:37 +08:00
|
|
|
|
if err != nil {
|
2024-01-16 09:24:51 +08:00
|
|
|
|
if errors.Is(err, caches.ErrEntityTooLarge) && addStatusHeader {
|
2022-03-06 17:18:06 +08:00
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, entity too large")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if !caches.CanIgnoreErr(err) {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
|
2022-06-18 19:31:10 +08:00
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, write cache failed")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.Header().Set("X-Cache", "BYPASS, "+err.Error())
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
return
|
2022-01-02 22:45:37 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.cacheWriter = cacheWriter
|
2022-01-02 22:45:37 +08:00
|
|
|
|
|
2022-03-04 22:42:03 +08:00
|
|
|
|
if this.isPartial {
|
|
|
|
|
|
this.partialFileIsNew = cacheWriter.(*caches.PartialFileWriter).IsNew()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 写入Header
|
2022-11-19 15:55:05 +08:00
|
|
|
|
var headerBuf = utils.SharedBufferPool.Get()
|
2022-02-15 14:55:49 +08:00
|
|
|
|
for k, v := range this.Header() {
|
2023-07-30 08:49:31 +08:00
|
|
|
|
if this.shouldIgnoreHeader(k) {
|
2022-11-19 17:35:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
for _, v1 := range v {
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if this.isPartial && k == "Content-Type" && strings.Contains(v1, "multipart/byteranges") {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2024-04-12 08:41:14 +08:00
|
|
|
|
_, err = headerBuf.WriteString(k + ":" + v1 + "\n")
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if err != nil {
|
2022-11-19 15:55:05 +08:00
|
|
|
|
utils.SharedBufferPool.Put(headerBuf)
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
this.cacheWriter = nil
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-01-02 22:45:37 +08:00
|
|
|
|
}
|
2022-11-19 15:55:05 +08:00
|
|
|
|
_, err = cacheWriter.WriteHeader(headerBuf.Bytes())
|
|
|
|
|
|
utils.SharedBufferPool.Put(headerBuf)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
this.cacheWriter = nil
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-01-02 22:45:37 +08:00
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if this.isPartial {
|
|
|
|
|
|
// content-range
|
|
|
|
|
|
var contentRange = this.GetHeader("Content-Range")
|
|
|
|
|
|
if len(contentRange) > 0 {
|
2022-03-04 11:51:59 +08:00
|
|
|
|
start, total := httpRequestParseContentRangeHeader(contentRange)
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if start < 0 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-03-04 11:51:59 +08:00
|
|
|
|
if total > 0 {
|
|
|
|
|
|
partialWriter, ok := cacheWriter.(*caches.PartialFileWriter)
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
partialWriter.SetBodyLength(total)
|
|
|
|
|
|
}
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var filterReader = readers.NewFilterReaderCloser(resp.Body)
|
|
|
|
|
|
this.cacheIsFinished = true
|
|
|
|
|
|
var hasError = false
|
2023-08-08 11:23:04 +08:00
|
|
|
|
filterReader.Add(func(p []byte, readErr error) error {
|
|
|
|
|
|
// 这里不用处理readErr,因为只要把成功读取的部分写入缓存即可
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if hasError {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var l = len(p)
|
|
|
|
|
|
if l == 0 {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
start += int64(l)
|
|
|
|
|
|
}()
|
|
|
|
|
|
err = cacheWriter.WriteAt(start, p)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
this.cacheIsFinished = false
|
|
|
|
|
|
hasError = true
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
})
|
|
|
|
|
|
resp.Body = filterReader
|
|
|
|
|
|
this.rawReader = filterReader
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// multipart/byteranges
|
|
|
|
|
|
var contentType = this.GetHeader("Content-Type")
|
|
|
|
|
|
if strings.Contains(contentType, "multipart/byteranges") {
|
|
|
|
|
|
partialWriter, ok := cacheWriter.(*caches.PartialFileWriter)
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var boundary = httpRequestParseBoundary(contentType)
|
|
|
|
|
|
if len(boundary) == 0 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var reader = readers.NewByteRangesReaderCloser(resp.Body, boundary)
|
|
|
|
|
|
var contentTypeWritten = false
|
|
|
|
|
|
|
|
|
|
|
|
this.cacheIsFinished = true
|
|
|
|
|
|
var hasError = false
|
|
|
|
|
|
var writtenTotal = false
|
|
|
|
|
|
reader.OnPartRead(func(start int64, end int64, total int64, data []byte, header textproto.MIMEHeader) {
|
2022-03-05 19:31:50 +08:00
|
|
|
|
// TODO 如果 total 超出缓存限制,则不写入缓存数据,并且记录到某个内存表中,下次不再OpenWriter
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if hasError {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 写入total
|
|
|
|
|
|
if !writtenTotal && total > 0 {
|
|
|
|
|
|
partialWriter.SetBodyLength(total)
|
|
|
|
|
|
writtenTotal = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 写入Content-Type
|
|
|
|
|
|
if partialWriter.IsNew() && !contentTypeWritten {
|
|
|
|
|
|
var realContentType = header.Get("Content-Type")
|
|
|
|
|
|
if len(realContentType) > 0 {
|
|
|
|
|
|
var h = []byte("Content-Type:" + realContentType + "\n")
|
|
|
|
|
|
err = partialWriter.AppendHeader(h)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
hasError = true
|
|
|
|
|
|
this.cacheIsFinished = false
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
contentTypeWritten = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-11 10:17:17 +08:00
|
|
|
|
writeErr := cacheWriter.WriteAt(start, data)
|
|
|
|
|
|
if writeErr != nil {
|
2022-03-03 19:36:28 +08:00
|
|
|
|
hasError = true
|
|
|
|
|
|
this.cacheIsFinished = false
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
resp.Body = reader
|
|
|
|
|
|
this.rawReader = reader
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-27 20:55:12 +08:00
|
|
|
|
var cacheReader = readers.NewTeeReaderCloser(resp.Body, this.cacheWriter, false)
|
2022-02-15 14:55:49 +08:00
|
|
|
|
resp.Body = cacheReader
|
|
|
|
|
|
this.rawReader = cacheReader
|
|
|
|
|
|
|
|
|
|
|
|
cacheReader.OnFail(func(err error) {
|
2022-06-19 11:39:21 +08:00
|
|
|
|
if this.cacheWriter != nil {
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.cacheWriter = nil
|
|
|
|
|
|
})
|
|
|
|
|
|
cacheReader.OnEOF(func() {
|
|
|
|
|
|
this.cacheIsFinished = true
|
|
|
|
|
|
})
|
2022-01-02 22:45:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// PrepareWebP 准备WebP
|
|
|
|
|
|
func (this *HTTPWriter) PrepareWebP(resp *http.Response, size int64) {
|
|
|
|
|
|
if resp == nil {
|
|
|
|
|
|
return
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-01 16:18:15 +08:00
|
|
|
|
// 集群配置
|
2022-04-04 12:06:53 +08:00
|
|
|
|
var policy = this.req.nodeConfig.FindWebPImagePolicyWithClusterId(this.req.ReqServer.ClusterId)
|
2022-04-01 16:18:15 +08:00
|
|
|
|
if policy == nil {
|
|
|
|
|
|
policy = nodeconfigs.DefaultWebPImagePolicy
|
|
|
|
|
|
}
|
|
|
|
|
|
if !policy.IsOn {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-31 16:22:23 +08:00
|
|
|
|
// 只有在开启了缓存之后,才会转换,防止占用的系统资源过高
|
2022-04-01 16:18:15 +08:00
|
|
|
|
if policy.RequireCache && this.req.cacheRef == nil {
|
2022-03-31 16:22:23 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-12-11 10:17:17 +08:00
|
|
|
|
this.webpQuality = policy.Quality
|
2022-03-31 16:22:23 +08:00
|
|
|
|
|
2022-03-31 16:30:15 +08:00
|
|
|
|
// 限制最小和最大尺寸
|
|
|
|
|
|
// TODO 需要将reader修改为LimitReader
|
|
|
|
|
|
if resp.ContentLength == 0 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-04-01 16:18:15 +08:00
|
|
|
|
|
|
|
|
|
|
if resp.ContentLength > 0 && (resp.ContentLength < policy.MinLengthBytes() || (policy.MaxLengthBytes() > 0 && resp.ContentLength > policy.MaxLengthBytes())) {
|
2022-03-31 16:30:15 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var contentType = this.GetHeader("Content-Type")
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if this.req.web != nil &&
|
|
|
|
|
|
this.req.web.WebP != nil &&
|
|
|
|
|
|
this.req.web.WebP.IsOn &&
|
|
|
|
|
|
this.req.web.WebP.MatchResponse(contentType, size, filepath.Ext(this.req.Path()), this.req.Format) &&
|
|
|
|
|
|
this.req.web.WebP.MatchAccept(this.req.requestHeader("Accept")) {
|
2023-09-11 16:05:59 +08:00
|
|
|
|
// 检查是否已经因为尺寸过大而忽略
|
2023-12-12 09:55:18 +08:00
|
|
|
|
if webPIgnoreURLSet.Has(this.req.URL()) {
|
2023-09-11 16:05:59 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 如果已经是WebP不再重复处理
|
|
|
|
|
|
// TODO 考虑是否需要很严格的匹配
|
|
|
|
|
|
if strings.Contains(contentType, "image/webp") {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2023-12-11 09:33:04 +08:00
|
|
|
|
// 检查当前是否正在转换
|
2023-12-12 09:55:18 +08:00
|
|
|
|
if atomic.LoadInt32(&webPThreads) >= webPMaxThreads {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var contentEncoding = this.GetHeader("Content-Encoding")
|
2024-01-15 10:31:42 +08:00
|
|
|
|
if len(contentEncoding) > 0 {
|
|
|
|
|
|
if compressions.SupportEncoding(contentEncoding) {
|
|
|
|
|
|
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.Header().Del("Content-Encoding")
|
|
|
|
|
|
this.Header().Del("Content-Length")
|
|
|
|
|
|
this.rawReader = reader
|
|
|
|
|
|
} else {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.webpOriginContentType = contentType
|
|
|
|
|
|
this.webpIsEncoding = true
|
2022-08-04 11:34:06 +08:00
|
|
|
|
resp.Body = io.NopCloser(&bytes.Buffer{})
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.delayRead = true
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.Header().Del("Content-Length")
|
|
|
|
|
|
this.Header().Set("Content-Type", "image/webp")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// PrepareCompression 准备压缩
|
|
|
|
|
|
func (this *HTTPWriter) PrepareCompression(resp *http.Response, size int64) {
|
2022-02-22 21:43:47 +08:00
|
|
|
|
var method = this.req.Method()
|
|
|
|
|
|
if method == http.MethodHead {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if this.StatusCode() == http.StatusNoContent {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-18 11:05:09 +08:00
|
|
|
|
var acceptEncodings = this.req.RawReq.Header.Get("Accept-Encoding")
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var contentEncoding = this.GetHeader("Content-Encoding")
|
2022-02-18 11:05:09 +08:00
|
|
|
|
|
|
|
|
|
|
if this.compressionConfig == nil || !this.compressionConfig.IsOn {
|
2024-01-15 10:31:42 +08:00
|
|
|
|
if compressions.SupportEncoding(contentEncoding) && !httpAcceptEncoding(acceptEncodings, contentEncoding) {
|
2022-02-18 11:05:09 +08:00
|
|
|
|
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.Header().Del("Content-Encoding")
|
|
|
|
|
|
this.Header().Del("Content-Length")
|
|
|
|
|
|
resp.Body = reader
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-17 08:36:14 +08:00
|
|
|
|
// 检查是否正繁忙
|
|
|
|
|
|
if compressions.IsBusy() {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-17 13:56:38 +08:00
|
|
|
|
// 检查URL
|
|
|
|
|
|
if !this.compressionConfig.MatchURL(this.req.URL()) {
|
2022-12-30 11:44:07 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-17 13:56:38 +08:00
|
|
|
|
// 分区内容不压缩,防止读取失败
|
|
|
|
|
|
if !this.compressionConfig.EnablePartialContent && this.StatusCode() == http.StatusPartialContent {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果已经有编码则不处理
|
2024-01-15 10:31:42 +08:00
|
|
|
|
if len(contentEncoding) > 0 && (!this.compressionConfig.DecompressData || !compressions.SupportEncoding(contentEncoding)) {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 尺寸和类型
|
2022-03-03 19:36:28 +08:00
|
|
|
|
var contentType = this.GetHeader("Content-Type")
|
2022-02-15 18:31:37 +08:00
|
|
|
|
if !this.compressionConfig.MatchResponse(contentType, size, filepath.Ext(this.req.Path()), this.req.Format) {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断Accept是否支持压缩
|
2022-02-18 11:05:09 +08:00
|
|
|
|
compressionType, compressionEncoding, ok := this.compressionConfig.MatchAcceptEncoding(acceptEncodings)
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if !ok {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 压缩前后如果编码一致,则不处理
|
|
|
|
|
|
if compressionEncoding == contentEncoding {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(contentEncoding) > 0 && resp != nil {
|
|
|
|
|
|
if !this.compressionConfig.DecompressData {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-02-17 16:56:13 +08:00
|
|
|
|
this.Header().Del("Content-Encoding")
|
2022-02-18 11:05:09 +08:00
|
|
|
|
this.Header().Del("Content-Length")
|
2022-02-15 14:55:49 +08:00
|
|
|
|
resp.Body = reader
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-22 19:29:27 +08:00
|
|
|
|
// 需要放在compression cache writer之前
|
|
|
|
|
|
var header = this.rawWriter.Header()
|
|
|
|
|
|
header.Set("Content-Encoding", compressionEncoding)
|
|
|
|
|
|
header.Set("Vary", "Accept-Encoding")
|
|
|
|
|
|
header.Del("Content-Length")
|
|
|
|
|
|
|
|
|
|
|
|
// compression cache writer
|
2022-02-24 20:13:05 +08:00
|
|
|
|
// 只有在本身内容已经缓存的情况下才会写入缓存,防止同时写入缓存导致IO负载升高
|
2022-03-06 17:18:06 +08:00
|
|
|
|
var cacheRef = this.req.cacheRef
|
2022-02-24 20:13:05 +08:00
|
|
|
|
if !this.isPartial &&
|
|
|
|
|
|
this.cacheStorage != nil &&
|
2022-03-06 17:18:06 +08:00
|
|
|
|
cacheRef != nil &&
|
2022-02-24 20:13:05 +08:00
|
|
|
|
(this.cacheReader != nil || (this.cacheStorage.Policy().SyncCompressionCache && this.cacheWriter != nil)) &&
|
|
|
|
|
|
!this.webpIsEncoding {
|
2022-02-22 19:29:27 +08:00
|
|
|
|
var cacheKey = ""
|
|
|
|
|
|
var expiredAt int64 = 0
|
|
|
|
|
|
|
|
|
|
|
|
if this.cacheReader != nil {
|
|
|
|
|
|
cacheKey = this.req.cacheKey
|
|
|
|
|
|
expiredAt = this.cacheReader.ExpiresAt()
|
|
|
|
|
|
} else if this.cacheWriter != nil {
|
|
|
|
|
|
cacheKey = this.cacheWriter.Key()
|
|
|
|
|
|
expiredAt = this.cacheWriter.ExpiredAt()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(this.cacheReaderSuffix) > 0 {
|
|
|
|
|
|
cacheKey += this.cacheReaderSuffix
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-19 17:23:45 +08:00
|
|
|
|
compressionCacheWriter, err := this.cacheStorage.OpenWriter(cacheKey+caches.SuffixCompression+compressionEncoding, expiredAt, this.StatusCode(), this.calculateHeaderLength(), -1, cacheRef.MaxSizeBytes(), false)
|
2022-02-22 19:29:27 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 写入Header
|
2024-04-12 08:41:14 +08:00
|
|
|
|
var headerBuf = utils.SharedBufferPool.Get()
|
2022-02-22 19:29:27 +08:00
|
|
|
|
for k, v := range this.Header() {
|
2023-07-30 08:49:31 +08:00
|
|
|
|
if this.shouldIgnoreHeader(k) {
|
2022-11-19 17:35:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2022-02-22 19:29:27 +08:00
|
|
|
|
for _, v1 := range v {
|
2024-04-12 08:41:14 +08:00
|
|
|
|
_, err = headerBuf.WriteString(k + ":" + v1 + "\n")
|
2022-02-22 19:29:27 +08:00
|
|
|
|
if err != nil {
|
2024-04-12 08:41:14 +08:00
|
|
|
|
utils.SharedBufferPool.Put(headerBuf)
|
2022-02-22 19:29:27 +08:00
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write compression cache failed: "+err.Error())
|
|
|
|
|
|
_ = compressionCacheWriter.Discard()
|
|
|
|
|
|
compressionCacheWriter = nil
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-04-12 08:41:14 +08:00
|
|
|
|
_, err = compressionCacheWriter.WriteHeader(headerBuf.Bytes())
|
|
|
|
|
|
utils.SharedBufferPool.Put(headerBuf)
|
2022-11-19 15:55:05 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write compression cache failed: "+err.Error())
|
|
|
|
|
|
_ = compressionCacheWriter.Discard()
|
|
|
|
|
|
compressionCacheWriter = nil
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2024-04-12 08:41:14 +08:00
|
|
|
|
if this.compressionCacheWriter != nil {
|
|
|
|
|
|
_ = this.compressionCacheWriter.Close()
|
2022-02-22 19:29:27 +08:00
|
|
|
|
}
|
2024-04-12 08:41:14 +08:00
|
|
|
|
this.compressionCacheWriter = compressionCacheWriter
|
|
|
|
|
|
var teeWriter = writers.NewTeeWriterCloser(this.writer, compressionCacheWriter)
|
|
|
|
|
|
teeWriter.OnFail(func(err error) {
|
|
|
|
|
|
_ = compressionCacheWriter.Discard()
|
|
|
|
|
|
this.compressionCacheWriter = nil
|
|
|
|
|
|
})
|
|
|
|
|
|
this.writer = teeWriter
|
2022-02-22 19:29:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// compression writer
|
|
|
|
|
|
compressionWriter, err := compressions.NewWriter(this.writer, compressionType, int(this.compressionConfig.Level))
|
|
|
|
|
|
if err != nil {
|
2024-04-17 08:36:14 +08:00
|
|
|
|
if !compressions.CanIgnore(err) {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "open compress writer failed: "+err.Error())
|
|
|
|
|
|
}
|
2022-02-22 19:29:27 +08:00
|
|
|
|
header.Del("Content-Encoding")
|
|
|
|
|
|
if this.compressionCacheWriter != nil {
|
|
|
|
|
|
_ = this.compressionCacheWriter.Discard()
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.writer = compressionWriter
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SetCompression 设置内容压缩配置
|
|
|
|
|
|
func (this *HTTPWriter) SetCompression(config *serverconfigs.HTTPCompressionConfig) {
|
|
|
|
|
|
this.compressionConfig = config
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Raw 包装前的原始的Writer
|
|
|
|
|
|
func (this *HTTPWriter) Raw() http.ResponseWriter {
|
|
|
|
|
|
return this.rawWriter
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Header 获取Header
|
|
|
|
|
|
func (this *HTTPWriter) Header() http.Header {
|
|
|
|
|
|
if this.rawWriter == nil {
|
|
|
|
|
|
return http.Header{}
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.rawWriter.Header()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
// GetHeader 读取Header值
|
|
|
|
|
|
func (this *HTTPWriter) GetHeader(name string) string {
|
|
|
|
|
|
return this.Header().Get(name)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// DeleteHeader 删除Header
|
|
|
|
|
|
func (this *HTTPWriter) DeleteHeader(name string) {
|
|
|
|
|
|
this.rawWriter.Header().Del(name)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SetHeader 设置Header
|
|
|
|
|
|
func (this *HTTPWriter) SetHeader(name string, values []string) {
|
|
|
|
|
|
this.rawWriter.Header()[name] = values
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// AddHeaders 添加一组Header
|
|
|
|
|
|
func (this *HTTPWriter) AddHeaders(header http.Header) {
|
|
|
|
|
|
if this.rawWriter == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-07-02 10:31:08 +08:00
|
|
|
|
var newHeaders = this.rawWriter.Header()
|
2022-02-15 14:55:49 +08:00
|
|
|
|
for key, value := range header {
|
|
|
|
|
|
if key == "Connection" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2023-07-02 10:31:08 +08:00
|
|
|
|
switch key {
|
2023-08-23 16:33:19 +08:00
|
|
|
|
case "Accept-CH", "ETag", "Content-MD5", "IM", "P3P", "WWW-Authenticate", "X-Request-ID":
|
2023-07-03 16:23:54 +08:00
|
|
|
|
newHeaders[key] = value
|
2023-07-02 10:31:08 +08:00
|
|
|
|
default:
|
2023-08-23 16:33:19 +08:00
|
|
|
|
newHeaders[http.CanonicalHeaderKey(key)] = value
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Write 写入数据
|
|
|
|
|
|
func (this *HTTPWriter) Write(data []byte) (n int, err error) {
|
|
|
|
|
|
if this.webpIsEncoding {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
n, err = this.writer.Write(data)
|
|
|
|
|
|
|
2024-01-11 15:25:47 +08:00
|
|
|
|
this.checkPlanBandwidth(n)
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WriteString 写入字符串
|
|
|
|
|
|
func (this *HTTPWriter) WriteString(s string) (n int, err error) {
|
|
|
|
|
|
return this.Write([]byte(s))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SentBodyBytes 读取发送的字节数
|
|
|
|
|
|
func (this *HTTPWriter) SentBodyBytes() int64 {
|
|
|
|
|
|
return this.sentBodyBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-26 12:29:34 +08:00
|
|
|
|
// SentHeaderBytes 计算发送的Header字节数
|
|
|
|
|
|
func (this *HTTPWriter) SentHeaderBytes() int64 {
|
|
|
|
|
|
if this.sentHeaderBytes > 0 {
|
|
|
|
|
|
return this.sentHeaderBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
for k, v := range this.Header() {
|
|
|
|
|
|
for _, v1 := range v {
|
|
|
|
|
|
this.sentHeaderBytes += int64(len(k) + 2 + len(v1) + 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.sentHeaderBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *HTTPWriter) SetSentHeaderBytes(sentHeaderBytes int64) {
|
|
|
|
|
|
this.sentHeaderBytes = sentHeaderBytes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// WriteHeader 写入状态码
|
|
|
|
|
|
func (this *HTTPWriter) WriteHeader(statusCode int) {
|
2024-05-07 16:20:22 +08:00
|
|
|
|
if this.statusSent {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.statusSent = true
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if this.rawWriter != nil {
|
|
|
|
|
|
this.rawWriter.WriteHeader(statusCode)
|
|
|
|
|
|
}
|
|
|
|
|
|
this.statusCode = statusCode
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Send 直接发送内容,并终止请求
|
|
|
|
|
|
func (this *HTTPWriter) Send(status int, body string) {
|
2023-06-11 10:46:20 +08:00
|
|
|
|
this.req.ProcessResponseHeaders(this.Header(), status)
|
2023-06-09 14:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
// content-length
|
|
|
|
|
|
_, hasContentLength := this.Header()["Content-Length"]
|
|
|
|
|
|
if !hasContentLength {
|
|
|
|
|
|
this.Header()["Content-Length"] = []string{types.String(len(body))}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
this.WriteHeader(status)
|
|
|
|
|
|
_, _ = this.WriteString(body)
|
|
|
|
|
|
this.isFinished = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SendFile 发送文件内容,并终止请求
|
|
|
|
|
|
func (this *HTTPWriter) SendFile(status int, path string) (int64, error) {
|
|
|
|
|
|
this.WriteHeader(status)
|
|
|
|
|
|
this.isFinished = true
|
|
|
|
|
|
|
|
|
|
|
|
fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
|
|
|
|
|
|
if err != nil {
|
2023-08-11 14:38:00 +08:00
|
|
|
|
return 0, fmt.Errorf("open file '%s' failed: %w", path, err)
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
_ = fp.Close()
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
stat, err := fp.Stat()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return 0, err
|
|
|
|
|
|
}
|
|
|
|
|
|
if stat.IsDir() {
|
|
|
|
|
|
return 0, errors.New("open file '" + path + "' failed: it is a directory")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var bufPool = this.req.bytePool(stat.Size())
|
|
|
|
|
|
var buf = bufPool.Get()
|
|
|
|
|
|
defer bufPool.Put(buf)
|
|
|
|
|
|
|
2024-04-15 09:26:00 +08:00
|
|
|
|
written, err := io.CopyBuffer(this, fp, buf.Bytes)
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return written, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return written, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-24 21:42:03 +08:00
|
|
|
|
// SendResp 发送响应对象
|
|
|
|
|
|
func (this *HTTPWriter) SendResp(resp *http.Response) (int64, error) {
|
|
|
|
|
|
this.isFinished = true
|
|
|
|
|
|
|
|
|
|
|
|
for k, v := range resp.Header {
|
|
|
|
|
|
this.SetHeader(k, v)
|
|
|
|
|
|
}
|
2023-06-09 14:49:32 +08:00
|
|
|
|
|
2022-03-24 21:42:03 +08:00
|
|
|
|
this.WriteHeader(resp.StatusCode)
|
|
|
|
|
|
var bufPool = this.req.bytePool(resp.ContentLength)
|
|
|
|
|
|
var buf = bufPool.Get()
|
|
|
|
|
|
defer bufPool.Put(buf)
|
|
|
|
|
|
|
2024-04-15 09:26:00 +08:00
|
|
|
|
return io.CopyBuffer(this, resp.Body, buf.Bytes)
|
2022-03-24 21:42:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Redirect 跳转
|
|
|
|
|
|
func (this *HTTPWriter) Redirect(status int, url string) {
|
2023-08-20 10:10:23 +08:00
|
|
|
|
httpRedirect(this, this.req.RawReq, url, status)
|
2022-03-24 21:42:03 +08:00
|
|
|
|
this.isFinished = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// StatusCode 读取状态码
|
|
|
|
|
|
func (this *HTTPWriter) StatusCode() int {
|
|
|
|
|
|
if this.statusCode == 0 {
|
|
|
|
|
|
return http.StatusOK
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.statusCode
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HeaderData 读取Header二进制数据
|
|
|
|
|
|
func (this *HTTPWriter) HeaderData() []byte {
|
|
|
|
|
|
if this.rawWriter == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-09 20:26:36 +08:00
|
|
|
|
var resp = &http.Response{}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
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
|
2020-09-26 08:07:07 +08:00
|
|
|
|
|
|
|
|
|
|
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() {
|
2022-06-19 11:39:21 +08:00
|
|
|
|
this.finishWebP()
|
|
|
|
|
|
this.finishRequest()
|
|
|
|
|
|
this.finishCache()
|
|
|
|
|
|
this.finishCompression()
|
|
|
|
|
|
|
|
|
|
|
|
// 统计
|
|
|
|
|
|
if this.sentBodyBytes == 0 {
|
|
|
|
|
|
this.sentBodyBytes = this.counterWriter.TotalBytes()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Hijack Hijack
|
|
|
|
|
|
func (this *HTTPWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) {
|
|
|
|
|
|
hijack, ok := this.rawWriter.(http.Hijacker)
|
|
|
|
|
|
if ok {
|
2024-01-16 09:24:51 +08:00
|
|
|
|
this.req.isHijacked = true
|
2022-06-19 11:39:21 +08:00
|
|
|
|
return hijack.Hijack()
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Flush Flush
|
|
|
|
|
|
func (this *HTTPWriter) Flush() {
|
|
|
|
|
|
flusher, ok := this.rawWriter.(http.Flusher)
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
flusher.Flush()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DelayRead 是否延迟读取Reader
|
|
|
|
|
|
func (this *HTTPWriter) DelayRead() bool {
|
|
|
|
|
|
return this.delayRead
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算stale时长
|
|
|
|
|
|
func (this *HTTPWriter) calculateStaleLife() int {
|
2023-08-27 14:49:28 +08:00
|
|
|
|
var staleLife = caches.DefaultStaleCacheSeconds
|
2022-06-19 11:39:21 +08:00
|
|
|
|
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.GetHeader("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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 结束WebP
|
|
|
|
|
|
func (this *HTTPWriter) finishWebP() {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 处理WebP
|
2021-10-01 18:59:44 +08:00
|
|
|
|
if this.webpIsEncoding {
|
2023-12-12 09:55:18 +08:00
|
|
|
|
atomic.AddInt32(&webPThreads, 1)
|
2023-12-11 09:33:04 +08:00
|
|
|
|
defer func() {
|
2023-12-12 09:55:18 +08:00
|
|
|
|
atomic.AddInt32(&webPThreads, -1)
|
2023-12-11 09:33:04 +08:00
|
|
|
|
}()
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
var webpCacheWriter caches.Writer
|
|
|
|
|
|
|
|
|
|
|
|
// 准备WebP Cache
|
2022-02-22 19:29:27 +08:00
|
|
|
|
if this.cacheReader != nil || this.cacheWriter != nil {
|
|
|
|
|
|
var cacheKey = ""
|
|
|
|
|
|
var expiredAt int64 = 0
|
|
|
|
|
|
|
|
|
|
|
|
if this.cacheReader != nil {
|
2022-03-04 11:51:59 +08:00
|
|
|
|
cacheKey = this.req.cacheKey + caches.SuffixWebP
|
2022-02-22 19:29:27 +08:00
|
|
|
|
expiredAt = this.cacheReader.ExpiresAt()
|
|
|
|
|
|
} else if this.cacheWriter != nil {
|
2022-03-04 11:51:59 +08:00
|
|
|
|
cacheKey = this.cacheWriter.Key() + caches.SuffixWebP
|
2022-02-22 19:29:27 +08:00
|
|
|
|
expiredAt = this.cacheWriter.ExpiredAt()
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
2022-11-19 17:23:45 +08:00
|
|
|
|
webpCacheWriter, _ = this.cacheStorage.OpenWriter(cacheKey, expiredAt, this.StatusCode(), -1, -1, -1, false)
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if webpCacheWriter != nil {
|
|
|
|
|
|
// 写入Header
|
|
|
|
|
|
for k, v := range this.Header() {
|
2023-07-30 08:49:31 +08:00
|
|
|
|
if this.shouldIgnoreHeader(k) {
|
2022-11-19 17:35:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-22 19:29:27 +08:00
|
|
|
|
// 这里是原始的数据,不需要内容编码
|
|
|
|
|
|
if k == "Content-Encoding" || k == "Transfer-Encoding" {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
for _, v1 := range v {
|
|
|
|
|
|
_, err := webpCacheWriter.WriteHeader([]byte(k + ":" + v1 + "\n"))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "write webp cache failed: "+err.Error())
|
|
|
|
|
|
_ = webpCacheWriter.Discard()
|
|
|
|
|
|
webpCacheWriter = nil
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if webpCacheWriter != nil {
|
|
|
|
|
|
var teeWriter = writers.NewTeeWriterCloser(this.writer, webpCacheWriter)
|
|
|
|
|
|
teeWriter.OnFail(func(err error) {
|
2023-07-31 16:05:08 +08:00
|
|
|
|
if webpCacheWriter != nil {
|
|
|
|
|
|
_ = webpCacheWriter.Discard()
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
webpCacheWriter = nil
|
|
|
|
|
|
})
|
|
|
|
|
|
this.writer = teeWriter
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var reader = readers.NewBytesCounterReader(this.rawReader)
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
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
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if isGif {
|
|
|
|
|
|
gifImage, err = gif.DecodeAll(reader)
|
2023-09-11 16:05:59 +08:00
|
|
|
|
if gifImage != nil && (gifImage.Config.Width > gowebp.WebPMaxDimension || gifImage.Config.Height > gowebp.WebPMaxDimension) {
|
2023-12-12 09:55:18 +08:00
|
|
|
|
webPIgnoreURLSet.Push(this.req.URL())
|
2023-09-11 16:05:59 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-10-13 11:56:40 +08:00
|
|
|
|
} else {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
imageData, _, err = image.Decode(reader)
|
2023-09-11 16:05:59 +08:00
|
|
|
|
if imageData != nil {
|
|
|
|
|
|
var bound = imageData.Bounds()
|
|
|
|
|
|
if bound.Max.X > gowebp.WebPMaxDimension || bound.Max.Y > gowebp.WebPMaxDimension {
|
2023-12-12 09:55:18 +08:00
|
|
|
|
webPIgnoreURLSet.Push(this.req.URL())
|
2023-09-11 16:05:59 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-10-13 11:56:40 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
2021-10-01 17:20:37 +08:00
|
|
|
|
if err != nil {
|
2022-06-19 11:39:21 +08:00
|
|
|
|
// 发生了错误终止处理
|
2023-12-12 09:55:18 +08:00
|
|
|
|
webPIgnoreURLSet.Push(this.req.URL())
|
2022-02-15 14:55:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2021-10-01 17:20:37 +08:00
|
|
|
|
|
2023-12-11 10:17:17 +08:00
|
|
|
|
var f = types.Float32(this.webpQuality)
|
|
|
|
|
|
if f <= 0 || f > 100 {
|
|
|
|
|
|
if this.size > (8<<20) || this.size <= 0 {
|
|
|
|
|
|
f = 30
|
|
|
|
|
|
} else if this.size > (1 << 20) {
|
|
|
|
|
|
f = 50
|
|
|
|
|
|
} else if this.size > (128 << 10) {
|
|
|
|
|
|
f = 60
|
|
|
|
|
|
} else {
|
|
|
|
|
|
f = 75
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
2021-10-29 12:19:26 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if imageData != nil {
|
|
|
|
|
|
err = gowebp.Encode(this.writer, imageData, &gowebp.Options{
|
|
|
|
|
|
Lossless: false,
|
|
|
|
|
|
Quality: f,
|
|
|
|
|
|
Exact: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
} else if gifImage != nil {
|
2022-08-19 13:27:18 +08:00
|
|
|
|
var anim = gowebp.NewWebpAnimation(gifImage.Config.Width, gifImage.Config.Height, gifImage.LoopCount)
|
|
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
anim.WebPAnimEncoderOptions.SetKmin(9)
|
|
|
|
|
|
anim.WebPAnimEncoderOptions.SetKmax(17)
|
2022-08-19 13:27:18 +08:00
|
|
|
|
var webpConfig = gowebp.NewWebpConfig()
|
2022-02-15 14:55:49 +08:00
|
|
|
|
//webpConfig.SetLossless(1)
|
|
|
|
|
|
webpConfig.SetQuality(f)
|
|
|
|
|
|
|
2022-06-19 11:39:21 +08:00
|
|
|
|
var timeline = 0
|
2022-08-19 13:27:18 +08:00
|
|
|
|
var lastErr error
|
2022-02-15 14:55:49 +08:00
|
|
|
|
for i, img := range gifImage.Image {
|
|
|
|
|
|
err = anim.AddFrame(img, timeline, webpConfig)
|
|
|
|
|
|
if err != nil {
|
2022-08-19 13:27:18 +08:00
|
|
|
|
// 有错误直接跳过
|
|
|
|
|
|
lastErr = err
|
|
|
|
|
|
err = nil
|
2021-10-29 12:19:26 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
timeline += gifImage.Delay[i] * 10
|
2021-10-29 12:19:26 +08:00
|
|
|
|
}
|
2022-08-19 13:27:18 +08:00
|
|
|
|
if lastErr != nil {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "'"+this.req.URL()+"' encode webp failed: "+lastErr.Error())
|
|
|
|
|
|
}
|
2022-08-19 14:50:26 +08:00
|
|
|
|
err = anim.AddFrame(nil, timeline, webpConfig)
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
2022-08-19 14:50:26 +08:00
|
|
|
|
if err == nil {
|
|
|
|
|
|
err = anim.Encode(this.writer)
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
2022-08-19 14:50:26 +08:00
|
|
|
|
|
|
|
|
|
|
anim.ReleaseMemory()
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
2021-10-01 17:20:37 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if err != nil && !this.req.canIgnore(err) {
|
|
|
|
|
|
remotelogs.Error("HTTP_WRITER", "'"+this.req.URL()+"' encode webp failed: "+err.Error())
|
|
|
|
|
|
}
|
2021-10-13 11:11:57 +08:00
|
|
|
|
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if err == nil && webpCacheWriter != nil {
|
|
|
|
|
|
err = webpCacheWriter.Close()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = webpCacheWriter.Discard()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.cacheStorage.AddToList(&caches.Item{
|
|
|
|
|
|
Type: webpCacheWriter.ItemType(),
|
|
|
|
|
|
Key: webpCacheWriter.Key(),
|
2024-03-22 09:21:45 +08:00
|
|
|
|
ExpiresAt: webpCacheWriter.ExpiredAt(),
|
2022-02-15 14:55:49 +08:00
|
|
|
|
StaleAt: webpCacheWriter.ExpiredAt() + int64(this.calculateStaleLife()),
|
|
|
|
|
|
HeaderSize: webpCacheWriter.HeaderSize(),
|
|
|
|
|
|
BodySize: webpCacheWriter.BodySize(),
|
|
|
|
|
|
Host: this.req.ReqHost,
|
|
|
|
|
|
ServerId: this.req.ReqServer.Id,
|
|
|
|
|
|
})
|
2021-10-01 17:20:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
}
|
2022-06-19 11:39:21 +08:00
|
|
|
|
}
|
2021-10-01 18:59:44 +08:00
|
|
|
|
|
2022-06-19 11:39:21 +08:00
|
|
|
|
// 结束缓存相关处理
|
|
|
|
|
|
func (this *HTTPWriter) finishCache() {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
// 缓存
|
2020-10-05 16:55:14 +08:00
|
|
|
|
if this.cacheWriter != nil {
|
2022-02-15 14:55:49 +08:00
|
|
|
|
if this.isOk && this.cacheIsFinished {
|
2022-02-22 21:43:47 +08:00
|
|
|
|
// 对比缓存前后的Content-Length
|
|
|
|
|
|
var method = this.req.Method()
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if method != http.MethodHead && this.StatusCode() != http.StatusNoContent && !this.isPartial {
|
|
|
|
|
|
var contentLengthString = this.GetHeader("Content-Length")
|
2022-02-22 21:43:47 +08:00
|
|
|
|
if len(contentLengthString) > 0 {
|
|
|
|
|
|
var contentLength = types.Int64(contentLengthString)
|
|
|
|
|
|
if contentLength != this.cacheWriter.BodySize() {
|
|
|
|
|
|
this.isOk = false
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
2022-03-03 19:36:28 +08:00
|
|
|
|
this.cacheWriter = nil
|
2022-02-22 21:43:47 +08:00
|
|
|
|
}
|
2021-07-20 19:48:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if this.isOk && this.cacheWriter != nil {
|
2021-07-20 19:48:08 +08:00
|
|
|
|
err := this.cacheWriter.Close()
|
|
|
|
|
|
if err == nil {
|
2022-03-04 22:42:03 +08:00
|
|
|
|
if !this.isPartial || this.partialFileIsNew {
|
|
|
|
|
|
var expiredAt = this.cacheWriter.ExpiredAt()
|
|
|
|
|
|
this.cacheStorage.AddToList(&caches.Item{
|
|
|
|
|
|
Type: this.cacheWriter.ItemType(),
|
|
|
|
|
|
Key: this.cacheWriter.Key(),
|
2024-03-22 09:21:45 +08:00
|
|
|
|
ExpiresAt: expiredAt,
|
2022-03-04 22:42:03 +08:00
|
|
|
|
StaleAt: expiredAt + int64(this.calculateStaleLife()),
|
|
|
|
|
|
HeaderSize: this.cacheWriter.HeaderSize(),
|
|
|
|
|
|
BodySize: this.cacheWriter.BodySize(),
|
|
|
|
|
|
Host: this.req.ReqHost,
|
|
|
|
|
|
ServerId: this.req.ReqServer.Id,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if !this.isPartial || !this.cacheIsFinished {
|
|
|
|
|
|
_ = this.cacheWriter.Discard()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Partial的文件内容不删除
|
|
|
|
|
|
err := this.cacheWriter.Close()
|
|
|
|
|
|
if err == nil && this.partialFileIsNew {
|
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(),
|
2024-03-22 09:21:45 +08:00
|
|
|
|
ExpiresAt: expiredAt,
|
2021-12-16 17:27:21 +08:00
|
|
|
|
StaleAt: expiredAt + int64(this.calculateStaleLife()),
|
2021-07-20 19:48:08 +08:00
|
|
|
|
HeaderSize: this.cacheWriter.HeaderSize(),
|
|
|
|
|
|
BodySize: this.cacheWriter.BodySize(),
|
2022-01-01 20:15:39 +08:00
|
|
|
|
Host: this.req.ReqHost,
|
|
|
|
|
|
ServerId: this.req.ReqServer.Id,
|
2021-07-20 19:48:08 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2021-06-06 23:42:11 +08:00
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-06-19 11:39:21 +08:00
|
|
|
|
}
|
2022-02-15 14:55:49 +08:00
|
|
|
|
|
2022-06-19 11:39:21 +08:00
|
|
|
|
// 结束压缩相关处理
|
|
|
|
|
|
func (this *HTTPWriter) finishCompression() {
|
2022-02-22 19:29:27 +08:00
|
|
|
|
if this.compressionCacheWriter != nil {
|
|
|
|
|
|
if this.isOk {
|
|
|
|
|
|
err := this.compressionCacheWriter.Close()
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
var expiredAt = this.compressionCacheWriter.ExpiredAt()
|
|
|
|
|
|
this.cacheStorage.AddToList(&caches.Item{
|
|
|
|
|
|
Type: this.compressionCacheWriter.ItemType(),
|
|
|
|
|
|
Key: this.compressionCacheWriter.Key(),
|
2024-03-22 09:21:45 +08:00
|
|
|
|
ExpiresAt: expiredAt,
|
2022-02-22 19:29:27 +08:00
|
|
|
|
StaleAt: expiredAt + int64(this.calculateStaleLife()),
|
|
|
|
|
|
HeaderSize: this.compressionCacheWriter.HeaderSize(),
|
|
|
|
|
|
BodySize: this.compressionCacheWriter.BodySize(),
|
|
|
|
|
|
Host: this.req.ReqHost,
|
|
|
|
|
|
ServerId: this.req.ReqServer.Id,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_ = this.compressionCacheWriter.Discard()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-06-19 11:39:21 +08:00
|
|
|
|
// 最终关闭
|
|
|
|
|
|
func (this *HTTPWriter) finishRequest() {
|
|
|
|
|
|
if this.writer != nil {
|
|
|
|
|
|
_ = this.writer.Close()
|
2020-09-26 08:07:07 +08:00
|
|
|
|
}
|
2020-10-05 16:55:14 +08:00
|
|
|
|
|
2022-06-19 11:39:21 +08:00
|
|
|
|
if this.rawReader != nil {
|
|
|
|
|
|
_ = this.rawReader.Close()
|
2021-12-16 17:27:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-11-19 17:23:45 +08:00
|
|
|
|
|
|
|
|
|
|
// 计算Header长度
|
|
|
|
|
|
func (this *HTTPWriter) calculateHeaderLength() (result int) {
|
|
|
|
|
|
for k, v := range this.Header() {
|
2023-07-30 08:49:31 +08:00
|
|
|
|
if this.shouldIgnoreHeader(k) {
|
2022-11-19 17:35:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2022-11-19 17:23:45 +08:00
|
|
|
|
for _, v1 := range v {
|
|
|
|
|
|
result += len(k) + 1 /**:**/ + len(v1) + 1 /**\n**/
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-07-30 08:49:31 +08:00
|
|
|
|
|
|
|
|
|
|
func (this *HTTPWriter) shouldIgnoreHeader(name string) bool {
|
|
|
|
|
|
switch name {
|
2023-09-17 10:54:14 +08:00
|
|
|
|
case "Set-Cookie", "Strict-Transport-Security", "Alt-Svc", "Upgrade", "X-Cache":
|
2023-07-30 08:49:31 +08:00
|
|
|
|
return true
|
|
|
|
|
|
default:
|
2024-04-12 08:41:14 +08:00
|
|
|
|
return this.isPartial && name == "Content-Range"
|
2023-07-30 08:49:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|