Bug 537120 part 1: Make sure the multipart converter's part channels install a type sniffer as needed. r=jduell

This commit is contained in:
Boris Zbarsky 2010-04-10 09:10:21 -07:00
parent e38734397e
commit cd5d74649f
3 changed files with 145 additions and 7 deletions

View File

@ -48,6 +48,7 @@
#include "nsCRT.h"
#include "nsIHttpChannelInternal.h"
#include "nsURLHelper.h"
#include "nsIStreamConverterService.h"
//
// Helper function for determining the length of data bytes up to
@ -67,7 +68,10 @@ LengthToToken(const char *cursor, const char *token)
return len;
}
nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID) :
nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID,
nsIStreamListener* aListener) :
mMultipartChannel(aMultipartChannel),
mListener(aListener),
mStatus(NS_OK),
mContentLength(LL_MAXUINT),
mIsByteRangeRequest(PR_FALSE),
@ -96,6 +100,26 @@ void nsPartChannel::InitializeByteRange(PRInt64 aStart, PRInt64 aEnd)
mByteRangeEnd = aEnd;
}
nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext)
{
return mListener->OnStartRequest(this, aContext);
}
nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext,
nsIInputStream* aStream,
PRUint32 aOffset, PRUint32 aLen)
{
return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aLen);
}
nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext,
nsresult aStatus)
{
// Drop the listener
nsCOMPtr<nsIStreamListener> listener;
listener.swap(mListener);
return listener->OnStopRequest(this, aContext, aStatus);
}
//
// nsISupports implementation...
@ -734,15 +758,30 @@ nsresult
nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
nsresult rv = NS_OK;
if (mContentType.IsEmpty())
nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
if (mContentType.IsEmpty()) {
mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
nsCOMPtr<nsIStreamConverterService> serv =
do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIStreamListener> converter;
rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
"*/*",
mFinalListener,
mContext,
getter_AddRefs(converter));
if (NS_SUCCEEDED(rv)) {
partListener = converter;
}
}
}
// if we already have an mPartChannel, that means we never sent a Stop()
// before starting up another "part." that would be bad.
NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
nsPartChannel *newChannel;
newChannel = new nsPartChannel(aChannel, mCurrentPartID++);
newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener);
if (!newChannel)
return NS_ERROR_OUT_OF_MEMORY;
@ -780,7 +819,7 @@ nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
// Let's start off the load. NOTE: we don't forward on the channel passed
// into our OnDataAvailable() as it's the root channel for the raw stream.
return mFinalListener->OnStartRequest(mPartChannel, mContext);
return mPartChannel->SendOnStartRequest(mContext);
}
@ -789,7 +828,7 @@ nsMultiMixedConv::SendStop(nsresult aStatus) {
nsresult rv = NS_OK;
if (mPartChannel) {
rv = mFinalListener->OnStopRequest(mPartChannel, mContext, aStatus);
rv = mPartChannel->SendOnStopRequest(mContext, aStatus);
// don't check for failure here, we need to remove the channel from
// the loadgroup.
@ -836,7 +875,7 @@ nsMultiMixedConv::SendData(char *aBuffer, PRUint32 aLen) {
nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
if (NS_FAILED(rv)) return rv;
return mFinalListener->OnDataAvailable(mPartChannel, mContext, inStream, offset, aLen);
return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen);
}
PRInt32

View File

@ -68,10 +68,15 @@ class nsPartChannel : public nsIChannel,
public nsIMultiPartChannel
{
public:
nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID);
nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID,
nsIStreamListener* aListener);
void InitializeByteRange(PRInt64 aStart, PRInt64 aEnd);
void SetIsLastPart() { mIsLastPart = PR_TRUE; }
nsresult SendOnStartRequest(nsISupports* aContext);
nsresult SendOnDataAvailable(nsISupports* aContext, nsIInputStream* aStream,
PRUint32 aOffset, PRUint32 aLen);
nsresult SendOnStopRequest(nsISupports* aContext, nsresult aStatus);
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
@ -84,6 +89,7 @@ protected:
protected:
nsCOMPtr<nsIChannel> mMultipartChannel;
nsCOMPtr<nsIStreamListener> mListener;
nsresult mStatus;
nsLoadFlags mLoadFlags;

View File

@ -0,0 +1,93 @@
do_load_httpd_js();
var httpserver = null;
var uri = "http://localhost:4444/multipart";
function make_channel(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
var multipartBody = "--boundary\r\n\r\nSome text\r\n--boundary\r\n\r\n<?xml version='1.0'?><root/>\r\n--boundary--";
function make_channel(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", 'multipart/mixed; boundary="boundary"');
response.bodyOutputStream.write(multipartBody, multipartBody.length);
}
var numTests = 2;
var testNum = 0;
var testData =
[
{ data: "Some text", type: "text/plain" },
{ data: "<?xml version='1.0'?><root/>", type: "text/xml" }
];
function responseHandler(request, buffer)
{
do_check_eq(buffer, testData[testNum].data);
do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,
testData[testNum].type);
if (++testNum == numTests)
httpserver.stop(do_test_finished);
}
var multipartListener = {
_buffer: "",
QueryInterface: function(iid) {
if (iid.equals(Components.interfaces.nsIStreamListener) ||
iid.equals(Components.interfaces.nsIRequestObserver) ||
iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
onStartRequest: function(request, context) {
this._buffer = "";
},
onDataAvailable: function(request, context, stream, offset, count) {
try {
this._buffer = this._buffer.concat(read_stream(stream, count));
dump("BUFFEEE: " + this._buffer + "\n\n");
} catch (ex) {
do_throw("Error in onDataAvailable: " + ex);
}
},
onStopRequest: function(request, context, status) {
try {
responseHandler(request, this._buffer);
} catch (ex) {
do_throw("Error in closure function: " + ex);
}
}
};
function run_test()
{
httpserver = new nsHttpServer();
httpserver.registerPathHandler("/multipart", contentHandler);
httpserver.start(4444);
var streamConv = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
var conv = streamConv.asyncConvertData("multipart/mixed",
"*/*",
multipartListener,
null);
var chan = make_channel(uri);
chan.asyncOpen(conv, null);
do_test_pending();
}