2020-11-18 01:44:52 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-11-18 01:44:52 +03:00
package httpcache
import (
2023-05-13 17:04:57 +03:00
"io"
2020-11-18 01:44:52 +03:00
"net/http"
"strconv"
2021-04-12 17:49:26 +03:00
"strings"
2020-11-18 01:44:52 +03:00
"time"
"code.gitea.io/gitea/modules/setting"
)
2023-03-08 23:40:04 +03:00
// SetCacheControlInHeader sets suitable cache-control headers in the response
func SetCacheControlInHeader ( h http . Header , maxAge time . Duration , additionalDirectives ... string ) {
2022-07-23 09:38:03 +03:00
directives := make ( [ ] string , 0 , 2 + len ( additionalDirectives ) )
2023-02-02 20:39:38 +03:00
// "max-age=0 + must-revalidate" (aka "no-cache") is preferred instead of "no-store"
// because browsers may restore some input fields after navigate-back / reload a page.
2021-10-20 17:37:19 +03:00
if setting . IsProd {
2022-07-23 09:38:03 +03:00
if maxAge == 0 {
2023-02-02 00:28:06 +03:00
directives = append ( directives , "max-age=0" , "private" , "must-revalidate" )
2022-07-23 09:38:03 +03:00
} else {
directives = append ( directives , "private" , "max-age=" + strconv . Itoa ( int ( maxAge . Seconds ( ) ) ) )
}
Avatar refactor, move avatar code from `models` to `models.avatars`, remove duplicated code (#17123)
Why this refactor
The goal is to move most files from `models` package to `models.xxx` package. Many models depend on avatar model, so just move this first.
And the existing logic is not clear, there are too many function like `AvatarLink`, `RelAvatarLink`, `SizedRelAvatarLink`, `SizedAvatarLink`, `MakeFinalAvatarURL`, `HashedAvatarLink`, etc. This refactor make everything clear:
* user.AvatarLink()
* user.AvatarLinkWithSize(size)
* avatars.GenerateEmailAvatarFastLink(email, size)
* avatars.GenerateEmailAvatarFinalLink(email, size)
And many duplicated code are deleted in route handler, the handler and the model share the same avatar logic now.
2021-10-06 02:25:46 +03:00
} else {
2023-02-02 00:28:06 +03:00
directives = append ( directives , "max-age=0" , "private" , "must-revalidate" )
2022-07-23 09:38:03 +03:00
Avatar refactor, move avatar code from `models` to `models.avatars`, remove duplicated code (#17123)
Why this refactor
The goal is to move most files from `models` package to `models.xxx` package. Many models depend on avatar model, so just move this first.
And the existing logic is not clear, there are too many function like `AvatarLink`, `RelAvatarLink`, `SizedRelAvatarLink`, `SizedAvatarLink`, `MakeFinalAvatarURL`, `HashedAvatarLink`, etc. This refactor make everything clear:
* user.AvatarLink()
* user.AvatarLinkWithSize(size)
* avatars.GenerateEmailAvatarFastLink(email, size)
* avatars.GenerateEmailAvatarFinalLink(email, size)
And many duplicated code are deleted in route handler, the handler and the model share the same avatar logic now.
2021-10-06 02:25:46 +03:00
// to remind users they are using non-prod setting.
2023-03-08 23:40:04 +03:00
h . Set ( "X-Gitea-Debug" , "RUN_MODE=" + setting . RunMode )
2020-11-18 01:44:52 +03:00
}
2022-07-23 09:38:03 +03:00
h . Set ( "Cache-Control" , strings . Join ( append ( directives , additionalDirectives ... ) , ", " ) )
2020-11-18 01:44:52 +03:00
}
2023-05-13 17:04:57 +03:00
func ServeContentWithCacheControl ( w http . ResponseWriter , req * http . Request , name string , modTime time . Time , content io . ReadSeeker ) {
2023-03-08 23:40:04 +03:00
SetCacheControlInHeader ( w . Header ( ) , setting . StaticCacheTime )
2023-05-13 17:04:57 +03:00
http . ServeContent ( w , req , name , modTime , content )
2021-04-12 17:49:26 +03:00
}
2020-11-18 01:44:52 +03:00
2021-04-12 17:49:26 +03:00
// HandleGenericETagCache handles ETag-based caching for a HTTP request.
// It returns true if the request was handled.
func HandleGenericETagCache ( req * http . Request , w http . ResponseWriter , etag string ) ( handled bool ) {
if len ( etag ) > 0 {
w . Header ( ) . Set ( "Etag" , etag )
if checkIfNoneMatchIsValid ( req , etag ) {
w . WriteHeader ( http . StatusNotModified )
return true
}
}
2023-03-08 23:40:04 +03:00
SetCacheControlInHeader ( w . Header ( ) , setting . StaticCacheTime )
2021-04-12 17:49:26 +03:00
return false
}
// checkIfNoneMatchIsValid tests if the header If-None-Match matches the ETag
func checkIfNoneMatchIsValid ( req * http . Request , etag string ) bool {
ifNoneMatch := req . Header . Get ( "If-None-Match" )
if len ( ifNoneMatch ) > 0 {
for _ , item := range strings . Split ( ifNoneMatch , "," ) {
2024-01-29 19:18:40 +03:00
item = strings . TrimPrefix ( strings . TrimSpace ( item ) , "W/" ) // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives
2021-04-12 17:49:26 +03:00
if item == etag {
return true
}
}
}
2020-11-18 01:44:52 +03:00
return false
}
2022-05-09 18:54:51 +03:00
// HandleGenericETagTimeCache handles ETag-based caching with Last-Modified caching for a HTTP request.
// It returns true if the request was handled.
2023-07-07 08:31:56 +03:00
func HandleGenericETagTimeCache ( req * http . Request , w http . ResponseWriter , etag string , lastModified * time . Time ) ( handled bool ) {
2022-05-09 18:54:51 +03:00
if len ( etag ) > 0 {
w . Header ( ) . Set ( "Etag" , etag )
}
2023-07-07 08:31:56 +03:00
if lastModified != nil && ! lastModified . IsZero ( ) {
2022-05-09 18:54:51 +03:00
w . Header ( ) . Set ( "Last-Modified" , lastModified . Format ( http . TimeFormat ) )
}
if len ( etag ) > 0 {
if checkIfNoneMatchIsValid ( req , etag ) {
w . WriteHeader ( http . StatusNotModified )
return true
}
}
2023-07-07 08:31:56 +03:00
if lastModified != nil && ! lastModified . IsZero ( ) {
2022-05-09 18:54:51 +03:00
ifModifiedSince := req . Header . Get ( "If-Modified-Since" )
if ifModifiedSince != "" {
t , err := time . Parse ( http . TimeFormat , ifModifiedSince )
if err == nil && lastModified . Unix ( ) <= t . Unix ( ) {
w . WriteHeader ( http . StatusNotModified )
return true
}
}
}
2023-03-08 23:40:04 +03:00
SetCacheControlInHeader ( w . Header ( ) , setting . StaticCacheTime )
2022-05-09 18:54:51 +03:00
return false
}