1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/NetUtil.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,329 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: sw=4 ts=4 sts=4 et filetype=javascript 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +this.EXPORTED_SYMBOLS = [ 1.11 + "NetUtil", 1.12 +]; 1.13 + 1.14 +/** 1.15 + * Necko utilities 1.16 + */ 1.17 + 1.18 +//////////////////////////////////////////////////////////////////////////////// 1.19 +//// Constants 1.20 + 1.21 +const Ci = Components.interfaces; 1.22 +const Cc = Components.classes; 1.23 +const Cr = Components.results; 1.24 +const Cu = Components.utils; 1.25 + 1.26 +const PR_UINT32_MAX = 0xffffffff; 1.27 + 1.28 +//////////////////////////////////////////////////////////////////////////////// 1.29 +//// NetUtil Object 1.30 + 1.31 +this.NetUtil = { 1.32 + /** 1.33 + * Function to perform simple async copying from aSource (an input stream) 1.34 + * to aSink (an output stream). The copy will happen on some background 1.35 + * thread. Both streams will be closed when the copy completes. 1.36 + * 1.37 + * @param aSource 1.38 + * The input stream to read from 1.39 + * @param aSink 1.40 + * The output stream to write to 1.41 + * @param aCallback [optional] 1.42 + * A function that will be called at copy completion with a single 1.43 + * argument: the nsresult status code for the copy operation. 1.44 + * 1.45 + * @return An nsIRequest representing the copy operation (for example, this 1.46 + * can be used to cancel the copying). The consumer can ignore the 1.47 + * return value if desired. 1.48 + */ 1.49 + asyncCopy: function NetUtil_asyncCopy(aSource, aSink, 1.50 + aCallback = null) 1.51 + { 1.52 + if (!aSource || !aSink) { 1.53 + let exception = new Components.Exception( 1.54 + "Must have a source and a sink", 1.55 + Cr.NS_ERROR_INVALID_ARG, 1.56 + Components.stack.caller 1.57 + ); 1.58 + throw exception; 1.59 + } 1.60 + 1.61 + // make a stream copier 1.62 + var copier = Cc["@mozilla.org/network/async-stream-copier;1"]. 1.63 + createInstance(Ci.nsIAsyncStreamCopier2); 1.64 + copier.init(aSource, aSink, 1.65 + null /* Default event target */, 1.66 + 0 /* Default length */, 1.67 + true, true /* Auto-close */); 1.68 + 1.69 + var observer; 1.70 + if (aCallback) { 1.71 + observer = { 1.72 + onStartRequest: function(aRequest, aContext) {}, 1.73 + onStopRequest: function(aRequest, aContext, aStatusCode) { 1.74 + aCallback(aStatusCode); 1.75 + } 1.76 + } 1.77 + } else { 1.78 + observer = null; 1.79 + } 1.80 + 1.81 + // start the copying 1.82 + copier.QueryInterface(Ci.nsIAsyncStreamCopier).asyncCopy(observer, null); 1.83 + return copier; 1.84 + }, 1.85 + 1.86 + /** 1.87 + * Asynchronously opens a source and fetches the response. A source can be 1.88 + * an nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream. The 1.89 + * provided callback will get an input stream containing the response, the 1.90 + * result code, and a reference to the request. 1.91 + * 1.92 + * @param aSource 1.93 + * The nsIURI, nsIFile, string spec, nsIChannel, or nsIInputStream 1.94 + * to open. 1.95 + * @param aCallback 1.96 + * The callback function that will be notified upon completion. It 1.97 + * will get two arguments: 1.98 + * 1) An nsIInputStream containing the data from aSource, if any. 1.99 + * 2) The status code from opening the source. 1.100 + * 3) Reference to the nsIRequest. 1.101 + */ 1.102 + asyncFetch: function NetUtil_asyncOpen(aSource, aCallback) 1.103 + { 1.104 + if (!aSource || !aCallback) { 1.105 + let exception = new Components.Exception( 1.106 + "Must have a source and a callback", 1.107 + Cr.NS_ERROR_INVALID_ARG, 1.108 + Components.stack.caller 1.109 + ); 1.110 + throw exception; 1.111 + } 1.112 + 1.113 + // Create a pipe that will create our output stream that we can use once 1.114 + // we have gotten all the data. 1.115 + let pipe = Cc["@mozilla.org/pipe;1"]. 1.116 + createInstance(Ci.nsIPipe); 1.117 + pipe.init(true, true, 0, PR_UINT32_MAX, null); 1.118 + 1.119 + // Create a listener that will give data to the pipe's output stream. 1.120 + let listener = Cc["@mozilla.org/network/simple-stream-listener;1"]. 1.121 + createInstance(Ci.nsISimpleStreamListener); 1.122 + listener.init(pipe.outputStream, { 1.123 + onStartRequest: function(aRequest, aContext) {}, 1.124 + onStopRequest: function(aRequest, aContext, aStatusCode) { 1.125 + pipe.outputStream.close(); 1.126 + aCallback(pipe.inputStream, aStatusCode, aRequest); 1.127 + } 1.128 + }); 1.129 + 1.130 + // Input streams are handled slightly differently from everything else. 1.131 + if (aSource instanceof Ci.nsIInputStream) { 1.132 + let pump = Cc["@mozilla.org/network/input-stream-pump;1"]. 1.133 + createInstance(Ci.nsIInputStreamPump); 1.134 + pump.init(aSource, -1, -1, 0, 0, true); 1.135 + pump.asyncRead(listener, null); 1.136 + return; 1.137 + } 1.138 + 1.139 + let channel = aSource; 1.140 + if (!(channel instanceof Ci.nsIChannel)) { 1.141 + channel = this.newChannel(aSource); 1.142 + } 1.143 + 1.144 + try { 1.145 + channel.asyncOpen(listener, null); 1.146 + } 1.147 + catch (e) { 1.148 + let exception = new Components.Exception( 1.149 + "Failed to open input source '" + channel.originalURI.spec + "'", 1.150 + e.result, 1.151 + Components.stack.caller, 1.152 + aSource, 1.153 + e 1.154 + ); 1.155 + throw exception; 1.156 + } 1.157 + }, 1.158 + 1.159 + /** 1.160 + * Constructs a new URI for the given spec, character set, and base URI, or 1.161 + * an nsIFile. 1.162 + * 1.163 + * @param aTarget 1.164 + * The string spec for the desired URI or an nsIFile. 1.165 + * @param aOriginCharset [optional] 1.166 + * The character set for the URI. Only used if aTarget is not an 1.167 + * nsIFile. 1.168 + * @param aBaseURI [optional] 1.169 + * The base URI for the spec. Only used if aTarget is not an 1.170 + * nsIFile. 1.171 + * 1.172 + * @return an nsIURI object. 1.173 + */ 1.174 + newURI: function NetUtil_newURI(aTarget, aOriginCharset, aBaseURI) 1.175 + { 1.176 + if (!aTarget) { 1.177 + let exception = new Components.Exception( 1.178 + "Must have a non-null string spec or nsIFile object", 1.179 + Cr.NS_ERROR_INVALID_ARG, 1.180 + Components.stack.caller 1.181 + ); 1.182 + throw exception; 1.183 + } 1.184 + 1.185 + if (aTarget instanceof Ci.nsIFile) { 1.186 + return this.ioService.newFileURI(aTarget); 1.187 + } 1.188 + 1.189 + return this.ioService.newURI(aTarget, aOriginCharset, aBaseURI); 1.190 + }, 1.191 + 1.192 + /** 1.193 + * Constructs a new channel for the given spec, character set, and base URI, 1.194 + * or nsIURI, or nsIFile. 1.195 + * 1.196 + * @param aWhatToLoad 1.197 + * The string spec for the desired URI, an nsIURI, or an nsIFile. 1.198 + * @param aOriginCharset [optional] 1.199 + * The character set for the URI. Only used if aWhatToLoad is a 1.200 + * string. 1.201 + * @param aBaseURI [optional] 1.202 + * The base URI for the spec. Only used if aWhatToLoad is a string. 1.203 + * 1.204 + * @return an nsIChannel object. 1.205 + */ 1.206 + newChannel: function NetUtil_newChannel(aWhatToLoad, aOriginCharset, 1.207 + aBaseURI) 1.208 + { 1.209 + if (!aWhatToLoad) { 1.210 + let exception = new Components.Exception( 1.211 + "Must have a non-null string spec, nsIURI, or nsIFile object", 1.212 + Cr.NS_ERROR_INVALID_ARG, 1.213 + Components.stack.caller 1.214 + ); 1.215 + throw exception; 1.216 + } 1.217 + 1.218 + let uri = aWhatToLoad; 1.219 + if (!(aWhatToLoad instanceof Ci.nsIURI)) { 1.220 + // We either have a string or an nsIFile that we'll need a URI for. 1.221 + uri = this.newURI(aWhatToLoad, aOriginCharset, aBaseURI); 1.222 + } 1.223 + 1.224 + return this.ioService.newChannelFromURI(uri); 1.225 + }, 1.226 + 1.227 + /** 1.228 + * Reads aCount bytes from aInputStream into a string. 1.229 + * 1.230 + * @param aInputStream 1.231 + * The input stream to read from. 1.232 + * @param aCount 1.233 + * The number of bytes to read from the stream. 1.234 + * @param aOptions [optional] 1.235 + * charset 1.236 + * The character encoding of stream data. 1.237 + * replacement 1.238 + * The character to replace unknown byte sequences. 1.239 + * If unset, it causes an exceptions to be thrown. 1.240 + * 1.241 + * @return the bytes from the input stream in string form. 1.242 + * 1.243 + * @throws NS_ERROR_INVALID_ARG if aInputStream is not an nsIInputStream. 1.244 + * @throws NS_BASE_STREAM_WOULD_BLOCK if reading from aInputStream would 1.245 + * block the calling thread (non-blocking mode only). 1.246 + * @throws NS_ERROR_FAILURE if there are not enough bytes available to read 1.247 + * aCount amount of data. 1.248 + * @throws NS_ERROR_ILLEGAL_INPUT if aInputStream has invalid sequences 1.249 + */ 1.250 + readInputStreamToString: function NetUtil_readInputStreamToString(aInputStream, 1.251 + aCount, 1.252 + aOptions) 1.253 + { 1.254 + if (!(aInputStream instanceof Ci.nsIInputStream)) { 1.255 + let exception = new Components.Exception( 1.256 + "First argument should be an nsIInputStream", 1.257 + Cr.NS_ERROR_INVALID_ARG, 1.258 + Components.stack.caller 1.259 + ); 1.260 + throw exception; 1.261 + } 1.262 + 1.263 + if (!aCount) { 1.264 + let exception = new Components.Exception( 1.265 + "Non-zero amount of bytes must be specified", 1.266 + Cr.NS_ERROR_INVALID_ARG, 1.267 + Components.stack.caller 1.268 + ); 1.269 + throw exception; 1.270 + } 1.271 + 1.272 + if (aOptions && "charset" in aOptions) { 1.273 + let cis = Cc["@mozilla.org/intl/converter-input-stream;1"]. 1.274 + createInstance(Ci.nsIConverterInputStream); 1.275 + try { 1.276 + // When replacement is set, the character that is unknown sequence 1.277 + // replaces with aOptions.replacement character. 1.278 + if (!("replacement" in aOptions)) { 1.279 + // aOptions.replacement isn't set. 1.280 + // If input stream has unknown sequences for aOptions.charset, 1.281 + // throw NS_ERROR_ILLEGAL_INPUT. 1.282 + aOptions.replacement = 0; 1.283 + } 1.284 + 1.285 + cis.init(aInputStream, aOptions.charset, aCount, 1.286 + aOptions.replacement); 1.287 + let str = {}; 1.288 + cis.readString(-1, str); 1.289 + cis.close(); 1.290 + return str.value; 1.291 + } 1.292 + catch (e) { 1.293 + // Adjust the stack so it throws at the caller's location. 1.294 + throw new Components.Exception(e.message, e.result, 1.295 + Components.stack.caller, e.data); 1.296 + } 1.297 + } 1.298 + 1.299 + let sis = Cc["@mozilla.org/scriptableinputstream;1"]. 1.300 + createInstance(Ci.nsIScriptableInputStream); 1.301 + sis.init(aInputStream); 1.302 + try { 1.303 + return sis.readBytes(aCount); 1.304 + } 1.305 + catch (e) { 1.306 + // Adjust the stack so it throws at the caller's location. 1.307 + throw new Components.Exception(e.message, e.result, 1.308 + Components.stack.caller, e.data); 1.309 + } 1.310 + }, 1.311 + 1.312 + /** 1.313 + * Returns a reference to nsIIOService. 1.314 + * 1.315 + * @return a reference to nsIIOService. 1.316 + */ 1.317 + get ioService() 1.318 + { 1.319 + delete this.ioService; 1.320 + return this.ioService = Cc["@mozilla.org/network/io-service;1"]. 1.321 + getService(Ci.nsIIOService); 1.322 + }, 1.323 +}; 1.324 + 1.325 +//////////////////////////////////////////////////////////////////////////////// 1.326 +//// Initialization 1.327 + 1.328 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); 1.329 + 1.330 +// Define our lazy getters. 1.331 +XPCOMUtils.defineLazyServiceGetter(this, "ioUtil", "@mozilla.org/io-util;1", 1.332 + "nsIIOUtil");