bugzilla 640213 Implement RFC 2616 "Upgrade" (Section 14.42) r=biesi sr=bz
authorPatrick McManus <mcmanus@ducksong.com>
Thu, 19 May 2011 19:43:37 -0400
changeset 69732 9197b91fd9f4eb36f2df6f3a534115a9b69ffeed
parent 69731 75fc3e9b1a6ee4ce60e8b8164c9843bb6e6afe81
child 69733 8c4813583040aaecb817dd8e5cc2243a5ca87b64
push id20086
push usermcmanus@ducksong.com
push dateThu, 19 May 2011 23:59:31 +0000
treeherdermozilla-central@9197b91fd9f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, bz
bugs640213
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bugzilla 640213 Implement RFC 2616 "Upgrade" (Section 14.42) r=biesi sr=bz
netwerk/base/src/Makefile.in
netwerk/base/src/nsPreloadedStream.cpp
netwerk/base/src/nsPreloadedStream.h
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/nsAHttpConnection.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpPipeline.cpp
netwerk/protocol/http/nsHttpTransaction.cpp
netwerk/protocol/http/nsHttpTransaction.h
netwerk/protocol/http/nsIHttpChannelInternal.idl
--- a/netwerk/base/src/Makefile.in
+++ b/netwerk/base/src/Makefile.in
@@ -86,16 +86,17 @@ CPPSRCS		= \
 		nsURIChecker.cpp \
 		nsURLHelper.cpp \
 		nsURLParsers.cpp \
 		nsNetStrings.cpp \
 		nsBase64Encoder.cpp \
 		nsSerializationHelper.cpp \
 		nsDNSPrefetch.cpp \
 		RedirectChannelRegistrar.cpp \
+		nsPreloadedStream.cpp \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
 	CPPSRCS += nsURLHelperOS2.cpp
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 	CPPSRCS += nsURLHelperWin.cpp
 	CPPSRCS += nsNativeConnectionHelper.cpp
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/nsPreloadedStream.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsPreloadedStream.h"
+#include "nsIRunnable.h"
+
+#include "nsThreadUtils.h"
+#include "nsAlgorithm.h"
+#include "prmem.h"
+   
+namespace mozilla {
+namespace net {
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsPreloadedStream,
+                              nsIInputStream,
+                              nsIAsyncInputStream)
+
+nsPreloadedStream::nsPreloadedStream(nsIAsyncInputStream *aStream,
+                                     const char *data, PRUint32 datalen)
+    : mStream(aStream),
+      mOffset(0),
+      mLen(datalen)
+{
+    mBuf = (char *) moz_xmalloc(datalen);
+    memcpy(mBuf, data, datalen);
+}
+
+nsPreloadedStream::~nsPreloadedStream()
+{
+    moz_free(mBuf);
+}
+
+NS_IMETHODIMP
+nsPreloadedStream::Close()
+{
+    mLen = 0;
+    return mStream->Close();
+}
+
+
+NS_IMETHODIMP
+nsPreloadedStream::Available(PRUint32 *_retval NS_OUTPARAM)
+{
+    PRUint32 avail = 0;
+    
+    nsresult rv = mStream->Available(&avail);
+    if (NS_FAILED(rv))
+        return rv;
+    *_retval = avail + mLen;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPreloadedStream::Read(char *aBuf, PRUint32 aCount,
+                        PRUint32 *_retval NS_OUTPARAM)
+{
+    if (!mLen)
+        return mStream->Read(aBuf, aCount, _retval);
+    
+    PRUint32 toRead = NS_MIN(mLen, aCount);
+    memcpy(aBuf, mBuf + mOffset, toRead);
+    mOffset += toRead;
+    mLen -= toRead;
+    *_retval = toRead;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPreloadedStream::ReadSegments(nsWriteSegmentFun aWriter,
+                                void *aClosure, PRUint32 aCount,
+                                PRUint32 *result)
+{
+    if (!mLen)
+        return mStream->ReadSegments(aWriter, aClosure, aCount, result);
+
+    *result = 0;
+    while (mLen > 0 && aCount > 0) {
+        PRUint32 toRead = NS_MIN(mLen, aCount);
+        PRUint32 didRead = 0;
+        nsresult rv;
+
+        rv = aWriter(this, aClosure, mBuf + mOffset, *result, toRead, &didRead);
+
+        if (NS_FAILED(rv))
+            return NS_OK;
+
+        *result += didRead;
+        mOffset += didRead;
+        mLen -= didRead;
+        aCount -= didRead;
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPreloadedStream::IsNonBlocking(PRBool *_retval NS_OUTPARAM)
+{
+    return mStream->IsNonBlocking(_retval);
+}
+
+NS_IMETHODIMP
+nsPreloadedStream::CloseWithStatus(nsresult aStatus)
+{
+    mLen = 0;
+    return mStream->CloseWithStatus(aStatus);
+}
+
+class RunOnThread : public nsRunnable
+{
+public:
+    RunOnThread(nsIAsyncInputStream *aStream,
+                nsIInputStreamCallback *aCallback)
+      : mStream(aStream),
+        mCallback(aCallback) {}
+    
+    virtual ~RunOnThread() {}
+    
+    NS_IMETHOD Run()
+    {
+        mCallback->OnInputStreamReady(mStream);
+        return NS_OK;
+    }
+
+private:
+    nsCOMPtr<nsIAsyncInputStream>    mStream;
+    nsCOMPtr<nsIInputStreamCallback> mCallback;
+};
+
+NS_IMETHODIMP
+nsPreloadedStream::AsyncWait(nsIInputStreamCallback *aCallback,
+                             PRUint32 aFlags,
+                             PRUint32 aRequestedCount,
+                             nsIEventTarget *aEventTarget)
+{
+    if (!mLen)
+        return mStream->AsyncWait(aCallback, aFlags, aRequestedCount,
+                                  aEventTarget);
+
+    if (!aEventTarget)
+        return aCallback->OnInputStreamReady(this);
+
+    nsCOMPtr<nsIRunnable> event =
+        new RunOnThread(this, aCallback);
+    return aEventTarget->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/nsPreloadedStream.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This class allows you to prefix an existing nsIAsyncInputStream
+ * with a preloaded block of data known at construction time by wrapping the
+ * two data sources into a new nsIAsyncInputStream. Readers of the new
+ * stream initially see the preloaded data and when that has been exhausted
+ * they automatically read from the wrapped stream.
+ *
+ * It is used by nsHttpConnection when it has over buffered while reading from
+ * the HTTP input socket and accidentally consumed data that belongs to
+ * a different protocol via the HTTP Upgrade mechanism. That over-buffered
+ * data is preloaded together with the input socket to form the new input socket
+ * given to the new protocol handler.
+*/
+
+#ifndef nsPreloadedStream_h__
+#define nsPreloadedStream_h__
+
+#include "nsIAsyncInputStream.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+namespace net {
+
+class nsPreloadedStream : public nsIAsyncInputStream
+{
+ public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIINPUTSTREAM
+    NS_DECL_NSIASYNCINPUTSTREAM
+
+    nsPreloadedStream(nsIAsyncInputStream *aStream, 
+                      const char *data, PRUint32 datalen);
+private:
+    ~nsPreloadedStream();
+
+    nsCOMPtr<nsIAsyncInputStream> mStream;
+
+    char *mBuf;
+    PRUint32 mOffset;
+    PRUint32 mLen;
+};
+        
+} // namespace net
+} // namespace mozilla
+
+#endif
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1234,16 +1234,28 @@ HttpBaseChannel::GetRemotePort(PRInt32* 
     *port = (PRInt32)PR_ntohs(mPeerAddr.ipv6.port);
   }
   else
     return NS_ERROR_NOT_AVAILABLE;
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::HTTPUpgrade(const nsACString &aProtocolName,
+                             nsIHttpUpgradeListener *aListener)
+{
+    NS_ENSURE_ARG(!aProtocolName.IsEmpty());
+    NS_ENSURE_ARG_POINTER(aListener);
+    
+    mUpgradeProtocol = aProtocolName;
+    mUpgradeProtocolCallback = aListener;
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsISupportsPriority
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetPriority(PRInt32 *value)
 {
   *value = mPriority;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -161,16 +161,18 @@ public:
   NS_IMETHOD GetRemotePort(PRInt32* port);
   inline void CleanRedirectCacheChainIfNecessary()
   {
       if (mRedirectedCachekeys) {
           delete mRedirectedCachekeys;
           mRedirectedCachekeys = nsnull;
       }
   }
+  NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
+                         nsIHttpUpgradeListener *aListener); 
 
   // nsISupportsPriority
   NS_IMETHOD GetPriority(PRInt32 *value);
   NS_IMETHOD AdjustPriority(PRInt32 delta);
 
   // nsIResumableChannel
   NS_IMETHOD GetEntityID(nsACString& aEntityID);
 
@@ -241,16 +243,20 @@ protected:
   nsCString                         mSpec; // ASCII encoded URL spec
   nsCString                         mContentTypeHint;
   nsCString                         mContentCharsetHint;
   nsCString                         mUserSetCookieHeader;
 
   PRNetAddr                         mSelfAddr;
   PRNetAddr                         mPeerAddr;
 
+  // HTTP Upgrade Data
+  nsCString                        mUpgradeProtocol;
+  nsCOMPtr<nsIHttpUpgradeListener> mUpgradeProtocolCallback;
+
   // Resumable channel specific data
   nsCString                         mEntityID;
   PRUint64                          mStartPos;
 
   nsresult                          mStatus;
   PRUint32                          mLoadFlags;
   PRInt16                           mPriority;
   PRUint8                           mCaps;
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -40,16 +40,19 @@
 
 #include "nsISupports.h"
 
 class nsAHttpTransaction;
 class nsHttpRequestHead;
 class nsHttpResponseHead;
 class nsHttpConnectionInfo;
 class nsHttpConnection;
+class nsISocketTransport;
+class nsIAsyncInputStream;
+class nsIAsyncOutputStream;
 
 //-----------------------------------------------------------------------------
 // Abstract base class for a HTTP connection
 //-----------------------------------------------------------------------------
 
 class nsAHttpConnection : public nsISupports
 {
 public:
@@ -88,16 +91,22 @@ public:
     //        is equivalent to NS_OK.
     //
     virtual void CloseTransaction(nsAHttpTransaction *transaction,
                                   nsresult reason) = 0;
 
     // get a reference to the connection's connection info object.
     virtual void GetConnectionInfo(nsHttpConnectionInfo **) = 0;
 
+    // get the transport level information for this connection. This may fail
+    // if it is in use.
+    virtual nsresult TakeTransport(nsISocketTransport **,
+                                   nsIAsyncInputStream **,
+                                   nsIAsyncOutputStream **) = 0;
+
     // called by a transaction to get the security info from the socket.
     virtual void GetSecurityInfo(nsISupports **) = 0;
 
     // called by a transaction to determine whether or not the connection is
     // persistent... important in determining the end of a response.
     virtual PRBool IsPersistent() = 0;
 
     // called to determine if a connection has been reused.
@@ -118,16 +127,19 @@ public:
 };
 
 #define NS_DECL_NSAHTTPCONNECTION \
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, PRBool *reset); \
     nsresult ResumeSend(); \
     nsresult ResumeRecv(); \
     void CloseTransaction(nsAHttpTransaction *, nsresult); \
     void GetConnectionInfo(nsHttpConnectionInfo **); \
+    nsresult TakeTransport(nsISocketTransport **,    \
+                           nsIAsyncInputStream **,   \
+                           nsIAsyncOutputStream **); \
     void GetSecurityInfo(nsISupports **); \
     PRBool IsPersistent(); \
     PRBool IsReused(); \
     nsresult PushBack(const char *, PRUint32); \
     PRBool LastTransactionExpectedNoContent(); \
     void   SetLastTransactionExpectedNoContent(PRBool); \
     nsHttpConnection *TakeHttpConnection();
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -660,16 +660,26 @@ nsHttpChannel::SetupTransaction()
         return NS_ERROR_OUT_OF_MEMORY;
 
     // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
     if (mLoadFlags & LOAD_ANONYMOUS)
         mCaps |= NS_HTTP_LOAD_ANONYMOUS;
 
     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
 
+    if (mUpgradeProtocolCallback) {
+        mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, PR_FALSE);
+        mRequestHead.SetHeader(nsHttp::Connection,
+                               nsDependentCString(nsHttp::Upgrade.get()),
+                               PR_TRUE);
+        mCaps |=  NS_HTTP_STICKY_CONNECTION;
+        mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
+        mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
+    }
+
     nsCOMPtr<nsIAsyncInputStream> responseStream;
     rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
                             mUploadStream, mUploadStreamHasHeaders,
                             NS_GetCurrentThread(), callbacks, this,
                             getter_AddRefs(responseStream));
     if (NS_FAILED(rv)) {
         mTransaction = nsnull;
         return rv;
@@ -3999,35 +4009,38 @@ nsHttpChannel::OnStopRequest(nsIRequest 
             gHttpHandler->CancelTransaction(mTransaction, status); 
     }
 
     if (mTransaction) {
         // determine if we should call DoAuthRetry
         PRBool authRetry = mAuthRetryPending && NS_SUCCEEDED(status);
 
         //
-        // grab reference to connection in case we need to retry an
-        // authentication request over it.  this applies to connection based
-        // authentication schemes only.  for request based schemes, conn is not
-        // needed, so it may be null.
-        // 
+        // grab references to connection in case we need to retry an
+        // authentication request over it or use it for an upgrade
+        // to another protocol.
+        //
         // this code relies on the code in nsHttpTransaction::Close, which
         // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
         // keep the connection around after the transaction is finished.
         //
         nsRefPtr<nsAHttpConnection> conn;
         if (authRetry && (mCaps & NS_HTTP_STICKY_CONNECTION)) {
             conn = mTransaction->Connection();
             // This is so far a workaround to fix leak when reusing unpersistent
             // connection for authentication retry. See bug 459620 comment 4
             // for details.
             if (conn && !conn->IsPersistent())
                 conn = nsnull;
         }
 
+        nsRefPtr<nsAHttpConnection> stickyConn;
+        if (mCaps & NS_HTTP_STICKY_CONNECTION)
+            stickyConn = mTransaction->Connection();
+        
         // at this point, we're done with the transaction
         mTransaction = nsnull;
         mTransactionPump = 0;
 
         // handle auth retry...
         if (authRetry) {
             mAuthRetryPending = PR_FALSE;
             status = DoAuthRetry(conn);
@@ -4042,16 +4055,32 @@ nsHttpChannel::OnStopRequest(nsIRequest 
             // NOTE: since we have a failure status, we can ignore the return
             // value from onStartRequest.
             mListener->OnStartRequest(this, mListenerContext);
         }
 
         // if this transaction has been replaced, then bail.
         if (mTransactionReplaced)
             return NS_OK;
+        
+        if (mUpgradeProtocolCallback && stickyConn &&
+            mResponseHead && mResponseHead->Status() == 101) {
+            nsCOMPtr<nsISocketTransport>    socketTransport;
+            nsCOMPtr<nsIAsyncInputStream>   socketIn;
+            nsCOMPtr<nsIAsyncOutputStream>  socketOut;
+
+            nsresult rv;
+            rv = stickyConn->TakeTransport(getter_AddRefs(socketTransport),
+                                           getter_AddRefs(socketIn),
+                                           getter_AddRefs(socketOut));
+            if (NS_SUCCEEDED(rv))
+                mUpgradeProtocolCallback->OnTransportAvailable(socketTransport,
+                                                               socketIn,
+                                                               socketOut);
+        }
     }
 
     mIsPending = PR_FALSE;
     mStatus = status;
 
     // perform any final cache operations before we close the cache entry.
     if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE) &&
         mRequestTimeInitialized){
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -47,24 +47,27 @@
 #include "nsISocketTransport.h"
 #include "nsIServiceManager.h"
 #include "nsISSLSocketControl.h"
 #include "nsStringStream.h"
 #include "netCore.h"
 #include "nsNetCID.h"
 #include "nsProxyRelease.h"
 #include "prmem.h"
+#include "nsPreloadedStream.h"
 
 #ifdef DEBUG
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 #endif
 
 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
 
+using namespace mozilla::net;
+
 //-----------------------------------------------------------------------------
 // nsHttpConnection <public>
 //-----------------------------------------------------------------------------
 
 nsHttpConnection::nsHttpConnection()
     : mTransaction(nsnull)
     , mLastReadTime(0)
     , mIdleTimeout(0)
@@ -448,16 +451,36 @@ nsHttpConnection::OnHeadersAvailable(nsA
             NS_ASSERTION(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed");
         }
         else {
             LOG(("proxy CONNECT failed! ssl=%s\n",
                  mConnInfo->UsingSSL() ? "true" :"false"));
             mTransaction->SetSSLConnectFailed();
         }
     }
+    
+    const char *upgradeReq = requestHead->PeekHeader(nsHttp::Upgrade);
+    if (upgradeReq) {
+        LOG(("HTTP Upgrade in play - disable keepalive\n"));
+        DontReuse();
+    }
+    
+    if (responseHead->Status() == 101) {
+        const char *upgradeResp = responseHead->PeekHeader(nsHttp::Upgrade);
+        if (!upgradeReq || !upgradeResp ||
+            !nsHttp::FindToken(upgradeResp, upgradeReq,
+                               HTTP_HEADER_VALUE_SEPS)) {
+            LOG(("HTTP 101 Upgrade header mismatch req = %s, resp = %s\n",
+                 upgradeReq, upgradeResp));
+            Close(NS_ERROR_ABORT);
+        }
+        else {
+            LOG(("HTTP Upgrade Response to %s\n", upgradeResp));
+        }
+    }
 
     return NS_OK;
 }
 
 PRBool
 nsHttpConnection::IsReused()
 {
     if (mIsReused)
@@ -473,28 +496,68 @@ nsHttpConnection::IsReused()
 
 void
 nsHttpConnection::SetIsReusedAfter(PRUint32 afterMilliseconds)
 {
     mConsiderReusedAfterEpoch = PR_IntervalNow();
     mConsiderReusedAfterInterval = PR_MillisecondsToInterval(afterMilliseconds);
 }
 
+nsresult
+nsHttpConnection::TakeTransport(nsISocketTransport  **aTransport,
+                                nsIAsyncInputStream **aInputStream,
+                                nsIAsyncOutputStream **aOutputStream)
+{
+    if (mTransaction && !mTransaction->IsDone())
+        return NS_ERROR_IN_PROGRESS;
+    if (!(mSocketTransport && mSocketIn && mSocketOut))
+        return NS_ERROR_NOT_INITIALIZED;
+
+    if (mInputOverflow)
+        mSocketIn = mInputOverflow.forget();
+
+    NS_IF_ADDREF(*aTransport = mSocketTransport);
+    NS_IF_ADDREF(*aInputStream = mSocketIn);
+    NS_IF_ADDREF(*aOutputStream = mSocketOut);
+
+    mSocketTransport->SetSecurityCallbacks(nsnull);
+    mSocketTransport->SetEventSink(nsnull, nsnull);
+    mSocketTransport = nsnull;
+    mSocketIn = nsnull;
+    mSocketOut = nsnull;
+    
+    return NS_OK;
+}
+
 void
 nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketTransport) {
         if (NS_FAILED(mSocketTransport->GetSecurityInfo(secinfo)))
             *secinfo = nsnull;
     }
 }
 
 nsresult
+nsHttpConnection::PushBack(const char *data, PRUint32 length)
+{
+    LOG(("nsHttpConnection::PushBack [this=%p, length=%d]\n", this, length));
+
+    if (mInputOverflow) {
+        NS_ERROR("nsHttpConnection::PushBack only one buffer supported");
+        return NS_ERROR_UNEXPECTED;
+    }
+    
+    mInputOverflow = new nsPreloadedStream(mSocketIn, data, length);
+    return NS_OK;
+}
+
+nsresult
 nsHttpConnection::ResumeSend()
 {
     LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketOut)
         return mSocketOut->AsyncWait(this, 0, 0, nsnull);
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -125,22 +125,25 @@ public:
 
     nsAHttpTransaction   *Transaction()    { return mTransaction; }
     nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; }
 
     // nsAHttpConnection compatible methods (non-virtual):
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, PRBool *reset);
     void     CloseTransaction(nsAHttpTransaction *, nsresult reason);
     void     GetConnectionInfo(nsHttpConnectionInfo **ci) { NS_IF_ADDREF(*ci = mConnInfo); }
+    nsresult TakeTransport(nsISocketTransport **,
+                           nsIAsyncInputStream **,
+                           nsIAsyncOutputStream **);
     void     GetSecurityInfo(nsISupports **);
     PRBool   IsPersistent() { return IsKeepAlive(); }
     PRBool   IsReused();
     void     SetIsReusedAfter(PRUint32 afterMilliseconds);
     void     SetIdleTimeout(PRUint16 val) {mIdleTimeout = val;}
-    nsresult PushBack(const char *data, PRUint32 length) { NS_NOTREACHED("PushBack"); return NS_ERROR_UNEXPECTED; }
+    nsresult PushBack(const char *data, PRUint32 length);
     nsresult ResumeSend();
     nsresult ResumeRecv();
     PRInt64  MaxBytesRead() {return mMaxBytesRead;}
 
     static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
                                     PRUint32, PRUint32, PRUint32 *);
 
 private:
@@ -179,16 +182,18 @@ private:
     PRUint32                        mLastReadTime;
     PRUint16                        mMaxHangTime;    // max download time before dropping keep-alive status
     PRUint16                        mIdleTimeout;    // value of keep-alive: timeout=
     PRIntervalTime                  mConsiderReusedAfterInterval;
     PRIntervalTime                  mConsiderReusedAfterEpoch;
     PRInt64                         mCurrentBytesRead;   // data read per activation
     PRInt64                         mMaxBytesRead;       // max read in 1 activation
 
+    nsRefPtr<nsIAsyncInputStream>   mInputOverflow;
+
     PRPackedBool                    mKeepAlive;
     PRPackedBool                    mKeepAliveMask;
     PRPackedBool                    mSupportsPipelining;
     PRPackedBool                    mIsReused;
     PRPackedBool                    mIsActivated;
     PRPackedBool                    mCompletedProxyConnect;
     PRPackedBool                    mLastTransactionExpectedNoContent;
 };
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -1184,16 +1184,25 @@ nsHttpConnectionMgr::nsConnectionHandle:
 }
 
 void
 nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
 {
     mConn->GetConnectionInfo(result);
 }
 
+nsresult
+nsHttpConnectionMgr::
+nsConnectionHandle::TakeTransport(nsISocketTransport  **aTransport,
+                                  nsIAsyncInputStream **aInputStream,
+                                  nsIAsyncOutputStream **aOutputStream)
+{
+    return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
+}
+
 void
 nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
 {
     mConn->GetSecurityInfo(result);
 }
 
 PRBool
 nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
--- a/netwerk/protocol/http/nsHttpPipeline.cpp
+++ b/netwerk/protocol/http/nsHttpPipeline.cpp
@@ -225,16 +225,24 @@ nsHttpPipeline::CloseTransaction(nsAHttp
 
 void
 nsHttpPipeline::GetConnectionInfo(nsHttpConnectionInfo **result)
 {
     NS_ASSERTION(mConnection, "no connection");
     mConnection->GetConnectionInfo(result);
 }
 
+nsresult
+nsHttpPipeline::TakeTransport(nsISocketTransport  **aTransport,
+                              nsIAsyncInputStream **aInputStream,
+                              nsIAsyncOutputStream **aOutputStream)
+{
+    return mConnection->TakeTransport(aTransport, aInputStream, aOutputStream);
+}
+
 void
 nsHttpPipeline::GetSecurityInfo(nsISupports **result)
 {
     NS_ASSERTION(mConnection, "no connection");
     mConnection->GetSecurityInfo(result);
 }
 
 PRBool
@@ -252,16 +260,21 @@ nsHttpPipeline::IsReused()
 nsresult
 nsHttpPipeline::PushBack(const char *data, PRUint32 length)
 {
     LOG(("nsHttpPipeline::PushBack [this=%x len=%u]\n", this, length));
     
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ASSERTION(mPushBackLen == 0, "push back buffer already has data!");
 
+    // If we have no chance for a pipeline (e.g. due to an Upgrade)
+    // then push this data down to original connection
+    if (!mConnection->IsPersistent())
+        return mConnection->PushBack(data, length);
+
     // PushBack is called recursively from WriteSegments
 
     // XXX we have a design decision to make here.  either we buffer the data
     // and process it when we return to WriteSegments, or we attempt to move
     // onto the next transaction from here.  doing so adds complexity with the
     // benefit of eliminating the extra buffer copy.  the buffer is at most
     // 4096 bytes, so it is really unclear if there is any value in the added
     // complexity.  besides simplicity, buffering this data has the advantage
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -131,16 +131,17 @@ nsHttpTransaction::nsHttpTransaction()
     , mDidContentStart(PR_FALSE)
     , mNoContent(PR_FALSE)
     , mSentData(PR_FALSE)
     , mReceivedData(PR_FALSE)
     , mStatusEventPending(PR_FALSE)
     , mHasRequestBody(PR_FALSE)
     , mSSLConnectFailed(PR_FALSE)
     , mHttpResponseMatched(PR_FALSE)
+    , mPreserveStream(PR_FALSE)
 {
     LOG(("Creating nsHttpTransaction @%x\n", this));
 }
 
 nsHttpTransaction::~nsHttpTransaction()
 {
     LOG(("Destroying nsHttpTransaction @%x\n", this));
 
@@ -801,17 +802,18 @@ nsHttpTransaction::ParseLineSegment(char
         return NS_ERROR_ABORT;
     }
     mLineBuf.Append(segment, len);
     
     // a line buf with only a new line char signifies the end of headers.
     if (mLineBuf.First() == '\n') {
         mLineBuf.Truncate();
         // discard this response if it is a 100 continue or other 1xx status.
-        if (mResponseHead->Status() / 100 == 1) {
+        PRUint16 status = mResponseHead->Status();
+        if ((status != 101) && (status / 100 == 1)) {
             LOG(("ignoring 1xx response\n"));
             mHaveStatusLine = PR_FALSE;
             mHttpResponseMatched = PR_FALSE;
             mConnection->SetLastTransactionExpectedNoContent(PR_TRUE);
             mResponseHead->Reset();
             return NS_OK;
         }
         mHaveAllHeaders = PR_TRUE;
@@ -972,16 +974,18 @@ nsHttpTransaction::HandleContentStart()
             mHttpResponseMatched = PR_FALSE;
             mResponseHead->Reset();
             // wait to be called again...
             return NS_OK;
         }
 
         // check if this is a no-content response
         switch (mResponseHead->Status()) {
+        case 101:
+            mPreserveStream = PR_TRUE;    // fall through to other no content
         case 204:
         case 205:
         case 304:
             mNoContent = PR_TRUE;
             LOG(("this response should not contain a body.\n"));
             break;
         }
         mConnection->SetLastTransactionExpectedNoContent(mNoContent);
@@ -1048,17 +1052,17 @@ nsHttpTransaction::HandleContent(char *b
         rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
         if (NS_FAILED(rv)) return rv;
     }
     else if (mContentLength >= PRInt64(0)) {
         // HTTP/1.0 servers have been known to send erroneous Content-Length
         // headers. So, unless the connection is persistent, we must make
         // allowances for a possibly invalid Content-Length header. Thus, if
         // NOT persistent, we simply accept everything in |buf|.
-        if (mConnection->IsPersistent()) {
+        if (mConnection->IsPersistent() || mPreserveStream) {
             PRInt64 remaining = mContentLength - mContentRead;
             PRInt64 count64 = count;
             *contentRead = PR_MIN(count64, remaining);
             *contentRemaining = count - *contentRead;
         }
         else {
             *contentRead = count;
             // mContentLength might need to be increased...
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -206,15 +206,16 @@ private:
     PRPackedBool                    mDidContentStart;
     PRPackedBool                    mNoContent; // expecting an empty entity body
     PRPackedBool                    mSentData;
     PRPackedBool                    mReceivedData;
     PRPackedBool                    mStatusEventPending;
     PRPackedBool                    mHasRequestBody;
     PRPackedBool                    mSSLConnectFailed;
     PRPackedBool                    mHttpResponseMatched;
+    PRPackedBool                    mPreserveStream;
 
     // mClosed           := transaction has been explicitly closed
     // mTransactionDone  := transaction ran to completion or was interrupted
     // mResponseComplete := transaction ran to completion
 };
 
 #endif // nsHttpTransaction_h__
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -38,25 +38,40 @@
 #include "nsISupports.idl"
 
 %{C++
 #include "nsTArray.h"
 class nsCString;
 %}
 [ptr] native StringArray(nsTArray<nsCString>);
 
+interface nsISocketTransport;
+interface nsIAsyncInputStream;
+interface nsIAsyncOutputStream;
 interface nsIURI;
 interface nsIProxyInfo;
 
 /**
+ * The callback interface for nsIHttpChannelInternal::HTTPUpgrade()
+ */
+
+[scriptable, uuid(5644af88-09e1-4fbd-83da-f012b3b30180)]
+interface nsIHttpUpgradeListener : nsISupports
+{
+    void onTransportAvailable(in nsISocketTransport   aTransport,
+                              in nsIAsyncInputStream  aSocketIn,
+                              in nsIAsyncOutputStream aSocketOut);
+};
+
+/**
  * Dumping ground for http.  This interface will never be frozen.  If you are
  * using any feature exposed by this interface, be aware that this interface
  * will change and you will be broken.  You have been warned.
  */
-[scriptable, uuid(12eb906a-71fe-4b79-b33a-6fe9ab57ea38)]
+[scriptable, uuid(9363fd96-af59-47e8-bddf-1d5e91acd336)]
 interface nsIHttpChannelInternal : nsISupports
 {
     /**
      * An http channel can own a reference to the document URI
      */
     attribute nsIURI documentURI;
 
     /**
@@ -140,9 +155,30 @@ interface nsIHttpChannelInternal : nsISu
      * nsIHttpActivityObserver.isActive is false. See bugs 534698 and 526207.
      */
     readonly attribute PRInt32 remotePort;
 
     /**
      * Transfer chain of redirected cache-keys.
      */
     [noscript] void setCacheKeysRedirectChain(in StringArray cacheKeys);
+
+    /**
+     * HTTPUpgrade allows for the use of HTTP to bootstrap another protocol
+     * via the RFC 2616 Upgrade request header in conjunction with a 101 level
+     * response. The nsIHttpUpgradeListener will have its 
+     * onTransportAvailable() method invoked if a matching 101 is processed.
+     * The arguments to onTransportAvailable provide the new protocol the low
+     * level tranport streams that are no longer used by HTTP.
+     *
+     * The onStartRequest and onStopRequest events are still delivered and the
+     * listener gets full control over the socket if and when onTransportAvailable
+     * is delievered.
+     *
+     * @param aProtocolName
+     *        The value of the HTTP Upgrade request header
+     * @param aListener
+     *        The callback object used to handle a successful upgrade
+     */
+    void HTTPUpgrade(in ACString aProtocolName,
+                     in nsIHttpUpgradeListener aListener);
+
 };