diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 4d7f13adb09..66e234ae908 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -1194,9 +1194,6 @@ nsHttpChannel::ProcessResponse() } MOZ_ASSERT(!mCachedContentIsValid); - if (httpStatus != 304 && httpStatus != 206) { - mCacheInputStream.CloseAndRelease(); - } // notify "http-on-examine-response" observers gHttpHandler->OnExamineResponse(this); @@ -3859,7 +3856,14 @@ nsHttpChannel::InstallCacheListener(PRUint32 offset) LOG(("unable to mark cache entry for compression")); } } - + + LOG(("Trading cache input stream for output stream [channel=%p]", this)); + + // We must close the input stream first because cache entries do not + // correctly handle having an output stream and input streams open at + // the same time. + mCacheInputStream.CloseAndRelease(); + nsCOMPtr out; rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out)); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/test/unit/test_bug770243.js b/netwerk/test/unit/test_bug770243.js new file mode 100644 index 00000000000..0c1d7cff734 --- /dev/null +++ b/netwerk/test/unit/test_bug770243.js @@ -0,0 +1,199 @@ +/* this test does the following: + Always requests the same resource, while for each request getting: + 1. 200 + ETag: "one" + 2. 401 followed by 200 + ETag: "two" + 3. 401 followed by 304 + 4. 407 followed by 200 + ETag: "three" + 5. 407 followed by 304 +*/ + +do_load_httpd_js(); + +var httpserv; + +function addCreds(scheme, host) +{ + var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1'] + .getService(Ci.nsIHttpAuthManager); + authMgr.setAuthIdentity(scheme, host, 4444, "basic", "secret", "/", "", "user", "pass"); +} + +function clearCreds() +{ + var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1'] + .getService(Ci.nsIHttpAuthManager); + authMgr.clearAll(); +} + +function makeChan() { + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var chan = ios.newChannel("http://localhost:4444/", null, null) + .QueryInterface(Ci.nsIHttpChannel); + return chan; +} + +// Array of handlers that are called one by one in response to expected requests + +var handlers = [ + // Test 1 + function(metadata, response) { + do_check_eq(metadata.hasHeader("Authorization"), false); + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("ETag", '"one"', false); + response.setHeader("Cache-control", 'no-cache', false); + response.setHeader("Content-type", 'text/plain', false); + var body = "Response body 1"; + response.bodyOutputStream.write(body, body.length); + }, + + // Test 2 + function(metadata, response) { + do_check_eq(metadata.hasHeader("Authorization"), false); + do_check_eq(metadata.getHeader("If-None-Match"), '"one"'); + response.setStatusLine(metadata.httpVersion, 401, "Authenticate"); + response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); + addCreds("http", "localhost"); + }, + function(metadata, response) { + do_check_eq(metadata.hasHeader("Authorization"), true); + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("ETag", '"two"', false); + response.setHeader("Cache-control", 'no-cache', false); + response.setHeader("Content-type", 'text/plain', false); + var body = "Response body 2"; + response.bodyOutputStream.write(body, body.length); + clearCreds(); + }, + + // Test 3 + function(metadata, response) { + do_check_eq(metadata.hasHeader("Authorization"), false); + do_check_eq(metadata.getHeader("If-None-Match"), '"two"'); + response.setStatusLine(metadata.httpVersion, 401, "Authenticate"); + response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false); + addCreds("http", "localhost"); + }, + function(metadata, response) { + do_check_eq(metadata.hasHeader("Authorization"), true); + do_check_eq(metadata.getHeader("If-None-Match"), '"two"'); + response.setStatusLine(metadata.httpVersion, 304, "OK"); + response.setHeader("ETag", '"two"', false); + clearCreds(); + }, + + // Test 4 + function(metadata, response) { + do_check_eq(metadata.hasHeader("Authorization"), false); + do_check_eq(metadata.getHeader("If-None-Match"), '"two"'); + response.setStatusLine(metadata.httpVersion, 407, "Proxy Authenticate"); + response.setHeader("Proxy-Authenticate", 'Basic realm="secret"', false); + addCreds("http", "localhost"); + }, + function(metadata, response) { + do_check_eq(metadata.hasHeader("Proxy-Authorization"), true); + do_check_eq(metadata.getHeader("If-None-Match"), '"two"'); + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("ETag", '"three"', false); + response.setHeader("Cache-control", 'no-cache', false); + response.setHeader("Content-type", 'text/plain', false); + var body = "Response body 3"; + response.bodyOutputStream.write(body, body.length); + clearCreds(); + }, + + // Test 5 + function(metadata, response) { + do_check_eq(metadata.hasHeader("Proxy-Authorization"), false); + do_check_eq(metadata.getHeader("If-None-Match"), '"three"'); + response.setStatusLine(metadata.httpVersion, 407, "Proxy Authenticate"); + response.setHeader("Proxy-Authenticate", 'Basic realm="secret"', false); + addCreds("http", "localhost"); + }, + function(metadata, response) { + do_check_eq(metadata.hasHeader("Proxy-Authorization"), true); + do_check_eq(metadata.getHeader("If-None-Match"), '"three"'); + response.setStatusLine(metadata.httpVersion, 304, "OK"); + response.setHeader("ETag", '"three"', false); + response.setHeader("Cache-control", 'no-cache', false); + clearCreds(); + } +]; + +function handler(metadata, response) +{ + handlers.shift()(metadata, response); +} + +// Array of tests to run, self-driven + +var tests = [ + // Test 1: 200 (cacheable) + function() { + var ch = makeChan(); + ch.asyncOpen(new ChannelListener(function(req, body) { + do_check_eq(body, "Response body 1"); + tests.shift()(); + }), null); + }, + + // Test 2: 401 and 200 + new content + function() { + var ch = makeChan(); + ch.asyncOpen(new ChannelListener(function(req, body) { + do_check_eq(body, "Response body 2"); + tests.shift()(); + }, null, 0), null); + }, + + // Test 3: 401 and 304 + function() { + var ch = makeChan(); + ch.asyncOpen(new ChannelListener(function(req, body) { + do_check_eq(body, "Response body 2"); + tests.shift()(); + }, null, 0), null); + }, + + // Test 4: 407 and 200 + new content + function() { + var ch = makeChan(); + ch.asyncOpen(new ChannelListener(function(req, body) { + do_check_eq(body, "Response body 3"); + tests.shift()(); + }, null, 0), null); + }, + + // Test 5: 407 and 304 + function() { + var ch = makeChan(); + ch.asyncOpen(new ChannelListener(function(req, body) { + do_check_eq(body, "Response body 3"); + tests.shift()(); + }, null, 0), null); + }, + + // End of test run + function() { + httpserv.stop(do_test_finished); + } +]; + +function run_test() +{ + do_get_profile(); + + httpserv = new nsHttpServer(); + httpserv.registerPathHandler("/", handler); + httpserv.start(4444); + + const prefs = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefBranch); + prefs.setCharPref("network.proxy.http", "localhost"); + prefs.setIntPref("network.proxy.http_port", 4444); + prefs.setCharPref("network.proxy.no_proxies_on", ""); + prefs.setIntPref("network.proxy.type", 1); + + tests.shift()(); + do_test_pending(); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 8e964a9ae77..ddb37340e59 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -87,6 +87,7 @@ fail-if = os == "android" [test_bug667818.js] [test_bug669001.js] [test_bug712914_secinfo_validation.js] +[test_bug770243.js] [test_doomentry.js] [test_cacheflags.js] [test_channel_close.js]