Bug 690170 - Part 1: Fix channel management to avoid nsIHTTPChannel.responseStatus => NS_ERROR_NOT_AVAILABLE in Sync HTTP requests. r=philikon

This commit is contained in:
Richard Newman 2011-10-28 14:43:18 -07:00
parent 6f17eccc6c
commit 6b62127af2
6 changed files with 158 additions and 8 deletions

View File

@ -270,7 +270,7 @@ AsyncResource.prototype = {
_doRequest: function _doRequest(action, data, callback) {
this._log.trace("In _doRequest.");
this._callback = callback;
let channel = this._channel = this._createRequest();
let channel = this._createRequest();
if ("undefined" != typeof(data))
this._data = data;
@ -303,7 +303,7 @@ AsyncResource.prototype = {
channel.asyncOpen(listener, null);
},
_onComplete: function _onComplete(error, data) {
_onComplete: function _onComplete(error, data, channel) {
this._log.trace("In _onComplete. Error is " + error + ".");
if (error) {
@ -312,7 +312,6 @@ AsyncResource.prototype = {
}
this._data = data;
let channel = this._channel;
let action = channel.requestMethod;
this._log.trace("Channel: " + channel);
@ -594,14 +593,14 @@ ChannelListener.prototype = {
let code = statusSuccess ? requestStatus : status;
let message = Components.Exception("", code).name;
let error = Components.Exception(message, code);
this._onComplete(error);
this._onComplete(error, undefined, channel);
return;
}
this._log.trace("Channel: flags = " + channel.loadFlags +
", URI = " + uri +
", HTTP success? " + channel.requestSucceeded);
this._onComplete(null, this._data);
this._onComplete(null, this._data, channel);
},
onDataAvailable: function Channel_onDataAvail(req, cb, stream, off, count) {

View File

@ -370,6 +370,9 @@ RESTRequest.prototype = {
/*** nsIStreamListener ***/
onStartRequest: function onStartRequest(channel) {
// Update the channel in case we got redirected.
this.channel = channel;
if (this.status == this.ABORTED) {
this._log.trace("Not proceeding with onStartRequest, request was aborted.");
return;
@ -394,6 +397,9 @@ RESTRequest.prototype = {
},
onStopRequest: function onStopRequest(channel, context, statusCode) {
// Update the channel in case we got redirected.
this.channel = channel;
if (this.timeoutTimer) {
// Clear the abort timer now that the channel is done.
this.timeoutTimer.clear();

View File

@ -1,3 +1,5 @@
const Cm = Components.manager;
// Shared logging for all HTTP server functions.
Cu.import("resource://services-sync/log4moz.js");
const SYNC_HTTP_LOGGER = "Sync.Test.Server";
@ -999,3 +1001,51 @@ function serverForUsers(users, contents, callback) {
server.start();
return server;
}
/**
* Proxy auth helpers.
*/
/**
* Fake a PAC to prompt a channel replacement.
*/
let PACSystemSettings = {
CID: Components.ID("{5645d2c1-d6d8-4091-b117-fe7ee4027db7}"),
contractID: "@mozilla.org/system-proxy-settings;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory,
Ci.nsISystemProxySettings]),
createInstance: function createInstance(outer, iid) {
if (outer) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(iid);
},
lockFactory: function lockFactory(lock) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
// Replace this URI for each test to avoid caching. We want to ensure that
// each test gets a completely fresh setup.
PACURI: null,
getProxyForURI: function getProxyForURI(aURI) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
};
function installFakePAC() {
_("Installing fake PAC.");
Cm.nsIComponentRegistrar
.registerFactory(PACSystemSettings.CID,
"Fake system proxy-settings",
PACSystemSettings.contractID,
PACSystemSettings);
}
function uninstallFakePAC() {
_("Uninstalling fake PAC.");
let CID = PACSystemSettings.CID;
Cm.nsIComponentRegistrar.unregisterFactory(CID, PACSystemSettings);
}

View File

@ -6,9 +6,11 @@ Cu.import("resource://services-sync/util.js");
let logger;
let fetched = false;
function server_open(metadata, response) {
let body;
if (metadata.method == "GET") {
fetched = true;
body = "This path exists";
response.setStatusLine(metadata.httpVersion, 200, "OK");
} else {
@ -40,6 +42,15 @@ function server_404(metadata, response) {
response.bodyOutputStream.write(body, body.length);
}
let pacFetched = false;
function server_pac(metadata, response) {
pacFetched = true;
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
response.bodyOutputStream.write(body, body.length);
}
let sample_data = {
some: "sample_data",
@ -150,12 +161,26 @@ function run_test() {
"/timestamp": server_timestamp,
"/headers": server_headers,
"/backoff": server_backoff,
"/pac1": server_pac,
"/quota-notice": server_quota_notice,
"/quota-error": server_quota_error
});
Svc.Prefs.set("network.numRetries", 1); // speed up test
// This apparently has to come first in order for our PAC URL to be hit.
// Don't put any other HTTP requests earlier in the file!
_("Testing handling of proxy auth redirection.");
PACSystemSettings.PACURI = "http://localhost:8080/pac1";
installFakePAC();
let proxiedRes = new Resource("http://localhost:8080/open");
let content = proxiedRes.get();
do_check_true(pacFetched);
do_check_true(fetched);
do_check_eq(content, "This path exists");
pacFetched = fetched = false;
uninstallFakePAC();
_("Resource object members");
let res = new Resource("http://localhost:8080/open");
do_check_true(res.uri instanceof Ci.nsIURI);
@ -168,7 +193,7 @@ function run_test() {
do_check_eq(res.data, null);
_("GET a non-password-protected resource");
let content = res.get();
content = res.get();
do_check_eq(content, "This path exists");
do_check_eq(content.status, 200);
do_check_true(content.success);
@ -474,6 +499,5 @@ function run_test() {
let uri2 = Utils.makeURL("http://foo/");
uri2.query = query;
do_check_eq(uri1.query, uri2.query);
server.stop(do_test_finished);
}

View File

@ -6,9 +6,11 @@ Cu.import("resource://services-sync/util.js");
let logger;
let fetched = false;
function server_open(metadata, response) {
let body;
if (metadata.method == "GET") {
fetched = true;
body = "This path exists";
response.setStatusLine(metadata.httpVersion, 200, "OK");
} else {
@ -40,6 +42,15 @@ function server_404(metadata, response) {
response.bodyOutputStream.write(body, body.length);
}
let pacFetched = false;
function server_pac(metadata, response) {
_("Invoked PAC handler.");
pacFetched = true;
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
response.bodyOutputStream.write(body, body.length);
}
let sample_data = {
some: "sample_data",
@ -154,6 +165,7 @@ function run_test() {
"/timestamp": server_timestamp,
"/headers": server_headers,
"/backoff": server_backoff,
"/pac2": server_pac,
"/quota-notice": server_quota_notice,
"/quota-error": server_quota_error
});
@ -162,9 +174,27 @@ function run_test() {
run_next_test();
}
// This apparently has to come first in order for our PAC URL to be hit.
// Don't put any other HTTP requests earlier in the file!
add_test(function test_proxy_auth_redirect() {
_("Ensure that a proxy auth redirect (which switches out our channel) " +
"doesn't break AsyncResource.");
PACSystemSettings.PACURI = "http://localhost:8080/pac2";
installFakePAC();
let res = new AsyncResource("http://localhost:8080/open");
res.get(function (error, result) {
do_check_true(!error);
do_check_true(pacFetched);
do_check_true(fetched);
do_check_eq("This path exists", result);
pacFetched = fetched = false;
uninstallFakePAC();
run_next_test();
});
});
add_test(function test_members() {
_("Resource object memebers");
_("Resource object members");
let res = new AsyncResource("http://localhost:8080/open");
do_check_true(res.uri instanceof Ci.nsIURI);
do_check_eq(res.uri.spec, "http://localhost:8080/open");

View File

@ -41,6 +41,47 @@ add_test(function test_attributes() {
run_next_test();
});
/**
* Verify that a proxy auth redirect doesn't break us. This has to be the first
* request made in the file!
*/
add_test(function test_proxy_auth_redirect() {
let pacFetched = false;
function pacHandler(metadata, response) {
pacFetched = true;
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
response.bodyOutputStream.write(body, body.length);
}
let fetched = false;
function original(metadata, response) {
fetched = true;
let body = "TADA!";
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.bodyOutputStream.write(body, body.length);
}
let server = httpd_setup({
"/original": original,
"/pac3": pacHandler
});
PACSystemSettings.PACURI = "http://localhost:8080/pac3";
installFakePAC();
let res = new RESTRequest("http://localhost:8080/original");
res.get(function (error) {
do_check_true(pacFetched);
do_check_true(fetched);
do_check_true(!error);
do_check_true(this.response.success);
do_check_eq("TADA!", this.response.body);
uninstallFakePAC();
server.stop(run_next_test);
});
});
/**
* Demonstrate API short-hand: create a request and dispatch it immediately.
*/