bug 665885 respect keepalive: "max=" attribute r=honzab
authorPatrick McManus <mcmanus@ducksong.com>
Thu, 22 Mar 2012 19:39:31 -0400
changeset 90100 e1e48e9ce79565e48f0b93ca75192976e92e379d
parent 90099 e83a04b972e9e858ae3a025a4ac0eac4286e1d22
child 90101 87ea59a6d1625dd810e9bf3985fceed8948437fe
push id22312
push usermak77@bonardo.net
push dateFri, 23 Mar 2012 11:50:43 +0000
treeherdermozilla-central@c20ec27eb0e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonzab
bugs665885
milestone14.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
bug 665885 respect keepalive: "max=" attribute r=honzab
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -81,16 +81,17 @@ nsHttpConnection::nsHttpConnection()
     , mKeepAliveMask(true)
     , mSupportsPipelining(false) // assume low-grade server
     , mIsReused(false)
     , mCompletedProxyConnect(false)
     , mLastTransactionExpectedNoContent(false)
     , mIdleMonitoring(false)
     , mProxyConnectInProgress(false)
     , mHttp1xTransactionCount(0)
+    , mRemainingConnectionUses(0xffffffff)
     , mClassification(nsAHttpTransaction::CLASS_GENERAL)
     , mNPNComplete(false)
     , mSetupNPNCalled(false)
     , mUsingSpdy(false)
     , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
     , mReportedSpdy(false)
     , mEverUsedSpdy(false)
 {
@@ -532,19 +533,38 @@ nsHttpConnection::DontReuse()
 {
     mKeepAliveMask = false;
     mKeepAlive = false;
     mIdleTimeout = 0;
     if (mSpdySession)
         mSpdySession->DontReuse();
 }
 
+// Checked by the Connection Manager before scheduling a pipelined transaction
+bool
+nsHttpConnection::SupportsPipelining()
+{
+    if (mTransaction &&
+        mTransaction->PipelineDepth() >= mRemainingConnectionUses) {
+        LOG(("nsHttpConnection::SupportsPipelining this=%p deny pipeline "
+             "because current depth %d exceeds max remaining uses %d\n",
+             this, mTransaction->PipelineDepth(), mRemainingConnectionUses));
+        return false;
+    }
+    return mSupportsPipelining && IsKeepAlive();
+}
+
 bool
 nsHttpConnection::CanReuse()
 {
+    if ((mTransaction ? mTransaction->PipelineDepth() : 0) >=
+        mRemainingConnectionUses) {
+        return false;
+    }
+
     bool canReuse;
     
     if (mSpdySession)
         canReuse = mSpdySession->CanReuse();
     else
         canReuse = IsKeepAlive();
 
     canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
@@ -732,19 +752,23 @@ nsHttpConnection::OnHeadersAvailable(nsA
         // We need at least version 1.1 to use pipelines
         gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
             mConnInfo, nsHttpConnectionMgr::RedVersionTooLow, this, 0);
     }
     else {
         // HTTP/1.1 connections are by default persistent
         if (val && !PL_strcasecmp(val, "close")) {
             mKeepAlive = false;
-            // persistent connections are required for pipelining to work
-            gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
-                mConnInfo, nsHttpConnectionMgr::BadExplicitClose, this, 0);
+
+            // persistent connections are required for pipelining to work - if
+            // this close was not pre-announced then generate the negative
+            // BadExplicitClose feedback
+            if (mRemainingConnectionUses > 1)
+                gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
+                    mConnInfo, nsHttpConnectionMgr::BadExplicitClose, this, 0);
         }
         else {
             mKeepAlive = true;
 
             // Do not support pipelining when we are establishing
             // an SSL tunnel though an HTTP proxy. Pipelining support
             // determination must be based on comunication with the
             // target server in this case. See bug 422016 for futher
@@ -782,34 +806,47 @@ nsHttpConnection::OnHeadersAvailable(nsA
     
     // if this connection is persistent, then the server may send a "Keep-Alive"
     // header specifying the maximum number of times the connection can be
     // reused as well as the maximum amount of time the connection can be idle
     // before the server will close it.  we ignore the max reuse count, because
     // a "keep-alive" connection is by definition capable of being reused, and
     // we only care about being able to reuse it once.  if a timeout is not 
     // specified then we use our advertized timeout value.
+    bool foundKeepAliveMax = false;
     if (mKeepAlive) {
         val = responseHead->PeekHeader(nsHttp::Keep_Alive);
 
         if (!mUsingSpdy) {
             const char *cp = PL_strcasestr(val, "timeout=");
             if (cp)
                 mIdleTimeout = PR_SecondsToInterval((PRUint32) atoi(cp + 8));
             else
                 mIdleTimeout = gHttpHandler->SpdyTimeout();
+
+            cp = PL_strcasestr(val, "max=");
+            if (cp) {
+                int val = atoi(cp + 4);
+                if (val > 0) {
+                    foundKeepAliveMax = true;
+                    mRemainingConnectionUses = static_cast<PRUint32>(val);
+                }
+            }
         }
         else {
             mIdleTimeout = gHttpHandler->SpdyTimeout();
         }
         
         LOG(("Connection can be reused [this=%p idle-timeout=%usec]\n",
              this, PR_IntervalToSeconds(mIdleTimeout)));
     }
 
+    if (!foundKeepAliveMax && mRemainingConnectionUses && !mUsingSpdy)
+        --mRemainingConnectionUses;
+
     if (!mProxyConnectStream)
         HandleAlternateProtocol(responseHead);
 
     // if we're doing an SSL proxy connect, then we need to check whether or not
     // the connect was successful.  if so, then we have to reset the transaction
     // and step-up the socket connection to SSL. finally, we have to wake up the
     // socket write request.
     if (mProxyConnectStream) {
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -101,17 +101,17 @@ public:
     nsresult Activate(nsAHttpTransaction *, PRUint8 caps, PRInt32 pri);
 
     // Close the underlying socket transport.
     void Close(nsresult reason);
 
     //-------------------------------------------------------------------------
     // XXX document when these are ok to call
 
-    bool     SupportsPipelining() { return mSupportsPipelining && IsKeepAlive(); }
+    bool     SupportsPipelining();
     bool     IsKeepAlive() { return mUsingSpdy ||
                                     (mKeepAliveMask && mKeepAlive); }
     bool     CanReuse();   // can this connection be reused?
     bool     CanDirectlyActivate();
 
     // Returns time in seconds for how long connection can be reused.
     PRUint32 TimeToLive();
 
@@ -245,16 +245,21 @@ private:
     bool                            mLastTransactionExpectedNoContent;
     bool                            mIdleMonitoring;
     bool                            mProxyConnectInProgress;
 
     // The number of <= HTTP/1.1 transactions performed on this connection. This
     // excludes spdy transactions.
     PRUint32                        mHttp1xTransactionCount;
 
+    // Keep-Alive: max="mRemainingConnectionUses" provides the number of future
+    // transactions (including the current one) that the server expects to allow
+    // on this persistent connection.
+    PRUint32                        mRemainingConnectionUses;
+
     nsAHttpTransaction::Classifier  mClassification;
 
     // SPDY related
     bool                            mNPNComplete;
     bool                            mSetupNPNCalled;
     bool                            mUsingSpdy;
     nsRefPtr<mozilla::net::SpdySession> mSpdySession;
     PRInt32                         mPriority;