Bug 1669129 - [devtools] Enable devtools.overflow.debugging.enabled. r=jdescottes
[gecko.git] / toolkit / modules / Http.jsm
blob812da61baf127f1674f9f8d35f4efd2189ad7aae
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const EXPORTED_SYMBOLS = ["httpRequest", "percentEncode"];
7 // Strictly follow RFC 3986 when encoding URI components.
8 // Accepts a unescaped string and returns the URI encoded string for use in
9 // an HTTP request.
10 function percentEncode(aString) {
11   return encodeURIComponent(aString)
12     .replace(/[!'()]/g, escape)
13     .replace(/\*/g, "%2A");
17  * aOptions can have a variety of fields:
18  *  headers, an array of headers
19  *  postData, this can be:
20  *    a string: send it as is
21  *    an array of parameters: encode as form values
22  *    null/undefined: no POST data.
23  *  method, GET, POST or PUT (this is set automatically if postData exists).
24  *  onLoad, a function handle to call when the load is complete, it takes two
25  *          parameters: the responseText and the XHR object.
26  *  onError, a function handle to call when an error occcurs, it takes three
27  *          parameters: the error, the responseText and the XHR object.
28  *  logger, an object that implements the debug and log methods (e.g. log.jsm).
29  *
30  * Headers or post data are given as an array of arrays, for each each inner
31  * array the first value is the key and the second is the value, e.g.
32  *  [["key1", "value1"], ["key2", "value2"]].
33  */
34 function httpRequest(aUrl, aOptions) {
35   let xhr = new XMLHttpRequest();
36   xhr.mozBackgroundRequest = true; // no error dialogs
37   xhr.open(aOptions.method || (aOptions.postData ? "POST" : "GET"), aUrl);
38   xhr.channel.loadFlags =
39     Ci.nsIChannel.LOAD_ANONYMOUS | // don't send cookies
40     Ci.nsIChannel.LOAD_BYPASS_CACHE |
41     Ci.nsIChannel.INHIBIT_CACHING;
42   xhr.onerror = function(aProgressEvent) {
43     if (aOptions.onError) {
44       // adapted from toolkit/mozapps/extensions/nsBlocklistService.js
45       let request = aProgressEvent.target;
46       let status;
47       try {
48         // may throw (local file or timeout)
49         status = request.status;
50       } catch (e) {
51         request = request.channel.QueryInterface(Ci.nsIRequest);
52         status = request.status;
53       }
54       // When status is 0 we don't have a valid channel.
55       let statusText = status ? request.statusText : "offline";
56       aOptions.onError(statusText, null, this);
57     }
58   };
59   xhr.onload = function(aRequest) {
60     try {
61       let target = aRequest.target;
62       if (aOptions.logger) {
63         aOptions.logger.debug("Received response: " + target.responseText);
64       }
65       if (target.status < 200 || target.status >= 300) {
66         let errorText = target.responseText;
67         if (!errorText || /<(ht|\?x)ml\b/i.test(errorText)) {
68           errorText = target.statusText;
69         }
70         throw new Error(target.status + " - " + errorText);
71       }
72       if (aOptions.onLoad) {
73         aOptions.onLoad(target.responseText, this);
74       }
75     } catch (e) {
76       if (aOptions.onError) {
77         aOptions.onError(e, aRequest.target.responseText, this);
78       }
79     }
80   };
82   if (aOptions.headers) {
83     aOptions.headers.forEach(function(header) {
84       xhr.setRequestHeader(header[0], header[1]);
85     });
86   }
88   // Handle adding postData as defined above.
89   let POSTData = aOptions.postData || null;
90   if (POSTData && Array.isArray(POSTData)) {
91     xhr.setRequestHeader(
92       "Content-Type",
93       "application/x-www-form-urlencoded; charset=utf-8"
94     );
95     POSTData = POSTData.map(p => p[0] + "=" + percentEncode(p[1])).join("&");
96   }
98   if (aOptions.logger) {
99     aOptions.logger.log(
100       "sending request to " + aUrl + " (POSTData = " + POSTData + ")"
101     );
102   }
103   xhr.send(POSTData);
104   return xhr;