diff --git a/lib/cartodb/controllers/layergroup.js b/lib/cartodb/controllers/layergroup.js index 5747be57..98089eac 100644 --- a/lib/cartodb/controllers/layergroup.js +++ b/lib/cartodb/controllers/layergroup.js @@ -53,6 +53,10 @@ LayergroupController.prototype.register = function(app) { this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), this.tile(this.tileBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse(), this.tileError(), vectorError() ); @@ -64,6 +68,10 @@ LayergroupController.prototype.register = function(app) { this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), this.tile(this.tileBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse(), this.tileError(), vectorError() ); @@ -76,6 +84,10 @@ LayergroupController.prototype.register = function(app) { this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), this.layer(this.tileBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse(), this.tileError(), vectorError() ); @@ -86,7 +98,11 @@ LayergroupController.prototype.register = function(app) { userMiddleware, this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), - this.attributes(this.attributesBackend) + this.attributes(this.attributesBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); const forcedFormat = 'png'; @@ -98,7 +114,11 @@ LayergroupController.prototype.register = function(app) { allowQueryParams(['layer']), this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi, forcedFormat), - this.center(this.previewBackend) + this.center(this.previewBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); app.get( @@ -108,7 +128,11 @@ LayergroupController.prototype.register = function(app) { allowQueryParams(['layer']), this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi, forcedFormat), - this.bbox(this.previewBackend) + this.bbox(this.previewBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); // Undocumented/non-supported API endpoint methods. @@ -136,7 +160,11 @@ LayergroupController.prototype.register = function(app) { allowQueryParams(allowedDataviewQueryParams), this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), - this.getDataview(this.dataviewBackend) + this.getDataview(this.dataviewBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); app.get( @@ -146,7 +174,11 @@ LayergroupController.prototype.register = function(app) { allowQueryParams(allowedDataviewQueryParams), this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), - this.getDataview(this.dataviewBackend) + this.getDataview(this.dataviewBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); app.get( @@ -156,7 +188,11 @@ LayergroupController.prototype.register = function(app) { allowQueryParams(allowedDataviewQueryParams), this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), - this.dataviewSearch(this.dataviewBackend) + this.dataviewSearch(this.dataviewBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); app.get( @@ -166,7 +202,11 @@ LayergroupController.prototype.register = function(app) { allowQueryParams(allowedDataviewQueryParams), this.prepareContext, this.getMapStoreMapConfigProvider(this.mapStore, this.userLimitsApi), - this.dataviewSearch(this.dataviewBackend) + this.dataviewSearch(this.dataviewBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); app.get( @@ -174,7 +214,11 @@ LayergroupController.prototype.register = function(app) { cors(), userMiddleware, this.prepareContext, - this.analysisNodeStatus(this.analysisStatusBackend) + this.analysisNodeStatus(this.analysisStatusBackend), + this.setCacheControlHeader(), + this.setLastModifiedHeader(), + this.affectedTables(), + this.sendResponse() ); }; @@ -196,10 +240,14 @@ LayergroupController.prototype.analysisNodeStatus = function (analysisStatusBack return next(err); } - this.sendResponse(req, res, nodeStatus, 200, { + res.set({ 'Cache-Control': 'public,max-age=5', 'Last-Modified': new Date().toUTCString() }); + + res.body = nodeStatus; + + next(); }); }.bind(this); }; @@ -242,7 +290,9 @@ LayergroupController.prototype.getDataview = function (dataviewBackend) { return next(err); } - this.sendResponse(req, res, dataview, 200); + res.body = dataview; + + next(); }); }.bind(this); }; @@ -259,7 +309,9 @@ LayergroupController.prototype.dataviewSearch = function (dataviewBackend) { return next(err); } - this.sendResponse(req, res, searchResult, 200); + res.body = searchResult; + + next(); }); }.bind(this); }; @@ -278,13 +330,15 @@ LayergroupController.prototype.attributes = function (attributesBackend) { return next(err); } - this.sendResponse(req, res, tile, 200); + res.body = tile; + + next(); }); }.bind(this); }; function getStatusCode(tile, format){ - return tile.length===0 && format==='mvt'? 204:200; + return tile.length === 0 && format === 'mvt'? 204 : 200; } const supportedFormats = { @@ -328,7 +382,14 @@ LayergroupController.prototype.tile = function (tileBackend) { return; } - this.sendResponse(req, res, tile, getStatusCode(tile, formatStat), headers); + if (headers) { + res.set(headers); + } + + res.statusCode = getStatusCode(tile, formatStat); + res.body = tile; + + next(); global.statsClient.increment('windshaft.tiles.success'); global.statsClient.increment('windshaft.tiles.' + formatStat + '.success'); @@ -357,7 +418,15 @@ LayergroupController.prototype.layer = function (tileBackend) { return; } - this.sendResponse(req, res, tile, getStatusCode(tile, formatStat), headers); + if (headers) { + res.set(headers); + } + + res.statusCode = getStatusCode(tile, formatStat); + res.body = tile; + + next(); + global.statsClient.increment('windshaft.tiles.success'); global.statsClient.increment('windshaft.tiles.' + formatStat + '.success'); }); @@ -405,8 +474,15 @@ LayergroupController.prototype.center = function (previewBackend) { return next(err); } + if (headers) { + res.set(headers); + } + res.set('Content-Type', headers['Content-Type'] || 'image/' + format); - this.sendResponse(req, res, image, 200); + + res.body = image; + + next(); }); }.bind(this); }; @@ -433,61 +509,65 @@ LayergroupController.prototype.bbox = function (previewBackend) { return next(err); } + if (headers) { + res.set(headers); + } + res.set('Content-Type', headers['Content-Type'] || 'image/' + format); - this.sendResponse(req, res, image, 200); + res.body = image; + + next(); }); }.bind(this); }; -LayergroupController.prototype.sendResponse = function(req, res, body, status, headers) { - req.profiler.done('res'); +LayergroupController.prototype.setLastModifiedHeader = function () { + return function setLastModifiedHeaderMiddleware (req, res, next) { + let { cache_buster: cacheBuster } = res.locals; - res.set('Cache-Control', 'public,max-age=31536000'); + cacheBuster = parseInt(cacheBuster); - // Set Last-Modified header - var lastUpdated; - if (res.locals.cache_buster) { - // Assuming cache_buster is a timestamp - lastUpdated = new Date(parseInt(res.locals.cache_buster)); - } else { - lastUpdated = new Date(); - } - res.set('Last-Modified', lastUpdated.toUTCString()); + const lastUpdated = res.locals.cache_buster ? new Date(cacheBuster) : new Date(); - var dbName = res.locals.dbname; + res.set('Last-Modified', lastUpdated.toUTCString()); - this.getAffectedTables(res.locals.user, dbName, res.locals.token, (err, affectedTables) => { - req.profiler.done('affectedTables'); + next(); + }; +}; - if (err) { - global.logger.warn('ERROR generating cache channel: ' + err); - } - if (!!affectedTables) { - res.set('X-Cache-Channel', affectedTables.getCacheChannel()); - this.surrogateKeysCache.tag(res, affectedTables); +LayergroupController.prototype.setCacheControlHeader = function () { + return function setCacheControlHeaderMiddleware (req, res, next) { + if (!res.get('Cache-Control')) { + res.set('Cache-Control', 'public,max-age=31536000'); } - if (headers) { - res.set(headers); - } + next(); + }; +}; - res.status(status); +LayergroupController.prototype.affectedTables = function () { + return function affectedTablesMiddleware (req, res, next) { + const { user, dbname, token } = res.locals; - if (!Buffer.isBuffer(body) && typeof body === 'object') { - if (req.query && req.query.callback) { - res.jsonp(body); - } else { - res.json(body); + this.getAffectedTables(user, dbname, token, (err, affectedTables) => { + req.profiler.done('affectedTables'); + + if (err) { + global.logger.warn('ERROR generating cache channel: ' + err); } - } else { - res.send(body); - } - }); + + if (!!affectedTables) { + res.set('X-Cache-Channel', affectedTables.getCacheChannel()); + this.surrogateKeysCache.tag(res, affectedTables); + } + + next(); + }); + }.bind(this); }; LayergroupController.prototype.getAffectedTables = function(user, dbName, layergroupId, callback) { - if (this.layergroupAffectedTables.hasAffectedTables(dbName, layergroupId)) { return callback(null, this.layergroupAffectedTables.get(dbName, layergroupId)); } @@ -544,3 +624,21 @@ LayergroupController.prototype.getAffectedTables = function(user, dbName, layerg callback ); }; + +LayergroupController.prototype.sendResponse = function () { + return function sendResponseMiddleware (req, res) { + req.profiler.done('res'); + + res.status(res.statusCode || 200); + + if (!Buffer.isBuffer(res.body) && typeof res.body === 'object') { + if (req.query && req.query.callback) { + res.jsonp(res.body); + } else { + res.json(res.body); + } + } else { + res.send(res.body); + } + }; +};