package gitea import ( "bytes" "fmt" "io" "net/http" "time" "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" ) const ( // defaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. defaultBranchCacheTimeout = 15 * time.Minute // branchExistenceCacheTimeout specifies the timeout for the branch timestamp & existence cache. It should be shorter // than fileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be // picked up faster, while still allowing the content to be cached longer if nothing changes. branchExistenceCacheTimeout = 5 * time.Minute // fileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending // on your available memory. // TODO: move as option into cache interface fileCacheTimeout = 5 * time.Minute // fileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. fileCacheSizeLimit = int64(1024 * 1024) ) type FileResponse struct { Exists bool IsSymlink bool ETag string MimeType string Body []byte } func (f FileResponse) IsEmpty() bool { return len(f.Body) != 0 } func (f FileResponse) createHttpResponse() *http.Response { resp := &http.Response{ Header: make(http.Header), } if f.Exists { resp.StatusCode = http.StatusOK } else { resp.StatusCode = http.StatusNotFound } if f.IsSymlink { resp.Header.Set(giteaObjectTypeHeader, objTypeSymlink) } resp.Header.Set(ETagHeader, f.ETag) resp.Header.Set(ContentTypeHeader, f.MimeType) resp.Header.Set(ContentLengthHeader, fmt.Sprint(len(f.Body))) resp.Header.Set(PagesCacheIndicatorHeader, "true") return resp } type BranchTimestamp struct { Branch string Timestamp time.Time } type writeCacheReader struct { rc io.ReadCloser buff *bytes.Buffer f *FileResponse cacheKey string cache cache.SetGetKey hasErr bool } func (t *writeCacheReader) Read(p []byte) (n int, err error) { n, err = t.rc.Read(p) if err != nil { t.hasErr = true } else if n > 0 { _, _ = t.buff.Write(p[:n]) } return } func (t *writeCacheReader) Close() error { if !t.hasErr { fc := *t.f fc.Body = t.buff.Bytes() _ = t.cache.Set(t.cacheKey, fc, fileCacheTimeout) } return t.rc.Close() } func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.SetGetKey, cacheKey string) io.ReadCloser { buf := []byte{} if r == nil || cache == nil || cacheKey == "" { log.Error().Msg("could not create CacheReader") return r } return &writeCacheReader{ rc: r, buff: bytes.NewBuffer(buf), f: &f, cache: cache, cacheKey: cacheKey, } }