diff --git a/CHANGELOG.md b/CHANGELOG.md index ec4a39fb..422c3f18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Change - Don't report `The image request is cancelled` errors. +- Create and destroy a tiny image during health check to check that vips is operational. - (pro) Change the `/info` endpoint behavior to return only the first EXIF/XMP/IPTC block data of JPEG if the image contains multiple metadata blocks of the same type. ### Fix diff --git a/server.go b/server.go index 402d5fc9..f5783f32 100644 --- a/server.go +++ b/server.go @@ -17,6 +17,7 @@ import ( "github.com/imgproxy/imgproxy/v3/metrics" "github.com/imgproxy/imgproxy/v3/reuseport" "github.com/imgproxy/imgproxy/v3/router" + "github.com/imgproxy/imgproxy/v3/vips" ) var ( @@ -165,10 +166,26 @@ func withPanicHandler(h router.RouteHandler) router.RouteHandler { } func handleHealth(reqID string, rw http.ResponseWriter, r *http.Request) { - router.LogResponse(reqID, r, 200, nil) + var ( + status int + msg []byte + ierr *ierrors.Error + ) + + if err := vips.Health(); err == nil { + status = http.StatusOK + msg = imgproxyIsRunningMsg + } else { + status = http.StatusInternalServerError + msg = []byte("Error") + ierr = ierrors.Wrap(err, 1) + } + + router.LogResponse(reqID, r, status, ierr) + rw.Header().Set("Cache-Control", "no-cache") - rw.WriteHeader(200) - rw.Write(imgproxyIsRunningMsg) + rw.WriteHeader(status) + rw.Write(msg) } func handleHead(reqID string, rw http.ResponseWriter, r *http.Request) { diff --git a/vips/vips.c b/vips/vips.c index 353cbcf6..c2abf67a 100644 --- a/vips/vips.c +++ b/vips/vips.c @@ -50,6 +50,20 @@ gif_resolution_limit() { #endif } +// Just create and destroy a tiny image to ensure vips is operational +int +vips_health() { + VipsImage *base = vips_image_new(); + VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 2); + + int res = vips_black(&t[0], 4, 4, "bands", 4, NULL) || + !(t[1] = vips_image_copy_memory(t[0])); + + clear_image(&base); + + return res; +} + int vips_jpegload_go(void *buf, size_t len, int shrink, VipsImage **out) { if (shrink > 1) diff --git a/vips/vips.go b/vips/vips.go index 829d4c76..bb6980e1 100644 --- a/vips/vips.go +++ b/vips/vips.go @@ -7,12 +7,14 @@ package vips */ import "C" import ( + "context" "errors" "math" "os" "runtime" "strings" "sync" + "time" "unsafe" log "github.com/sirupsen/logrus" @@ -147,6 +149,35 @@ func GetAllocs() float64 { return float64(C.vips_tracked_get_allocs()) } +func Health() error { + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + + done := make(chan struct{}) + + var err error + + go func(done chan struct{}) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + defer Cleanup() + + if C.vips_health() != 0 { + err = Error() + } + + close(done) + }(done) + + select { + case <-done: + return err + case <-timer.C: + return context.DeadlineExceeded + } +} + func Cleanup() { C.vips_cleanup() } diff --git a/vips/vips.h b/vips/vips.h index c4071442..e06cef09 100644 --- a/vips/vips.h +++ b/vips/vips.h @@ -13,6 +13,8 @@ void swap_and_clear(VipsImage **in, VipsImage *out); int gif_resolution_limit(); +int vips_health(); + int vips_jpegload_go(void *buf, size_t len, int shrink, VipsImage **out); int vips_pngload_go(void *buf, size_t len, VipsImage **out); int vips_webpload_go(void *buf, size_t len, double scale, int pages, VipsImage **out);