From 5e88bb97d50c1ee32f60aa9ae062a101781de97c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= <mcepl@redhat.com>
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