From b93aa2f144744272dc40c8ccac4c61d6415c05ce Mon Sep 17 00:00:00 2001 From: Svyatoslav Kryukov Date: Fri, 19 Mar 2021 12:28:23 +0300 Subject: [PATCH] Fallback image http code config (#589) --- config.go | 13 ++++++++++--- docs/configuration.md | 1 + processing_handler.go | 24 +++++++++++++++++++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/config.go b/config.go index 02302c8d..d594d994 100644 --- a/config.go +++ b/config.go @@ -279,9 +279,10 @@ type config struct { WatermarkURL string WatermarkOpacity float64 - FallbackImageData string - FallbackImagePath string - FallbackImageURL string + FallbackImageData string + FallbackImagePath string + FallbackImageURL string + FallbackImageHTTPCode int NewRelicAppName string NewRelicKey string @@ -328,6 +329,7 @@ var conf = config{ UserAgent: fmt.Sprintf("imgproxy/%s", version), Presets: make(presets), WatermarkOpacity: 1, + FallbackImageHTTPCode: 200, BugsnagStage: "production", HoneybadgerEnv: "production", SentryEnvironment: "production", @@ -451,6 +453,7 @@ func configure() error { strEnvConfig(&conf.FallbackImageData, "IMGPROXY_FALLBACK_IMAGE_DATA") strEnvConfig(&conf.FallbackImagePath, "IMGPROXY_FALLBACK_IMAGE_PATH") strEnvConfig(&conf.FallbackImageURL, "IMGPROXY_FALLBACK_IMAGE_URL") + intEnvConfig(&conf.FallbackImageHTTPCode, "IMGPROXY_FALLBACK_IMAGE_HTTP_CODE") strEnvConfig(&conf.NewRelicAppName, "IMGPROXY_NEW_RELIC_APP_NAME") strEnvConfig(&conf.NewRelicKey, "IMGPROXY_NEW_RELIC_KEY") @@ -574,6 +577,10 @@ func configure() error { return fmt.Errorf("Watermark opacity should be less than or equal to 1") } + if conf.FallbackImageHTTPCode < 100 || conf.FallbackImageHTTPCode > 599 { + return fmt.Errorf("Fallback image HTTP code should be between 100 and 599") + } + if len(conf.PrometheusBind) > 0 && conf.PrometheusBind == conf.Bind { return fmt.Errorf("Can't use the same binding for the main server and Prometheus") } diff --git a/docs/configuration.md b/docs/configuration.md index 6a247c46..e82b20c2 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -185,6 +185,7 @@ You can set up a fallback image that will be used in case imgproxy can't fetch t * `IMGPROXY_FALLBACK_IMAGE_DATA`: Base64-encoded image data. You can easily calculate it with `base64 tmp/fallback.png | tr -d '\n'`; * `IMGPROXY_FALLBACK_IMAGE_PATH`: path to the locally stored image; * `IMGPROXY_FALLBACK_IMAGE_URL`: fallback image URL. +* `IMGPROXY_FALLBACK_IMAGE_HTTP_CODE`: HTTP code for the fallback image response. Default: `200`. ## Skip processing diff --git a/processing_handler.go b/processing_handler.go index d81d508c..c426ac90 100644 --- a/processing_handler.go +++ b/processing_handler.go @@ -16,6 +16,10 @@ var ( fallbackImage *imageData ) +const ( + fallbackImageUsedCtxKey = ctxKey("fallbackImageUsed") +) + func initProcessingHandler() error { var err error @@ -94,12 +98,16 @@ func respondWithImage(ctx context.Context, reqID string, r *http.Request, rw htt } rw.Header().Set("Content-Length", strconv.Itoa(len(data))) - rw.WriteHeader(200) + statusCode := 200 + if getFallbackImageUsed(ctx) { + statusCode = conf.FallbackImageHTTPCode + } + rw.WriteHeader(statusCode) rw.Write(data) imageURL := getImageURL(ctx) - logResponse(reqID, r, 200, nil, &imageURL, po) + logResponse(reqID, r, statusCode, nil, &imageURL, po) } func respondWithNotModified(ctx context.Context, reqID string, r *http.Request, rw http.ResponseWriter) { @@ -158,12 +166,13 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) { } logWarning("Could not load image. Using fallback image: %s", err.Error()) + ctx = setFallbackImageUsedCtx(ctx) ctx = context.WithValue(ctx, imageDataCtxKey, fallbackImage) } checkTimeout(ctx) - if conf.ETagEnabled { + if conf.ETagEnabled && !getFallbackImageUsed(ctx) { eTag := calcETag(ctx) rw.Header().Set("ETag", eTag) @@ -206,3 +215,12 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) { respondWithImage(ctx, reqID, r, rw, imageData) } + +func setFallbackImageUsedCtx(ctx context.Context) context.Context { + return context.WithValue(ctx, fallbackImageUsedCtxKey, true) +} + +func getFallbackImageUsed(ctx context.Context) bool { + result, _ := ctx.Value(fallbackImageUsedCtxKey).(bool) + return result +}