diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2023-05-09 15:34:36 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-09 15:34:36 +0800 |
commit | 023a048f52b5bf8c4b715285245a129f04e05a8c (patch) | |
tree | 2ea5c0d940a9439e9760b00f735d869444d87165 /routers | |
parent | c090f87a8db5b51e0aa9c7278b38ddc862c048ac (diff) | |
download | gitea-023a048f52b5bf8c4b715285245a129f04e05a8c.tar.gz gitea-023a048f52b5bf8c4b715285245a129f04e05a8c.zip |
Make repository response support HTTP range request (#24592)
Replace #20480
Replace #18448
Close #16414
Diffstat (limited to 'routers')
-rw-r--r-- | routers/api/v1/repo/file.go | 19 | ||||
-rw-r--r-- | routers/common/repo.go | 116 | ||||
-rw-r--r-- | routers/common/serve.go | 43 | ||||
-rw-r--r-- | routers/web/repo/attachment.go | 5 | ||||
-rw-r--r-- | routers/web/repo/download.go | 3 |
5 files changed, 53 insertions, 133 deletions
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 5f7ed255bc..eb63dda590 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -150,6 +150,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) { return } + // FIXME: code from #19689, what if the file is large ... OOM ... buf, err := io.ReadAll(dataRc) if err != nil { _ = dataRc.Close() @@ -164,7 +165,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) { // Check if the blob represents a pointer pointer, _ := lfs.ReadPointer(bytes.NewReader(buf)) - // if its not a pointer just serve the data directly + // if it's not a pointer, just serve the data directly if !pointer.IsValid() { // First handle caching for the blob if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) { @@ -172,25 +173,21 @@ func GetRawFileOrLFS(ctx *context.APIContext) { } // OK not cached - serve! - if err := common.ServeData(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf)); err != nil { - ctx.ServerError("ServeBlob", err) - } + common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf)) return } - // Now check if there is a meta object for this pointer + // Now check if there is a MetaObject for this pointer meta, err := git_model.GetLFSMetaObjectByOid(ctx, ctx.Repo.Repository.ID, pointer.Oid) - // If there isn't one just serve the data directly + // If there isn't one, just serve the data directly if err == git_model.ErrLFSObjectNotExist { // Handle caching for the blob SHA (not the LFS object OID) if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) { return } - if err := common.ServeData(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf)); err != nil { - ctx.ServerError("ServeBlob", err) - } + common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf)) return } else if err != nil { ctx.ServerError("GetLFSMetaObjectByOid", err) @@ -218,9 +215,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) { } defer lfsDataRc.Close() - if err := common.ServeData(ctx.Context, ctx.Repo.TreePath, meta.Size, lfsDataRc); err != nil { - ctx.ServerError("ServeData", err) - } + common.ServeContentByReadSeeker(ctx.Context, ctx.Repo.TreePath, lastModified, lfsDataRc) } func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified time.Time) { diff --git a/routers/common/repo.go b/routers/common/repo.go deleted file mode 100644 index 4b04ddae34..0000000000 --- a/routers/common/repo.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package common - -import ( - "io" - "path" - "path/filepath" - "strings" - "time" - - charsetModule "code.gitea.io/gitea/modules/charset" - "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/httpcache" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/typesniffer" - "code.gitea.io/gitea/modules/util" -) - -// ServeBlob download a git.Blob -func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) error { - if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) { - return nil - } - - dataRc, err := blob.DataAsync() - if err != nil { - return err - } - defer func() { - if err = dataRc.Close(); err != nil { - log.Error("ServeBlob: Close: %v", err) - } - }() - - return ServeData(ctx, ctx.Repo.TreePath, blob.Size(), dataRc) -} - -// ServeData download file from io.Reader -func ServeData(ctx *context.Context, filePath string, size int64, reader io.Reader) error { - buf := make([]byte, 1024) - n, err := util.ReadAtMost(reader, buf) - if err != nil { - return err - } - if n >= 0 { - buf = buf[:n] - } - - opts := &context.ServeHeaderOptions{ - Filename: path.Base(filePath), - } - - if size >= 0 { - opts.ContentLength = &size - } else { - log.Error("ServeData called to serve data: %s with size < 0: %d", filePath, size) - } - - sniffedType := typesniffer.DetectContentType(buf) - isPlain := sniffedType.IsText() || ctx.FormBool("render") - - if setting.MimeTypeMap.Enabled { - fileExtension := strings.ToLower(filepath.Ext(filePath)) - opts.ContentType = setting.MimeTypeMap.Map[fileExtension] - } - - if opts.ContentType == "" { - if sniffedType.IsBrowsableBinaryType() { - opts.ContentType = sniffedType.GetMimeType() - } else if isPlain { - opts.ContentType = "text/plain" - } else { - opts.ContentType = typesniffer.ApplicationOctetStream - } - } - - if isPlain { - var charset string - charset, err = charsetModule.DetectEncoding(buf) - if err != nil { - log.Error("Detect raw file %s charset failed: %v, using by default utf-8", filePath, err) - charset = "utf-8" - } - opts.ContentTypeCharset = strings.ToLower(charset) - } - - isSVG := sniffedType.IsSvgImage() - - // serve types that can present a security risk with CSP - if isSVG { - ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") - } else if sniffedType.IsPDF() { - // no sandbox attribute for pdf as it breaks rendering in at least safari. this - // should generally be safe as scripts inside PDF can not escape the PDF document - // see https://bugs.chromium.org/p/chromium/issues/detail?id=413851 for more discussion - ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'") - } - - opts.Disposition = "inline" - if isSVG && !setting.UI.SVG.Enabled { - opts.Disposition = "attachment" - } - - ctx.SetServeHeaders(opts) - - _, err = ctx.Resp.Write(buf) - if err != nil { - return err - } - _, err = io.Copy(ctx.Resp, reader) - return err -} diff --git a/routers/common/serve.go b/routers/common/serve.go new file mode 100644 index 0000000000..59b993328e --- /dev/null +++ b/routers/common/serve.go @@ -0,0 +1,43 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package common + +import ( + "io" + "time" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/httpcache" + "code.gitea.io/gitea/modules/httplib" + "code.gitea.io/gitea/modules/log" +) + +// ServeBlob download a git.Blob +func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) error { + if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) { + return nil + } + + dataRc, err := blob.DataAsync() + if err != nil { + return err + } + defer func() { + if err = dataRc.Close(); err != nil { + log.Error("ServeBlob: Close: %v", err) + } + }() + + httplib.ServeContentByReader(ctx.Req, ctx.Resp, ctx.Repo.TreePath, blob.Size(), dataRc) + return nil +} + +func ServeContentByReader(ctx *context.Context, filePath string, size int64, reader io.Reader) { + httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader) +} + +func ServeContentByReadSeeker(ctx *context.Context, filePath string, modTime time.Time, reader io.ReadSeeker) { + httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader) +} diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index c6ea4e3cdb..c46ec29841 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -153,10 +153,7 @@ func ServeAttachment(ctx *context.Context, uuid string) { } defer fr.Close() - if err = common.ServeData(ctx, attach.Name, attach.Size, fr); err != nil { - ctx.ServerError("ServeData", err) - return - } + common.ServeContentByReadSeeker(ctx, attach.Name, attach.CreatedUnix.AsTime(), fr) } // GetAttachment serve attachments diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go index 9e95f9dd0c..1c87f9bed7 100644 --- a/routers/web/repo/download.go +++ b/routers/web/repo/download.go @@ -71,7 +71,8 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time log.Error("ServeBlobOrLFS: Close: %v", err) } }() - return common.ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc) + common.ServeContentByReadSeeker(ctx, ctx.Repo.TreePath, lastModified, lfsDataRc) + return nil } if err = dataRc.Close(); err != nil { log.Error("ServeBlobOrLFS: Close: %v", err) |