Bug 536273 - e10s HTTP: get POSTs working. r=jduell

This commit is contained in:
Frederic Plourde 2010-05-30 15:30:28 -07:00
parent 2e2352b06b
commit 4ddd8b4cf8
10 changed files with 201 additions and 148 deletions

View File

@ -1145,6 +1145,33 @@ NS_NewPostDataStream(nsIInputStream **result,
return NS_OK;
}
inline nsresult
NS_ReadInputStreamToString(nsIInputStream *aInputStream,
nsACString &aDest,
PRUint32 aCount)
{
nsresult rv;
aDest.SetLength(aCount);
if (aDest.Length() != aCount)
return NS_ERROR_OUT_OF_MEMORY;
char * p = aDest.BeginWriting();
PRUint32 bytesRead;
PRUint32 totalRead = 0;
while (1) {
rv = aInputStream->Read(p + totalRead, aCount - totalRead, &bytesRead);
if (!NS_SUCCEEDED(rv))
return rv;
totalRead += bytesRead;
if (totalRead == aCount)
break;
// if Read reads 0 bytes, we've hit EOF
if (bytesRead == 0)
return NS_ERROR_UNEXPECTED;
}
return rv;
}
inline nsresult
NS_LoadPersistentPropertiesFromURI(nsIPersistentProperties **result,
nsIURI *uri,

View File

@ -23,6 +23,8 @@
*
* Contributor(s):
* Daniel Witte <dwitte@mozilla.com>
* Frederic Plourde <bugzillaFred@gmail.com>
* Jason Duell <jduell.mcbugs@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -58,6 +60,7 @@ HttpBaseChannel::HttpBaseChannel()
, mResponseHeadersModified(PR_FALSE)
, mAllowPipelining(PR_TRUE)
, mForceAllowThirdPartyCookie(PR_FALSE)
, mUploadStreamHasHeaders(PR_FALSE)
{
LOG(("Creating HttpBaseChannel @%x\n", this));
@ -141,12 +144,14 @@ HttpBaseChannel::Init(nsIURI *aURI,
// HttpBaseChannel::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS_INHERITED5(HttpBaseChannel,
NS_IMPL_ISUPPORTS_INHERITED7(HttpBaseChannel,
nsHashPropertyBag,
nsIRequest,
nsIChannel,
nsIHttpChannel,
nsIHttpChannelInternal,
nsIUploadChannel,
nsIUploadChannel2,
nsISupportsPriority)
//-----------------------------------------------------------------------------
@ -368,6 +373,105 @@ HttpBaseChannel::Open(nsIInputStream **aResult)
return NS_ImplementChannelOpen(this, aResult);
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIUploadChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpBaseChannel::GetUploadStream(nsIInputStream **stream)
{
NS_ENSURE_ARG_POINTER(stream);
*stream = mUploadStream;
NS_IF_ADDREF(*stream);
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
const nsACString &contentType,
PRInt32 contentLength)
{
// NOTE: for backwards compatibility and for compatibility with old style
// plugins, |stream| may include headers, specifically Content-Type and
// Content-Length headers. in this case, |contentType| and |contentLength|
// would be unspecified. this is traditionally the case of a POST request,
// and so we select POST as the request method if contentType and
// contentLength are unspecified.
if (stream) {
if (contentType.IsEmpty()) {
mUploadStreamHasHeaders = PR_TRUE;
mRequestHead.SetMethod(nsHttp::Post); // POST request
} else {
if (contentLength < 0) {
// Not really kosher to assume Available == total length of
// stream, but apparently works for the streams we see here.
stream->Available((PRUint32 *) &contentLength);
if (contentLength < 0) {
NS_ERROR("unable to determine content length");
return NS_ERROR_FAILURE;
}
}
// SetRequestHeader propagates headers to chrome if HttpChannelChild
nsCAutoString contentLengthStr;
contentLengthStr.AppendInt(PRInt64(contentLength));
SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), contentLengthStr,
PR_FALSE);
SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), contentType,
PR_FALSE);
mUploadStreamHasHeaders = PR_FALSE;
mRequestHead.SetMethod(nsHttp::Put); // PUT request
}
} else {
mUploadStreamHasHeaders = PR_FALSE;
mRequestHead.SetMethod(nsHttp::Get); // revert to GET request
}
mUploadStream = stream;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIUploadChannel2
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream,
const nsACString &aContentType,
PRInt64 aContentLength,
const nsACString &aMethod,
PRBool aStreamHasHeaders)
{
// Ensure stream is set and method is valid
NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);
if (aContentLength < 0 && !aStreamHasHeaders) {
PRUint32 streamLength;
aStream->Available(&streamLength);
aContentLength = streamLength;
if (aContentLength < 0) {
NS_ERROR("unable to determine content length");
return NS_ERROR_FAILURE;
}
}
nsresult rv = SetRequestMethod(aMethod);
NS_ENSURE_SUCCESS(rv, rv);
if (!aStreamHasHeaders) {
// SetRequestHeader propagates headers to chrome if HttpChannelChild
nsCAutoString contentLengthStr;
contentLengthStr.AppendInt(aContentLength);
SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), contentLengthStr,
PR_FALSE);
SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), aContentType,
PR_FALSE);
}
mUploadStreamHasHeaders = aStreamHasHeaders;
mUploadStream = aStream;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIHttpChannel
//-----------------------------------------------------------------------------

View File

@ -50,6 +50,8 @@
#include "nsHttpConnectionInfo.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIUploadChannel.h"
#include "nsIUploadChannel2.h"
#include "nsIProgressEventSink.h"
#include "nsIURI.h"
#include "nsISupportsPriority.h"
@ -75,6 +77,10 @@
namespace mozilla {
namespace net {
typedef enum { eUploadStream_null = -1,
eUploadStream_hasNoHeaders = 0,
eUploadStream_hasHeaders = 1 } UploadStreamInfoType;
/*
* This class is a partial implementation of nsIHttpChannel. It contains code
* shared by nsHttpChannel and HttpChannelChild.
@ -85,10 +91,14 @@ namespace net {
class HttpBaseChannel : public nsHashPropertyBag
, public nsIHttpChannel
, public nsIHttpChannelInternal
, public nsIUploadChannel
, public nsIUploadChannel2
, public nsISupportsPriority
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIUPLOADCHANNEL
NS_DECL_NSIUPLOADCHANNEL2
HttpBaseChannel();
virtual ~HttpBaseChannel();
@ -178,6 +188,7 @@ protected:
nsCOMPtr<nsIURI> mReferrer;
nsHttpRequestHead mRequestHead;
nsCOMPtr<nsIInputStream> mUploadStream;
nsAutoPtr<nsHttpResponseHead> mResponseHead;
nsRefPtr<nsHttpConnectionInfo> mConnectionInfo;
@ -196,6 +207,7 @@ protected:
PRUint8 mResponseHeadersModified : 1;
PRUint8 mAllowPipelining : 1;
PRUint8 mForceAllowThirdPartyCookie : 1;
PRUint32 mUploadStreamHasHeaders : 1;
};

View File

@ -88,8 +88,6 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel)
NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
@ -281,8 +279,36 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
if (NS_FAILED(rv))
return rv;
// Prepare uploadStream for POST data
nsCAutoString uploadStreamData;
PRInt32 uploadStreamInfo;
if (mUploadStream) {
// Read entire POST stream into string:
// This is a temporary measure until bug 564553 is implemented: we're doing
// a blocking read of a potentially arbitrarily large stream, so this isn't
// performant/safe for large file uploads.
PRUint32 bytes;
mUploadStream->Available(&bytes);
if (bytes > 0) {
rv = NS_ReadInputStreamToString(mUploadStream, uploadStreamData, bytes);
if (!NS_SUCCEEDED(rv))
return rv;
}
uploadStreamInfo = mUploadStreamHasHeaders ?
eUploadStream_hasHeaders : eUploadStream_hasNoHeaders;
} else {
uploadStreamInfo = eUploadStream_null;
}
// FIXME bug 562587: need to dupe nsHttpChannel::AsyncOpen cookies logic
//
// NOTE: From now on we must return NS_OK; all errors must be handled via
// OnStart/OnStopRequest
//
// notify "http-on-modify-request" observers
gHttpHandler->OnModifyRequest(this);
@ -317,8 +343,9 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
gNeckoChild->SendPHttpChannelConstructor(this);
SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI), IPC::URI(mDocumentURI),
IPC::URI(mReferrer), mLoadFlags, mRequestHeaders,
mRequestHead.Method(), mPriority, mRedirectionLimit,
IPC::URI(mReferrer), mLoadFlags, mRequestHeaders,
mRequestHead.Method(), uploadStreamData,
uploadStreamInfo, mPriority, mRedirectionLimit,
mAllowPipelining, mForceAllowThirdPartyCookie);
// The socket transport layer in the chrome process now has a logical ref to
@ -454,41 +481,6 @@ HttpChannelChild::IsFromCache(PRBool *value)
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIUploadChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelChild::SetUploadStream(nsIInputStream *aStream,
const nsACString& aContentType,
PRInt32 aContentLength)
{
DROP_DEAD();
}
NS_IMETHODIMP
HttpChannelChild::GetUploadStream(nsIInputStream **stream)
{
// FIXME: stub for bug 536273
NS_ENSURE_ARG_POINTER(stream);
*stream = 0;
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIUploadChannel2
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelChild::ExplicitSetUploadStream(nsIInputStream *aStream,
const nsACString& aContentType,
PRInt64 aContentLength,
const nsACString& aMethod,
PRBool aStreamHasHeaders)
{
DROP_DEAD();
}
//-----------------------------------------------------------------------------
// HttpChannelChild::nsIEncodedChannel
//-----------------------------------------------------------------------------

View File

@ -54,7 +54,6 @@
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheChannel.h"
#include "nsIEncodedChannel.h"
#include "nsIUploadChannel.h"
#include "nsIUploadChannel2.h"
#include "nsIResumableChannel.h"
#include "nsIProxiedChannel.h"
@ -76,8 +75,6 @@ enum HttpChannelChildState {
class HttpChannelChild : public PHttpChannelChild
, public HttpBaseChannel
, public nsICachingChannel
, public nsIUploadChannel
, public nsIUploadChannel2
, public nsIEncodedChannel
, public nsIResumableChannel
, public nsIProxiedChannel
@ -87,8 +84,6 @@ class HttpChannelChild : public PHttpChannelChild
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICACHINGCHANNEL
NS_DECL_NSIUPLOADCHANNEL
NS_DECL_NSIUPLOADCHANNEL2
NS_DECL_NSIENCODEDCHANNEL
NS_DECL_NSIRESUMABLECHANNEL
NS_DECL_NSIPROXIEDCHANNEL

View File

@ -94,6 +94,8 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
const PRUint32& loadFlags,
const RequestHeaderTuples& requestHeaders,
const nsHttpAtom& requestMethod,
const nsCString& uploadStreamData,
const PRInt32& uploadStreamInfo,
const PRUint16& priority,
const PRUint8& redirectionLimit,
const PRBool& allowPipelining,
@ -113,11 +115,11 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
if (NS_FAILED(rv))
return false; // TODO: send fail msg to child, return true
return false; // TODO: cancel request (bug 536317), return true
rv = NS_NewChannel(getter_AddRefs(mChannel), uri, ios, nsnull, nsnull, loadFlags);
if (NS_FAILED(rv))
return false; // TODO: send fail msg to child, return true
return false; // TODO: cancel request (bug 536317), return true
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
httpChan->SetRemoteChannel();
@ -139,6 +141,19 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
httpChan->SetNotificationCallbacks(this);
httpChan->SetRequestMethod(nsDependentCString(requestMethod.get()));
if (uploadStreamInfo != eUploadStream_null) {
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewPostDataStream(getter_AddRefs(stream), false, uploadStreamData, 0);
if (!NS_SUCCEEDED(rv)) {
return false; // TODO: cancel request (bug 536317), return true
}
httpChan->InternalSetUploadStream(stream);
// We're casting uploadStreamInfo into PRBool here on purpose because
// we know possible values are either 0 or 1. See uploadStreamInfoType.
httpChan->SetUploadStreamHasHeaders((PRBool) uploadStreamInfo);
}
if (priority != nsISupportsPriority::PRIORITY_NORMAL)
httpChan->SetPriority(priority);
httpChan->SetRedirectionLimit(redirectionLimit);
@ -147,7 +162,7 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
rv = httpChan->AsyncOpen(this, nsnull);
if (NS_FAILED(rv))
return false; // TODO: send fail msg to child, return true
return false; // TODO: cancel request (bug 536317), return true
return true;
}

View File

@ -75,6 +75,8 @@ protected:
const PRUint32& loadFlags,
const RequestHeaderTuples& requestHeaders,
const nsHttpAtom& requestMethod,
const nsCString& uploadStreamData,
const PRInt32& uploadStreamInfo,
const PRUint16& priority,
const PRUint8& redirectionLimit,
const PRBool& allowPipelining,

View File

@ -69,6 +69,8 @@ parent:
PRUint32 loadFlags,
RequestHeaderTuples requestHeaders,
nsHttpAtom requestMethod,
nsCString uploadStreamData,
PRInt32 uploadStreamInfo,
PRUint16 priority,
PRUint8 redirectionLimit,
PRBool allowPipelining,

View File

@ -100,7 +100,6 @@ nsHttpChannel::nsHttpChannel()
, mCachedContentIsPartial(PR_FALSE)
, mCanceled(PR_FALSE)
, mTransactionReplaced(PR_FALSE)
, mUploadStreamHasHeaders(PR_FALSE)
, mAuthRetryPending(PR_FALSE)
, mProxyAuth(PR_FALSE)
, mTriedProxyAuth(PR_FALSE)
@ -4200,97 +4199,6 @@ nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIUploadChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::GetUploadStream(nsIInputStream **stream)
{
NS_ENSURE_ARG_POINTER(stream);
*stream = mUploadStream;
NS_IF_ADDREF(*stream);
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::SetUploadStream(nsIInputStream *stream,
const nsACString &contentType,
PRInt32 contentLength)
{
// NOTE: for backwards compatibility and for compatibility with old style
// plugins, |stream| may include headers, specifically Content-Type and
// Content-Length headers. in this case, |contentType| and |contentLength|
// would be unspecified. this is traditionally the case of a POST request,
// and so we select POST as the request method if contentType and
// contentLength are unspecified.
if (stream) {
if (!contentType.IsEmpty()) {
if (contentLength < 0) {
stream->Available((PRUint32 *) &contentLength);
if (contentLength < 0) {
NS_ERROR("unable to determine content length");
return NS_ERROR_FAILURE;
}
}
mRequestHead.SetHeader(nsHttp::Content_Length,
nsPrintfCString("%d", contentLength));
mRequestHead.SetHeader(nsHttp::Content_Type, contentType);
mUploadStreamHasHeaders = PR_FALSE;
mRequestHead.SetMethod(nsHttp::Put); // PUT request
}
else {
mUploadStreamHasHeaders = PR_TRUE;
mRequestHead.SetMethod(nsHttp::Post); // POST request
}
}
else {
mUploadStreamHasHeaders = PR_FALSE;
mRequestHead.SetMethod(nsHttp::Get); // revert to GET request
}
mUploadStream = stream;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIUploadChannel2
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::ExplicitSetUploadStream(nsIInputStream *aStream,
const nsACString &aContentType,
PRInt64 aContentLength,
const nsACString &aMethod,
PRBool aStreamHasHeaders)
{
// Ensure stream is set and method is valid
NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);
if (aContentLength < 0 && !aStreamHasHeaders) {
PRUint32 streamLength;
aStream->Available(&streamLength);
aContentLength = streamLength;
if (aContentLength < 0) {
NS_ERROR("unable to determine content length");
return NS_ERROR_FAILURE;
}
}
nsresult rv = SetRequestMethod(aMethod);
NS_ENSURE_SUCCESS(rv, rv);
if (!aStreamHasHeaders) {
mRequestHead.SetHeader(nsHttp::Content_Length,
nsPrintfCString("%lld", aContentLength));
mRequestHead.SetHeader(nsHttp::Content_Type, aContentType);
}
mUploadStreamHasHeaders = aStreamHasHeaders;
mUploadStream = aStream;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIEncodedChannel

View File

@ -56,8 +56,6 @@
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheChannel.h"
#include "nsIEncodedChannel.h"
#include "nsIUploadChannel.h"
#include "nsIUploadChannel2.h"
#include "nsIStringEnumerator.h"
#include "nsIPrompt.h"
#include "nsIResumableChannel.h"
@ -79,8 +77,6 @@ using namespace mozilla::net;
class nsHttpChannel : public HttpBaseChannel
, public nsIStreamListener
, public nsICachingChannel
, public nsIUploadChannel
, public nsIUploadChannel2
, public nsICacheListener
, public nsIEncodedChannel
, public nsITransportEventSink
@ -96,8 +92,6 @@ public:
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSICACHINGCHANNEL
NS_DECL_NSIUPLOADCHANNEL
NS_DECL_NSIUPLOADCHANNEL2
NS_DECL_NSICACHELISTENER
NS_DECL_NSIENCODEDCHANNEL
NS_DECL_NSITRANSPORTEVENTSINK
@ -130,6 +124,10 @@ public: /* internal necko use only */
typedef void (nsHttpChannel:: *nsAsyncCallback)(void);
nsHttpResponseHead * GetResponseHead() const { return mResponseHead; }
void SetRemoteChannel() { mRemoteChannel = 1; }
void InternalSetUploadStream(nsIInputStream *uploadStream)
{ mUploadStream = uploadStream; }
void SetUploadStreamHasHeaders(PRBool hasHeaders)
{ mUploadStreamHasHeaders = hasHeaders; }
nsresult SetReferrerInternal(nsIURI *referrer) {
nsCAutoString spec;
@ -246,7 +244,6 @@ private:
nsresult ContinueOnAuthAvailable(const nsCSubstring& creds);
private:
nsCOMPtr<nsIInputStream> mUploadStream;
nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsICancelable> mProxyRequest;
@ -318,7 +315,6 @@ private:
PRUint32 mCachedContentIsPartial : 1;
PRUint32 mCanceled : 1;
PRUint32 mTransactionReplaced : 1;
PRUint32 mUploadStreamHasHeaders : 1;
PRUint32 mAuthRetryPending : 1;
// True when we need to authenticate to proxy, i.e. when we get 407
// response. Used in OnAuthAvailable and OnAuthCancelled callbacks.