mirror of https://github.com/harness/drone.git
feat: add etag for raw api (#2018)
parent
a692707c6c
commit
a576087694
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/harness/gitness/app/api/usererror"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/git"
|
||||
"github.com/harness/gitness/git/sha"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
|
@ -32,10 +33,10 @@ func (c *Controller) Raw(ctx context.Context,
|
|||
repoRef string,
|
||||
gitRef string,
|
||||
path string,
|
||||
) (io.ReadCloser, int64, error) {
|
||||
) (io.ReadCloser, int64, sha.SHA, error) {
|
||||
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView, true)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, 0, sha.Nil, err
|
||||
}
|
||||
|
||||
// set gitRef to default branch in case an empty reference was provided
|
||||
|
@ -52,12 +53,12 @@ func (c *Controller) Raw(ctx context.Context,
|
|||
IncludeLatestCommit: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to read tree node: %w", err)
|
||||
return nil, 0, sha.Nil, fmt.Errorf("failed to read tree node: %w", err)
|
||||
}
|
||||
|
||||
// viewing Raw content is only supported for blob content
|
||||
if treeNodeOutput.Node.Type != git.TreeNodeTypeBlob {
|
||||
return nil, 0, usererror.BadRequestf(
|
||||
return nil, 0, sha.Nil, usererror.BadRequestf(
|
||||
"Object in '%s' at '/%s' is of type '%s'. Only objects of type %s support raw viewing.",
|
||||
gitRef, path, treeNodeOutput.Node.Type, git.TreeNodeTypeBlob)
|
||||
}
|
||||
|
@ -68,8 +69,8 @@ func (c *Controller) Raw(ctx context.Context,
|
|||
SizeLimit: 0, // no size limit, we stream whatever data there is
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to read blob: %w", err)
|
||||
return nil, 0, sha.Nil, fmt.Errorf("failed to read blob: %w", err)
|
||||
}
|
||||
|
||||
return blobReader.Content, blobReader.ContentSize, nil
|
||||
return blobReader.Content, blobReader.ContentSize, blobReader.SHA, nil
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func HandleRaw(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
gitRef := request.GetGitRefFromQueryOrDefault(r, "")
|
||||
path := request.GetOptionalRemainderFromPath(r)
|
||||
|
||||
dataReader, dataLength, err := repoCtrl.Raw(ctx, session, repoRef, gitRef, path)
|
||||
dataReader, dataLength, sha, err := repoCtrl.Raw(ctx, session, repoRef, gitRef, path)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
|
@ -53,8 +53,14 @@ func HandleRaw(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
}
|
||||
}()
|
||||
|
||||
w.Header().Add("Content-Length", fmt.Sprint(dataLength))
|
||||
ifNoneMatch, ok := request.GetIfNoneMatchFromHeader(r)
|
||||
if ok && ifNoneMatch == sha.String() {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Length", fmt.Sprint(dataLength))
|
||||
w.Header().Add(request.HeaderETag, sha.String())
|
||||
render.Reader(ctx, w, http.StatusOK, dataReader)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2023 Harness, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package nocache
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Ported from Chi's middleware, source:
|
||||
// https://github.com/go-chi/chi/blob/v5.0.12/middleware/nocache.go
|
||||
|
||||
// Modified the middleware to retain ETags.
|
||||
|
||||
var epoch = time.Unix(0, 0).Format(time.RFC1123)
|
||||
|
||||
var noCacheHeaders = map[string]string{
|
||||
"Expires": epoch,
|
||||
"Cache-Control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0",
|
||||
"Pragma": "no-cache",
|
||||
"X-Accel-Expires": "0",
|
||||
}
|
||||
|
||||
// NoCache is same as chi's default NoCache middleware except it doesn't remove etag headers.
|
||||
func NoCache(h http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
// Set our NoCache headers
|
||||
for k, v := range noCacheHeaders {
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
|
@ -55,6 +55,9 @@ const (
|
|||
HeaderUserAgent = "User-Agent"
|
||||
HeaderAuthorization = "Authorization"
|
||||
HeaderContentEncoding = "Content-Encoding"
|
||||
|
||||
HeaderIfNoneMatch = "If-None-Match"
|
||||
HeaderETag = "ETag"
|
||||
)
|
||||
|
||||
// GetOptionalRemainderFromPath returns the remainder ("*") from the path or an empty string if it doesn't exist.
|
||||
|
@ -166,3 +169,7 @@ func GetDeletedBeforeOrAtFromQuery(r *http.Request) (int64, bool, error) {
|
|||
func GetDeletedAtFromQuery(r *http.Request) (int64, bool, error) {
|
||||
return QueryParamAsPositiveInt64(r, QueryParamDeletedAt)
|
||||
}
|
||||
|
||||
func GetIfNoneMatchFromHeader(r *http.Request) (string, bool) {
|
||||
return GetHeader(r, HeaderIfNoneMatch)
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ import (
|
|||
middlewareauthn "github.com/harness/gitness/app/api/middleware/authn"
|
||||
"github.com/harness/gitness/app/api/middleware/encode"
|
||||
"github.com/harness/gitness/app/api/middleware/logging"
|
||||
"github.com/harness/gitness/app/api/middleware/nocache"
|
||||
middlewareprincipal "github.com/harness/gitness/app/api/middleware/principal"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
"github.com/harness/gitness/app/auth/authn"
|
||||
|
@ -126,7 +127,7 @@ func NewAPIHandler(
|
|||
r := chi.NewRouter()
|
||||
|
||||
// Apply common api middleware.
|
||||
r.Use(middleware.NoCache)
|
||||
r.Use(nocache.NoCache)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
// configure logging middleware.
|
||||
|
|
Loading…
Reference in New Issue