fix(server): missing cache-control header (#3737)

This fixes a missing cache control header.

Fixes #3732.
This commit is contained in:
James Elliott 2022-07-25 20:43:50 +10:00 committed by GitHub
parent db53b32877
commit f12346e39c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 93 additions and 14 deletions

View File

@ -1,15 +1,20 @@
package server package server
import ( import (
"bytes"
"crypto/sha1" //nolint:gosec // Usage is for collision avoidance not security.
"embed" "embed"
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"mime"
"net/http" "net/http"
"path"
"path/filepath"
"github.com/valyala/fasthttp" "github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpadaptor"
"github.com/authelia/authelia/v4/internal/handlers"
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/utils" "github.com/authelia/authelia/v4/internal/utils"
) )
@ -21,9 +26,43 @@ var locales embed.FS
var assets embed.FS var assets embed.FS
func newPublicHTMLEmbeddedHandler() fasthttp.RequestHandler { func newPublicHTMLEmbeddedHandler() fasthttp.RequestHandler {
embeddedPath, _ := fs.Sub(assets, "public_html") etags := map[string][]byte{}
return fasthttpadaptor.NewFastHTTPHandler(http.FileServer(http.FS(embeddedPath))) getEmbedETags(assets, "public_html", etags)
return func(ctx *fasthttp.RequestCtx) {
p := path.Join("public_html", string(ctx.Path()))
if etag, ok := etags[p]; ok {
ctx.Response.Header.SetBytesKV(headerETag, etag)
ctx.Response.Header.SetBytesKV(headerCacheControl, headerValueCacheControlETaggedAssets)
if bytes.Equal(etag, ctx.Request.Header.PeekBytes(headerIfNoneMatch)) {
ctx.SetStatusCode(fasthttp.StatusNotModified)
return
}
}
var (
data []byte
err error
)
if data, err = assets.ReadFile(p); err != nil {
hfsHandleErr(ctx, err)
return
}
contentType := mime.TypeByExtension(path.Ext(p))
if len(contentType) == 0 {
contentType = http.DetectContentType(data)
}
ctx.SetContentType(contentType)
ctx.SetBody(data)
}
} }
func newLocalesEmbeddedHandler() (handler fasthttp.RequestHandler) { func newLocalesEmbeddedHandler() (handler fasthttp.RequestHandler) {
@ -72,18 +111,46 @@ func newLocalesEmbeddedHandler() (handler fasthttp.RequestHandler) {
} }
} }
func hfsHandleErr(ctx *fasthttp.RequestCtx, err error) { func getEmbedETags(embedFS embed.FS, root string, etags map[string][]byte) {
switch { var (
case errors.Is(err, fs.ErrNotExist): err error
writeStatus(ctx, fasthttp.StatusNotFound) entries []fs.DirEntry
case errors.Is(err, fs.ErrPermission): )
writeStatus(ctx, fasthttp.StatusForbidden)
default: if entries, err = embedFS.ReadDir(root); err != nil {
writeStatus(ctx, fasthttp.StatusInternalServerError) return
}
for _, entry := range entries {
if entry.IsDir() {
getEmbedETags(embedFS, filepath.Join(root, entry.Name()), etags)
continue
}
p := filepath.Join(root, entry.Name())
var data []byte
if data, err = embedFS.ReadFile(p); err != nil {
continue
}
sum := sha1.New() //nolint:gosec // Usage is for collision avoidance not security.
sum.Write(data)
etags[p] = []byte(fmt.Sprintf("%x", sum.Sum(nil)))
} }
} }
func writeStatus(ctx *fasthttp.RequestCtx, status int) { func hfsHandleErr(ctx *fasthttp.RequestCtx, err error) {
ctx.SetStatusCode(status) switch {
ctx.SetBodyString(fmt.Sprintf("%d %s", status, fasthttp.StatusMessage(status))) case errors.Is(err, fs.ErrNotExist):
handlers.SetStatusCodeResponse(ctx, fasthttp.StatusNotFound)
case errors.Is(err, fs.ErrPermission):
handlers.SetStatusCodeResponse(ctx, fasthttp.StatusForbidden)
default:
handlers.SetStatusCodeResponse(ctx, fasthttp.StatusInternalServerError)
}
} }

View File

@ -1,5 +1,9 @@
package server package server
import (
"github.com/valyala/fasthttp"
)
const ( const (
embeddedAssets = "public_html/" embeddedAssets = "public_html/"
swaggerAssets = embeddedAssets + "api/" swaggerAssets = embeddedAssets + "api/"
@ -50,6 +54,14 @@ const (
schemeHTTPS = "https" schemeHTTPS = "https"
) )
var (
headerETag = []byte(fasthttp.HeaderETag)
headerIfNoneMatch = []byte(fasthttp.HeaderIfNoneMatch)
headerCacheControl = []byte(fasthttp.HeaderCacheControl)
headerValueCacheControlETaggedAssets = []byte("public, max-age=0, must-revalidate")
)
const healthCheckEnv = `# Written by Authelia Process const healthCheckEnv = `# Written by Authelia Process
X_AUTHELIA_HEALTHCHECK=1 X_AUTHELIA_HEALTHCHECK=1
X_AUTHELIA_HEALTHCHECK_SCHEME=%s X_AUTHELIA_HEALTHCHECK_SCHEME=%s