diff --git a/lib/response.js b/lib/response.js index 7a2f0ecce56..2169fa2d694 100644 --- a/lib/response.js +++ b/lib/response.js @@ -190,7 +190,8 @@ res.send = function send(body) { // populate ETag var etag; if (generateETag && len !== undefined) { - if ((etag = etagFn(chunk, encoding))) { + var corsOrigin = this.get('Access-Control-Allow-Origin'); + if ((etag = etagFn(chunk, encoding, corsOrigin))) { this.set('ETag', etag); } } diff --git a/lib/utils.js b/lib/utils.js index 4f21e7ef1e3..4089466d157 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -247,11 +247,14 @@ exports.setCharset = function setCharset(type, charset) { */ function createETagGenerator (options) { - return function generateETag (body, encoding) { + return function generateETag (body, encoding,origin) { var buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body - + if (origin!==undefined && origin!==null){ + var originBuf=Buffer.from('\n__origin__:' + String(origin)) + buf=Buffer.concat([buf,originBuf],buf.length+originBuf.length) + } return etag(buf, options) } } diff --git a/test/etag.cors.js b/test/etag.cors.js new file mode 100644 index 00000000000..67833f65d41 --- /dev/null +++ b/test/etag.cors.js @@ -0,0 +1,59 @@ +"use strict"; +var assert = require("node:assert"); +var express = require(".."); +var request = require("supertest"); +const { Buffer } = require("node:buffer"); + +describe("ETag and CORS interaction", function () { + it("should generate different ETags when Access-Control-Allow-Origin differs", function (done) { + var app = express(); + + app.enable("etag"); + + app.use(function (req, res) { + var origin = req.get("Origin") || "https://a.example"; + res.set("Access-Control-Allow-Origin", origin); + res.send("same-body"); + }); + + request(app) + .get("/") + .set("Origin", "https://a.example") + .expect(200) + .end(function (err, res1) { + if (err) return done(err); + var etag1 = res1.headers["etag"]; + + request(app) + .get("/") + .set("Origin", "https://b.example") + .expect(200) + .end(function (err, res2) { + if (err) return done(err); + var etag2 = res2.headers["etag"]; + assert.notStrictEqual( + etag1, + etag2, + "ETag should differ for different Access-Control-Allow-Origin" + ); + done(); + }); + }); + }); + + it("should be backward compatible with custom etag functions (extra arg ignored)", function (done) { + var app = express(); + + app.set("etag", function (body, encoding) { + var chunk = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body; + assert.strictEqual(chunk.toString(), "same-body"); + return '"custom"'; + }); + + app.use(function (req, res) { + res.send("same-body"); + }); + + request(app).get("/").expect("ETag", '"custom"').expect(200, done); + }); +});