fix(server): missing cache-control header (#3737)
This fixes a missing cache control header. Fixes #3732.pull/3738/head
parent
db53b32877
commit
f12346e39c
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue