toolkit/modules/Http.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/modules/Http.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,100 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +const EXPORTED_SYMBOLS = ["httpRequest", "percentEncode"];
     1.9 +
    1.10 +const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
    1.11 +
    1.12 +// Strictly follow RFC 3986 when encoding URI components.
    1.13 +// Accepts a unescaped string and returns the URI encoded string for use in
    1.14 +// an HTTP request.
    1.15 +function percentEncode(aString)
    1.16 +  encodeURIComponent(aString).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
    1.17 +
    1.18 +/*
    1.19 + * aOptions can have a variety of fields:
    1.20 + *  headers, an array of headers
    1.21 + *  postData, this can be:
    1.22 + *    a string: send it as is
    1.23 + *    an array of parameters: encode as form values
    1.24 + *    null/undefined: no POST data.
    1.25 + *  method, GET, POST or PUT (this is set automatically if postData exists).
    1.26 + *  onLoad, a function handle to call when the load is complete, it takes two
    1.27 + *          parameters: the responseText and the XHR object.
    1.28 + *  onError, a function handle to call when an error occcurs, it takes three
    1.29 + *          parameters: the error, the responseText and the XHR object.
    1.30 + *  logger, an object that implements the debug and log methods (e.g. log.jsm).
    1.31 + *
    1.32 + * Headers or post data are given as an array of arrays, for each each inner
    1.33 + * array the first value is the key and the second is the value, e.g.
    1.34 + *  [["key1", "value1"], ["key2", "value2"]].
    1.35 + */
    1.36 +function httpRequest(aUrl, aOptions) {
    1.37 +  let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
    1.38 +              .createInstance(Ci.nsIXMLHttpRequest);
    1.39 +  xhr.mozBackgroundRequest = true; // no error dialogs
    1.40 +  xhr.open(aOptions.method || (aOptions.postData ? "POST" : "GET"), aUrl);
    1.41 +  xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS | // don't send cookies
    1.42 +                          Ci.nsIChannel.LOAD_BYPASS_CACHE |
    1.43 +                          Ci.nsIChannel.INHIBIT_CACHING;
    1.44 +  xhr.onerror = function(aProgressEvent) {
    1.45 +    if (aOptions.onError) {
    1.46 +      // adapted from toolkit/mozapps/extensions/nsBlocklistService.js
    1.47 +      let request = aProgressEvent.target;
    1.48 +      let status;
    1.49 +      try {
    1.50 +        // may throw (local file or timeout)
    1.51 +        status = request.status;
    1.52 +      }
    1.53 +      catch (e) {
    1.54 +        request = request.channel.QueryInterface(Ci.nsIRequest);
    1.55 +        status = request.status;
    1.56 +      }
    1.57 +      // When status is 0 we don't have a valid channel.
    1.58 +      let statusText = status ? request.statusText : "offline";
    1.59 +      aOptions.onError(statusText, null, this);
    1.60 +    }
    1.61 +  };
    1.62 +  xhr.onload = function(aRequest) {
    1.63 +    try {
    1.64 +      let target = aRequest.target;
    1.65 +      if (aOptions.logger)
    1.66 +        aOptions.logger.debug("Received response: " + target.responseText);
    1.67 +      if (target.status < 200 || target.status >= 300) {
    1.68 +        let errorText = target.responseText;
    1.69 +        if (!errorText || /<(ht|\?x)ml\b/i.test(errorText))
    1.70 +          errorText = target.statusText;
    1.71 +        throw target.status + " - " + errorText;
    1.72 +      }
    1.73 +      if (aOptions.onLoad)
    1.74 +        aOptions.onLoad(target.responseText, this);
    1.75 +    } catch (e) {
    1.76 +      Cu.reportError(e);
    1.77 +      if (aOptions.onError)
    1.78 +        aOptions.onError(e, aRequest.target.responseText, this);
    1.79 +    }
    1.80 +  };
    1.81 +
    1.82 +  if (aOptions.headers) {
    1.83 +    aOptions.headers.forEach(function(header) {
    1.84 +      xhr.setRequestHeader(header[0], header[1]);
    1.85 +    });
    1.86 +  }
    1.87 +
    1.88 +  // Handle adding postData as defined above.
    1.89 +  let POSTData = aOptions.postData || "";
    1.90 +  if (Array.isArray(POSTData)) {
    1.91 +    xhr.setRequestHeader("Content-Type",
    1.92 +                         "application/x-www-form-urlencoded; charset=utf-8");
    1.93 +    POSTData = POSTData.map(function(p) p[0] + "=" + percentEncode(p[1]))
    1.94 +                       .join("&");
    1.95 +  }
    1.96 +
    1.97 +  if (aOptions.logger) {
    1.98 +    aOptions.logger.log("sending request to " + aUrl + " (POSTData = " +
    1.99 +                        POSTData + ")");
   1.100 +  }
   1.101 +  xhr.send(POSTData);
   1.102 +  return xhr;
   1.103 +}

mercurial