Skip to content

Commit

Permalink
Parsoid: Rate limit stashing requests
Browse files Browse the repository at this point in the history
Stashing requests are expensive and take up storage, so make sure they
are rate-limited. We check the rates only for external requests that
explicitly request stashing. By default, at most 5 requests per second
per client IP are allowed, but that can be adjusted by setting the
`stash_ratelimit` in the module's configuration options.

Bug: T224055
  • Loading branch information
Marko Obrovac committed May 23, 2019
1 parent de620a7 commit 29819a9
Showing 1 changed file with 50 additions and 4 deletions.
54 changes: 50 additions & 4 deletions sys/parsoid.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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.
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 29819a9

Please sign in to comment.