2020-11-18 01:44:52 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package httpcache
import (
"encoding/base64"
"fmt"
"net/http"
"os"
"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"
)
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
// AddCacheControlToHeader adds suitable cache-control headers to response
func AddCacheControlToHeader ( h http . Header , d time . Duration ) {
if setting . IsProd ( ) {
h . Set ( "Cache-Control" , "private, max-age=" + strconv . Itoa ( int ( d . Seconds ( ) ) ) )
} else {
h . Set ( "Cache-Control" , "no-store" )
// to remind users they are using non-prod setting.
// some users may be confused by "Cache-Control: no-store" in their setup if they did wrong to `RUN_MODE` in `app.ini`.
h . Add ( "X-Gitea-Debug" , "RUN_MODE=" + setting . RunMode )
h . Add ( "X-Gitea-Debug" , "CacheControl=no-store" )
2020-11-18 01:44:52 +03:00
}
}
// generateETag generates an ETag based on size, filename and file modification time
func generateETag ( fi os . FileInfo ) string {
etag := fmt . Sprint ( fi . Size ( ) ) + fi . Name ( ) + fi . ModTime ( ) . UTC ( ) . Format ( http . TimeFormat )
2021-04-12 17:49:26 +03:00
return ` " ` + base64 . StdEncoding . EncodeToString ( [ ] byte ( etag ) ) + ` " `
2020-11-18 01:44:52 +03:00
}
// HandleTimeCache handles time-based caching for a HTTP request
func HandleTimeCache ( req * http . Request , w http . ResponseWriter , fi os . FileInfo ) ( handled bool ) {
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
AddCacheControlToHeader ( w . Header ( ) , setting . StaticCacheTime )
2021-04-12 17:49:26 +03:00
2020-11-18 01:44:52 +03:00
ifModifiedSince := req . Header . Get ( "If-Modified-Since" )
if ifModifiedSince != "" {
t , err := time . Parse ( http . TimeFormat , ifModifiedSince )
if err == nil && fi . ModTime ( ) . Unix ( ) <= t . Unix ( ) {
w . WriteHeader ( http . StatusNotModified )
return true
}
}
w . Header ( ) . Set ( "Last-Modified" , fi . ModTime ( ) . Format ( http . TimeFormat ) )
return false
}
2021-04-12 17:49:26 +03:00
// HandleFileETagCache handles ETag-based caching for a HTTP request
func HandleFileETagCache ( req * http . Request , w http . ResponseWriter , fi os . FileInfo ) ( handled bool ) {
2020-11-18 01:44:52 +03:00
etag := generateETag ( fi )
2021-04-12 17:49:26 +03:00
return HandleGenericETagCache ( req , w , etag )
}
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
}
}
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
AddCacheControlToHeader ( 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 , "," ) {
item = strings . TrimSpace ( item )
if item == etag {
return true
}
}
}
2020-11-18 01:44:52 +03:00
return false
}