diff --git a/deployment/serverless-image-handler.template b/deployment/serverless-image-handler.template index 3e655f49e..496b1cfdb 100644 --- a/deployment/serverless-image-handler.template +++ b/deployment/serverless-image-handler.template @@ -8,7 +8,7 @@ "Type" : "String", "AllowedValues" : [ "Yes", "No" ] }, - "CorsOrigin" : { + "CorsOrigin" : { "Description" : "If you selected 'Yes' above, please specify an origin value here. A wildcard (*) value will support any origin. We recommend specifying an origin (i.e. https://example.domain) to restrict cross-site access to your API.", "Default" : "*", "Type" : "String" @@ -220,7 +220,7 @@ "S3Bucket" : { "Fn::Join": [ "-", [{ "Fn::FindInMap": [ "SourceCode", "General", "S3Bucket" ]}, { "Ref": "AWS::Region" }]]}, "S3Key" : "serverless-image-handler/%%VERSION%%/image-handler.zip" }, - "Runtime": "nodejs8.10", + "Runtime": "nodejs12.x", "MemorySize": 1024, "Timeout": 30, "Environment" : { @@ -496,7 +496,7 @@ "S3Key": "serverless-image-handler/%%VERSION%%/custom-resource.zip" }, "Timeout": 30, - "Runtime": "nodejs8.10", + "Runtime": "nodejs12.x", "Role": { "Fn::GetAtt": [ "CustomResourceRole", "Arn" ] }, "Handler": "index.handler" } @@ -675,4 +675,4 @@ "Value" : { "Ref" : "LogRetentionPeriod" } } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/source/image-handler/image-handler.js b/source/image-handler/image-handler.js index 4b4b15c59..838d451a5 100644 --- a/source/image-handler/image-handler.js +++ b/source/image-handler/image-handler.js @@ -24,10 +24,10 @@ class ImageHandler { const originalImage = request.originalImage; const edits = request.edits; if (edits !== undefined) { - const modifiedImage = await this.applyEdits(originalImage, edits); + const modifiedImage = await this.applyEdits(originalImage, edits); if (request.outputFormat !== undefined) { await modifiedImage.toFormat(request.outputFormat); - } + } const bufferImage = await modifiedImage.toBuffer(); return bufferImage.toString('base64'); } else { @@ -41,8 +41,14 @@ class ImageHandler { * @param {Buffer} originalImage - The original image. * @param {Object} edits - The edits to be made to the original image. */ - async applyEdits(originalImage, edits) { - const image = sharp(originalImage); + async applyEdits(originalImage, edits) { + + if (edits.resize === undefined) { + edits.resize = {}; + edits.resize.fit = "inside"; + } + + const image = sharp(originalImage, { failOnError: false }).rotate(); const keys = Object.keys(edits); const values = Object.values(edits); // Apply the image edits diff --git a/source/image-handler/image-request.js b/source/image-handler/image-request.js index 75b7da7e7..2b8699078 100644 --- a/source/image-handler/image-request.js +++ b/source/image-handler/image-request.js @@ -14,7 +14,7 @@ const ThumborMapping = require('./thumbor-mapping'); class ImageRequest { - + /** * Initializer function for creating a new image request, used by the image * handler to perform image modifications. @@ -136,9 +136,7 @@ class ImageRequest { const decoded = this.decodeRequest(event); return decoded.key; } else if (requestType === "Thumbor" || requestType === "Custom") { - // Parse the key from the end of the path - const key = (event["path"]).split("/"); - return key[key.length - 1]; + return decodeURIComponent(event["path"].replace(/\d+x\d+\/|filters[:-][^/;]+|\/fit-in\/+|^\/+/g,'').replace(/^\/+/,'')); } else { // Return an error for all other conditions throw ({ @@ -160,8 +158,8 @@ class ImageRequest { const path = event["path"]; // ---- const matchDefault = new RegExp(/^(\/?)([0-9a-zA-Z+\/]{4})*(([0-9a-zA-Z+\/]{2}==)|([0-9a-zA-Z+\/]{3}=))?$/); - const matchThumbor = new RegExp(/^(\/?)((fit-in)?|(filters:.+\(.?\))?|(unsafe)?).*(.+jpg|.+png|.+webp|.+tiff|.+jpeg)$/); - const matchCustom = new RegExp(/(\/?)(.*)(jpg|png|webp|tiff|jpeg)/); + const matchThumbor = new RegExp(/^(\/?)((fit-in)?|(filters:.+\(.?\))?|(unsafe)?).*(.+jpg|.+png|.+webp|.+tiff|.+jpeg)$/i); + const matchCustom = new RegExp(/(\/?)(.*)(jpg|png|webp|tiff|jpeg)/i); const definedEnvironmentVariables = ( (process.env.REWRITE_MATCH_PATTERN !== "") && (process.env.REWRITE_SUBSTITUTION !== "") && diff --git a/source/image-handler/package.json b/source/image-handler/package.json index 3dee6a793..8f3ca5d16 100644 --- a/source/image-handler/package.json +++ b/source/image-handler/package.json @@ -10,7 +10,7 @@ "license": "ISC", "dependencies": { "mocha": "^6.1.4", - "sharp": "^0.21.3", + "sharp": "^0.22.1", "sinon": "^7.3.2", "nyc": "^14.0.0" }, diff --git a/source/image-handler/test/test-thumbor-mapping.js b/source/image-handler/test/test-thumbor-mapping.js index 0968fd3ea..cf0a7eed0 100644 --- a/source/image-handler/test/test-thumbor-mapping.js +++ b/source/image-handler/test/test-thumbor-mapping.js @@ -30,12 +30,13 @@ describe('process()', function() { thumborMapping.process(event); // Assert const expectedResult = { - edits: { + edits: { + grayscale: true, resize: { width: 200, - height: 300 - }, - grayscale: true + height: 300, + fit: 'inside' + } } }; assert.deepEqual(thumborMapping.edits, expectedResult.edits); @@ -628,7 +629,7 @@ describe('mapFilter()', function() { thumborMapping.mapFilter(edit, filetype); // Assert const expectedResult = { - edits: { + edits: { rotate: 0 } }; diff --git a/source/image-handler/thumbor-mapping.js b/source/image-handler/thumbor-mapping.js index dc9e8a76c..f30218721 100644 --- a/source/image-handler/thumbor-mapping.js +++ b/source/image-handler/thumbor-mapping.js @@ -29,20 +29,33 @@ class ThumborMapping { this.path = event.path; const edits = this.path.split('/'); const filetype = (this.path.split('.'))[(this.path.split('.')).length - 1]; + + //Process the Dimensions + const dimPath = this.path.match(/[^\/]\d+x\d+/g); + if (dimPath) { + const dims = dimPath[0].split('x'); + // Set only if the dimensions provided are valid + if (isNaN(dims[0]) == false && isNaN(dims[1]) == false ){ + this.edits.resize = {}; + this.edits.resize.fit = 'inside' + if (this.edits.resize.width == undefined && this.edits.resize.height == undefined) { + // Assign dimenions from the first match only to avoid parsing dimension from image file names + this.edits.resize.width = Number(dims[0]); + this.edits.resize.height = Number(dims[1]); + } + } + } + // Parse the image path for (let i = 0; i < edits.length; i++) { const edit = edits[i]; if (edit === ('fit-in')) { - this.edits.resize = {}; + if (this.edits.resize === undefined) { + this.edits.resize = {}; + } + this.edits.resize.fit = 'inside' this.sizingMethod = edit; - } - else if (edit.includes('x')) { - this.edits.resize = {}; - const dims = edit.split('x'); - this.edits.resize.width = Number(dims[0]); - this.edits.resize.height = Number(dims[1]); - } - if (edit.includes('filters:')) { + } else if (edit.includes('filters:')) { this.mapFilter(edit, filetype); } }