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:
parent
3d6fae3ea7
commit
4cd85ab8b7
4 changed files with 86 additions and 12 deletions
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue