diff --git a/sys/parsoid.js b/sys/parsoid.js index 858394aac..6e1018613 100644 --- a/sys/parsoid.js +++ b/sys/parsoid.js @@ -103,10 +103,7 @@ function compileReRenderBlacklist(blacklist) { class ParsoidService { constructor(options) { - this.options = options = options || {}; - this.parsoidHost = options.parsoidHost; - - this._blacklist = compileReRenderBlacklist(options.rerenderBlacklist); + this._initOpts(options); // Set up operations this.operations = { @@ -125,6 +122,50 @@ class ParsoidService { }; } + _initOpts(opts) { + this.options = opts = opts || {}; + this.parsoidHost = opts.parsoidHost; + this.options.stash_ratelimit = opts.stash_ratelimit || 5; + this._blacklist = compileReRenderBlacklist(opts.rerenderBlacklist); + if (!opts.parsoidHost) { + throw new Error('Parsoid module: the option parsoidHost must be provided!'); + } + } + + _checkStashRate(hyper, req) { + if (!hyper.ratelimiter) { + return; + } + if (hyper._rootReq.headers['x-request-class'] !== 'external') { + return; + } + if (!((req.query && req.query.stash) || (req.body && req.body.stash))) { + return; + } + if (hyper._rootReq.headers['x-stash-rate-checked']) { + return; + } + hyper._rootReq.headers['x-stash-rate-checked'] = true; + const key = `${hyper.config.service_name}.parsoid_stash.${req.params.domain}.` + + `${req.params.title || 'Transform'}.${req.params.revision || 0}|` + + `${hyper._rootReq.headers['x-client-ip']}`; + if (hyper.ratelimiter.isAboveLimit(key, this.options.stash_ratelimit)) { + hyper.logger.log('warn/parsoid/stashlimit', { + key, + rate_limit_per_second: this.options.stash_ratelimit, + message: 'Stashing rate limit exceeded' + }); + throw new HTTPError({ + status: 429, + body: { + type: 'request_rate_exceeded', + title: 'Stashing rate limit exceeded', + rate_limit_per_second: this.options.stash_ratelimit + } + }); + } + } + /** * Get the URI of a bucket for the latest Parsoid content. * @param {string} domain the domain name. @@ -453,6 +494,9 @@ class ParsoidService { }); } + // check the rate limit for stashing requests + this._checkStashRate(hyper, req); + let contentReq = this._getContentWithFallback(hyper, rp.domain, rp.title, rp.revision, rp.tid); @@ -690,6 +734,8 @@ class ParsoidService { if (!rp.revision) { rp.revision = '0'; } + // check the rate limit for stashing requests + this._checkStashRate(hyper, req); } let transform;