mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 884319 - Add http.jsm to toolkit for usage by Thunderbird FileLink, Lightning and Instantbird. r=Mossop
This commit is contained in:
parent
d0852d2d3f
commit
f02f38b438
102
toolkit/modules/Http.jsm
Normal file
102
toolkit/modules/Http.jsm
Normal file
@ -0,0 +1,102 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const EXPORTED_SYMBOLS = ["httpRequest", "percentEncode"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
// Strictly follow RFC 3986 when encoding URI components.
|
||||
// Accepts a unescaped string and returns the URI encoded string for use in
|
||||
// an HTTP request.
|
||||
function percentEncode(aString)
|
||||
encodeURIComponent(aString).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
|
||||
|
||||
/*
|
||||
* aOptions can have a variety of fields:
|
||||
* headers, an array of headers
|
||||
* postData, this can be:
|
||||
* a string: send it as is
|
||||
* an array of parameters: encode as form values
|
||||
* null/undefined: no POST data.
|
||||
* method, GET, POST or PUT (this is set automatically if postData exists).
|
||||
* onLoad, a function handle to call when the load is complete, it takes two
|
||||
* parameters: the responseText and the XHR object.
|
||||
* onError, a function handle to call when an error occcurs, it takes three
|
||||
* parameters: the error, the responseText and the XHR object.
|
||||
* logger, an object that implements the debug and log methods (e.g. log4moz).
|
||||
*
|
||||
* Headers or post data are given as an array of arrays, for each each inner
|
||||
* array the first value is the key and the second is the value, e.g.
|
||||
* [["key1", "value1"], ["key2", "value2"]].
|
||||
*/
|
||||
function httpRequest(aUrl, aOptions) {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.mozBackgroundRequest = true; // no error dialogs
|
||||
let hasPostData = "postData" in aOptions;
|
||||
xhr.open("aMethod" in aOptions ? aMethod :
|
||||
(hasPostData ? "POST" : "GET"), aUrl);
|
||||
xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS | // don't send cookies
|
||||
Ci.nsIChannel.LOAD_BYPASS_CACHE |
|
||||
Ci.nsIChannel.INHIBIT_CACHING;
|
||||
xhr.onerror = function(aProgressEvent) {
|
||||
if ("onError" in aOptions) {
|
||||
// adapted from toolkit/mozapps/extensions/nsBlocklistService.js
|
||||
let request = aProgressEvent.target;
|
||||
let status;
|
||||
try {
|
||||
// may throw (local file or timeout)
|
||||
status = request.status;
|
||||
}
|
||||
catch (e) {
|
||||
request = request.channel.QueryInterface(Ci.nsIRequest);
|
||||
status = request.status;
|
||||
}
|
||||
// When status is 0 we don't have a valid channel.
|
||||
let statusText = status ? request.statusText : "offline";
|
||||
aOptions.onError(statusText, null, this);
|
||||
}
|
||||
};
|
||||
xhr.onload = function (aRequest) {
|
||||
try {
|
||||
let target = aRequest.target;
|
||||
if ("logger" in aOptions)
|
||||
aOptions.logger.debug("Received response: " + target.responseText);
|
||||
if (target.status < 200 || target.status >= 300) {
|
||||
let errorText = target.responseText;
|
||||
if (!errorText || /<(ht|\?x)ml\b/i.test(errorText))
|
||||
errorText = target.statusText;
|
||||
throw target.status + " - " + errorText;
|
||||
}
|
||||
if ("onLoad" in aOptions)
|
||||
aOptions.onLoad(target.responseText, this);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
if ("onError" in aOptions)
|
||||
aOptions.onError(e, aRequest.target.responseText, this);
|
||||
}
|
||||
};
|
||||
|
||||
if ("headers" in aOptions) {
|
||||
aOptions.headers.forEach(function(header) {
|
||||
xhr.setRequestHeader(header[0], header[1]);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle adding postData as defined above.
|
||||
let POSTData = hasPostData ? aOptions.postData : "";
|
||||
if (Array.isArray(POSTData)) {
|
||||
xhr.setRequestHeader("Content-Type",
|
||||
"application/x-www-form-urlencoded; charset=utf-8");
|
||||
POSTData = POSTData.map(function(p) p[0] + "=" + percentEncode(p[1]))
|
||||
.join("&");
|
||||
}
|
||||
|
||||
if ("logger" in aOptions) {
|
||||
aOptions.logger.log("sending request to " + aUrl + " (POSTData = " +
|
||||
POSTData + ")");
|
||||
}
|
||||
xhr.send(POSTData);
|
||||
return xhr;
|
||||
}
|
@ -12,6 +12,7 @@ EXTRA_JS_MODULES += [
|
||||
'Dict.jsm',
|
||||
'FileUtils.jsm',
|
||||
'Geometry.jsm',
|
||||
'Http.jsm',
|
||||
'InlineSpellChecker.jsm',
|
||||
'NewTabUtils.jsm',
|
||||
'PageMenu.jsm',
|
||||
|
109
toolkit/modules/tests/xpcshell/test_Http.js
Normal file
109
toolkit/modules/tests/xpcshell/test_Http.js
Normal file
@ -0,0 +1,109 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Http.jsm");
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
|
||||
const BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream", "setInputStream");
|
||||
|
||||
var server;
|
||||
|
||||
const kDefaultServerPort = 9000;
|
||||
const kSuccessPath = "/success";
|
||||
const kPostPath = "/post";
|
||||
const kBaseUrl = "http://localhost:" + kDefaultServerPort;
|
||||
const kSuccessUrl = kBaseUrl + kSuccessPath;
|
||||
const kPostUrl = kBaseUrl + kPostPath;
|
||||
|
||||
function successResult(aRequest, aResponse) {
|
||||
aResponse.setStatusLine(null, 200, "OK");
|
||||
aResponse.setHeader("Content-Type", "application/json");
|
||||
aResponse.write("Success!");
|
||||
}
|
||||
|
||||
function checkData(aRequest, aResponse) {
|
||||
let body = new BinaryInputStream(aRequest.bodyInputStream);
|
||||
let bytes = [];
|
||||
let avail;
|
||||
while ((avail = body.available()) > 0)
|
||||
Array.prototype.push.apply(bytes, body.readByteArray(avail));
|
||||
|
||||
do_check_eq(aRequest.method, "POST");
|
||||
|
||||
var data = String.fromCharCode.apply(null, bytes);
|
||||
|
||||
do_check_eq(data, "foo=bar&complex=%21%2A%28%29%40");
|
||||
|
||||
aResponse.setStatusLine(null, 200, "OK");
|
||||
aResponse.setHeader("Content-Type", "application/json");
|
||||
aResponse.write("Success!");
|
||||
}
|
||||
|
||||
add_test(function test_successCallback() {
|
||||
do_test_pending();
|
||||
let options = {
|
||||
onLoad: function(aResponse) {
|
||||
do_check_eq(aResponse, "Success!");
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
},
|
||||
onError: function(e) {
|
||||
do_check_true(false);
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
}
|
||||
httpRequest(kSuccessUrl, options);
|
||||
});
|
||||
|
||||
add_test(function test_errorCallback() {
|
||||
do_test_pending();
|
||||
let options = {
|
||||
onSuccess: function(aResponse) {
|
||||
do_check_true(false);
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
},
|
||||
onError: function(e, aResponse) {
|
||||
do_check_eq(e, "404 - Not Found");
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
}
|
||||
}
|
||||
httpRequest(kBaseUrl + "/failure", options);
|
||||
});
|
||||
|
||||
add_test(function test_PostData() {
|
||||
do_test_pending();
|
||||
let options = {
|
||||
onLoad: function(aResponse) {
|
||||
do_check_eq(aResponse, "Success!");
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
},
|
||||
onError: function(e) {
|
||||
do_check_true(false);
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
},
|
||||
postData: [["foo", "bar"], ["complex", "!*()@"]]
|
||||
}
|
||||
httpRequest(kPostUrl, options);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
// Set up a mock HTTP server to serve a success page.
|
||||
server = new HttpServer();
|
||||
server.registerPathHandler(kSuccessPath, successResult);
|
||||
server.registerPathHandler(kPostPath, checkData);
|
||||
server.start(kDefaultServerPort);
|
||||
|
||||
run_next_test();
|
||||
|
||||
// Teardown.
|
||||
do_register_cleanup(function() {
|
||||
server.stop(function() { });
|
||||
});
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ tail =
|
||||
|
||||
[test_dict.js]
|
||||
[test_FileUtils.js]
|
||||
[test_Http.js]
|
||||
[test_Preferences.js]
|
||||
[test_Promise.js]
|
||||
[test_propertyListsUtils.js]
|
||||
|
Loading…
Reference in New Issue
Block a user