From 5e88bb97d50c1ee32f60aa9ae062a101781de97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Sun, 22 May 2011 02:46:39 +0200 Subject: [PATCH] Make cached parameter for making a resource explicitly cached. Attempt to fix https://bugzilla.mozilla.org/show_bug.cgi?id=655749 (I wonder how much this cache is different from Firefox own caching). --- packages/addon-kit/lib/request.js | 92 +++++++++++++++++++++++++++++++++++-- 1 files changed, 87 insertions(+), 5 deletions(-) diff --git a/packages/addon-kit/lib/request.js b/packages/addon-kit/lib/request.js index 50da22a..f93716d 100644 --- a/packages/addon-kit/lib/request.js +++ b/packages/addon-kit/lib/request.js @@ -39,6 +39,8 @@ const xpcom = require("xpcom"); const xhr = require("xhr"); const errors = require("errors"); const apiUtils = require("api-utils"); +const ss = require("simple-storage"); + // Ugly but will fix with: https://bugzilla.mozilla.org/show_bug.cgi?id=596248 const EventEmitter = require('events').EventEmitter.compose({ @@ -62,6 +64,10 @@ const validator = new OptionsValidator({ contentType: { map: function (v) v || "application/x-www-form-urlencoded", is: ["string"] + }, + cached: { + map: function (v) v || false, + is: ["boolean"] } }); @@ -74,9 +80,9 @@ function Request(options) { // request will hold the actual XHR object let request; let response; - - if ('onComplete' in options) - self.on('complete', options.onComplete) + if ('onComplete' in options) { + self.on('complete', options.onComplete); + } options = validator.validateOptions(options); // function to prep the request since it's the same between GET and POST @@ -86,6 +92,17 @@ function Request(options) { throw new Error(REUSE_ERROR); } + var _recordingCache = (typeof options.recording === "undefined") ? false : options.recording; + + if (options.cached && !("httpRequestCache" in ss.storage)) { + ss.storage.httpRequestCache = {}; + } + + if (options.cached) { + makeCachedRequest(mode); + return; + } + request = new xhr.XMLHttpRequest(); let url = options.url; @@ -111,19 +128,84 @@ function Request(options) { // handle the readystate, create the response, and call the callback request.onreadystatechange = function () { if (request.readyState == 4) { + if (_recordingCache && request.status == 200) { + // index by unmodified URL we don't want to index by data + ss.storage.httpRequestCache[options.url] = { + responseText: request.responseText, + lastModified: new Date(request.getResponseHeader("Last-Modified")), + eTag: request.getResponseHeader("ETag"), + allResponseHeaders: request.getAllResponseHeaders() + }; + } response = new Response(request); errors.catchAndLog(function () { self._emit('complete', response); })(); } - } + }; // actually send the request. we only want to send data on POST requests request.send(mode == "POST" ? data : null); } + /** + * return simulated request object or null, if the request is not cached + */ + function makeCachedRequest(_mode) { + // request must provide in emitting "complete" at least these properties: + // request.responseText, request.status, request.statusText, + // and request.getAllResponseHeaders() + if (ss.storage.httpRequestCache && (options.url in ss.storage.httpRequestCache)) { + var cache = ss.storage.httpRequestCache[options.url]; + + // Randomize URL to avoid caching + // TODO see https://fedorahosted.org/bugzilla-triage-scripts/ticket/21 + // for more thorough discussion and possible further improvement + options.url += (options.url.match(/\?/) == null ? "?" : "&") + (new Date()).getTime(); + + // check HEAD request and if (etag != cachedETag) && (last-modified > cachedLastModified) + // recursive call, but with options.cached set to false + var _headRequest = new xhr.XMLHttpRequest(); + _headRequest.open("HEAD", options.url, true); + _headRequest.onreadystatechange=function() { + if (_headRequest.readyState==4) { + var _curETag = _headRequest.getResponseHeader("ETag"); + var _curLastModified = new Date(_headRequest.getResponseHeader("Last-Modified")); + console.log("_curLastModified = " + _curLastModified); + console.log("cache.lastModified = " + cache.lastModified); + if ((_curETag == cache.eTag) || (_curLastModified <= cache.lastModified)) { + console.log("Loading from cache!"); + // create false request with cached values + var fake_response = new Response({ + responseText: cache.responseText, + status: 200, // could we have anything else than 200? + statusText: "200 OK", + getAllResponseHeaders: function() { + return cache.allResponseHeaders; + } + }); + errors.catchAndLog(function () { + self._emit('complete', fake_response); + })(); + } else { // cache is not up-to-date + console.log("Too old cache!"); + options.cached = false; + options.recording = true; + makeRequest(_mode); + } + } + } + _headRequest.send(null); + } else { // We don't have this URL in cache at all + console.log("Not cached at all!"); + options.cached = false; + options.recording = true; + makeRequest(_mode); + } + } + // Map these setters/getters to the options - ["url", "headers", "content", "contentType"].forEach(function (k) { + ["url", "headers", "cached", "content", "contentType"].forEach(function (k) { _public.__defineGetter__(k, function () options[k]); _public.__defineSetter__(k, function (v) { // This will automatically rethrow errors from apiUtils.validateOptions. -- 1.7.6.1