0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Added timeout when resizing an image (#20087)

refs
[ENG-827](https://linear.app/tryghost/issue/ENG-827/🐛-crash-on-resizing-animated-gif)

Added a timeout to the image resizing middleware to prevent crashes when
an image is taking too long to resize. When the timeout is reached and
the image has not been resized, the middleware will return the original
image
This commit is contained in:
Michael Barrett 2024-04-30 08:39:30 +01:00 committed by GitHub
parent 3d6fae3ea7
commit 4cd85ab8b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 86 additions and 12 deletions

View file

@ -9,9 +9,10 @@ const {imageSize} = require('../../../server/lib/image');
const SIZE_PATH_REGEX = /^\/size\/([^/]+)\//;
const FORMAT_PATH_REGEX = /^\/format\/([^./]+)\//;
const TRAILING_SLASH_REGEX = /\/+$/;
const RESIZE_TIMEOUT_SECONDS = 10;
module.exports = function handleImageSizes(req, res, next) {
// In admin we need to read images and calculate the average color (blocked by CORS otherwise)
res.setHeader('Access-Control-Allow-Origin', '*');
@ -123,7 +124,12 @@ module.exports = function handleImageSizes(req, res, next) {
if (originalImageBuffer.length <= 0) {
throw new NoContentError();
}
return imageTransform.resizeFromBuffer(originalImageBuffer, {withoutEnlargement: requestUrlFileExtension !== '.svg', ...imageDimensionConfig, format});
return imageTransform.resizeFromBuffer(originalImageBuffer, {
withoutEnlargement: requestUrlFileExtension !== '.svg',
...imageDimensionConfig,
format,
timeout: RESIZE_TIMEOUT_SECONDS
});
})
.then((resizedImageBuffer) => {
return storageInstance.saveRaw(resizedImageBuffer, req.url);
@ -147,3 +153,5 @@ module.exports = function handleImageSizes(req, res, next) {
next(err);
});
};
module.exports.RESIZE_TIMEOUT_SECONDS = RESIZE_TIMEOUT_SECONDS;

View file

@ -94,7 +94,7 @@
"@tryghost/html-to-plaintext": "0.0.0",
"@tryghost/http-cache-utils": "0.1.11",
"@tryghost/i18n": "0.0.0",
"@tryghost/image-transform": "1.2.11",
"@tryghost/image-transform": "1.3.0",
"@tryghost/importer-handler-content-files": "0.0.0",
"@tryghost/importer-revue": "0.0.0",
"@tryghost/job-manager": "0.0.0",

View file

@ -294,6 +294,47 @@ describe('handleImageSizes middleware', function () {
});
});
it('redirects if timeout is exceeded', function (done) {
sinon.stub(imageTransform, 'canTransformFiles').returns(true);
dummyStorage.exists = async function () {
return false;
};
dummyStorage.read = async function () {
return buffer;
};
const error = new Error('Resize timeout');
error.code = 'IMAGE_PROCESSING';
resizeFromBufferStub.throws(error);
const fakeReq = {
url: '/size/w1000/blank.png',
originalUrl: '/blog/content/images/size/w1000/blank.png'
};
const fakeRes = {
redirect(url) {
try {
url.should.equal('/blog/content/images/blank.png');
} catch (e) {
return done(e);
}
done();
},
setHeader() {}
};
handleImageSizes(fakeReq, fakeRes, function next(err) {
if (err) {
return done(err);
}
done(new Error('Should not have called next'));
});
});
it('continues if file exists', function (done) {
dummyStorage.exists = async function (path) {
if (path === '/size/w1000/blank.png') {
@ -526,7 +567,12 @@ describe('handleImageSizes middleware', function () {
return done(err);
}
try {
resizeFromBufferStub.calledOnceWithExactly(buffer, {withoutEnlargement: false, width: 1000, format: 'png'}).should.be.true();
resizeFromBufferStub.calledOnceWithExactly(buffer, {
withoutEnlargement: false,
width: 1000,
format: 'png',
timeout: handleImageSizes.RESIZE_TIMEOUT_SECONDS
}).should.be.true();
typeStub.calledOnceWithExactly('png').should.be.true();
} catch (e) {
return done(e);
@ -561,7 +607,12 @@ describe('handleImageSizes middleware', function () {
return done(err);
}
try {
resizeFromBufferStub.calledOnceWithExactly(buffer, {withoutEnlargement: true, width: 1000, format: 'webp'}).should.be.true();
resizeFromBufferStub.calledOnceWithExactly(buffer, {
withoutEnlargement: true,
width: 1000,
format: 'webp',
timeout: handleImageSizes.RESIZE_TIMEOUT_SECONDS
}).should.be.true();
typeStub.calledOnceWithExactly('webp').should.be.true();
} catch (e) {
return done(e);
@ -596,7 +647,12 @@ describe('handleImageSizes middleware', function () {
return done(err);
}
try {
resizeFromBufferStub.calledOnceWithExactly(buffer, {withoutEnlargement: true, width: 1000, format: 'avif'}).should.be.true();
resizeFromBufferStub.calledOnceWithExactly(buffer, {
withoutEnlargement: true,
width: 1000,
format: 'avif',
timeout: handleImageSizes.RESIZE_TIMEOUT_SECONDS
}).should.be.true();
typeStub.calledOnceWithExactly('image/avif').should.be.true();
} catch (e) {
return done(e);
@ -631,7 +687,12 @@ describe('handleImageSizes middleware', function () {
return done(err);
}
try {
resizeFromBufferStub.calledOnceWithExactly(buffer, {withoutEnlargement: true, width: 1000, format: 'webp'}).should.be.true();
resizeFromBufferStub.calledOnceWithExactly(buffer, {
withoutEnlargement: true,
width: 1000,
format: 'webp',
timeout: handleImageSizes.RESIZE_TIMEOUT_SECONDS
}).should.be.true();
typeStub.calledOnceWithExactly('webp').should.be.true();
} catch (e) {
return done(e);
@ -666,7 +727,12 @@ describe('handleImageSizes middleware', function () {
return done(err);
}
try {
resizeFromBufferStub.calledOnceWithExactly(buffer, {withoutEnlargement: true, width: 1000, format: 'gif'}).should.be.true();
resizeFromBufferStub.calledOnceWithExactly(buffer, {
withoutEnlargement: true,
width: 1000,
format: 'gif',
timeout: handleImageSizes.RESIZE_TIMEOUT_SECONDS
}).should.be.true();
typeStub.calledOnceWithExactly('gif').should.be.true();
} catch (e) {
return done(e);

View file

@ -7066,10 +7066,10 @@
"@tryghost/errors" "^1.3.1"
"@tryghost/request" "^1.0.3"
"@tryghost/image-transform@1.2.11":
version "1.2.11"
resolved "https://registry.yarnpkg.com/@tryghost/image-transform/-/image-transform-1.2.11.tgz#82463d97f8747db6db70165a04e824eed6791fee"
integrity sha512-O4DRZw3lXj9E4LCV8Mm/gMchhyH9rq4/4h4f4+8tb/dhanz7DMhP5yXHH4WBooF2SG1HWV/XITVSY1erFYQFyA==
"@tryghost/image-transform@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@tryghost/image-transform/-/image-transform-1.3.0.tgz#e68e630c4e42e1af193a18894a864e9e2da9fc1f"
integrity sha512-WQFSMh1eAWbZqiheMR5TuTOTll4RDRKZ5p/JIOvejiBzgu/OLBLGKrFKpPb9JSVySTFxpj1dInKmMu8z1so7nQ==
dependencies:
"@tryghost/errors" "^1.2.26"
fs-extra "^11.0.0"