Bug 599725 - web console reports data which the server did not send (304 Not Modified); f=rcampbell,pwalton r=dolske a=final+hardblocker
authorMihai Sucan <msucan@mozilla.com>
Tue, 11 Jan 2011 14:34:39 -0600
changeset 60316 635f0075c1840533991994bcdfe381dea43648f5
parent 60315 14752eafce65b2c01f7ca246b56fbd846d233217
child 60317 985c54525bc0c39d7e624c68ca2966bd4456b804
push idunknown
push userunknown
push dateunknown
reviewersdolske, final
bugs599725
milestone2.0b10pre
Bug 599725 - web console reports data which the server did not send (304 Not Modified); f=rcampbell,pwalton r=dolske a=final+hardblocker
toolkit/components/console/hudservice/HUDService.jsm
toolkit/components/console/hudservice/tests/browser/Makefile.in
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_599725_response_headers.js
toolkit/components/console/hudservice/tests/browser/test-bug-599725-response-headers.sjs
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -371,25 +371,40 @@ ResponseListener.prototype =
    */
   onStopRequest: function RL_onStopRequest(aRequest, aContext, aStatusCode)
   {
     try {
     this.originalListener.onStopRequest(aRequest, aContext, aStatusCode);
     }
     catch (ex) { }
 
-    this.setResponseHeader(aRequest);
-
     if (HUDService.saveRequestAndResponseBodies) {
       this.httpActivity.response.body = this.receivedData;
     }
     else {
       this.httpActivity.response.bodyDiscarded = true;
     }
 
+    // Retrieve the response headers, as they are, from the server.
+    let response = null;
+    for each (let item in HUDService.openResponseHeaders) {
+      if (item.channel === aRequest) {
+        response = item;
+        break;
+      }
+    }
+
+    if (response) {
+      this.httpActivity.response.header = response.headers;
+      delete HUDService.openResponseHeaders[response.id];
+    }
+    else {
+      this.setResponseHeader(aRequest);
+    }
+
     if (HUDService.lastFinishedRequestCallback) {
       HUDService.lastFinishedRequestCallback(this.httpActivity);
     }
 
     // Call update on all panels.
     this.httpActivity.panels.forEach(function(weakRef) {
       let panel = weakRef.get();
       if (panel) {
@@ -1687,16 +1702,22 @@ HUD_SERVICE.prototype =
    *
    * @returns void
    */
   suspend: function HS_suspend()
   {
     activityDistributor.removeObserver(this.httpObserver);
     delete this.httpObserver;
 
+    Services.obs.removeObserver(this.httpResponseExaminer,
+                                "http-on-examine-response");
+
+    this.openRequests = {};
+    this.openResponseHeaders = {};
+
     // delete the storage as it holds onto channels
     delete this.storage;
     delete this.defaultFilterPrefs;
     delete this.defaultGlobalConsolePrefs;
 
     HUDWindowObserver.uninit();
     HUDConsoleObserver.uninit();
     ConsoleAPIObserver.shutdown();
@@ -1942,16 +1963,21 @@ HUD_SERVICE.prototype =
   },
 
   /**
    * Requests that haven't finished yet.
    */
   openRequests: {},
 
   /**
+   * Response headers for requests that haven't finished yet.
+   */
+  openResponseHeaders: {},
+
+  /**
    * Assign a function to this property to listen for finished httpRequests.
    * Used by unit tests.
    */
   lastFinishedRequestCallback: null,
 
   /**
    * Opens a NetworkPanel.
    *
@@ -2256,16 +2282,68 @@ HUD_SERVICE.prototype =
         0x804b000a: "STATUS_WAITING_FOR",
         0x804b0006: "STATUS_RECEIVING_FROM"
       }
     };
 
     this.httpObserver = httpObserver;
 
     activityDistributor.addObserver(httpObserver);
+
+    // This is used to find the correct HTTP response headers.
+    Services.obs.addObserver(this.httpResponseExaminer,
+                             "http-on-examine-response", false);
+  },
+
+  /**
+   * Observe notifications for the http-on-examine-response topic, coming from
+   * the nsIObserver service.
+   *
+   * @param string aTopic
+   * @param nsIHttpChannel aSubject
+   * @returns void
+   */
+  httpResponseExaminer: function HS_httpResponseExaminer(aSubject, aTopic)
+  {
+    if (aTopic != "http-on-examine-response" ||
+        !(aSubject instanceof Ci.nsIHttpChannel)) {
+      return;
+    }
+
+    let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+    let win = NetworkHelper.getWindowForRequest(channel);
+    if (!win) {
+      return;
+    }
+    let hudId = HUDService.getHudIdByWindow(win);
+    if (!hudId) {
+      return;
+    }
+
+    let response = {
+      id: HUDService.sequenceId(),
+      hudId: hudId,
+      channel: channel,
+      headers: {},
+    };
+
+    try {
+      channel.visitResponseHeaders({
+        visitHeader: function(aName, aValue) {
+          response.headers[aName] = aValue;
+        }
+      });
+    }
+    catch (ex) {
+      delete response.headers;
+    }
+
+    if (response.headers) {
+      HUDService.openResponseHeaders[response.id] = response;
+    }
   },
 
   /**
    * Logs network activity.
    *
    * @param object aActivityObject
    *        The activity to log.
    * @returns void
--- a/toolkit/components/console/hudservice/tests/browser/Makefile.in
+++ b/toolkit/components/console/hudservice/tests/browser/Makefile.in
@@ -110,16 +110,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_bug_597460_filter_scroll.js \
 	browser_webconsole_console_extras.js \
 	browser_webconsole_bug_598357_jsterm_output.js \
 	browser_webconsole_bug_603750_websocket.js \
 	browser_webconsole_abbreviate_source_url.js \
 	browser_webconsole_view_source.js \
 	browser_webconsole_bug_602572_log_bodies_checkbox.js \
 	browser_webconsole_bug_614793_jsterm_scroll.js \
+	browser_webconsole_bug_599725_response_headers.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	test-console.html \
 	test-network.html \
 	test-network-request.html \
 	test-mutation.html \
@@ -173,15 +174,16 @@ include $(topsrcdir)/config/rules.mk
 	test-bug-597756-reopen-closed-tab.html \
 	test-bug-600183-charset.html \
 	test-bug-600183-charset.html^headers^ \
 	test-bug-601177-log-levels.html \
 	test-bug-601177-log-levels.js \
 	test-console-extras.html \
 	test-bug-603750-websocket.html \
 	test-bug-603750-websocket.js \
+	test-bug-599725-response-headers.sjs \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_599725_response_headers.js
@@ -0,0 +1,48 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Contributor(s):
+ *  Mihai Șucan <mihai.sucan@gmail.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-bug-599725-response-headers.sjs";
+
+let lastFinishedRequest = null;
+
+function requestDoneCallback(aHttpRequest)
+{
+  lastFinishedRequest = aHttpRequest;
+}
+
+function performTest()
+{
+  ok(lastFinishedRequest, "page load was logged");
+
+  let headers = lastFinishedRequest.response.header;
+  ok(headers, "we have the response headers");
+  ok(!headers["Content-Type"], "we do not have the Content-Type header");
+  ok(headers["Content-Length"] != 60, "Content-Length != 60");
+
+  lastFinishedRequest = null;
+  HUDService.lastFinishedRequestCallback = null;
+  finishTest();
+}
+
+function test()
+{
+  addTab(TEST_URI);
+
+  browser.addEventListener("load", function(aEvent) {
+    browser.removeEventListener(aEvent.type, arguments.callee, true);
+
+    openConsole();
+
+    HUDService.lastFinishedRequestCallback = requestDoneCallback;
+
+    browser.addEventListener("load", performTest, true);
+    content.location.reload();
+  }, true);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/test-bug-599725-response-headers.sjs
@@ -0,0 +1,25 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(request, response)
+{
+  var Etag = '"4c881ab-b03-435f0a0f9ef00"';
+  var IfNoneMatch = request.hasHeader("If-None-Match")
+                    ? request.getHeader("If-None-Match")
+                    : "";
+
+  var page = "<!DOCTYPE html><html><body><p>hello world!</p></body></html>";
+
+  response.setHeader("Etag", Etag, false);
+
+  if (IfNoneMatch == Etag) {
+    response.setStatusLine(request.httpVersion, "304", "Not Modified");
+  }
+  else {
+    response.setHeader("Content-Type", "text/html", false);
+    response.setHeader("Content-Length", page.length + "", false);
+    response.write(page);
+  }
+}