Bug 590654 - Let JavaScript read embedded nulls from input streams

Adds a new method to NetUtil (readInputStreamToString) that will read a string
with or without embedded NULLs from an input stream.  Also adds the needed API
on nsIScriptableInputStream to make this happen.
r=bz
sr=biesi
a=blocking2.0
This commit is contained in:
Shawn Wilsher 2010-08-27 12:42:51 -07:00
parent 75637d22a2
commit b3b7f224fd
4 changed files with 197 additions and 10 deletions

View File

@ -16,7 +16,7 @@
* The Original Code is Mozilla code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
@ -257,6 +257,56 @@ const NetUtil = {
return this.ioService.newChannelFromURI(uri);
},
/**
* Reads aCount bytes from aInputStream into a string.
*
* @param aInputStream
* The input stream to read from.
* @param aCount
* The number of bytes to read from the stream.
*
* @return the bytes from the input stream in string form.
*
* @throws NS_ERROR_INVALID_ARG if aInputStream is not an nsIInputStream.
* @throws NS_BASE_STREAM_WOULD_BLOCK if reading from aInputStream would
* block the calling thread (non-blocking mode only).
* @throws NS_ERROR_FAILURE if there are not enough bytes available to read
* aCount amount of data.
*/
readInputStreamToString: function NetUtil_readInputStreamToString(aInputStream,
aCount)
{
if (!(aInputStream instanceof Ci.nsIInputStream)) {
let exception = new Components.Exception(
"First argument should be an nsIInputStream",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
if (!aCount) {
let exception = new Components.Exception(
"Non-zero amount of bytes must be specified",
Cr.NS_ERROR_INVALID_ARG,
Components.stack.caller
);
throw exception;
}
let sis = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
sis.init(aInputStream);
try {
return sis.readBytes(aCount);
}
catch (e) {
// Adjust the stack so it throws at the caller's location.
throw new Components.Exception(e.message, e.result,
Components.stack.caller, e.data);
}
},
/**
* Returns a reference to nsIIOService.
*

View File

@ -447,6 +447,83 @@ function test_newChannel_with_nsIFile()
run_next_test();
}
function test_readInputStreamToString()
{
const TEST_DATA = "this is a test string\0 with an embedded null";
let istream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsISupportsCString);
istream.data = TEST_DATA;
do_check_eq(NetUtil.readInputStreamToString(istream, TEST_DATA.length),
TEST_DATA);
run_next_test();
}
function test_readInputStreamToString_no_input_stream()
{
try {
NetUtil.readInputStreamToString("hi", 2);
do_throw("should throw!");
}
catch (e) {
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
}
function test_readInputStreamToString_no_bytes_arg()
{
const TEST_DATA = "this is a test string";
let istream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
istream.setData(TEST_DATA, TEST_DATA.length);
try {
NetUtil.readInputStreamToString(istream);
do_throw("should throw!");
}
catch (e) {
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
}
run_next_test();
}
function test_readInputStreamToString_blocking_stream()
{
let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
pipe.init(true, true, 0, 0, null);
try {
NetUtil.readInputStreamToString(pipe.inputStream, 10);
do_throw("should throw!");
}
catch (e) {
do_check_eq(e.result, Cr.NS_BASE_STREAM_WOULD_BLOCK);
}
run_next_test();
}
function test_readInputStreamToString_too_many_bytes()
{
const TEST_DATA = "this is a test string";
let istream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
istream.setData(TEST_DATA, TEST_DATA.length);
try {
NetUtil.readInputStreamToString(istream, TEST_DATA.length + 10);
do_throw("should throw!");
}
catch (e) {
do_check_eq(e.result, Cr.NS_ERROR_FAILURE);
}
run_next_test();
}
////////////////////////////////////////////////////////////////////////////////
//// Test Runner
@ -468,6 +545,11 @@ let tests = [
test_newChannel_with_string,
test_newChannel_with_nsIURI,
test_newChannel_with_nsIFile,
test_readInputStreamToString,
test_readInputStreamToString_no_input_stream,
test_readInputStreamToString_no_bytes_arg,
test_readInputStreamToString_blocking_stream,
test_readInputStreamToString_too_many_bytes,
];
let index = 0;
@ -475,15 +557,17 @@ function run_next_test()
{
if (index < tests.length) {
do_test_pending();
print("Running the next test: " + tests[index].name);
// Asynchronous test exceptions do not kill the test...
try {
tests[index++]();
}
catch (e) {
do_throw(e);
}
do_execute_soon(function() {
try {
print("Running the next test: " + tests[index].name);
tests[index++]();
}
catch (e) {
do_throw(e);
}
});
}
do_test_finished();

View File

@ -44,7 +44,7 @@ interface nsIInputStream;
* nsIScriptableInputStream provides scriptable access to an nsIInputStream
* instance.
*/
[scriptable, uuid(a2a32f90-9b90-11d3-a189-0050041caf44)]
[scriptable, uuid(e546afd6-1248-4deb-8940-4b000b618a58)]
interface nsIScriptableInputStream : nsISupports
{
/**
@ -82,4 +82,19 @@ interface nsIScriptableInputStream : nsISupports
* @throws NS_ERROR_NOT_INITIALIZED if init was not called
*/
string read(in unsigned long aCount);
};
/**
* Read data from the stream, including NULL bytes.
*
* @param aCount the maximum number of bytes to read.
*
* @return the data from the stream, which will be an empty string if EOF
* has been reached.
*
* @throws NS_BASE_STREAM_WOULD_BLOCK if reading from the input stream
* would block the calling thread (non-blocking mode only).
* @throws NS_ERROR_FAILURE if there are not enough bytes available to read
* aCount amount of data.
*/
ACString readBytes(in unsigned long aCount);
};

View File

@ -37,6 +37,7 @@
#include "nsScriptableInputStream.h"
#include "nsMemory.h"
#include "nsString.h"
NS_IMPL_ISUPPORTS1(nsScriptableInputStream, nsIScriptableInputStream)
@ -88,6 +89,43 @@ nsScriptableInputStream::Read(PRUint32 aCount, char **_retval) {
return NS_OK;
}
NS_IMETHODIMP
nsScriptableInputStream::ReadBytes(PRUint32 aCount, nsACString &_retval) {
if (!mInputStream) {
return NS_ERROR_NOT_INITIALIZED;
}
_retval.SetLength(aCount);
if (_retval.Length() != aCount) {
return NS_ERROR_OUT_OF_MEMORY;
}
char *ptr = _retval.BeginWriting();
PRUint32 totalBytesRead = 0;
while (1) {
PRUint32 bytesRead;
nsresult rv = mInputStream->Read(ptr + totalBytesRead,
aCount - totalBytesRead,
&bytesRead);
if (NS_FAILED(rv)) {
return rv;
}
totalBytesRead += bytesRead;
if (totalBytesRead == aCount) {
break;
}
// If we have read zero bytes, we have hit EOF.
if (bytesRead == 0) {
_retval.Truncate();
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
nsresult
nsScriptableInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) {
if (aOuter) return NS_ERROR_NO_AGGREGATION;