Skip to content

Commit

Permalink
Merge pull request #239 from gwicke/etags
Browse files Browse the repository at this point in the history
Provide full version in etag headers
  • Loading branch information
eevans committed May 1, 2015
2 parents 35e406d + 2fa4efb commit 76e4374
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 41 deletions.
22 changes: 22 additions & 0 deletions lib/rbUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,26 @@ rbUtil.httpErrors = {
};


// Create an etag value of the form
// "<revision>/<tid>"
rbUtil.makeETag = function(rev, tid) {
return '"' + rev + '/' + tid + '"';
};

// Parse an etag value of the form
// "<revision>/<tid>"
// @param {string} etag
// @return {object} with params rev / tid
rbUtil.parseETag = function(etag) {
var bits = /^"?([^"\/]+)(?:\/([^"\/]+))"?$/.exec(etag);
if (bits) {
return {
rev: bits[1],
tid: bits[2],
};
} else {
return null;
}
};

module.exports = rbUtil;
4 changes: 2 additions & 2 deletions mods/key_rev_value.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function returnRevision(req) {
if (dbResult.body && dbResult.body.items && dbResult.body.items.length) {
var row = dbResult.body.items[0];
var headers = {
etag: row.tid,
etag: rbUtil.makeETag(row.rev, row.tid),
'content-type': row['content-type']
};
return {
Expand Down Expand Up @@ -268,7 +268,7 @@ KRVBucket.prototype.putRevision = function(restbase, req) {
return {
status: 201,
headers: {
etag: rp.revision
etag: rbUtil.makeETag(rp.revision, tid),
},
body: {
message: "Created.",
Expand Down
4 changes: 2 additions & 2 deletions mods/key_value.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ function returnRevision(req) {
if (dbResult.body && dbResult.body.items && dbResult.body.items.length) {
var row = dbResult.body.items[0];
var headers = {
etag: row.tid,
etag: rbUtil.makeETag(row.rev, row.tid),
'content-type': row['content-type']
};
return {
Expand Down Expand Up @@ -255,7 +255,7 @@ KVBucket.prototype.putRevision = function(restbase, req) {
return {
status: 201,
headers: {
etag: tid
etag: rbUtil.makeETag(rp.revision, tid)
},
body: {
message: "Created.",
Expand Down
3 changes: 2 additions & 1 deletion mods/page_revisions.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ PRS.prototype.getTitleRevision = function(restbase, req) {
if (!res.headers) {
res.headers = {};
}
res.headers.etag = res.body.items[0].tid;
var info = res.body.items[0];
res.headers.etag = rbUtil.makeETag(info.rev, info.tid);
return res;
});
// TODO: handle other revision formats (tid)
Expand Down
10 changes: 6 additions & 4 deletions mods/parsoid.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ PSP.saveParsoidResult = function (restbase, req, format, tid, parsoidResp) {
headers: parsoidResp.body[format].headers,
body: parsoidResp.body[format].body
};
resp.headers.etag = tid;
resp.headers.etag = rbUtil.makeETag(rp.revision, tid);
return this.wrapContentReq(restbase, req, P.resolve(resp));
} else {
return parsoidResp;
Expand Down Expand Up @@ -275,7 +275,8 @@ PSP.getFormat = function (format, restbase, req) {
if (req.headers['if-unmodified-since']) {
try {
var jobTime = new Date(req.headers['if-unmodified-since']);
if (uuid.v1time(res.headers.etag) >= jobTime) {
var revInfo = rbUtil.parseETag(res.headers.etag);
if (revInfo && uuid.v1time(revInfo.tid) >= jobTime) {
// Already up to date, nothing to do.
return {
status: 412,
Expand Down Expand Up @@ -350,9 +351,10 @@ PSP.transformRevision = function (restbase, req, from, to) {

var tid;
if (from === 'html') {
if (req.headers && req.headers['if-match']) {
if (req.headers && req.headers['if-match']
&& rbUtil.parseETag(req.headers['if-match'])) {
// Prefer the If-Match header
tid = req.headers['if-match'];
tid = rbUtil.parseETag(req.headers['if-match']).tid;
} else if (req.body && req.body.html) {
// Fall back to an inline meta tag in the HTML
var tidMatch = /<meta property="mw:TimeUuid" content="([^"]+)"\/?>/
Expand Down
69 changes: 37 additions & 32 deletions test/features/pagecontent/rerendering.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ var preq = require('preq');
var server = require('../../utils/server.js');
var P = require('bluebird');

function getTid(etag) {
return /^"[^\/]+\/([^"]+)"/.exec(etag)[1];
}

describe('page re-rendering', function () {
this.timeout(20000);

Expand All @@ -23,15 +27,15 @@ describe('page re-rendering', function () {
}

it('should render & re-render independent revisions', function () {
var r1tid1;
var r1tid2;
var r2tid1;
var r1etag1;
var r1etag2;
var r2etag1;
return preq.get({
uri: server.config.bucketURL + dynamic1
})
.then(function (res) {
assert.deepEqual(res.status, 200);
r1tid1 = res.headers.etag;
r1etag1 = res.headers.etag;
hasTextContentType(res);

// delay for 1s to make sure that the timestamp differs on re-render
Expand All @@ -46,58 +50,58 @@ describe('page re-rendering', function () {
.then(function (res) {
// Since this is a dynamic page which should render the same each
// time, the tid should not change.
r1tid2 = res.headers.etag;
assert.notDeepEqual(r1tid2, r1tid1);
assert.notDeepEqual(r1tid2, undefined);
r1etag2 = res.headers.etag;
assert.notDeepEqual(r1etag2, r1etag1);
assert.notDeepEqual(r1etag2, undefined);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + dynamic1 + '/' + r1tid1
uri: server.config.bucketURL + dynamic1 + '/' + getTid(r1etag1)
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid1);
assert.deepEqual(res.headers.etag, r1etag1);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + dynamic1
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid2);
assert.deepEqual(res.headers.etag, r1etag2);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + dynamic1 + '/' + r1tid2
uri: server.config.bucketURL + dynamic1 + '/' + getTid(r1etag2)
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid2);
assert.deepEqual(res.headers.etag, r1etag2);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + dynamic2
});
})
.then(function (res) {
r2tid1 = res.headers.etag;
r2etag1 = res.headers.etag;
assert.deepEqual(res.status, 200);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + dynamic2 + '/' + r2tid1
uri: server.config.bucketURL + dynamic2 + '/' + getTid(r2etag1)
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r2tid1);
assert.deepEqual(res.headers.etag, r2etag1);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + dynamic1
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid2);
assert.deepEqual(res.headers.etag, r1etag2);
hasTextContentType(res);
});
});
Expand All @@ -123,15 +127,16 @@ describe('page re-rendering', function () {
var static2 = '/html/User:GWicke%2fStatic/653529961';

it('should render & re-render independent revisions, but not update unchanged content', function () {
var r1tid1;
var r1tid2;
var r2tid1;
var r1etag1;
var r1etag2;
var r2etag1;
var tid;
return preq.get({
uri: server.config.bucketURL + static1
})
.then(function (res) {
assert.deepEqual(res.status, 200);
r1tid1 = res.headers.etag;
r1etag1 = res.headers.etag;
hasTextContentType(res);

return preq.get({
Expand All @@ -142,58 +147,58 @@ describe('page re-rendering', function () {
.then(function (res) {
// Since this is a static page which should render the same each
// time, the tid should not change.
r1tid2 = res.headers.etag;
assert.deepEqual(r1tid2, r1tid1);
assert.notDeepEqual(r1tid2, undefined);
r1etag2 = res.headers.etag;
assert.deepEqual(r1etag2, r1etag1);
assert.notDeepEqual(r1etag2, undefined);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + static1 + '/' + r1tid1
uri: server.config.bucketURL + static1 + '/' + getTid(r1etag1)
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid1);
assert.deepEqual(res.headers.etag, r1etag1);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + static1
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid2);
assert.deepEqual(res.headers.etag, r1etag2);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + static1 + '/' + r1tid2
uri: server.config.bucketURL + static1 + '/' + getTid(r1etag2)
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid2);
assert.deepEqual(res.headers.etag, r1etag2);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + static2
});
})
.then(function (res) {
r2tid1 = res.headers.etag;
r2etag1 = res.headers.etag;
assert.deepEqual(res.status, 200);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + static2 + '/' + r2tid1
uri: server.config.bucketURL + static2 + '/' + getTid(r2etag1)
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r2tid1);
assert.deepEqual(res.headers.etag, r2etag1);
hasTextContentType(res);

return preq.get({
uri: server.config.bucketURL + static1
});
})
.then(function (res) {
assert.deepEqual(res.headers.etag, r1tid2);
assert.deepEqual(res.headers.etag, r1etag2);
hasTextContentType(res);
});
});
Expand Down

0 comments on commit 76e4374

Please sign in to comment.