Bug 536273 - e10s HTTP: get POSTs working. r=jduell
authorFrederic Plourde <bugzillaFred@gmail.com>
Sun, 30 May 2010 15:30:28 -0700
changeset 46887 8ee0464d78ba0185c6987db0236d140d4d9be7c4
parent 46886 dcfeadea0cba5d103316d0bb3caf3b7030f39249
child 46888 0292f7330100e4550ae76a547fd9751af179038d
push idunknown
push userunknown
push dateunknown
reviewersjduell
bugs536273
milestone1.9.3a5pre
Bug 536273 - e10s HTTP: get POSTs working. r=jduell
netwerk/base/public/nsNetUtil.h
netwerk/protocol/http/src/HttpBaseChannel.cpp
netwerk/protocol/http/src/HttpBaseChannel.h
netwerk/protocol/http/src/HttpChannelChild.cpp
netwerk/protocol/http/src/HttpChannelChild.h
netwerk/protocol/http/src/HttpChannelParent.cpp
netwerk/protocol/http/src/HttpChannelParent.h
netwerk/protocol/http/src/PHttpChannel.ipdl
netwerk/protocol/http/src/nsHttpChannel.cpp
netwerk/protocol/http/src/nsHttpChannel.h
--- a/netwerk/base/public/nsNetUtil.h
+++ b/netwerk/base/public/nsNetUtil.h
@@ -1141,16 +1141,43 @@ NS_NewPostDataStream(nsIInputStream  **r
     if (NS_FAILED(rv))
         return rv;
 
     NS_ADDREF(*result = stream);
     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,
                                    nsIIOService             *ioService = nsnull)
 {
     nsCOMPtr<nsIInputStream> in;
     nsresult rv = NS_OpenURI(getter_AddRefs(in), uri, ioService);
     if (NS_SUCCEEDED(rv)) {
         nsCOMPtr<nsIPersistentProperties> properties = 
--- a/netwerk/protocol/http/src/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/src/HttpBaseChannel.cpp
@@ -18,16 +18,18 @@
  *
  * The Initial Developer of the Original Code is
  *  The Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * 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
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -53,16 +55,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mPriority(PRIORITY_NORMAL)
   , mCaps(0)
   , mRedirectionLimit(gHttpHandler->RedirectionLimit())
   , mIsPending(PR_FALSE)
   , mWasOpened(PR_FALSE)
   , mResponseHeadersModified(PR_FALSE)
   , mAllowPipelining(PR_TRUE)
   , mForceAllowThirdPartyCookie(PR_FALSE)
+  , mUploadStreamHasHeaders(PR_FALSE)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // grab a reference to the handler to ensure that it doesn't go away.
   NS_ADDREF(gHttpHandler);
 }
 
 HttpBaseChannel::~HttpBaseChannel()
@@ -136,22 +139,24 @@ HttpBaseChannel::Init(nsIURI *aURI,
 
   return rv;
 }
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_ISUPPORTS_INHERITED5(HttpBaseChannel,
+NS_IMPL_ISUPPORTS_INHERITED7(HttpBaseChannel,
                              nsHashPropertyBag, 
                              nsIRequest,
                              nsIChannel,
                              nsIHttpChannel,
                              nsIHttpChannelInternal,
+                             nsIUploadChannel,
+                             nsIUploadChannel2,
                              nsISupportsPriority)
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetName(nsACString& aName)
@@ -364,16 +369,115 @@ HttpBaseChannel::SetContentLength(PRInt3
 NS_IMETHODIMP
 HttpBaseChannel::Open(nsIInputStream **aResult)
 {
   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
   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
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetRequestMethod(nsACString& aMethod)
 {
   aMethod = mRequestHead.Method();
   return NS_OK;
--- a/netwerk/protocol/http/src/HttpBaseChannel.h
+++ b/netwerk/protocol/http/src/HttpBaseChannel.h
@@ -45,16 +45,18 @@
 #include "nsAutoPtr.h"
 #include "nsHashPropertyBag.h"
 #include "nsProxyInfo.h"
 #include "nsHttpRequestHead.h"
 #include "nsHttpResponseHead.h"
 #include "nsHttpConnectionInfo.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
+#include "nsIUploadChannel.h"
+#include "nsIUploadChannel2.h"
 #include "nsIProgressEventSink.h"
 #include "nsIURI.h"
 #include "nsISupportsPriority.h"
 
 #define DIE_WITH_ASYNC_OPEN_MSG()                                              \
   do {                                                                         \
     fprintf(stderr,                                                            \
             "*&*&*&*&*&*&*&**&*&&*& FATAL ERROR: '%s' "                        \
@@ -70,30 +72,38 @@
   if (mWasOpened)                                                              \
     DIE_WITH_ASYNC_OPEN_MSG();                                                 \
   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);                           \
   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
 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. 
  * - Note that this class has nothing to do with nsBaseChannel, which is an
  *   earlier effort at a base class for channels that somehow never made it all
  *   the way to the HTTP channel.
  */
 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();
 
   nsresult Init(nsIURI *aURI, PRUint8 aCaps, nsProxyInfo *aProxyInfo);
 
   // nsIRequest
   NS_IMETHOD GetName(nsACString& aName);
@@ -173,16 +183,17 @@ protected:
   nsCOMPtr<nsISupports>             mListenerContext;
   nsCOMPtr<nsILoadGroup>            mLoadGroup;
   nsCOMPtr<nsISupports>             mOwner;
   nsCOMPtr<nsIInterfaceRequestor>   mCallbacks;
   nsCOMPtr<nsIProgressEventSink>    mProgressSink;
   nsCOMPtr<nsIURI>                  mReferrer;
 
   nsHttpRequestHead                 mRequestHead;
+  nsCOMPtr<nsIInputStream>          mUploadStream;
   nsAutoPtr<nsHttpResponseHead>     mResponseHead;
   nsRefPtr<nsHttpConnectionInfo>    mConnectionInfo;
 
   nsCString                         mSpec; // ASCII encoded URL spec
   nsCString                         mContentTypeHint;
   nsCString                         mContentCharsetHint;
 
   nsresult                          mStatus;
@@ -191,15 +202,16 @@ protected:
   PRUint8                           mCaps;
   PRUint8                           mRedirectionLimit;
 
   PRUint8                           mIsPending                  : 1;
   PRUint8                           mWasOpened                  : 1;
   PRUint8                           mResponseHeadersModified    : 1;
   PRUint8                           mAllowPipelining            : 1;
   PRUint8                           mForceAllowThirdPartyCookie : 1;
+  PRUint32                          mUploadStreamHasHeaders     : 1;
 };
 
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_HttpBaseChannel_h
--- a/netwerk/protocol/http/src/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/src/HttpChannelChild.cpp
@@ -83,18 +83,16 @@ HttpChannelChild::RefcountHitZero()
 }
 
 NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
   NS_INTERFACE_MAP_ENTRY(nsIRequest)
   NS_INTERFACE_MAP_ENTRY(nsIChannel)
   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)
   NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
   NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
   NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
@@ -276,18 +274,46 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
 
   // Port checked in parent, but duplicate here so we can return with error
   // immediately
   nsresult rv;
   rv = NS_CheckPortSafety(mURI);
   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);
 
   mIsPending = PR_TRUE;
   mWasOpened = PR_TRUE;
   mListener = listener;
   mListenerContext = aContext;
 
@@ -312,18 +338,19 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   //
   // Send request to the chrome process...
   //
 
   // FIXME: bug 558623: Combine constructor and SendAsyncOpen into one IPC msg
   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
   // us, until either OnStopRequest or OnRedirect is called.
   this->AddRef();
 
   mState = HCC_OPENED;
   return NS_OK;
@@ -450,51 +477,16 @@ HttpChannelChild::IsFromCache(PRBool *va
     return NS_ERROR_NOT_AVAILABLE;
 
   // FIXME: stub for bug 537164
   *value = false;
   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
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelChild::GetContentEncodings(nsIUTF8StringEnumerator **result)
 {
   DROP_DEAD();
 }
--- a/netwerk/protocol/http/src/HttpChannelChild.h
+++ b/netwerk/protocol/http/src/HttpChannelChild.h
@@ -49,17 +49,16 @@
 #include "nsILoadGroup.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIProgressEventSink.h"
 #include "nsICachingChannel.h"
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIEncodedChannel.h"
-#include "nsIUploadChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsIResumableChannel.h"
 #include "nsIProxiedChannel.h"
 #include "nsITraceableChannel.h"
 
 namespace mozilla {
 namespace net {
 
@@ -71,29 +70,25 @@ enum HttpChannelChildState {
   HCC_ONDATA,
   HCC_ONSTOP
 };
 
 // Header file contents
 class HttpChannelChild : public PHttpChannelChild
                        , public HttpBaseChannel
                        , public nsICachingChannel
-                       , public nsIUploadChannel
-                       , public nsIUploadChannel2
                        , public nsIEncodedChannel
                        , public nsIResumableChannel
                        , public nsIProxiedChannel
                        , public nsITraceableChannel
                        , public nsIApplicationCacheChannel
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSICACHINGCHANNEL
-  NS_DECL_NSIUPLOADCHANNEL
-  NS_DECL_NSIUPLOADCHANNEL2
   NS_DECL_NSIENCODEDCHANNEL
   NS_DECL_NSIRESUMABLECHANNEL
   NS_DECL_NSIPROXIEDCHANNEL
   NS_DECL_NSITRACEABLECHANNEL
   NS_DECL_NSIAPPLICATIONCACHECONTAINER
   NS_DECL_NSIAPPLICATIONCACHECHANNEL
 
   HttpChannelChild();
--- a/netwerk/protocol/http/src/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/src/HttpChannelParent.cpp
@@ -89,16 +89,18 @@ NS_IMPL_ISUPPORTS4(HttpChannelParent,
 bool 
 HttpChannelParent::RecvAsyncOpen(const IPC::URI&            aURI,
                                  const IPC::URI&            aOriginalURI,
                                  const IPC::URI&            aDocURI,
                                  const IPC::URI&            aReferrerURI,
                                  const PRUint32&            loadFlags,
                                  const RequestHeaderTuples& requestHeaders,
                                  const nsHttpAtom&          requestMethod,
+                                 const nsCString&           uploadStreamData,
+                                 const PRInt32&             uploadStreamInfo,
                                  const PRUint16&            priority,
                                  const PRUint8&             redirectionLimit,
                                  const PRBool&              allowPipelining,
                                  const PRBool&              forceAllowThirdPartyCookie)
 {
   nsCOMPtr<nsIURI> uri = aURI;
   nsCOMPtr<nsIURI> originalUri = aOriginalURI;
   nsCOMPtr<nsIURI> docUri = aDocURI;
@@ -108,21 +110,21 @@ HttpChannelParent::RecvAsyncOpen(const I
   uri->GetSpec(uriSpec);
   LOG(("HttpChannelParent RecvAsyncOpen [this=%x uri=%s]\n", 
        this, uriSpec.get()));
 
   nsresult rv;
 
   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();
 
   if (originalUri)
     httpChan->SetOriginalURI(originalUri);
   if (docUri)
     httpChan->SetDocumentURI(docUri);
@@ -134,25 +136,38 @@ HttpChannelParent::RecvAsyncOpen(const I
   for (PRUint32 i = 0; i < requestHeaders.Length(); i++)
     httpChan->SetRequestHeader(requestHeaders[i].mHeader,
                                requestHeaders[i].mValue,
                                requestHeaders[i].mMerge);
 
   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);
   httpChan->SetAllowPipelining(allowPipelining);
   httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
 
   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;
 }
 
 bool 
 HttpChannelParent::RecvSetPriority(const PRUint16& priority)
 {
   nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
--- a/netwerk/protocol/http/src/HttpChannelParent.h
+++ b/netwerk/protocol/http/src/HttpChannelParent.h
@@ -70,16 +70,18 @@ public:
 protected:
   virtual bool RecvAsyncOpen(const IPC::URI&            uri,
                              const IPC::URI&            originalUri,
                              const IPC::URI&            docUri,
                              const IPC::URI&            referrerUri,
                              const PRUint32&            loadFlags,
                              const RequestHeaderTuples& requestHeaders,
                              const nsHttpAtom&          requestMethod,
+                             const nsCString&           uploadStreamData,
+                             const PRInt32&             uploadStreamInfo,
                              const PRUint16&            priority,
                              const PRUint8&             redirectionLimit,
                              const PRBool&              allowPipelining,
                              const PRBool&              forceAllowThirdPartyCookie);
 
   virtual bool RecvSetPriority(const PRUint16& priority);
 
   virtual void ActorDestroy(ActorDestroyReason why);
--- a/netwerk/protocol/http/src/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/src/PHttpChannel.ipdl
@@ -64,16 +64,18 @@ parent:
             // originalURI != uri (about:credits?); also not clear if chrome
             // channel would ever need to know.  Can we get rid of next arg?
             URI                 original,
             URI                 doc,
             URI                 referrer,
             PRUint32            loadFlags,
             RequestHeaderTuples requestHeaders,
             nsHttpAtom          requestMethod,
+            nsCString           uploadStreamData,
+            PRInt32             uploadStreamInfo,
             PRUint16            priority,
             PRUint8             redirectionLimit,
             PRBool              allowPipelining,
             PRBool              forceAllowThirdPartyCookie);
 
   SetPriority(PRUint16 priority);
 
 child:
--- a/netwerk/protocol/http/src/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/src/nsHttpChannel.cpp
@@ -95,17 +95,16 @@ nsHttpChannel::nsHttpChannel()
     , mStartPos(LL_MAXUINT)
     , mPendingAsyncCallOnResume(nsnull)
     , mSuspendCount(0)
     , mApplyConversion(PR_TRUE)
     , mCachedContentIsValid(PR_FALSE)
     , mCachedContentIsPartial(PR_FALSE)
     , mCanceled(PR_FALSE)
     , mTransactionReplaced(PR_FALSE)
-    , mUploadStreamHasHeaders(PR_FALSE)
     , mAuthRetryPending(PR_FALSE)
     , mProxyAuth(PR_FALSE)
     , mTriedProxyAuth(PR_FALSE)
     , mTriedHostAuth(PR_FALSE)
     , mSuppressDefensiveAuth(PR_FALSE)
     , mResuming(PR_FALSE)
     , mInitedCacheEntry(PR_FALSE)
     , mCacheForOfflineUse(PR_FALSE)
@@ -4195,107 +4194,16 @@ nsHttpChannel::SetupFallbackChannel(cons
 {
     LOG(("nsHttpChannel::SetupFallbackChannel [this=%x, key=%s]",
          this, aFallbackKey));
     mFallbackChannel = PR_TRUE;
     mFallbackKey = 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
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::GetApplyConversion(PRBool *value)
 {
--- a/netwerk/protocol/http/src/nsHttpChannel.h
+++ b/netwerk/protocol/http/src/nsHttpChannel.h
@@ -51,18 +51,16 @@
 
 #include "nsIHttpEventSink.h"
 #include "nsICachingChannel.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsICacheListener.h"
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIEncodedChannel.h"
-#include "nsIUploadChannel.h"
-#include "nsIUploadChannel2.h"
 #include "nsIStringEnumerator.h"
 #include "nsIPrompt.h"
 #include "nsIResumableChannel.h"
 #include "nsIProtocolProxyCallback.h"
 #include "nsICancelable.h"
 #include "nsIProxiedChannel.h"
 #include "nsITraceableChannel.h"
 #include "nsIAuthPromptCallback.h"
@@ -74,35 +72,31 @@ using namespace mozilla::net;
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel
 //-----------------------------------------------------------------------------
 
 class nsHttpChannel : public HttpBaseChannel
                     , public nsIStreamListener
                     , public nsICachingChannel
-                    , public nsIUploadChannel
-                    , public nsIUploadChannel2
                     , public nsICacheListener
                     , public nsIEncodedChannel
                     , public nsITransportEventSink
                     , public nsIResumableChannel
                     , public nsIProtocolProxyCallback
                     , public nsIProxiedChannel
                     , public nsITraceableChannel
                     , public nsIApplicationCacheChannel
                     , public nsIAuthPromptCallback
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSICACHINGCHANNEL
-    NS_DECL_NSIUPLOADCHANNEL
-    NS_DECL_NSIUPLOADCHANNEL2
     NS_DECL_NSICACHELISTENER
     NS_DECL_NSIENCODEDCHANNEL
     NS_DECL_NSITRANSPORTEVENTSINK
     NS_DECL_NSIRESUMABLECHANNEL
     NS_DECL_NSIPROTOCOLPROXYCALLBACK
     NS_DECL_NSIPROXIEDCHANNEL
     NS_DECL_NSITRACEABLECHANNEL
     NS_DECL_NSIAPPLICATIONCACHECONTAINER
@@ -125,16 +119,20 @@ public:
     NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey);
     // nsISupportsPriority
     NS_IMETHOD SetPriority(PRInt32 value);
 
 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;
         nsresult rv = referrer->GetAsciiSpec(spec);
         if (NS_FAILED(rv)) return rv;
         mReferrer = referrer;
         mRequestHead.SetHeader(nsHttp::Referer, spec);
         return NS_OK;
@@ -241,17 +239,16 @@ private:
     /**
      * Method called to resume suspended transaction after we got credentials
      * from the user. Called from OnAuthAvailable callback or OnAuthCancelled
      * when credentials for next challenge were obtained synchronously.
      */
     nsresult ContinueOnAuthAvailable(const nsCSubstring& creds);
 
 private:
-    nsCOMPtr<nsIInputStream>          mUploadStream;
     nsCOMPtr<nsISupports>             mSecurityInfo;
     nsCOMPtr<nsICancelable>           mProxyRequest;
 
     nsRefPtr<nsInputStreamPump>       mTransactionPump;
     nsRefPtr<nsHttpTransaction>       mTransaction;
 
     PRUint64                          mLogicalOffset;
     nsCString                         mUserSetCookieHeader;
@@ -313,17 +310,16 @@ private:
     nsCString                         mFallbackKey;
 
     // state flags
     PRUint32                          mApplyConversion          : 1;
     PRUint32                          mCachedContentIsValid     : 1;
     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.
     PRUint32                          mProxyAuth                : 1;
     PRUint32                          mTriedProxyAuth           : 1;
     PRUint32                          mTriedHostAuth            : 1;
     PRUint32                          mSuppressDefensiveAuth    : 1;
     PRUint32                          mResuming                 : 1;