Skip to content

Commit

Permalink
Interpret 'If-None-Match: *' correctly.
Browse files Browse the repository at this point in the history
  • Loading branch information
jcoglan committed Apr 6, 2015
1 parent 9c518d5 commit b24d0fa
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 12 deletions.
2 changes: 1 addition & 1 deletion lib/controllers/storage.js
Expand Up @@ -142,7 +142,7 @@ Storage.prototype.getVersion = function() {
ifNone = headers['if-none-match'];

if (ifMatch) return parseInt(ifMatch.match(/\d+/)[0], 10);
if (ifNone) return parseInt(ifNone.match(/\d+/)[0], 10);
if (ifNone) return ifNone === '*' ? '*' : parseInt(ifNone.match(/\d+/)[0], 10);

return null;
};
Expand Down
7 changes: 4 additions & 3 deletions lib/stores/file_tree.js
Expand Up @@ -219,7 +219,7 @@ FileTree.prototype.put = function(username, pathname, type, value, version, call
var dataPath = path.join(self._dir, self.dataPath(username, pathname)),
metaPath = path.join(self._dir, self.metaPath(username, pathname));

self.isCurrentVersion(dataPath, version, function(error, current) {
self.getCurrentState(dataPath, version, function(error, current) {
if (error || !current) {
release();
return callback(error, null, null, true);
Expand Down Expand Up @@ -269,7 +269,7 @@ FileTree.prototype._delete = function(username, pathname, version, callback) {
var dataPath = path.join(this._dir, this.dataPath(username, pathname)),
metaPath = path.join(this._dir, this.metaPath(username, pathname));

this.isCurrentVersion(dataPath, version, function(error, current, modified) {
this.getCurrentState(dataPath, version, function(error, current, modified) {
if (error || !current)
return callback(false, null, !current);

Expand Down Expand Up @@ -340,10 +340,11 @@ FileTree.prototype.writeFile = function(filename, writer) {
});
};

FileTree.prototype.isCurrentVersion = function(fullPath, version, callback) {
FileTree.prototype.getCurrentState = function(fullPath, version, callback) {
fs.stat(fullPath, function(error, stat) {
var mtime = stat && new Date(stat.mtime.getTime()).getTime();
if (!version) return callback(null, true, mtime);
if (version === '*') return callback(null, !mtime);
if (error) return callback(null, false);
callback(null, mtime === version, mtime);
});
Expand Down
17 changes: 9 additions & 8 deletions lib/stores/redis.js
Expand Up @@ -211,7 +211,7 @@ RedisStore.prototype.put = function(username, pathname, type, value, version, ca
dataKey = this._ns + 'users:' + username + ':data:' + pathname;

this._lock(username, function(release) {
self.getCurrentState(client, dataKey, version, function(error, current, mtime, exists) {
self.getCurrentState(client, dataKey, version, function(error, current, mtime) {
if (error || !current) {
release();
return callback(error, false, null, !current);
Expand All @@ -230,7 +230,7 @@ RedisStore.prototype.put = function(username, pathname, type, value, version, ca

multi.exec(function(error) {
release();
callback(error, !exists, parseInt(modified, 10));
callback(error, !mtime, parseInt(modified, 10));
});
});
});
Expand All @@ -245,7 +245,7 @@ RedisStore.prototype.delete = function(username, pathname, version, callback) {
dataKey = prefix + pathname;

this._lock(username, function(release) {
self.getCurrentState(client, dataKey, version, function(error, current, mtime, exists) {
self.getCurrentState(client, dataKey, version, function(error, current, mtime) {
if (error || !current) {
release();
return callback(error, false, null, !current);
Expand Down Expand Up @@ -310,20 +310,21 @@ RedisStore.prototype.delete = function(username, pathname, version, callback) {
}
], function(error) {
release();
callback(error, exists, mtime);
callback(error, !!mtime, mtime);
});
});
});
};

RedisStore.prototype.getCurrentState = function(client, dataKey, version, callback) {
client.hget(dataKey, 'modified', function(error, modified) {
if (error || !modified) return callback(error, !version, null, false);
if (error) return callback(error, !version, null);

var mtime = parseInt(modified, 10);
if (!version) return callback(null, true, mtime, true);
var mtime = modified && parseInt(modified, 10);
if (!version) return callback(null, true, mtime);
if (version === '*') return callback(null, !mtime);

callback(null, mtime === version, mtime, true);
callback(null, mtime === version, mtime);
});
};

Expand Down
6 changes: 6 additions & 0 deletions spec/restore/storage_spec.js
Expand Up @@ -284,6 +284,12 @@ JS.Test.describe("Storage", function() { with(this) {
put( "/storage/zebcoe/locog/seats", "a value" )
}})

it("tells the store to create a value conditionally based on If-None-Match", function() { with(this) {
expect(store, "put").given("zebcoe", "/locog/seats", "text/plain", buffer("a value"), "*").yielding([null])
header( "If-None-Match", "*" )
put( "/storage/zebcoe/locog/seats", "a value" )
}})

it("tells the store to save a value conditionally based on If-Match", function() { with(this) {
expect(store, "put").given("zebcoe", "/locog/seats", "text/plain", buffer("a value"), modifiedTimestamp).yielding([null])
header( "If-Match", '"' + modifiedTimestamp + '"' )
Expand Down
16 changes: 16 additions & 0 deletions spec/store_spec.js
Expand Up @@ -273,6 +273,14 @@ JS.Test.describe("Stores", function() { with(this) {
})
}})

it("sets the value if * is given for a non-existent item", function(resume) { with(this) {
store.put("boris", "/photos/zipwire", "image/poster", buffer("vertibo"), "*", function() {
store.get("boris", "/photos/zipwire", null, function(error, item) {
resume(function() { assertEqual( buffer("vertibo"), item.value ) })
})
})
}})

it("sets the value if the given version is current", function(resume) { with(this) {
store.put("boris", "/photos/election", "image/jpeg", buffer("mayor"), date, function() {
store.get("boris", "/photos/election", null, function(error, item) {
Expand All @@ -288,6 +296,14 @@ JS.Test.describe("Stores", function() { with(this) {
})
})
}})

it("does not set the value if * is given for an existing item", function(resume) { with(this) {
store.put("boris", "/photos/election", "image/jpeg", buffer("mayor"), "*", function() {
store.get("boris", "/photos/election", null, function(error, item) {
resume(function() { assertEqual( buffer("hair"), item.value ) })
})
})
}})

it("returns false with no conflict when the given version is current", function(resume) { with(this) {
store.put("boris", "/photos/election", "image/jpeg", buffer("mayor"), date, function(error, created, modified, conflict) {
Expand Down

0 comments on commit b24d0fa

Please sign in to comment.