github.com/TeaOSLab/EdgeNode@v1.3.8/internal/nodes/http_request_cache.go (about) 1 package nodes 2 3 import ( 4 "bytes" 5 "errors" 6 "github.com/TeaOSLab/EdgeCommon/pkg/configutils" 7 "github.com/TeaOSLab/EdgeNode/internal/caches" 8 "github.com/TeaOSLab/EdgeNode/internal/compressions" 9 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 10 "github.com/TeaOSLab/EdgeNode/internal/utils" 11 "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" 12 rangeutils "github.com/TeaOSLab/EdgeNode/internal/utils/ranges" 13 "github.com/iwind/TeaGo/types" 14 "io" 15 "net/http" 16 "path/filepath" 17 "strconv" 18 "strings" 19 "time" 20 ) 21 22 // 读取缓存 23 func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) { 24 // 需要动态Upgrade的不缓存 25 if len(this.RawReq.Header.Get("Upgrade")) > 0 { 26 return 27 } 28 29 this.cacheCanTryStale = false 30 31 var cachePolicy = this.ReqServer.HTTPCachePolicy 32 if cachePolicy == nil || !cachePolicy.IsOn { 33 return 34 } 35 36 if this.web.Cache == nil || !this.web.Cache.IsOn || (len(cachePolicy.CacheRefs) == 0 && len(this.web.Cache.CacheRefs) == 0) { 37 return 38 } 39 40 // 添加 X-Cache Header 41 var addStatusHeader = this.web.Cache.AddStatusHeader 42 var cacheBypassDescription = "" 43 if addStatusHeader { 44 defer func() { 45 if len(cacheBypassDescription) > 0 { 46 this.writer.Header().Set("X-Cache", cacheBypassDescription) 47 return 48 } 49 var cacheStatus = this.varMapping["cache.status"] 50 if cacheStatus != "HIT" { 51 this.writer.Header().Set("X-Cache", cacheStatus) 52 } 53 }() 54 } 55 56 // 检查服务独立的缓存条件 57 var refType = "" 58 for _, cacheRef := range this.web.Cache.CacheRefs { 59 if !cacheRef.IsOn { 60 continue 61 } 62 if (cacheRef.Conds != nil && cacheRef.Conds.HasRequestConds() && cacheRef.Conds.MatchRequest(this.Format)) || 63 (cacheRef.SimpleCond != nil && cacheRef.SimpleCond.Match(this.Format)) { 64 if cacheRef.IsReverse { 65 return 66 } 67 this.cacheRef = cacheRef 68 refType = "server" 69 break 70 } 71 } 72 if this.cacheRef == nil && !this.web.Cache.DisablePolicyRefs { 73 // 检查策略默认的缓存条件 74 for _, cacheRef := range cachePolicy.CacheRefs { 75 if !cacheRef.IsOn { 76 continue 77 } 78 if (cacheRef.Conds != nil && cacheRef.Conds.HasRequestConds() && cacheRef.Conds.MatchRequest(this.Format)) || 79 (cacheRef.SimpleCond != nil && cacheRef.SimpleCond.Match(this.Format)) { 80 if cacheRef.IsReverse { 81 return 82 } 83 this.cacheRef = cacheRef 84 refType = "policy" 85 break 86 } 87 } 88 } 89 90 if this.cacheRef == nil { 91 return 92 } 93 94 // 是否强制Range回源 95 if this.cacheRef.AlwaysForwardRangeRequest && len(this.RawReq.Header.Get("Range")) > 0 { 96 this.cacheRef = nil 97 cacheBypassDescription = "BYPASS, forward range" 98 return 99 } 100 101 // 是否正在Purge 102 var isPurging = this.web.Cache.PurgeIsOn && strings.ToUpper(this.RawReq.Method) == "PURGE" && this.RawReq.Header.Get("X-Edge-Purge-Key") == this.web.Cache.PurgeKey 103 if isPurging { 104 this.RawReq.Method = http.MethodGet 105 } 106 107 // 校验请求 108 if !this.cacheRef.MatchRequest(this.RawReq) { 109 this.cacheRef = nil 110 cacheBypassDescription = "BYPASS, not match" 111 return 112 } 113 114 // 相关变量 115 this.varMapping["cache.policy.name"] = cachePolicy.Name 116 this.varMapping["cache.policy.id"] = strconv.FormatInt(cachePolicy.Id, 10) 117 this.varMapping["cache.policy.type"] = cachePolicy.Type 118 119 // Cache-Pragma 120 if this.cacheRef.EnableRequestCachePragma { 121 if this.RawReq.Header.Get("Cache-Control") == "no-cache" || this.RawReq.Header.Get("Pragma") == "no-cache" { 122 this.cacheRef = nil 123 cacheBypassDescription = "BYPASS, Cache-Control or Pragma" 124 return 125 } 126 } 127 128 // TODO 支持Vary Header 129 130 // 缓存标签 131 var tags = []string{} 132 133 // 检查是否有缓存 134 var key string 135 if this.web.Cache.Key != nil && this.web.Cache.Key.IsOn && len(this.web.Cache.Key.Host) > 0 { 136 key = configutils.ParseVariables(this.cacheRef.Key, func(varName string) (value string) { 137 switch varName { 138 case "scheme": 139 return this.web.Cache.Key.Scheme 140 case "host": 141 return this.web.Cache.Key.Host 142 default: 143 return this.Format("${" + varName + "}") 144 } 145 }) 146 } else { 147 key = this.Format(this.cacheRef.Key) 148 } 149 150 if len(key) == 0 { 151 this.cacheRef = nil 152 cacheBypassDescription = "BYPASS, empty key" 153 return 154 } 155 var method = this.Method() 156 if method != http.MethodGet { 157 key += caches.SuffixMethod + method 158 tags = append(tags, strings.ToLower(method)) 159 } 160 161 this.cacheKey = key 162 this.varMapping["cache.key"] = key 163 164 // 读取缓存 165 var storage = caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id) 166 if storage == nil { 167 this.cacheRef = nil 168 cacheBypassDescription = "BYPASS, no policy found" 169 return 170 } 171 this.writer.cacheStorage = storage 172 173 // 如果正在预热,则不读取缓存,等待下一个步骤重新生成 174 if (strings.HasPrefix(this.RawReq.RemoteAddr, "127.") || strings.HasPrefix(this.RawReq.RemoteAddr, "[::1]")) && this.RawReq.Header.Get("X-Edge-Cache-Action") == "fetch" { 175 return 176 } 177 178 // 判断是否在Purge 179 if isPurging { 180 this.varMapping["cache.status"] = "PURGE" 181 182 var subKeys = []string{ 183 key, 184 key + caches.SuffixMethod + "HEAD", 185 key + caches.SuffixWebP, 186 key + caches.SuffixPartial, 187 } 188 // TODO 根据实际缓存的内容进行组合 189 for _, encoding := range compressions.AllEncodings() { 190 subKeys = append(subKeys, key+caches.SuffixCompression+encoding) 191 subKeys = append(subKeys, key+caches.SuffixWebP+caches.SuffixCompression+encoding) 192 } 193 for _, subKey := range subKeys { 194 err := storage.Delete(subKey) 195 if err != nil { 196 remotelogs.ErrorServer("HTTP_REQUEST_CACHE", "purge failed: "+err.Error()) 197 } 198 } 199 200 // 通过API节点清除别节点上的的Key 201 SharedHTTPCacheTaskManager.PushTaskKeys([]string{key}) 202 203 return true 204 } 205 206 // 调用回调 207 this.onRequest() 208 if this.writer.isFinished { 209 return 210 } 211 212 var reader caches.Reader 213 var err error 214 215 var rangeHeader = this.RawReq.Header.Get("Range") 216 var isPartialRequest = len(rangeHeader) > 0 217 218 // 检查是否支持WebP 219 var webPIsEnabled = false 220 var isHeadMethod = method == http.MethodHead 221 if !isPartialRequest && 222 !isHeadMethod && 223 this.web.WebP != nil && 224 this.web.WebP.IsOn && 225 this.web.WebP.MatchRequest(filepath.Ext(this.Path()), this.Format) && 226 this.web.WebP.MatchAccept(this.RawReq.Header.Get("Accept")) { 227 webPIsEnabled = true 228 } 229 230 // 检查WebP压缩缓存 231 if webPIsEnabled && !isPartialRequest && !isHeadMethod && reader == nil { 232 if this.web.Compression != nil && this.web.Compression.IsOn { 233 _, encoding, ok := this.web.Compression.MatchAcceptEncoding(this.RawReq.Header.Get("Accept-Encoding")) 234 if ok { 235 reader, err = storage.OpenReader(key+caches.SuffixWebP+caches.SuffixCompression+encoding, useStale, false) 236 if err != nil && caches.IsBusyError(err) { 237 this.varMapping["cache.status"] = "BUSY" 238 this.cacheRef = nil 239 return 240 } 241 if reader != nil { 242 tags = append(tags, "webp", encoding) 243 } 244 } 245 } 246 } 247 248 // 检查WebP 249 if webPIsEnabled && !isPartialRequest && 250 !isHeadMethod && 251 reader == nil { 252 reader, err = storage.OpenReader(key+caches.SuffixWebP, useStale, false) 253 if err != nil && caches.IsBusyError(err) { 254 this.varMapping["cache.status"] = "BUSY" 255 this.cacheRef = nil 256 return 257 } 258 if reader != nil { 259 this.writer.cacheReaderSuffix = caches.SuffixWebP 260 tags = append(tags, "webp") 261 } 262 } 263 264 // 检查普通压缩缓存 265 if !isPartialRequest && !isHeadMethod && reader == nil { 266 if this.web.Compression != nil && this.web.Compression.IsOn { 267 _, encoding, ok := this.web.Compression.MatchAcceptEncoding(this.RawReq.Header.Get("Accept-Encoding")) 268 if ok { 269 reader, err = storage.OpenReader(key+caches.SuffixCompression+encoding, useStale, false) 270 if err != nil && caches.IsBusyError(err) { 271 this.varMapping["cache.status"] = "BUSY" 272 this.cacheRef = nil 273 return 274 } 275 if reader != nil { 276 tags = append(tags, encoding) 277 } 278 } 279 } 280 } 281 282 // 检查正常的文件 283 var isPartialCache = false 284 var partialRanges []rangeutils.Range 285 if reader == nil { 286 reader, err = storage.OpenReader(key, useStale, false) 287 if err != nil && caches.IsBusyError(err) { 288 this.varMapping["cache.status"] = "BUSY" 289 this.cacheRef = nil 290 return 291 } 292 if err != nil && this.cacheRef.AllowPartialContent { 293 // 尝试读取分片的缓存内容 294 if len(rangeHeader) == 0 && this.cacheRef.ForcePartialContent { 295 // 默认读取开头 296 rangeHeader = "bytes=0-" 297 } 298 299 if len(rangeHeader) > 0 { 300 pReader, ranges, goNext := this.tryPartialReader(storage, key, useStale, rangeHeader, this.cacheRef.ForcePartialContent) 301 if !goNext { 302 this.cacheRef = nil 303 return 304 } 305 if pReader != nil { 306 isPartialCache = true 307 reader = pReader 308 partialRanges = ranges 309 err = nil 310 } 311 } 312 } 313 314 if err != nil { 315 if errors.Is(err, caches.ErrNotFound) { 316 // 移除请求中的 If-None-Match 和 If-Modified-Since,防止源站返回304而无法缓存 317 if this.reverseProxy != nil { 318 this.RawReq.Header.Del("If-None-Match") 319 this.RawReq.Header.Del("If-Modified-Since") 320 } 321 322 // cache相关变量 323 this.varMapping["cache.status"] = "MISS" 324 325 if !useStale && this.web.Cache.Stale != nil && this.web.Cache.Stale.IsOn { 326 this.cacheCanTryStale = true 327 } 328 return 329 } 330 331 if !this.canIgnore(err) { 332 remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: open cache failed: "+err.Error()) 333 } 334 return 335 } 336 } 337 338 defer func() { 339 if !this.writer.DelayRead() { 340 _ = reader.Close() 341 } 342 }() 343 344 if useStale { 345 this.varMapping["cache.status"] = "STALE" 346 this.logAttrs["cache.status"] = "STALE" 347 } else { 348 this.varMapping["cache.status"] = "HIT" 349 this.logAttrs["cache.status"] = "HIT" 350 } 351 352 // 准备Buffer 353 var fileSize = reader.BodySize() 354 var totalSizeString = types.String(fileSize) 355 if isPartialCache { 356 fileSize = reader.(*caches.PartialFileReader).MaxLength() 357 if totalSizeString == "0" { 358 totalSizeString = "*" 359 } 360 } 361 362 // 读取Header 363 var headerData = []byte{} 364 this.writer.SetSentHeaderBytes(reader.HeaderSize()) 365 var headerPool = this.bytePool(reader.HeaderSize()) 366 var headerBuf = headerPool.Get() 367 err = reader.ReadHeader(headerBuf.Bytes, func(n int) (goNext bool, readErr error) { 368 headerData = append(headerData, headerBuf.Bytes[:n]...) 369 for { 370 var nIndex = bytes.Index(headerData, []byte{'\n'}) 371 if nIndex >= 0 { 372 var row = headerData[:nIndex] 373 var spaceIndex = bytes.Index(row, []byte{':'}) 374 if spaceIndex <= 0 { 375 return false, errors.New("invalid header '" + string(row) + "'") 376 } 377 378 this.writer.Header().Set(string(row[:spaceIndex]), string(row[spaceIndex+1:])) 379 headerData = headerData[nIndex+1:] 380 } else { 381 break 382 } 383 } 384 return true, nil 385 }) 386 headerPool.Put(headerBuf) 387 if err != nil { 388 if !this.canIgnore(err) { 389 remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: read header failed: "+err.Error()) 390 } 391 return 392 } 393 394 // 设置cache.age变量 395 var age = strconv.FormatInt(fasttime.Now().Unix()-reader.LastModified(), 10) 396 this.varMapping["cache.age"] = age 397 398 if addStatusHeader { 399 if useStale { 400 this.writer.Header().Set("X-Cache", "STALE, "+refType+", "+reader.TypeName()) 401 } else { 402 this.writer.Header().Set("X-Cache", "HIT, "+refType+", "+reader.TypeName()) 403 } 404 } else { 405 this.writer.Header().Del("X-Cache") 406 } 407 if this.web.Cache.AddAgeHeader { 408 this.writer.Header().Set("Age", age) 409 } 410 411 // ETag 412 var respHeader = this.writer.Header() 413 var eTag = respHeader.Get("ETag") 414 var lastModifiedAt = reader.LastModified() 415 if len(eTag) == 0 { 416 if lastModifiedAt > 0 { 417 if len(tags) > 0 { 418 eTag = "\"" + strconv.FormatInt(lastModifiedAt, 10) + "_" + strings.Join(tags, "_") + "\"" 419 } else { 420 eTag = "\"" + strconv.FormatInt(lastModifiedAt, 10) + "\"" 421 } 422 respHeader.Del("Etag") 423 if !isPartialCache { 424 respHeader["ETag"] = []string{eTag} 425 } 426 } 427 } 428 429 // 支持 Last-Modified 430 var modifiedTime = "" 431 if lastModifiedAt > 0 { 432 modifiedTime = time.Unix(utils.GMTUnixTime(lastModifiedAt), 0).Format("Mon, 02 Jan 2006 15:04:05") + " GMT" 433 if !isPartialCache { 434 respHeader.Set("Last-Modified", modifiedTime) 435 } 436 } 437 438 // 支持 If-None-Match 439 if !this.isLnRequest && !isPartialCache && len(eTag) > 0 && this.requestHeader("If-None-Match") == eTag { 440 // 自定义Header 441 this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified) 442 this.addExpiresHeader(reader.ExpiresAt()) 443 this.writer.WriteHeader(http.StatusNotModified) 444 this.isCached = true 445 this.cacheRef = nil 446 this.writer.SetOk() 447 return true 448 } 449 450 // 支持 If-Modified-Since 451 if !this.isLnRequest && !isPartialCache && len(modifiedTime) > 0 && this.requestHeader("If-Modified-Since") == modifiedTime { 452 // 自定义Header 453 this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified) 454 this.addExpiresHeader(reader.ExpiresAt()) 455 this.writer.WriteHeader(http.StatusNotModified) 456 this.isCached = true 457 this.cacheRef = nil 458 this.writer.SetOk() 459 return true 460 } 461 462 this.ProcessResponseHeaders(this.writer.Header(), reader.Status()) 463 this.addExpiresHeader(reader.ExpiresAt()) 464 465 // 返回上级节点过期时间 466 if this.isLnRequest { 467 respHeader.Set(LNExpiresHeader, types.String(reader.ExpiresAt())) 468 } 469 470 // 输出Body 471 if this.RawReq.Method == http.MethodHead { 472 this.writer.WriteHeader(reader.Status()) 473 } else { 474 ifRangeHeaders, ok := this.RawReq.Header["If-Range"] 475 var supportRange = true 476 if ok { 477 supportRange = false 478 for _, v := range ifRangeHeaders { 479 if v == this.writer.Header().Get("ETag") || v == this.writer.Header().Get("Last-Modified") { 480 supportRange = true 481 break 482 } 483 } 484 } 485 486 // 支持Range 487 var ranges = partialRanges 488 if supportRange { 489 if len(rangeHeader) > 0 { 490 if fileSize == 0 { 491 this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable) 492 this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable) 493 return true 494 } 495 496 if len(ranges) == 0 { 497 ranges, ok = httpRequestParseRangeHeader(rangeHeader) 498 if !ok { 499 this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable) 500 this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable) 501 return true 502 } 503 } 504 if len(ranges) > 0 { 505 for k, r := range ranges { 506 r2, ok := r.Convert(fileSize) 507 if !ok { 508 this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable) 509 this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable) 510 return true 511 } 512 513 ranges[k] = r2 514 } 515 } 516 } 517 } 518 519 if len(ranges) == 1 { 520 respHeader.Set("Content-Range", ranges[0].ComposeContentRangeHeader(totalSizeString)) 521 respHeader.Set("Content-Length", strconv.FormatInt(ranges[0].Length(), 10)) 522 this.writer.WriteHeader(http.StatusPartialContent) 523 524 var pool = this.bytePool(fileSize) 525 var bodyBuf = pool.Get() 526 err = reader.ReadBodyRange(bodyBuf.Bytes, ranges[0].Start(), ranges[0].End(), func(n int) (goNext bool, readErr error) { 527 _, readErr = this.writer.Write(bodyBuf.Bytes[:n]) 528 if readErr != nil { 529 return false, errWritingToClient 530 } 531 return true, nil 532 }) 533 pool.Put(bodyBuf) 534 if err != nil { 535 this.varMapping["cache.status"] = "MISS" 536 537 if errors.Is(err, caches.ErrInvalidRange) { 538 this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable) 539 this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable) 540 return true 541 } 542 if !this.canIgnore(err) { 543 remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: "+err.Error()) 544 } 545 return 546 } 547 } else if len(ranges) > 1 { 548 var boundary = httpRequestGenBoundary() 549 respHeader.Set("Content-Type", "multipart/byteranges; boundary="+boundary) 550 respHeader.Del("Content-Length") 551 var contentType = respHeader.Get("Content-Type") 552 553 this.writer.WriteHeader(http.StatusPartialContent) 554 555 for index, r := range ranges { 556 if index == 0 { 557 _, err = this.writer.WriteString("--" + boundary + "\r\n") 558 } else { 559 _, err = this.writer.WriteString("\r\n--" + boundary + "\r\n") 560 } 561 if err != nil { 562 // 不提示写入客户端错误 563 return true 564 } 565 566 _, err = this.writer.WriteString("Content-Range: " + r.ComposeContentRangeHeader(totalSizeString) + "\r\n") 567 if err != nil { 568 // 不提示写入客户端错误 569 return true 570 } 571 572 if len(contentType) > 0 { 573 _, err = this.writer.WriteString("Content-Type: " + contentType + "\r\n\r\n") 574 if err != nil { 575 // 不提示写入客户端错误 576 return true 577 } 578 } 579 580 var pool = this.bytePool(fileSize) 581 var bodyBuf = pool.Get() 582 err = reader.ReadBodyRange(bodyBuf.Bytes, r.Start(), r.End(), func(n int) (goNext bool, readErr error) { 583 _, readErr = this.writer.Write(bodyBuf.Bytes[:n]) 584 if readErr != nil { 585 return false, errWritingToClient 586 } 587 return true, nil 588 }) 589 pool.Put(bodyBuf) 590 if err != nil { 591 if !this.canIgnore(err) { 592 remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: "+err.Error()) 593 } 594 return true 595 } 596 } 597 598 _, err = this.writer.WriteString("\r\n--" + boundary + "--\r\n") 599 if err != nil { 600 this.varMapping["cache.status"] = "MISS" 601 602 // 不提示写入客户端错误 603 return true 604 } 605 } else { // 没有Range 606 var resp = &http.Response{ 607 Body: reader, 608 ContentLength: reader.BodySize(), 609 } 610 this.writer.Prepare(resp, fileSize, reader.Status(), false) 611 this.writer.WriteHeader(reader.Status()) 612 613 if storage.CanSendfile() { 614 var pool = this.bytePool(fileSize) 615 var bodyBuf = pool.Get() 616 if fp, canSendFile := this.writer.canSendfile(); canSendFile { 617 this.writer.sentBodyBytes, err = io.CopyBuffer(this.writer.rawWriter, fp, bodyBuf.Bytes) 618 } else { 619 _, err = io.CopyBuffer(this.writer, resp.Body, bodyBuf.Bytes) 620 } 621 pool.Put(bodyBuf) 622 } else { 623 mmapReader, isMMAPReader := reader.(*caches.MMAPFileReader) 624 if isMMAPReader { 625 _, err = mmapReader.CopyBodyTo(this.writer) 626 } else { 627 var pool = this.bytePool(fileSize) 628 var bodyBuf = pool.Get() 629 _, err = io.CopyBuffer(this.writer, resp.Body, bodyBuf.Bytes) 630 pool.Put(bodyBuf) 631 } 632 } 633 634 if err == io.EOF { 635 err = nil 636 } 637 if err != nil { 638 this.varMapping["cache.status"] = "MISS" 639 640 if !this.canIgnore(err) { 641 remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: read body failed: "+err.Error()) 642 } 643 return 644 } 645 } 646 } 647 648 this.isCached = true 649 this.cacheRef = nil 650 651 this.writer.SetOk() 652 653 return true 654 } 655 656 // 设置Expires Header 657 func (this *HTTPRequest) addExpiresHeader(expiresAt int64) { 658 if this.cacheRef.ExpiresTime != nil && this.cacheRef.ExpiresTime.IsPrior && this.cacheRef.ExpiresTime.IsOn { 659 if this.cacheRef.ExpiresTime.Overwrite || len(this.writer.Header().Get("Expires")) == 0 { 660 if this.cacheRef.ExpiresTime.AutoCalculate { 661 this.writer.Header().Set("Expires", time.Unix(utils.GMTUnixTime(expiresAt), 0).Format("Mon, 2 Jan 2006 15:04:05")+" GMT") 662 this.writer.Header().Del("Cache-Control") 663 } else if this.cacheRef.ExpiresTime.Duration != nil { 664 var duration = this.cacheRef.ExpiresTime.Duration.Duration() 665 if duration > 0 { 666 this.writer.Header().Set("Expires", utils.GMTTime(time.Now().Add(duration)).Format("Mon, 2 Jan 2006 15:04:05")+" GMT") 667 this.writer.Header().Del("Cache-Control") 668 } 669 } 670 } 671 } 672 } 673 674 // 尝试读取区间缓存 675 func (this *HTTPRequest) tryPartialReader(storage caches.StorageInterface, key string, useStale bool, rangeHeader string, forcePartialContent bool) (resultReader caches.Reader, ranges []rangeutils.Range, goNext bool) { 676 goNext = true 677 678 // 尝试读取Partial cache 679 if len(rangeHeader) == 0 { 680 return 681 } 682 683 ranges, ok := httpRequestParseRangeHeader(rangeHeader) 684 if !ok { 685 return 686 } 687 688 pReader, pErr := storage.OpenReader(key+caches.SuffixPartial, useStale, true) 689 if pErr != nil { 690 if caches.IsBusyError(pErr) { 691 this.varMapping["cache.status"] = "BUSY" 692 goNext = false 693 return 694 } 695 return 696 } 697 698 partialReader, ok := pReader.(*caches.PartialFileReader) 699 if !ok { 700 _ = pReader.Close() 701 return 702 } 703 var isOk = false 704 defer func() { 705 if !isOk { 706 _ = pReader.Close() 707 } 708 }() 709 710 // 检查是否已下载完整 711 if !forcePartialContent && 712 len(ranges) > 0 && 713 ranges[0][1] < 0 && 714 !partialReader.IsCompleted() { 715 return 716 } 717 718 // 检查范围 719 // 这里 **切记不要** 为末尾位置指定一个中间值,因为部分软件客户端不支持 720 for index, r := range ranges { 721 r1, ok := r.Convert(partialReader.MaxLength()) 722 if !ok { 723 return 724 } 725 r2, ok := partialReader.ContainsRange(r1) 726 if !ok { 727 return 728 } 729 ranges[index] = r2 730 } 731 732 isOk = true 733 return pReader, ranges, true 734 }