mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2024-11-30 16:45:27 +00:00
Fix compression (#405)
closes #404 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/405 Co-authored-by: crapStone <me@crapstone.dev> Co-committed-by: crapStone <me@crapstone.dev>
This commit is contained in:
parent
85059aa46e
commit
044c684a47
@ -20,8 +20,8 @@
|
|||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 0,
|
"lastModified": 0,
|
||||||
"narHash": "sha256-x07g4NcqGP6mQn6AISXJaks9sQYDjZmTMBlKIvajvyc=",
|
"narHash": "sha256-29QfSvJwpjNwppFnU33nnyedAWpaaDBSlDJZzJhg97s=",
|
||||||
"path": "/nix/store/2w8kz6zh3aq80f1dypiin222fry1rv51-source",
|
"path": "/nix/store/1703v0vkmk136sca5rf1861jrn2ndajr-source",
|
||||||
"type": "path"
|
"type": "path"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
in {
|
in {
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
gcc
|
glibc.static
|
||||||
go
|
go
|
||||||
gofumpt
|
gofumpt
|
||||||
golangci-lint
|
golangci-lint
|
||||||
|
@ -38,7 +38,8 @@ type FileResponse struct {
|
|||||||
Exists bool `json:"exists"`
|
Exists bool `json:"exists"`
|
||||||
IsSymlink bool `json:"isSymlink"`
|
IsSymlink bool `json:"isSymlink"`
|
||||||
ETag string `json:"eTag"`
|
ETag string `json:"eTag"`
|
||||||
MimeType string `json:"mimeType"`
|
MimeType string `json:"mimeType"` // uncompressed MIME type
|
||||||
|
RawMime string `json:"rawMime"` // raw MIME type (if compressed, type of compression)
|
||||||
Body []byte `json:"-"` // saved separately
|
Body []byte `json:"-"` // saved separately
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ func (f FileResponse) IsEmpty() bool {
|
|||||||
return len(f.Body) == 0
|
return len(f.Body) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, statusCode int) {
|
func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (header http.Header, statusCode int) {
|
||||||
header = make(http.Header)
|
header = make(http.Header)
|
||||||
|
|
||||||
if f.Exists {
|
if f.Exists {
|
||||||
@ -59,7 +60,13 @@ func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, s
|
|||||||
header.Set(giteaObjectTypeHeader, objTypeSymlink)
|
header.Set(giteaObjectTypeHeader, objTypeSymlink)
|
||||||
}
|
}
|
||||||
header.Set(ETagHeader, f.ETag)
|
header.Set(ETagHeader, f.ETag)
|
||||||
|
|
||||||
|
if decompress {
|
||||||
header.Set(ContentTypeHeader, f.MimeType)
|
header.Set(ContentTypeHeader, f.MimeType)
|
||||||
|
} else {
|
||||||
|
header.Set(ContentTypeHeader, f.RawMime)
|
||||||
|
}
|
||||||
|
|
||||||
header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body)))
|
header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body)))
|
||||||
header.Set(PagesCacheIndicatorHeader, "true")
|
header.Set(PagesCacheIndicatorHeader, "true")
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ const (
|
|||||||
ETagHeader = "ETag"
|
ETagHeader = "ETag"
|
||||||
ContentTypeHeader = "Content-Type"
|
ContentTypeHeader = "Content-Type"
|
||||||
ContentLengthHeader = "Content-Length"
|
ContentLengthHeader = "Content-Length"
|
||||||
|
ContentEncodingHeader = "Content-Encoding"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
@ -104,7 +105,7 @@ func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) {
|
func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) {
|
||||||
reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource)
|
reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -112,7 +113,7 @@ func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource str
|
|||||||
return io.ReadAll(reader)
|
return io.ReadAll(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (io.ReadCloser, http.Header, int, error) {
|
func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) {
|
||||||
cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource)
|
cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource)
|
||||||
log := log.With().Str("cache_key", cacheKey).Logger()
|
log := log.With().Str("cache_key", cacheKey).Logger()
|
||||||
log.Trace().Msg("try file in cache")
|
log.Trace().Msg("try file in cache")
|
||||||
@ -136,12 +137,12 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str
|
|||||||
}
|
}
|
||||||
cache.Body = body.([]byte)
|
cache.Body = body.([]byte)
|
||||||
|
|
||||||
cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey)
|
cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress)
|
||||||
if cache.Exists {
|
if cache.Exists {
|
||||||
if cache.IsSymlink {
|
if cache.IsSymlink {
|
||||||
linkDest := string(cache.Body)
|
linkDest := string(cache.Body)
|
||||||
log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest)
|
log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest)
|
||||||
return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest)
|
return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress)
|
||||||
} else {
|
} else {
|
||||||
log.Debug().Msgf("[cache] return %d bytes", len(cache.Body))
|
log.Debug().Msgf("[cache] return %d bytes", len(cache.Body))
|
||||||
return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil
|
return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil
|
||||||
@ -193,19 +194,25 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest)
|
log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest)
|
||||||
return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest)
|
return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we are sure it's content so set the MIME type
|
// now we are sure it's content so set the MIME type
|
||||||
mimeType := client.getMimeTypeByExtension(resource)
|
mimeType, rawType := client.getMimeTypeByExtension(resource)
|
||||||
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
||||||
|
if decompress {
|
||||||
|
resp.Response.Header.Set(ContentTypeHeader, mimeType)
|
||||||
|
} else {
|
||||||
|
resp.Response.Header.Set(ContentTypeHeader, rawType)
|
||||||
|
}
|
||||||
|
|
||||||
// now we write to cache and respond at the same time
|
// now we write to cache and respond at the same time
|
||||||
fileResp := FileResponse{
|
fileResp := FileResponse{
|
||||||
Exists: true,
|
Exists: true,
|
||||||
ETag: resp.Header.Get(ETagHeader),
|
ETag: resp.Header.Get(ETagHeader),
|
||||||
MimeType: mimeType,
|
MimeType: mimeType,
|
||||||
|
RawMime: rawType,
|
||||||
}
|
}
|
||||||
return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil
|
return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil
|
||||||
|
|
||||||
@ -340,13 +347,29 @@ func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) getMimeTypeByExtension(resource string) string {
|
func (client *Client) extToMime(ext string) string {
|
||||||
mimeType := mime.TypeByExtension(path.Ext(resource))
|
mimeType := mime.TypeByExtension(path.Ext(ext))
|
||||||
mimeTypeSplit := strings.SplitN(mimeType, ";", 2)
|
mimeTypeSplit := strings.SplitN(mimeType, ";", 2)
|
||||||
if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" {
|
if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" {
|
||||||
mimeType = client.defaultMimeType
|
mimeType = client.defaultMimeType
|
||||||
}
|
}
|
||||||
log.Trace().Msgf("probe mime of %q is %q", resource, mimeType)
|
log.Trace().Msgf("probe mime of extension '%q' is '%q'", ext, mimeType)
|
||||||
|
|
||||||
return mimeType
|
return mimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (client *Client) getMimeTypeByExtension(resource string) (mimeType, rawType string) {
|
||||||
|
rawExt := path.Ext(resource)
|
||||||
|
innerExt := rawExt
|
||||||
|
switch rawExt {
|
||||||
|
case ".gz", ".br", ".zst":
|
||||||
|
innerExt = path.Ext(resource[:len(resource)-len(rawExt)])
|
||||||
|
}
|
||||||
|
rawType = client.extToMime(rawExt)
|
||||||
|
mimeType = rawType
|
||||||
|
if innerExt != rawExt {
|
||||||
|
mimeType = client.extToMime(innerExt)
|
||||||
|
}
|
||||||
|
log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType)
|
||||||
|
return mimeType, rawType
|
||||||
|
}
|
||||||
|
@ -24,5 +24,8 @@ func (o *Options) setHeader(ctx *context.Context, header http.Header) {
|
|||||||
} else {
|
} else {
|
||||||
ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime)
|
ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime)
|
||||||
}
|
}
|
||||||
|
if encoding := header.Get(gitea.ContentEncodingHeader); encoding != "" && encoding != "identity" {
|
||||||
|
ctx.RespWriter.Header().Set(gitea.ContentEncodingHeader, encoding)
|
||||||
|
}
|
||||||
ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat))
|
ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat))
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi
|
|||||||
|
|
||||||
// add extension for encoding
|
// add extension for encoding
|
||||||
path := o.TargetPath + allowedEncodings[encoding]
|
path := o.TargetPath + allowedEncodings[encoding]
|
||||||
reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path)
|
reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true)
|
||||||
if statusCode == 404 {
|
if statusCode == 404 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user