您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

httpcache.go 2.4KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package httpcache
  5. import (
  6. "encoding/base64"
  7. "fmt"
  8. "net/http"
  9. "os"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "code.gitea.io/gitea/modules/setting"
  14. )
  15. // GetCacheControl returns a suitable "Cache-Control" header value
  16. func GetCacheControl() string {
  17. if !setting.IsProd() {
  18. return "no-store"
  19. }
  20. return "private, max-age=" + strconv.FormatInt(int64(setting.StaticCacheTime.Seconds()), 10)
  21. }
  22. // generateETag generates an ETag based on size, filename and file modification time
  23. func generateETag(fi os.FileInfo) string {
  24. etag := fmt.Sprint(fi.Size()) + fi.Name() + fi.ModTime().UTC().Format(http.TimeFormat)
  25. return `"` + base64.StdEncoding.EncodeToString([]byte(etag)) + `"`
  26. }
  27. // HandleTimeCache handles time-based caching for a HTTP request
  28. func HandleTimeCache(req *http.Request, w http.ResponseWriter, fi os.FileInfo) (handled bool) {
  29. w.Header().Set("Cache-Control", GetCacheControl())
  30. ifModifiedSince := req.Header.Get("If-Modified-Since")
  31. if ifModifiedSince != "" {
  32. t, err := time.Parse(http.TimeFormat, ifModifiedSince)
  33. if err == nil && fi.ModTime().Unix() <= t.Unix() {
  34. w.WriteHeader(http.StatusNotModified)
  35. return true
  36. }
  37. }
  38. w.Header().Set("Last-Modified", fi.ModTime().Format(http.TimeFormat))
  39. return false
  40. }
  41. // HandleFileETagCache handles ETag-based caching for a HTTP request
  42. func HandleFileETagCache(req *http.Request, w http.ResponseWriter, fi os.FileInfo) (handled bool) {
  43. etag := generateETag(fi)
  44. return HandleGenericETagCache(req, w, etag)
  45. }
  46. // HandleGenericETagCache handles ETag-based caching for a HTTP request.
  47. // It returns true if the request was handled.
  48. func HandleGenericETagCache(req *http.Request, w http.ResponseWriter, etag string) (handled bool) {
  49. if len(etag) > 0 {
  50. w.Header().Set("Etag", etag)
  51. if checkIfNoneMatchIsValid(req, etag) {
  52. w.WriteHeader(http.StatusNotModified)
  53. return true
  54. }
  55. }
  56. w.Header().Set("Cache-Control", GetCacheControl())
  57. return false
  58. }
  59. // checkIfNoneMatchIsValid tests if the header If-None-Match matches the ETag
  60. func checkIfNoneMatchIsValid(req *http.Request, etag string) bool {
  61. ifNoneMatch := req.Header.Get("If-None-Match")
  62. if len(ifNoneMatch) > 0 {
  63. for _, item := range strings.Split(ifNoneMatch, ",") {
  64. item = strings.TrimSpace(item)
  65. if item == etag {
  66. return true
  67. }
  68. }
  69. }
  70. return false
  71. }