github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/http_writer.go (about)

     1  // Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
     2  
     3  package nodes
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"errors"
     9  	"fmt"
    10  	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
    11  	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
    12  	"github.com/TeaOSLab/EdgeNode/internal/caches"
    13  	"github.com/TeaOSLab/EdgeNode/internal/compressions"
    14  	"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
    15  	"github.com/TeaOSLab/EdgeNode/internal/utils"
    16  	"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
    17  	"github.com/TeaOSLab/EdgeNode/internal/utils/readers"
    18  	setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
    19  	"github.com/TeaOSLab/EdgeNode/internal/utils/writers"
    20  	_ "github.com/biessek/golang-ico"
    21  	"github.com/iwind/TeaGo/types"
    22  	"github.com/iwind/gowebp"
    23  	_ "golang.org/x/image/bmp"
    24  	_ "golang.org/x/image/webp"
    25  	"image"
    26  	"image/gif"
    27  	_ "image/jpeg"
    28  	_ "image/png"
    29  	"io"
    30  	"net"
    31  	"net/http"
    32  	"net/textproto"
    33  	"os"
    34  	"path/filepath"
    35  	"runtime"
    36  	"strings"
    37  	"sync/atomic"
    38  )
    39  
    40  var webPThreads int32
    41  var webPMaxThreads int32 = 1
    42  var webPIgnoreURLSet = setutils.NewFixedSet(131072)
    43  
    44  func init() {
    45  	webPMaxThreads = int32(runtime.NumCPU() / 4)
    46  	if webPMaxThreads < 1 {
    47  		webPMaxThreads = 1
    48  	}
    49  }
    50  
    51  // HTTPWriter 响应Writer
    52  type HTTPWriter struct {
    53  	req       *HTTPRequest
    54  	rawWriter http.ResponseWriter
    55  
    56  	rawReader io.ReadCloser
    57  	delayRead bool
    58  
    59  	counterWriter *writers.BytesCounterWriter
    60  	writer        io.WriteCloser
    61  
    62  	size int64
    63  
    64  	statusCode      int
    65  	sentBodyBytes   int64
    66  	sentHeaderBytes int64
    67  
    68  	isOk       bool // 是否完全成功
    69  	isFinished bool // 是否已完成
    70  
    71  	// Partial
    72  	isPartial        bool
    73  	partialFileIsNew bool
    74  
    75  	// WebP
    76  	webpIsEncoding        bool
    77  	webpOriginContentType string
    78  	webpQuality           int
    79  
    80  	// Compression
    81  	compressionConfig      *serverconfigs.HTTPCompressionConfig
    82  	compressionCacheWriter caches.Writer
    83  
    84  	// Cache
    85  	cacheStorage    caches.StorageInterface
    86  	cacheWriter     caches.Writer
    87  	cacheIsFinished bool
    88  
    89  	cacheReader       caches.Reader
    90  	cacheReaderSuffix string
    91  }
    92  
    93  // NewHTTPWriter 包装对象
    94  func NewHTTPWriter(req *HTTPRequest, httpResponseWriter http.ResponseWriter) *HTTPWriter {
    95  	var counterWriter = writers.NewBytesCounterWriter(httpResponseWriter)
    96  	return &HTTPWriter{
    97  		req:           req,
    98  		rawWriter:     httpResponseWriter,
    99  		writer:        counterWriter,
   100  		counterWriter: counterWriter,
   101  	}
   102  }
   103  
   104  // Prepare 准备输出
   105  func (this *HTTPWriter) Prepare(resp *http.Response, size int64, status int, enableCache bool) (delayHeaders bool) {
   106  	// 清理以前数据,防止重试时发生异常错误
   107  	if this.compressionCacheWriter != nil {
   108  		_ = this.compressionCacheWriter.Discard()
   109  		this.compressionCacheWriter = nil
   110  	}
   111  
   112  	if this.cacheWriter != nil {
   113  		_ = this.cacheWriter.Discard()
   114  		this.cacheWriter = nil
   115  	}
   116  
   117  	// 新的请求相关数据
   118  	this.size = size
   119  	this.statusCode = status
   120  
   121  	// 是否为区间请求
   122  	this.isPartial = status == http.StatusPartialContent
   123  
   124  	// 不支持对GET以外的方法返回的Partial内容的缓存
   125  	if this.isPartial && this.req.Method() != http.MethodGet {
   126  		enableCache = false
   127  	}
   128  
   129  	if resp != nil && resp.Body != nil {
   130  		cacheReader, ok := resp.Body.(caches.Reader)
   131  		if ok {
   132  			this.cacheReader = cacheReader
   133  		}
   134  
   135  		this.rawReader = resp.Body
   136  
   137  		if enableCache {
   138  			this.PrepareCache(resp, size)
   139  		}
   140  		if !this.isPartial {
   141  			this.PrepareWebP(resp, size)
   142  		}
   143  		this.PrepareCompression(resp, size)
   144  	}
   145  
   146  	// 是否限速写入
   147  	if this.req.web != nil &&
   148  		this.req.web.RequestLimit != nil &&
   149  		this.req.web.RequestLimit.IsOn &&
   150  		this.req.web.RequestLimit.OutBandwidthPerConnBytes() > 0 {
   151  		this.writer = writers.NewRateLimitWriter(this.req.RawReq.Context(), this.writer, this.req.web.RequestLimit.OutBandwidthPerConnBytes())
   152  	}
   153  
   154  	return
   155  }
   156  
   157  // PrepareCache 准备缓存
   158  func (this *HTTPWriter) PrepareCache(resp *http.Response, size int64) {
   159  	if resp == nil {
   160  		return
   161  	}
   162  
   163  	var cachePolicy = this.req.ReqServer.HTTPCachePolicy
   164  	if cachePolicy == nil || !cachePolicy.IsOn {
   165  		return
   166  	}
   167  
   168  	var cacheRef = this.req.cacheRef
   169  	if cacheRef == nil || !cacheRef.IsOn {
   170  		return
   171  	}
   172  
   173  	var addStatusHeader = this.req.web != nil && this.req.web.Cache != nil && this.req.web.Cache.AddStatusHeader
   174  
   175  	// 不支持Range
   176  	if this.isPartial {
   177  		if !cacheRef.AllowPartialContent {
   178  			this.req.varMapping["cache.status"] = "BYPASS"
   179  			if addStatusHeader {
   180  				this.Header().Set("X-Cache", "BYPASS, not supported partial content")
   181  			}
   182  			return
   183  		}
   184  		if this.cacheStorage.Policy().Type != serverconfigs.CachePolicyStorageFile {
   185  			this.req.varMapping["cache.status"] = "BYPASS"
   186  			if addStatusHeader {
   187  				this.Header().Set("X-Cache", "BYPASS, not supported partial content in memory storage")
   188  			}
   189  			return
   190  		}
   191  	}
   192  
   193  	// 如果允许 ChunkedEncoding,就无需尺寸的判断,因为此时的 size 为 -1
   194  	if !cacheRef.AllowChunkedEncoding && size < 0 {
   195  		this.req.varMapping["cache.status"] = "BYPASS"
   196  		if addStatusHeader {
   197  			this.Header().Set("X-Cache", "BYPASS, ChunkedEncoding")
   198  		}
   199  		return
   200  	}
   201  
   202  	var contentSize = size
   203  	if this.isPartial {
   204  		// 从Content-Range中读取内容总长度
   205  		var contentRange = this.Header().Get("Content-Range")
   206  		_, totalSize := httpRequestParseContentRangeHeader(contentRange)
   207  		if totalSize > 0 {
   208  			contentSize = totalSize
   209  		}
   210  	}
   211  	if contentSize >= 0 && ((cacheRef.MaxSizeBytes() > 0 && contentSize > cacheRef.MaxSizeBytes()) ||
   212  		(cachePolicy.MaxSizeBytes() > 0 && contentSize > cachePolicy.MaxSizeBytes()) || (cacheRef.MinSizeBytes() > contentSize)) {
   213  		this.req.varMapping["cache.status"] = "BYPASS"
   214  		if addStatusHeader {
   215  			this.Header().Set("X-Cache", "BYPASS, Content-Length")
   216  		}
   217  		return
   218  	}
   219  
   220  	// 检查状态
   221  	if !cacheRef.MatchStatus(this.StatusCode()) {
   222  		this.req.varMapping["cache.status"] = "BYPASS"
   223  		if addStatusHeader {
   224  			this.Header().Set("X-Cache", "BYPASS, Status: "+types.String(this.StatusCode()))
   225  		}
   226  		return
   227  	}
   228  
   229  	// Cache-Control
   230  	if len(cacheRef.SkipResponseCacheControlValues) > 0 {
   231  		var cacheControl = this.GetHeader("Cache-Control")
   232  		if len(cacheControl) > 0 {
   233  			values := strings.Split(cacheControl, ",")
   234  			for _, value := range values {
   235  				if cacheRef.ContainsCacheControl(strings.TrimSpace(value)) {
   236  					this.req.varMapping["cache.status"] = "BYPASS"
   237  					if addStatusHeader {
   238  						this.Header().Set("X-Cache", "BYPASS, Cache-Control: "+cacheControl)
   239  					}
   240  					return
   241  				}
   242  			}
   243  		}
   244  	}
   245  
   246  	// Set-Cookie
   247  	if cacheRef.SkipResponseSetCookie && len(this.GetHeader("Set-Cookie")) > 0 {
   248  		this.req.varMapping["cache.status"] = "BYPASS"
   249  		if addStatusHeader {
   250  			this.Header().Set("X-Cache", "BYPASS, Set-Cookie")
   251  		}
   252  		return
   253  	}
   254  
   255  	// 校验其他条件
   256  	if cacheRef.Conds != nil && cacheRef.Conds.HasResponseConds() && !cacheRef.Conds.MatchResponse(this.req.Format) {
   257  		this.req.varMapping["cache.status"] = "BYPASS"
   258  		if addStatusHeader {
   259  			this.Header().Set("X-Cache", "BYPASS, ResponseConds")
   260  		}
   261  		return
   262  	}
   263  
   264  	// 打开缓存写入
   265  	var storage = caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
   266  	if storage == nil {
   267  		this.req.varMapping["cache.status"] = "BYPASS"
   268  		if addStatusHeader {
   269  			this.Header().Set("X-Cache", "BYPASS, Storage")
   270  		}
   271  		return
   272  	}
   273  
   274  	this.req.varMapping["cache.status"] = "UPDATING"
   275  	if addStatusHeader {
   276  		this.Header().Set("X-Cache", "UPDATING")
   277  	}
   278  
   279  	this.cacheStorage = storage
   280  	var life = cacheRef.LifeSeconds()
   281  
   282  	if life <= 0 {
   283  		life = 60
   284  	}
   285  
   286  	// 支持源站设置的max-age
   287  	if this.req.web.Cache != nil && this.req.web.Cache.EnableCacheControlMaxAge {
   288  		var cacheControl = this.GetHeader("Cache-Control")
   289  		var pieces = strings.Split(cacheControl, ";")
   290  		for _, piece := range pieces {
   291  			var eqIndex = strings.Index(piece, "=")
   292  			if eqIndex > 0 && piece[:eqIndex] == "max-age" {
   293  				var maxAge = types.Int64(piece[eqIndex+1:])
   294  				if maxAge > 0 {
   295  					life = maxAge
   296  				}
   297  			}
   298  		}
   299  	}
   300  
   301  	var expiresAt = fasttime.Now().Unix() + life
   302  
   303  	if this.req.isLnRequest {
   304  		// 返回上级节点过期时间
   305  		this.SetHeader(LNExpiresHeader, []string{types.String(expiresAt)})
   306  	} else {
   307  		var expiresHeader = this.Header().Get(LNExpiresHeader)
   308  		if len(expiresHeader) > 0 {
   309  			this.Header().Del(LNExpiresHeader)
   310  
   311  			var expiresHeaderInt64 = types.Int64(expiresHeader)
   312  			if expiresHeaderInt64 > 0 {
   313  				expiresAt = expiresHeaderInt64
   314  			}
   315  		}
   316  	}
   317  
   318  	var cacheKey = this.req.cacheKey
   319  	if this.isPartial {
   320  		cacheKey += caches.SuffixPartial
   321  	}
   322  
   323  	// 待写入尺寸
   324  	var totalSize = size
   325  	if this.isPartial {
   326  		var contentRange = resp.Header.Get("Content-Range")
   327  		if len(contentRange) > 0 {
   328  			_, partialTotalSize := httpRequestParseContentRangeHeader(contentRange)
   329  			if partialTotalSize > 0 && partialTotalSize > totalSize {
   330  				totalSize = partialTotalSize
   331  			}
   332  		}
   333  	}
   334  
   335  	// 先清理以前的
   336  	if this.cacheWriter != nil {
   337  		_ = this.cacheWriter.Discard()
   338  	}
   339  
   340  	cacheWriter, err := storage.OpenWriter(cacheKey, expiresAt, this.StatusCode(), this.calculateHeaderLength(), totalSize, cacheRef.MaxSizeBytes(), this.isPartial)
   341  	if err != nil {
   342  		if errors.Is(err, caches.ErrEntityTooLarge) && addStatusHeader {
   343  			this.Header().Set("X-Cache", "BYPASS, entity too large")
   344  		}
   345  
   346  		if !caches.CanIgnoreErr(err) {
   347  			remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
   348  			this.Header().Set("X-Cache", "BYPASS, write cache failed")
   349  		} else {
   350  			this.Header().Set("X-Cache", "BYPASS, "+err.Error())
   351  		}
   352  		return
   353  	}
   354  	this.cacheWriter = cacheWriter
   355  
   356  	if this.isPartial {
   357  		this.partialFileIsNew = cacheWriter.(*caches.PartialFileWriter).IsNew()
   358  	}
   359  
   360  	// 写入Header
   361  	var headerBuf = utils.SharedBufferPool.Get()
   362  	for k, v := range this.Header() {
   363  		if this.shouldIgnoreHeader(k) {
   364  			continue
   365  		}
   366  		for _, v1 := range v {
   367  			if this.isPartial && k == "Content-Type" && strings.Contains(v1, "multipart/byteranges") {
   368  				continue
   369  			}
   370  			_, err = headerBuf.WriteString(k + ":" + v1 + "\n")
   371  			if err != nil {
   372  				utils.SharedBufferPool.Put(headerBuf)
   373  
   374  				remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
   375  				_ = this.cacheWriter.Discard()
   376  				this.cacheWriter = nil
   377  				return
   378  			}
   379  		}
   380  	}
   381  	_, err = cacheWriter.WriteHeader(headerBuf.Bytes())
   382  	utils.SharedBufferPool.Put(headerBuf)
   383  	if err != nil {
   384  		remotelogs.Error("HTTP_WRITER", "write cache failed: "+err.Error())
   385  		_ = this.cacheWriter.Discard()
   386  		this.cacheWriter = nil
   387  		return
   388  	}
   389  
   390  	if this.isPartial {
   391  		// content-range
   392  		var contentRange = this.GetHeader("Content-Range")
   393  		if len(contentRange) > 0 {
   394  			start, total := httpRequestParseContentRangeHeader(contentRange)
   395  			if start < 0 {
   396  				return
   397  			}
   398  			if total > 0 {
   399  				partialWriter, ok := cacheWriter.(*caches.PartialFileWriter)
   400  				if !ok {
   401  					return
   402  				}
   403  				partialWriter.SetBodyLength(total)
   404  			}
   405  			var filterReader = readers.NewFilterReaderCloser(resp.Body)
   406  			this.cacheIsFinished = true
   407  			var hasError = false
   408  			filterReader.Add(func(p []byte, readErr error) error {
   409  				// 这里不用处理readErr,因为只要把成功读取的部分写入缓存即可
   410  
   411  				if hasError {
   412  					return nil
   413  				}
   414  
   415  				var l = len(p)
   416  				if l == 0 {
   417  					return nil
   418  				}
   419  				defer func() {
   420  					start += int64(l)
   421  				}()
   422  				err = cacheWriter.WriteAt(start, p)
   423  				if err != nil {
   424  					this.cacheIsFinished = false
   425  					hasError = true
   426  				}
   427  				return nil
   428  			})
   429  			resp.Body = filterReader
   430  			this.rawReader = filterReader
   431  			return
   432  		}
   433  
   434  		// multipart/byteranges
   435  		var contentType = this.GetHeader("Content-Type")
   436  		if strings.Contains(contentType, "multipart/byteranges") {
   437  			partialWriter, ok := cacheWriter.(*caches.PartialFileWriter)
   438  			if !ok {
   439  				return
   440  			}
   441  
   442  			var boundary = httpRequestParseBoundary(contentType)
   443  			if len(boundary) == 0 {
   444  				return
   445  			}
   446  
   447  			var reader = readers.NewByteRangesReaderCloser(resp.Body, boundary)
   448  			var contentTypeWritten = false
   449  
   450  			this.cacheIsFinished = true
   451  			var hasError = false
   452  			var writtenTotal = false
   453  			reader.OnPartRead(func(start int64, end int64, total int64, data []byte, header textproto.MIMEHeader) {
   454  				// TODO 如果 total 超出缓存限制,则不写入缓存数据,并且记录到某个内存表中,下次不再OpenWriter
   455  
   456  				if hasError {
   457  					return
   458  				}
   459  
   460  				// 写入total
   461  				if !writtenTotal && total > 0 {
   462  					partialWriter.SetBodyLength(total)
   463  					writtenTotal = true
   464  				}
   465  
   466  				// 写入Content-Type
   467  				if partialWriter.IsNew() && !contentTypeWritten {
   468  					var realContentType = header.Get("Content-Type")
   469  					if len(realContentType) > 0 {
   470  						var h = []byte("Content-Type:" + realContentType + "\n")
   471  						err = partialWriter.AppendHeader(h)
   472  						if err != nil {
   473  							hasError = true
   474  							this.cacheIsFinished = false
   475  							return
   476  						}
   477  					}
   478  
   479  					contentTypeWritten = true
   480  				}
   481  
   482  				writeErr := cacheWriter.WriteAt(start, data)
   483  				if writeErr != nil {
   484  					hasError = true
   485  					this.cacheIsFinished = false
   486  				}
   487  			})
   488  
   489  			resp.Body = reader
   490  			this.rawReader = reader
   491  		}
   492  
   493  		return
   494  	}
   495  
   496  	var cacheReader = readers.NewTeeReaderCloser(resp.Body, this.cacheWriter, false)
   497  	resp.Body = cacheReader
   498  	this.rawReader = cacheReader
   499  
   500  	cacheReader.OnFail(func(err error) {
   501  		if this.cacheWriter != nil {
   502  			_ = this.cacheWriter.Discard()
   503  		}
   504  		this.cacheWriter = nil
   505  	})
   506  	cacheReader.OnEOF(func() {
   507  		this.cacheIsFinished = true
   508  	})
   509  }
   510  
   511  // PrepareWebP 准备WebP
   512  func (this *HTTPWriter) PrepareWebP(resp *http.Response, size int64) {
   513  	if resp == nil {
   514  		return
   515  	}
   516  
   517  	// 集群配置
   518  	var policy = this.req.nodeConfig.FindWebPImagePolicyWithClusterId(this.req.ReqServer.ClusterId)
   519  	if policy == nil {
   520  		policy = nodeconfigs.DefaultWebPImagePolicy
   521  	}
   522  	if !policy.IsOn {
   523  		return
   524  	}
   525  
   526  	// 只有在开启了缓存之后,才会转换,防止占用的系统资源过高
   527  	if policy.RequireCache && this.req.cacheRef == nil {
   528  		return
   529  	}
   530  	this.webpQuality = policy.Quality
   531  
   532  	// 限制最小和最大尺寸
   533  	// TODO 需要将reader修改为LimitReader
   534  	if resp.ContentLength == 0 {
   535  		return
   536  	}
   537  
   538  	if resp.ContentLength > 0 && (resp.ContentLength < policy.MinLengthBytes() || (policy.MaxLengthBytes() > 0 && resp.ContentLength > policy.MaxLengthBytes())) {
   539  		return
   540  	}
   541  
   542  	var contentType = this.GetHeader("Content-Type")
   543  
   544  	if this.req.web != nil &&
   545  		this.req.web.WebP != nil &&
   546  		this.req.web.WebP.IsOn &&
   547  		this.req.web.WebP.MatchResponse(contentType, size, filepath.Ext(this.req.Path()), this.req.Format) &&
   548  		this.req.web.WebP.MatchAccept(this.req.requestHeader("Accept")) {
   549  		// 检查是否已经因为尺寸过大而忽略
   550  		if webPIgnoreURLSet.Has(this.req.URL()) {
   551  			return
   552  		}
   553  
   554  		// 如果已经是WebP不再重复处理
   555  		// TODO 考虑是否需要很严格的匹配
   556  		if strings.Contains(contentType, "image/webp") {
   557  			return
   558  		}
   559  
   560  		// 检查当前是否正在转换
   561  		if atomic.LoadInt32(&webPThreads) >= webPMaxThreads {
   562  			return
   563  		}
   564  
   565  		var contentEncoding = this.GetHeader("Content-Encoding")
   566  		if len(contentEncoding) > 0 {
   567  			if compressions.SupportEncoding(contentEncoding) {
   568  				reader, err := compressions.NewReader(resp.Body, contentEncoding)
   569  				if err != nil {
   570  					return
   571  				}
   572  				this.Header().Del("Content-Encoding")
   573  				this.Header().Del("Content-Length")
   574  				this.rawReader = reader
   575  			} else {
   576  				return
   577  			}
   578  		}
   579  
   580  		this.webpOriginContentType = contentType
   581  		this.webpIsEncoding = true
   582  		resp.Body = io.NopCloser(&bytes.Buffer{})
   583  		this.delayRead = true
   584  
   585  		this.Header().Del("Content-Length")
   586  		this.Header().Set("Content-Type", "image/webp")
   587  	}
   588  }
   589  
   590  // PrepareCompression 准备压缩
   591  func (this *HTTPWriter) PrepareCompression(resp *http.Response, size int64) {
   592  	var method = this.req.Method()
   593  	if method == http.MethodHead {
   594  		return
   595  	}
   596  
   597  	if this.StatusCode() == http.StatusNoContent {
   598  		return
   599  	}
   600  
   601  	var acceptEncodings = this.req.RawReq.Header.Get("Accept-Encoding")
   602  	var contentEncoding = this.GetHeader("Content-Encoding")
   603  
   604  	if this.compressionConfig == nil || !this.compressionConfig.IsOn {
   605  		if compressions.SupportEncoding(contentEncoding) && !httpAcceptEncoding(acceptEncodings, contentEncoding) {
   606  			reader, err := compressions.NewReader(resp.Body, contentEncoding)
   607  			if err != nil {
   608  				return
   609  			}
   610  			this.Header().Del("Content-Encoding")
   611  			this.Header().Del("Content-Length")
   612  			resp.Body = reader
   613  		}
   614  		return
   615  	}
   616  
   617  	// 检查是否正繁忙
   618  	if compressions.IsBusy() {
   619  		return
   620  	}
   621  
   622  	// 检查URL
   623  	if !this.compressionConfig.MatchURL(this.req.URL()) {
   624  		return
   625  	}
   626  
   627  	// 分区内容不压缩,防止读取失败
   628  	if !this.compressionConfig.EnablePartialContent && this.StatusCode() == http.StatusPartialContent {
   629  		return
   630  	}
   631  
   632  	// 如果已经有编码则不处理
   633  	if len(contentEncoding) > 0 && (!this.compressionConfig.DecompressData || !compressions.SupportEncoding(contentEncoding)) {
   634  		return
   635  	}
   636  
   637  	// 尺寸和类型
   638  	var contentType = this.GetHeader("Content-Type")
   639  	if !this.compressionConfig.MatchResponse(contentType, size, filepath.Ext(this.req.Path()), this.req.Format) {
   640  		return
   641  	}
   642  
   643  	// 判断Accept是否支持压缩
   644  	compressionType, compressionEncoding, ok := this.compressionConfig.MatchAcceptEncoding(acceptEncodings)
   645  	if !ok {
   646  		return
   647  	}
   648  
   649  	// 压缩前后如果编码一致,则不处理
   650  	if compressionEncoding == contentEncoding {
   651  		return
   652  	}
   653  
   654  	if len(contentEncoding) > 0 && resp != nil {
   655  		if !this.compressionConfig.DecompressData {
   656  			return
   657  		}
   658  
   659  		reader, err := compressions.NewReader(resp.Body, contentEncoding)
   660  		if err != nil {
   661  			return
   662  		}
   663  		this.Header().Del("Content-Encoding")
   664  		this.Header().Del("Content-Length")
   665  		resp.Body = reader
   666  	}
   667  
   668  	// 需要放在compression cache writer之前
   669  	var header = this.rawWriter.Header()
   670  	header.Set("Content-Encoding", compressionEncoding)
   671  	header.Set("Vary", "Accept-Encoding")
   672  	header.Del("Content-Length")
   673  
   674  	// compression cache writer
   675  	// 只有在本身内容已经缓存的情况下才会写入缓存,防止同时写入缓存导致IO负载升高
   676  	var cacheRef = this.req.cacheRef
   677  	if !this.isPartial &&
   678  		this.cacheStorage != nil &&
   679  		cacheRef != nil &&
   680  		(this.cacheReader != nil || (this.cacheStorage.Policy().SyncCompressionCache && this.cacheWriter != nil)) &&
   681  		!this.webpIsEncoding {
   682  		var cacheKey = ""
   683  		var expiredAt int64 = 0
   684  
   685  		if this.cacheReader != nil {
   686  			cacheKey = this.req.cacheKey
   687  			expiredAt = this.cacheReader.ExpiresAt()
   688  		} else if this.cacheWriter != nil {
   689  			cacheKey = this.cacheWriter.Key()
   690  			expiredAt = this.cacheWriter.ExpiredAt()
   691  		}
   692  
   693  		if len(this.cacheReaderSuffix) > 0 {
   694  			cacheKey += this.cacheReaderSuffix
   695  		}
   696  
   697  		compressionCacheWriter, err := this.cacheStorage.OpenWriter(cacheKey+caches.SuffixCompression+compressionEncoding, expiredAt, this.StatusCode(), this.calculateHeaderLength(), -1, cacheRef.MaxSizeBytes(), false)
   698  		if err != nil {
   699  			return
   700  		}
   701  
   702  		// 写入Header
   703  		var headerBuf = utils.SharedBufferPool.Get()
   704  		for k, v := range this.Header() {
   705  			if this.shouldIgnoreHeader(k) {
   706  				continue
   707  			}
   708  			for _, v1 := range v {
   709  				_, err = headerBuf.WriteString(k + ":" + v1 + "\n")
   710  				if err != nil {
   711  					utils.SharedBufferPool.Put(headerBuf)
   712  					remotelogs.Error("HTTP_WRITER", "write compression cache failed: "+err.Error())
   713  					_ = compressionCacheWriter.Discard()
   714  					compressionCacheWriter = nil
   715  					return
   716  				}
   717  			}
   718  		}
   719  		_, err = compressionCacheWriter.WriteHeader(headerBuf.Bytes())
   720  		utils.SharedBufferPool.Put(headerBuf)
   721  		if err != nil {
   722  			remotelogs.Error("HTTP_WRITER", "write compression cache failed: "+err.Error())
   723  			_ = compressionCacheWriter.Discard()
   724  			compressionCacheWriter = nil
   725  			return
   726  		}
   727  		if this.compressionCacheWriter != nil {
   728  			_ = this.compressionCacheWriter.Close()
   729  		}
   730  		this.compressionCacheWriter = compressionCacheWriter
   731  		var teeWriter = writers.NewTeeWriterCloser(this.writer, compressionCacheWriter)
   732  		teeWriter.OnFail(func(err error) {
   733  			_ = compressionCacheWriter.Discard()
   734  			this.compressionCacheWriter = nil
   735  		})
   736  		this.writer = teeWriter
   737  	}
   738  
   739  	// compression writer
   740  	compressionWriter, err := compressions.NewWriter(this.writer, compressionType, int(this.compressionConfig.Level))
   741  	if err != nil {
   742  		if !compressions.CanIgnore(err) {
   743  			remotelogs.Error("HTTP_WRITER", "open compress writer failed: "+err.Error())
   744  		}
   745  		header.Del("Content-Encoding")
   746  		if this.compressionCacheWriter != nil {
   747  			_ = this.compressionCacheWriter.Discard()
   748  		}
   749  		return
   750  	}
   751  	this.writer = compressionWriter
   752  }
   753  
   754  // SetCompression 设置内容压缩配置
   755  func (this *HTTPWriter) SetCompression(config *serverconfigs.HTTPCompressionConfig) {
   756  	this.compressionConfig = config
   757  }
   758  
   759  // Raw 包装前的原始的Writer
   760  func (this *HTTPWriter) Raw() http.ResponseWriter {
   761  	return this.rawWriter
   762  }
   763  
   764  // Header 获取Header
   765  func (this *HTTPWriter) Header() http.Header {
   766  	if this.rawWriter == nil {
   767  		return http.Header{}
   768  	}
   769  	return this.rawWriter.Header()
   770  }
   771  
   772  // GetHeader 读取Header值
   773  func (this *HTTPWriter) GetHeader(name string) string {
   774  	return this.Header().Get(name)
   775  }
   776  
   777  // DeleteHeader 删除Header
   778  func (this *HTTPWriter) DeleteHeader(name string) {
   779  	this.rawWriter.Header().Del(name)
   780  }
   781  
   782  // SetHeader 设置Header
   783  func (this *HTTPWriter) SetHeader(name string, values []string) {
   784  	this.rawWriter.Header()[name] = values
   785  }
   786  
   787  // AddHeaders 添加一组Header
   788  func (this *HTTPWriter) AddHeaders(header http.Header) {
   789  	if this.rawWriter == nil {
   790  		return
   791  	}
   792  	var newHeaders = this.rawWriter.Header()
   793  	for key, value := range header {
   794  		if key == "Connection" {
   795  			continue
   796  		}
   797  		switch key {
   798  		case "Accept-CH", "ETag", "Content-MD5", "IM", "P3P", "WWW-Authenticate", "X-Request-ID":
   799  			newHeaders[key] = value
   800  		default:
   801  			newHeaders[http.CanonicalHeaderKey(key)] = value
   802  		}
   803  	}
   804  }
   805  
   806  // Write 写入数据
   807  func (this *HTTPWriter) Write(data []byte) (n int, err error) {
   808  	if this.webpIsEncoding {
   809  		return
   810  	}
   811  	n, err = this.writer.Write(data)
   812  
   813  	this.checkPlanBandwidth(n)
   814  
   815  	return
   816  }
   817  
   818  // WriteString 写入字符串
   819  func (this *HTTPWriter) WriteString(s string) (n int, err error) {
   820  	return this.Write([]byte(s))
   821  }
   822  
   823  // SentBodyBytes 读取发送的字节数
   824  func (this *HTTPWriter) SentBodyBytes() int64 {
   825  	return this.sentBodyBytes
   826  }
   827  
   828  // SentHeaderBytes 计算发送的Header字节数
   829  func (this *HTTPWriter) SentHeaderBytes() int64 {
   830  	if this.sentHeaderBytes > 0 {
   831  		return this.sentHeaderBytes
   832  	}
   833  	for k, v := range this.Header() {
   834  		for _, v1 := range v {
   835  			this.sentHeaderBytes += int64(len(k) + 2 + len(v1) + 1)
   836  		}
   837  	}
   838  	return this.sentHeaderBytes
   839  }
   840  
   841  func (this *HTTPWriter) SetSentHeaderBytes(sentHeaderBytes int64) {
   842  	this.sentHeaderBytes = sentHeaderBytes
   843  }
   844  
   845  // WriteHeader 写入状态码
   846  func (this *HTTPWriter) WriteHeader(statusCode int) {
   847  	if this.rawWriter != nil {
   848  		this.rawWriter.WriteHeader(statusCode)
   849  	}
   850  	this.statusCode = statusCode
   851  }
   852  
   853  // Send 直接发送内容,并终止请求
   854  func (this *HTTPWriter) Send(status int, body string) {
   855  	this.req.ProcessResponseHeaders(this.Header(), status)
   856  
   857  	// content-length
   858  	_, hasContentLength := this.Header()["Content-Length"]
   859  	if !hasContentLength {
   860  		this.Header()["Content-Length"] = []string{types.String(len(body))}
   861  	}
   862  
   863  	this.WriteHeader(status)
   864  	_, _ = this.WriteString(body)
   865  	this.isFinished = true
   866  }
   867  
   868  // SendFile 发送文件内容,并终止请求
   869  func (this *HTTPWriter) SendFile(status int, path string) (int64, error) {
   870  	this.WriteHeader(status)
   871  	this.isFinished = true
   872  
   873  	fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
   874  	if err != nil {
   875  		return 0, fmt.Errorf("open file '%s' failed: %w", path, err)
   876  	}
   877  	defer func() {
   878  		_ = fp.Close()
   879  	}()
   880  
   881  	stat, err := fp.Stat()
   882  	if err != nil {
   883  		return 0, err
   884  	}
   885  	if stat.IsDir() {
   886  		return 0, errors.New("open file '" + path + "' failed: it is a directory")
   887  	}
   888  
   889  	var bufPool = this.req.bytePool(stat.Size())
   890  	var buf = bufPool.Get()
   891  	defer bufPool.Put(buf)
   892  
   893  	written, err := io.CopyBuffer(this, fp, buf.Bytes)
   894  	if err != nil {
   895  		return written, err
   896  	}
   897  
   898  	return written, nil
   899  }
   900  
   901  // SendResp 发送响应对象
   902  func (this *HTTPWriter) SendResp(resp *http.Response) (int64, error) {
   903  	this.isFinished = true
   904  
   905  	for k, v := range resp.Header {
   906  		this.SetHeader(k, v)
   907  	}
   908  
   909  	this.WriteHeader(resp.StatusCode)
   910  	var bufPool = this.req.bytePool(resp.ContentLength)
   911  	var buf = bufPool.Get()
   912  	defer bufPool.Put(buf)
   913  
   914  	return io.CopyBuffer(this, resp.Body, buf.Bytes)
   915  }
   916  
   917  // Redirect 跳转
   918  func (this *HTTPWriter) Redirect(status int, url string) {
   919  	httpRedirect(this, this.req.RawReq, url, status)
   920  	this.isFinished = true
   921  }
   922  
   923  // StatusCode 读取状态码
   924  func (this *HTTPWriter) StatusCode() int {
   925  	if this.statusCode == 0 {
   926  		return http.StatusOK
   927  	}
   928  	return this.statusCode
   929  }
   930  
   931  // HeaderData 读取Header二进制数据
   932  func (this *HTTPWriter) HeaderData() []byte {
   933  	if this.rawWriter == nil {
   934  		return nil
   935  	}
   936  
   937  	var resp = &http.Response{}
   938  	resp.Header = this.Header()
   939  	if this.statusCode == 0 {
   940  		this.statusCode = http.StatusOK
   941  	}
   942  	resp.StatusCode = this.statusCode
   943  	resp.ProtoMajor = 1
   944  	resp.ProtoMinor = 1
   945  
   946  	resp.ContentLength = 1 // Trick:这样可以屏蔽Content-Length
   947  
   948  	writer := bytes.NewBuffer([]byte{})
   949  	_ = resp.Write(writer)
   950  	return writer.Bytes()
   951  }
   952  
   953  // SetOk 设置成功
   954  func (this *HTTPWriter) SetOk() {
   955  	this.isOk = true
   956  }
   957  
   958  // Close 关闭
   959  func (this *HTTPWriter) Close() {
   960  	this.finishWebP()
   961  	this.finishRequest()
   962  	this.finishCache()
   963  	this.finishCompression()
   964  
   965  	// 统计
   966  	if this.sentBodyBytes == 0 {
   967  		this.sentBodyBytes = this.counterWriter.TotalBytes()
   968  	}
   969  }
   970  
   971  // Hijack Hijack
   972  func (this *HTTPWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) {
   973  	hijack, ok := this.rawWriter.(http.Hijacker)
   974  	if ok {
   975  		this.req.isHijacked = true
   976  		return hijack.Hijack()
   977  	}
   978  	return
   979  }
   980  
   981  // Flush Flush
   982  func (this *HTTPWriter) Flush() {
   983  	flusher, ok := this.rawWriter.(http.Flusher)
   984  	if ok {
   985  		flusher.Flush()
   986  	}
   987  }
   988  
   989  // DelayRead 是否延迟读取Reader
   990  func (this *HTTPWriter) DelayRead() bool {
   991  	return this.delayRead
   992  }
   993  
   994  // 计算stale时长
   995  func (this *HTTPWriter) calculateStaleLife() int {
   996  	var staleLife = caches.DefaultStaleCacheSeconds
   997  	var staleConfig = this.req.web.Cache.Stale
   998  	if staleConfig != nil && staleConfig.IsOn {
   999  		// 从Header中读取stale-if-error
  1000  		var isDefinedInHeader = false
  1001  		if staleConfig.SupportStaleIfErrorHeader {
  1002  			var cacheControl = this.GetHeader("Cache-Control")
  1003  			var pieces = strings.Split(cacheControl, ",")
  1004  			for _, piece := range pieces {
  1005  				var eqIndex = strings.Index(piece, "=")
  1006  				if eqIndex > 0 && strings.TrimSpace(piece[:eqIndex]) == "stale-if-error" {
  1007  					// 这里预示着如果stale-if-error=0,可以关闭stale功能
  1008  					staleLife = types.Int(strings.TrimSpace(piece[eqIndex+1:]))
  1009  					isDefinedInHeader = true
  1010  					break
  1011  				}
  1012  			}
  1013  		}
  1014  
  1015  		// 自定义
  1016  		if !isDefinedInHeader && staleConfig.Life != nil {
  1017  			staleLife = types.Int(staleConfig.Life.Duration().Seconds())
  1018  		}
  1019  	}
  1020  	return staleLife
  1021  }
  1022  
  1023  // 结束WebP
  1024  func (this *HTTPWriter) finishWebP() {
  1025  	// 处理WebP
  1026  	if this.webpIsEncoding {
  1027  		atomic.AddInt32(&webPThreads, 1)
  1028  		defer func() {
  1029  			atomic.AddInt32(&webPThreads, -1)
  1030  		}()
  1031  
  1032  		var webpCacheWriter caches.Writer
  1033  
  1034  		// 准备WebP Cache
  1035  		if this.cacheReader != nil || this.cacheWriter != nil {
  1036  			var cacheKey = ""
  1037  			var expiredAt int64 = 0
  1038  
  1039  			if this.cacheReader != nil {
  1040  				cacheKey = this.req.cacheKey + caches.SuffixWebP
  1041  				expiredAt = this.cacheReader.ExpiresAt()
  1042  			} else if this.cacheWriter != nil {
  1043  				cacheKey = this.cacheWriter.Key() + caches.SuffixWebP
  1044  				expiredAt = this.cacheWriter.ExpiredAt()
  1045  			}
  1046  
  1047  			webpCacheWriter, _ = this.cacheStorage.OpenWriter(cacheKey, expiredAt, this.StatusCode(), -1, -1, -1, false)
  1048  			if webpCacheWriter != nil {
  1049  				// 写入Header
  1050  				for k, v := range this.Header() {
  1051  					if this.shouldIgnoreHeader(k) {
  1052  						continue
  1053  					}
  1054  
  1055  					// 这里是原始的数据,不需要内容编码
  1056  					if k == "Content-Encoding" || k == "Transfer-Encoding" {
  1057  						continue
  1058  					}
  1059  					for _, v1 := range v {
  1060  						_, err := webpCacheWriter.WriteHeader([]byte(k + ":" + v1 + "\n"))
  1061  						if err != nil {
  1062  							remotelogs.Error("HTTP_WRITER", "write webp cache failed: "+err.Error())
  1063  							_ = webpCacheWriter.Discard()
  1064  							webpCacheWriter = nil
  1065  							break
  1066  						}
  1067  					}
  1068  				}
  1069  
  1070  				if webpCacheWriter != nil {
  1071  					var teeWriter = writers.NewTeeWriterCloser(this.writer, webpCacheWriter)
  1072  					teeWriter.OnFail(func(err error) {
  1073  						if webpCacheWriter != nil {
  1074  							_ = webpCacheWriter.Discard()
  1075  						}
  1076  						webpCacheWriter = nil
  1077  					})
  1078  					this.writer = teeWriter
  1079  				}
  1080  			}
  1081  		}
  1082  
  1083  		var reader = readers.NewBytesCounterReader(this.rawReader)
  1084  
  1085  		var imageData image.Image
  1086  		var gifImage *gif.GIF
  1087  		var isGif = strings.Contains(this.webpOriginContentType, "image/gif")
  1088  		var err error
  1089  		if isGif {
  1090  			gifImage, err = gif.DecodeAll(reader)
  1091  			if gifImage != nil && (gifImage.Config.Width > gowebp.WebPMaxDimension || gifImage.Config.Height > gowebp.WebPMaxDimension) {
  1092  				webPIgnoreURLSet.Push(this.req.URL())
  1093  				return
  1094  			}
  1095  		} else {
  1096  			imageData, _, err = image.Decode(reader)
  1097  			if imageData != nil {
  1098  				var bound = imageData.Bounds()
  1099  				if bound.Max.X > gowebp.WebPMaxDimension || bound.Max.Y > gowebp.WebPMaxDimension {
  1100  					webPIgnoreURLSet.Push(this.req.URL())
  1101  					return
  1102  				}
  1103  			}
  1104  		}
  1105  
  1106  		if err != nil {
  1107  			// 发生了错误终止处理
  1108  			webPIgnoreURLSet.Push(this.req.URL())
  1109  			return
  1110  		}
  1111  
  1112  		var f = types.Float32(this.webpQuality)
  1113  		if f <= 0 || f > 100 {
  1114  			if this.size > (8<<20) || this.size <= 0 {
  1115  				f = 30
  1116  			} else if this.size > (1 << 20) {
  1117  				f = 50
  1118  			} else if this.size > (128 << 10) {
  1119  				f = 60
  1120  			} else {
  1121  				f = 75
  1122  			}
  1123  		}
  1124  
  1125  		if imageData != nil {
  1126  			err = gowebp.Encode(this.writer, imageData, &gowebp.Options{
  1127  				Lossless: false,
  1128  				Quality:  f,
  1129  				Exact:    true,
  1130  			})
  1131  		} else if gifImage != nil {
  1132  			var anim = gowebp.NewWebpAnimation(gifImage.Config.Width, gifImage.Config.Height, gifImage.LoopCount)
  1133  
  1134  			anim.WebPAnimEncoderOptions.SetKmin(9)
  1135  			anim.WebPAnimEncoderOptions.SetKmax(17)
  1136  			var webpConfig = gowebp.NewWebpConfig()
  1137  			//webpConfig.SetLossless(1)
  1138  			webpConfig.SetQuality(f)
  1139  
  1140  			var timeline = 0
  1141  			var lastErr error
  1142  			for i, img := range gifImage.Image {
  1143  				err = anim.AddFrame(img, timeline, webpConfig)
  1144  				if err != nil {
  1145  					// 有错误直接跳过
  1146  					lastErr = err
  1147  					err = nil
  1148  				}
  1149  				timeline += gifImage.Delay[i] * 10
  1150  			}
  1151  			if lastErr != nil {
  1152  				remotelogs.Error("HTTP_WRITER", "'"+this.req.URL()+"' encode webp failed: "+lastErr.Error())
  1153  			}
  1154  			err = anim.AddFrame(nil, timeline, webpConfig)
  1155  
  1156  			if err == nil {
  1157  				err = anim.Encode(this.writer)
  1158  			}
  1159  
  1160  			anim.ReleaseMemory()
  1161  		}
  1162  
  1163  		if err != nil && !this.req.canIgnore(err) {
  1164  			remotelogs.Error("HTTP_WRITER", "'"+this.req.URL()+"' encode webp failed: "+err.Error())
  1165  		}
  1166  
  1167  		if err == nil && webpCacheWriter != nil {
  1168  			err = webpCacheWriter.Close()
  1169  			if err != nil {
  1170  				_ = webpCacheWriter.Discard()
  1171  			} else {
  1172  				this.cacheStorage.AddToList(&caches.Item{
  1173  					Type:       webpCacheWriter.ItemType(),
  1174  					Key:        webpCacheWriter.Key(),
  1175  					ExpiresAt:  webpCacheWriter.ExpiredAt(),
  1176  					StaleAt:    webpCacheWriter.ExpiredAt() + int64(this.calculateStaleLife()),
  1177  					HeaderSize: webpCacheWriter.HeaderSize(),
  1178  					BodySize:   webpCacheWriter.BodySize(),
  1179  					Host:       this.req.ReqHost,
  1180  					ServerId:   this.req.ReqServer.Id,
  1181  				})
  1182  			}
  1183  		}
  1184  	}
  1185  }
  1186  
  1187  // 结束缓存相关处理
  1188  func (this *HTTPWriter) finishCache() {
  1189  	// 缓存
  1190  	if this.cacheWriter != nil {
  1191  		if this.isOk && this.cacheIsFinished {
  1192  			// 对比缓存前后的Content-Length
  1193  			var method = this.req.Method()
  1194  			if method != http.MethodHead && this.StatusCode() != http.StatusNoContent && !this.isPartial {
  1195  				var contentLengthString = this.GetHeader("Content-Length")
  1196  				if len(contentLengthString) > 0 {
  1197  					var contentLength = types.Int64(contentLengthString)
  1198  					if contentLength != this.cacheWriter.BodySize() {
  1199  						this.isOk = false
  1200  						_ = this.cacheWriter.Discard()
  1201  						this.cacheWriter = nil
  1202  					}
  1203  				}
  1204  			}
  1205  
  1206  			if this.isOk && this.cacheWriter != nil {
  1207  				err := this.cacheWriter.Close()
  1208  				if err == nil {
  1209  					if !this.isPartial || this.partialFileIsNew {
  1210  						var expiredAt = this.cacheWriter.ExpiredAt()
  1211  						this.cacheStorage.AddToList(&caches.Item{
  1212  							Type:       this.cacheWriter.ItemType(),
  1213  							Key:        this.cacheWriter.Key(),
  1214  							ExpiresAt:  expiredAt,
  1215  							StaleAt:    expiredAt + int64(this.calculateStaleLife()),
  1216  							HeaderSize: this.cacheWriter.HeaderSize(),
  1217  							BodySize:   this.cacheWriter.BodySize(),
  1218  							Host:       this.req.ReqHost,
  1219  							ServerId:   this.req.ReqServer.Id,
  1220  						})
  1221  					}
  1222  				}
  1223  			}
  1224  		} else {
  1225  			if !this.isPartial || !this.cacheIsFinished {
  1226  				_ = this.cacheWriter.Discard()
  1227  			} else {
  1228  				// Partial的文件内容不删除
  1229  				err := this.cacheWriter.Close()
  1230  				if err == nil && this.partialFileIsNew {
  1231  					var expiredAt = this.cacheWriter.ExpiredAt()
  1232  					this.cacheStorage.AddToList(&caches.Item{
  1233  						Type:       this.cacheWriter.ItemType(),
  1234  						Key:        this.cacheWriter.Key(),
  1235  						ExpiresAt:  expiredAt,
  1236  						StaleAt:    expiredAt + int64(this.calculateStaleLife()),
  1237  						HeaderSize: this.cacheWriter.HeaderSize(),
  1238  						BodySize:   this.cacheWriter.BodySize(),
  1239  						Host:       this.req.ReqHost,
  1240  						ServerId:   this.req.ReqServer.Id,
  1241  					})
  1242  				}
  1243  			}
  1244  		}
  1245  	}
  1246  }
  1247  
  1248  // 结束压缩相关处理
  1249  func (this *HTTPWriter) finishCompression() {
  1250  	if this.compressionCacheWriter != nil {
  1251  		if this.isOk {
  1252  			err := this.compressionCacheWriter.Close()
  1253  			if err == nil {
  1254  				var expiredAt = this.compressionCacheWriter.ExpiredAt()
  1255  				this.cacheStorage.AddToList(&caches.Item{
  1256  					Type:       this.compressionCacheWriter.ItemType(),
  1257  					Key:        this.compressionCacheWriter.Key(),
  1258  					ExpiresAt:  expiredAt,
  1259  					StaleAt:    expiredAt + int64(this.calculateStaleLife()),
  1260  					HeaderSize: this.compressionCacheWriter.HeaderSize(),
  1261  					BodySize:   this.compressionCacheWriter.BodySize(),
  1262  					Host:       this.req.ReqHost,
  1263  					ServerId:   this.req.ReqServer.Id,
  1264  				})
  1265  			}
  1266  		} else {
  1267  			_ = this.compressionCacheWriter.Discard()
  1268  		}
  1269  	}
  1270  }
  1271  
  1272  // 最终关闭
  1273  func (this *HTTPWriter) finishRequest() {
  1274  	if this.writer != nil {
  1275  		_ = this.writer.Close()
  1276  	}
  1277  
  1278  	if this.rawReader != nil {
  1279  		_ = this.rawReader.Close()
  1280  	}
  1281  }
  1282  
  1283  // 计算Header长度
  1284  func (this *HTTPWriter) calculateHeaderLength() (result int) {
  1285  	for k, v := range this.Header() {
  1286  		if this.shouldIgnoreHeader(k) {
  1287  			continue
  1288  		}
  1289  		for _, v1 := range v {
  1290  			result += len(k) + 1 /**:**/ + len(v1) + 1 /**\n**/
  1291  		}
  1292  	}
  1293  	return
  1294  }
  1295  
  1296  func (this *HTTPWriter) shouldIgnoreHeader(name string) bool {
  1297  	switch name {
  1298  	case "Set-Cookie", "Strict-Transport-Security", "Alt-Svc", "Upgrade", "X-Cache":
  1299  		return true
  1300  	default:
  1301  		return this.isPartial && name == "Content-Range"
  1302  	}
  1303  }