fix: do not cache api (incl. grpc) and http errors (#2088)

* fix: add cache-control headers (no-store, no-cache) on grpc (for grpc-web)

* fix: do not cache api response (incl. grpc) and http errors
This commit is contained in:
Livio Amstutz 2021-07-28 13:19:44 +02:00 committed by GitHub
parent 451afada90
commit bd8133aedd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 3 deletions

View File

@ -134,7 +134,6 @@ func createDialOptions(g Gateway) []grpc.DialOption {
func addInterceptors(handler http.Handler, g Gateway) http.Handler {
handler = http_mw.DefaultMetricsHandler(handler)
handler = http_mw.DefaultTelemetryHandler(handler)
handler = http_mw.NoCacheInterceptor(handler)
if interceptor, ok := g.(grpcGatewayCustomInterceptor); ok {
handler = interceptor.GatewayHTTPInterceptor(handler)
}

View File

@ -0,0 +1,32 @@
package middleware
import (
"context"
"net/http"
"time"
"github.com/caos/logging"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
_ "github.com/caos/zitadel/internal/statik"
)
func NoCacheInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
headers := map[string]string{
"cache-control": "no-store",
"expires": time.Now().UTC().Format(http.TimeFormat),
"pragma": "no-cache",
}
header := metadata.New(headers)
for key, value := range headers {
header.Append(runtime.MetadataHeaderPrefix+key, value)
}
err := grpc.SendHeader(ctx, header)
logging.Log("MIDDLE-efh41").OnError(err).WithField("req", info.FullMethod).Warn("cannot send cache-control on grpc response")
resp, err := handler(ctx, req)
return resp, err
}
}

View File

@ -36,6 +36,7 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, lang l
middleware.DefaultTracingServer(),
middleware.MetricsHandler(metricTypes, grpc_api.Probes...),
middleware.SentryHandler(),
middleware.NoCacheInterceptor(),
middleware.ErrorHandler(),
middleware.AuthorizationInterceptor(verifier, authConfig),
middleware.TranslationHandler(lang),

View File

@ -81,11 +81,29 @@ func AssetsCacheInterceptor(maxAge, sharedMaxAge time.Duration, h http.Handler)
func CacheInterceptorOpts(h http.Handler, cache *Cache) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
cache.serializeHeaders(w)
h.ServeHTTP(w, req)
cachingResponseWriter := &cachingResponseWriter{
ResponseWriter: w,
Cache: cache,
}
h.ServeHTTP(cachingResponseWriter, req)
})
}
type cachingResponseWriter struct {
http.ResponseWriter
*Cache
}
func (w *cachingResponseWriter) WriteHeader(code int) {
if code >= 400 {
NeverCacheOptions.serializeHeaders(w.ResponseWriter)
w.ResponseWriter.WriteHeader(code)
return
}
w.Cache.serializeHeaders(w.ResponseWriter)
w.ResponseWriter.WriteHeader(code)
}
func (c *Cache) serializeHeaders(w http.ResponseWriter) {
control := make([]string, 0, 6)
pragma := false