LCOV - code coverage report
Current view: directory - netwerk/protocol/http - SpdySession.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 942 0 0.0 %
Date: 2012-06-02 Functions: 77 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim: set sw=2 ts=8 et tw=80 : */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Patrick McManus <mcmanus@ducksong.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "nsHttp.h"
      41                 : #include "SpdySession.h"
      42                 : #include "SpdyStream.h"
      43                 : #include "nsHttpConnection.h"
      44                 : #include "nsHttpHandler.h"
      45                 : #include "prnetdb.h"
      46                 : #include "mozilla/Telemetry.h"
      47                 : #include "mozilla/Preferences.h"
      48                 : #include "prprf.h"
      49                 : 
      50                 : #ifdef DEBUG
      51                 : // defined by the socket transport service while active
      52                 : extern PRThread *gSocketThread;
      53                 : #endif
      54                 : 
      55                 : namespace mozilla {
      56                 : namespace net {
      57                 : 
      58                 : // SpdySession has multiple inheritance of things that implement
      59                 : // nsISupports, so this magic is taken from nsHttpPipeline that
      60                 : // implements some of the same abstract classes.
      61               0 : NS_IMPL_THREADSAFE_ADDREF(SpdySession)
      62               0 : NS_IMPL_THREADSAFE_RELEASE(SpdySession)
      63               0 : NS_INTERFACE_MAP_BEGIN(SpdySession)
      64               0 :     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
      65               0 : NS_INTERFACE_MAP_END
      66                 : 
      67               0 : SpdySession::SpdySession(nsAHttpTransaction *aHttpTransaction,
      68                 :                          nsISocketTransport *aSocketTransport,
      69                 :                          PRInt32 firstPriority)
      70                 :   : mSocketTransport(aSocketTransport),
      71                 :     mSegmentReader(nsnull),
      72                 :     mSegmentWriter(nsnull),
      73                 :     mSendingChunkSize(kSendingChunkSize),
      74                 :     mNextStreamID(1),
      75                 :     mConcurrentHighWater(0),
      76                 :     mDownstreamState(BUFFERING_FRAME_HEADER),
      77                 :     mInputFrameBufferSize(kDefaultBufferSize),
      78                 :     mInputFrameBufferUsed(0),
      79                 :     mInputFrameDataLast(false),
      80                 :     mInputFrameDataStream(nsnull),
      81                 :     mNeedsCleanup(nsnull),
      82                 :     mDecompressBufferSize(kDefaultBufferSize),
      83                 :     mDecompressBufferUsed(0),
      84                 :     mShouldGoAway(false),
      85                 :     mClosed(false),
      86                 :     mCleanShutdown(false),
      87                 :     mGoAwayID(0),
      88                 :     mMaxConcurrent(kDefaultMaxConcurrent),
      89                 :     mConcurrent(0),
      90                 :     mServerPushedResources(0),
      91                 :     mOutputQueueSize(kDefaultQueueSize),
      92                 :     mOutputQueueUsed(0),
      93                 :     mOutputQueueSent(0),
      94               0 :     mLastReadEpoch(PR_IntervalNow()),
      95                 :     mPingSentEpoch(0),
      96                 :     mNextPingID(1),
      97               0 :     mPingThresholdExperiment(false)
      98                 : {
      99               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     100                 : 
     101               0 :   LOG3(("SpdySession::SpdySession %p transaction 1 = %p",
     102                 :         this, aHttpTransaction));
     103                 :   
     104               0 :   mStreamIDHash.Init();
     105               0 :   mStreamTransactionHash.Init();
     106               0 :   mConnection = aHttpTransaction->Connection();
     107               0 :   mInputFrameBuffer = new char[mInputFrameBufferSize];
     108               0 :   mDecompressBuffer = new char[mDecompressBufferSize];
     109               0 :   mOutputQueueBuffer = new char[mOutputQueueSize];
     110               0 :   zlibInit();
     111                 :   
     112               0 :   mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
     113               0 :   AddStream(aHttpTransaction, firstPriority);
     114               0 :   mLastDataReadEpoch = mLastReadEpoch;
     115                 :   
     116               0 :   DeterminePingThreshold();
     117               0 : }
     118                 : 
     119                 : void
     120               0 : SpdySession::DeterminePingThreshold()
     121                 : {
     122               0 :   mPingThreshold = gHttpHandler->SpdyPingThreshold();
     123                 : 
     124               0 :   if (!mPingThreshold || !gHttpHandler->AllowExperiments())
     125               0 :     return;
     126                 : 
     127               0 :   PRUint32 randomVal = gHttpHandler->Get32BitsOfPseudoRandom();
     128                 :   
     129                 :   // Use the lower 10 bits to select 1 in 1024 sessions for the
     130                 :   // ping threshold experiment. Somewhat less than that will actually be
     131                 :   // used because random values greater than the total http idle timeout
     132                 :   // for the session are discarded.
     133               0 :   if ((randomVal & 0x3ff) != 1)  // lottery
     134               0 :     return;
     135                 :   
     136               0 :   randomVal = randomVal >> 10; // those bits are used up
     137                 : 
     138                 :   // This session has been selected - use a random ping threshold of 10 +
     139                 :   // a random number from 0 to 255, based on the next 8 bits of the
     140                 :   // random buffer
     141                 :   PRIntervalTime randomThreshold =
     142               0 :     PR_SecondsToInterval((randomVal & 0xff) + 10);
     143               0 :   if (randomThreshold > gHttpHandler->IdleTimeout())
     144               0 :     return;
     145                 :   
     146               0 :   mPingThreshold = randomThreshold;
     147               0 :   mPingThresholdExperiment = true;
     148               0 :   LOG3(("SpdySession %p Ping Threshold Experimental Selection : %dsec\n",
     149                 :         this, PR_IntervalToSeconds(mPingThreshold)));
     150                 : }
     151                 : 
     152                 : PLDHashOperator
     153               0 : SpdySession::ShutdownEnumerator(nsAHttpTransaction *key,
     154                 :                                 nsAutoPtr<SpdyStream> &stream,
     155                 :                                 void *closure)
     156                 : {
     157               0 :   SpdySession *self = static_cast<SpdySession *>(closure);
     158                 :  
     159                 :   // On a clean server hangup the server sets the GoAwayID to be the ID of
     160                 :   // the last transaction it processed. If the ID of stream in the
     161                 :   // local session is greater than that it can safely be restarted because the
     162                 :   // server guarantees it was not partially processed.
     163               0 :   if (self->mCleanShutdown && (stream->StreamID() > self->mGoAwayID))
     164               0 :     stream->Close(NS_ERROR_NET_RESET); // can be restarted
     165                 :   else
     166               0 :     stream->Close(NS_ERROR_ABORT);
     167                 : 
     168               0 :   return PL_DHASH_NEXT;
     169                 : }
     170                 : 
     171               0 : SpdySession::~SpdySession()
     172                 : {
     173               0 :   LOG3(("SpdySession::~SpdySession %p mDownstreamState=%X",
     174                 :         this, mDownstreamState));
     175                 : 
     176               0 :   inflateEnd(&mDownstreamZlib);
     177               0 :   deflateEnd(&mUpstreamZlib);
     178                 :   
     179               0 :   mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
     180               0 :   Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
     181               0 :   Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
     182                 :   Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
     183               0 :                         mServerPushedResources);
     184               0 : }
     185                 : 
     186                 : void
     187               0 : SpdySession::LogIO(SpdySession *self, SpdyStream *stream, const char *label,
     188                 :                    const char *data, PRUint32 datalen)
     189                 : {
     190               0 :   if (!LOG4_ENABLED())
     191               0 :     return;
     192                 :   
     193               0 :   LOG4(("SpdySession::LogIO %p stream=%p id=0x%X [%s]",
     194                 :         self, stream, stream ? stream->StreamID() : 0, label));
     195                 : 
     196                 :   // Max line is (16 * 3) + 10(prefix) + newline + null
     197                 :   char linebuf[128];
     198                 :   PRUint32 index;
     199               0 :   char *line = linebuf;
     200                 : 
     201               0 :   linebuf[127] = 0;
     202                 : 
     203               0 :   for (index = 0; index < datalen; ++index) {
     204               0 :     if (!(index % 16)) {
     205               0 :       if (index) {
     206               0 :         *line = 0;
     207               0 :         LOG4(("%s", linebuf));
     208                 :       }
     209               0 :       line = linebuf;
     210               0 :       PR_snprintf(line, 128, "%08X: ", index);
     211               0 :       line += 10;
     212                 :     }
     213                 :     PR_snprintf(line, 128 - (line - linebuf), "%02X ",
     214               0 :                 ((unsigned char *)data)[index]);
     215               0 :     line += 3;
     216                 :   }
     217               0 :   if (index) {
     218               0 :     *line = 0;
     219               0 :     LOG4(("%s", linebuf));
     220                 :   }
     221                 : }
     222                 : 
     223                 : typedef nsresult  (*Control_FX) (SpdySession *self);
     224                 : static Control_FX sControlFunctions[] = 
     225                 : {
     226                 :   nsnull,
     227                 :   SpdySession::HandleSynStream,
     228                 :   SpdySession::HandleSynReply,
     229                 :   SpdySession::HandleRstStream,
     230                 :   SpdySession::HandleSettings,
     231                 :   SpdySession::HandleNoop,
     232                 :   SpdySession::HandlePing,
     233                 :   SpdySession::HandleGoAway,
     234                 :   SpdySession::HandleHeaders,
     235                 :   SpdySession::HandleWindowUpdate
     236                 : };
     237                 : 
     238                 : bool
     239               0 : SpdySession::RoomForMoreConcurrent()
     240                 : {
     241               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     242                 : 
     243               0 :   return (mConcurrent < mMaxConcurrent);
     244                 : }
     245                 : 
     246                 : bool
     247               0 : SpdySession::RoomForMoreStreams()
     248                 : {
     249               0 :   if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
     250               0 :     return false;
     251                 : 
     252               0 :   return !mShouldGoAway;
     253                 : }
     254                 : 
     255                 : PRIntervalTime
     256               0 : SpdySession::IdleTime()
     257                 : {
     258               0 :   return PR_IntervalNow() - mLastDataReadEpoch;
     259                 : }
     260                 : 
     261                 : void
     262               0 : SpdySession::ReadTimeoutTick(PRIntervalTime now)
     263                 : {
     264               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     265               0 :     NS_ABORT_IF_FALSE(mNextPingID & 1, "Ping Counter Not Odd");
     266                 : 
     267               0 :     if (!mPingThreshold)
     268               0 :       return;
     269                 : 
     270               0 :     LOG(("SpdySession::ReadTimeoutTick %p delta since last read %ds\n",
     271                 :          this, PR_IntervalToSeconds(now - mLastReadEpoch)));
     272                 : 
     273               0 :     if ((now - mLastReadEpoch) < mPingThreshold) {
     274                 :       // recent activity means ping is not an issue
     275               0 :       if (mPingSentEpoch)
     276               0 :         ClearPing(true);
     277               0 :       return;
     278                 :     }
     279                 : 
     280               0 :     if (mPingSentEpoch) {
     281               0 :       LOG(("SpdySession::ReadTimeoutTick %p handle outstanding ping\n"));
     282               0 :       if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
     283               0 :         LOG(("SpdySession::ReadTimeoutTick %p Ping Timer Exhaustion\n",
     284                 :              this));
     285               0 :         ClearPing(false);
     286               0 :         Close(NS_ERROR_NET_TIMEOUT);
     287                 :       }
     288               0 :       return;
     289                 :     }
     290                 :     
     291               0 :     LOG(("SpdySession::ReadTimeoutTick %p generating ping 0x%x\n",
     292                 :          this, mNextPingID));
     293                 : 
     294               0 :     if (mNextPingID == 0xffffffff) {
     295               0 :       LOG(("SpdySession::ReadTimeoutTick %p cannot form ping - ids exhausted\n",
     296                 :            this));
     297               0 :       return;
     298                 :     }
     299                 : 
     300               0 :     mPingSentEpoch = PR_IntervalNow();
     301               0 :     if (!mPingSentEpoch)
     302               0 :       mPingSentEpoch = 1; // avoid the 0 sentinel value
     303               0 :     GeneratePing(mNextPingID);
     304               0 :     mNextPingID += 2;
     305                 : 
     306               0 :     if (mNextPingID == 0xffffffff) {
     307               0 :       LOG(("SpdySession::ReadTimeoutTick %p "
     308                 :            "ping ids exhausted marking goaway\n", this));
     309               0 :       mShouldGoAway = true;
     310                 :     }
     311                 : }
     312                 : 
     313                 : void
     314               0 : SpdySession::ClearPing(bool pingOK)
     315                 : {
     316               0 :   mPingSentEpoch = 0;
     317                 : 
     318               0 :   if (mPingThresholdExperiment) {
     319               0 :     LOG3(("SpdySession::ClearPing %p mPingThresholdExperiment %dsec %s\n",
     320                 :           this, PR_IntervalToSeconds(mPingThreshold),
     321                 :           pingOK ? "pass" :"fail"));
     322                 : 
     323               0 :     if (pingOK)
     324                 :       Telemetry::Accumulate(Telemetry::SPDY_PING_EXPERIMENT_PASS,
     325               0 :                             PR_IntervalToSeconds(mPingThreshold));
     326                 :     else
     327                 :       Telemetry::Accumulate(Telemetry::SPDY_PING_EXPERIMENT_FAIL,
     328               0 :                             PR_IntervalToSeconds(mPingThreshold));
     329               0 :     mPingThreshold = gHttpHandler->SpdyPingThreshold();
     330               0 :     mPingThresholdExperiment = false;
     331                 :   }
     332               0 : }
     333                 : 
     334                 : PRUint32
     335               0 : SpdySession::RegisterStreamID(SpdyStream *stream)
     336                 : {
     337               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     338                 : 
     339               0 :   LOG3(("SpdySession::RegisterStreamID session=%p stream=%p id=0x%X "
     340                 :         "concurrent=%d",this, stream, mNextStreamID, mConcurrent));
     341                 : 
     342               0 :   NS_ABORT_IF_FALSE(mNextStreamID < 0xfffffff0,
     343                 :                     "should have stopped admitting streams");
     344                 :   
     345               0 :   PRUint32 result = mNextStreamID;
     346               0 :   mNextStreamID += 2;
     347                 : 
     348                 :   // We've used up plenty of ID's on this session. Start
     349                 :   // moving to a new one before there is a crunch involving
     350                 :   // server push streams or concurrent non-registered submits
     351               0 :   if (mNextStreamID >= kMaxStreamID)
     352               0 :     mShouldGoAway = true;
     353                 : 
     354               0 :   mStreamIDHash.Put(result, stream);
     355               0 :   return result;
     356                 : }
     357                 : 
     358                 : bool
     359               0 : SpdySession::AddStream(nsAHttpTransaction *aHttpTransaction,
     360                 :                        PRInt32 aPriority)
     361                 : {
     362               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     363               0 :   NS_ABORT_IF_FALSE(!mStreamTransactionHash.Get(aHttpTransaction),
     364                 :                     "AddStream duplicate transaction pointer");
     365                 : 
     366               0 :   aHttpTransaction->SetConnection(this);
     367                 :   SpdyStream *stream = new SpdyStream(aHttpTransaction,
     368                 :                                       this,
     369                 :                                       mSocketTransport,
     370                 :                                       mSendingChunkSize,
     371                 :                                       &mUpstreamZlib,
     372               0 :                                       aPriority);
     373                 : 
     374                 :   
     375               0 :   LOG3(("SpdySession::AddStream session=%p stream=%p NextID=0x%X (tentative)",
     376                 :         this, stream, mNextStreamID));
     377                 : 
     378               0 :   mStreamTransactionHash.Put(aHttpTransaction, stream);
     379                 : 
     380               0 :   if (RoomForMoreConcurrent()) {
     381               0 :     LOG3(("SpdySession::AddStream %p stream %p activated immediately.",
     382                 :           this, stream));
     383               0 :     ActivateStream(stream);
     384                 :   }
     385                 :   else {
     386               0 :     LOG3(("SpdySession::AddStream %p stream %p queued.",
     387                 :           this, stream));
     388               0 :     mQueuedStreams.Push(stream);
     389                 :   }
     390                 :   
     391               0 :   return true;
     392                 : }
     393                 : 
     394                 : void
     395               0 : SpdySession::ActivateStream(SpdyStream *stream)
     396                 : {
     397               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     398                 : 
     399               0 :   mConcurrent++;
     400               0 :   if (mConcurrent > mConcurrentHighWater)
     401               0 :     mConcurrentHighWater = mConcurrent;
     402               0 :   LOG3(("SpdySession::AddStream %p activating stream %p Currently %d "
     403                 :         "streams in session, high water mark is %d",
     404                 :         this, stream, mConcurrent, mConcurrentHighWater));
     405                 : 
     406               0 :   mReadyForWrite.Push(stream);
     407               0 :   SetWriteCallbacks();
     408                 : 
     409                 :   // Kick off the SYN transmit without waiting for the poll loop
     410                 :   PRUint32 countRead;
     411               0 :   ReadSegments(nsnull, kDefaultBufferSize, &countRead);
     412               0 : }
     413                 : 
     414                 : void
     415               0 : SpdySession::ProcessPending()
     416                 : {
     417               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     418                 : 
     419               0 :   while (RoomForMoreConcurrent()) {
     420               0 :     SpdyStream *stream = static_cast<SpdyStream *>(mQueuedStreams.PopFront());
     421               0 :     if (!stream)
     422               0 :       return;
     423               0 :     LOG3(("SpdySession::ProcessPending %p stream %p activated from queue.",
     424                 :           this, stream));
     425               0 :     ActivateStream(stream);
     426                 :   }
     427                 : }
     428                 : 
     429                 : nsresult
     430               0 : SpdySession::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
     431                 :                          PRUint32 count, PRUint32 *countWritten)
     432                 : {
     433               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     434                 : 
     435               0 :   nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
     436               0 :   if (NS_SUCCEEDED(rv) && *countWritten > 0)
     437               0 :     mLastReadEpoch = PR_IntervalNow();
     438               0 :   return rv;
     439                 : }
     440                 : 
     441                 : void
     442               0 : SpdySession::SetWriteCallbacks()
     443                 : {
     444               0 :   if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
     445               0 :       mConnection->ResumeSend();
     446               0 : }
     447                 : 
     448                 : void
     449               0 : SpdySession::FlushOutputQueue()
     450                 : {
     451               0 :   if (!mSegmentReader || !mOutputQueueUsed)
     452               0 :     return;
     453                 :   
     454                 :   nsresult rv;
     455                 :   PRUint32 countRead;
     456               0 :   PRUint32 avail = mOutputQueueUsed - mOutputQueueSent;
     457                 : 
     458                 :   rv = mSegmentReader->
     459               0 :     OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
     460               0 :                                      &countRead);
     461               0 :   LOG3(("SpdySession::FlushOutputQueue %p sz=%d rv=%x actual=%d",
     462                 :         this, avail, rv, countRead));
     463                 :   
     464                 :   // Dont worry about errors on write, we will pick this up as a read error too
     465               0 :   if (NS_FAILED(rv))
     466               0 :     return;
     467                 :   
     468               0 :   if (countRead == avail) {
     469               0 :     mOutputQueueUsed = 0;
     470               0 :     mOutputQueueSent = 0;
     471               0 :     return;
     472                 :   }
     473                 : 
     474               0 :   mOutputQueueSent += countRead;
     475                 : 
     476                 :   // If the output queue is close to filling up and we have sent out a good
     477                 :   // chunk of data from the beginning then realign it.
     478                 :   
     479               0 :   if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
     480                 :       ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
     481               0 :     mOutputQueueUsed -= mOutputQueueSent;
     482               0 :     memmove(mOutputQueueBuffer.get(),
     483               0 :             mOutputQueueBuffer.get() + mOutputQueueSent,
     484               0 :             mOutputQueueUsed);
     485               0 :     mOutputQueueSent = 0;
     486                 :   }
     487                 : }
     488                 : 
     489                 : void
     490               0 : SpdySession::DontReuse()
     491                 : {
     492               0 :   mShouldGoAway = true;
     493               0 :   if (!mStreamTransactionHash.Count())
     494               0 :     Close(NS_OK);
     495               0 : }
     496                 : 
     497                 : PRUint32
     498               0 : SpdySession::GetWriteQueueSize()
     499                 : {
     500               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     501                 : 
     502               0 :   return mUrgentForWrite.GetSize() + mReadyForWrite.GetSize();
     503                 : }
     504                 : 
     505                 : void
     506               0 : SpdySession::ChangeDownstreamState(enum stateType newState)
     507                 : {
     508               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     509                 : 
     510               0 :   LOG3(("SpdyStream::ChangeDownstreamState() %p from %X to %X",
     511                 :         this, mDownstreamState, newState));
     512               0 :   mDownstreamState = newState;
     513               0 : }
     514                 : 
     515                 : void
     516               0 : SpdySession::ResetDownstreamState()
     517                 : {
     518               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     519                 : 
     520               0 :   LOG3(("SpdyStream::ResetDownstreamState() %p", this));
     521               0 :   ChangeDownstreamState(BUFFERING_FRAME_HEADER);
     522                 : 
     523               0 :   if (mInputFrameDataLast && mInputFrameDataStream) {
     524               0 :     mInputFrameDataLast = false;
     525               0 :     if (!mInputFrameDataStream->RecvdFin()) {
     526               0 :       mInputFrameDataStream->SetRecvdFin(true);
     527               0 :       --mConcurrent;
     528               0 :       ProcessPending();
     529                 :     }
     530                 :   }
     531               0 :   mInputFrameBufferUsed = 0;
     532               0 :   mInputFrameDataStream = nsnull;
     533               0 : }
     534                 : 
     535                 : void
     536               0 : SpdySession::EnsureBuffer(nsAutoArrayPtr<char> &buf,
     537                 :                           PRUint32 newSize,
     538                 :                           PRUint32 preserve,
     539                 :                           PRUint32 &objSize)
     540                 : {
     541               0 :   if (objSize >= newSize)
     542               0 :       return;
     543                 :   
     544                 :   // Leave a little slop on the new allocation - add 2KB to
     545                 :   // what we need and then round the result up to a 4KB (page)
     546                 :   // boundary.
     547                 : 
     548               0 :   objSize = (newSize + 2048 + 4095) & ~4095;
     549                 :   
     550               0 :   nsAutoArrayPtr<char> tmp(new char[objSize]);
     551               0 :   memcpy(tmp, buf, preserve);
     552               0 :   buf = tmp;
     553                 : }
     554                 : 
     555                 : void
     556               0 : SpdySession::zlibInit()
     557                 : {
     558               0 :   mDownstreamZlib.zalloc = SpdyStream::zlib_allocator;
     559               0 :   mDownstreamZlib.zfree = SpdyStream::zlib_destructor;
     560               0 :   mDownstreamZlib.opaque = Z_NULL;
     561                 : 
     562               0 :   inflateInit(&mDownstreamZlib);
     563                 : 
     564               0 :   mUpstreamZlib.zalloc = SpdyStream::zlib_allocator;
     565               0 :   mUpstreamZlib.zfree = SpdyStream::zlib_destructor;
     566               0 :   mUpstreamZlib.opaque = Z_NULL;
     567                 : 
     568               0 :   deflateInit(&mUpstreamZlib, Z_DEFAULT_COMPRESSION);
     569                 :   deflateSetDictionary(&mUpstreamZlib,
     570                 :                        reinterpret_cast<const unsigned char *>
     571                 :                        (SpdyStream::kDictionary),
     572               0 :                        strlen(SpdyStream::kDictionary) + 1);
     573                 : 
     574               0 : }
     575                 : 
     576                 : nsresult
     577               0 : SpdySession::DownstreamUncompress(char *blockStart, PRUint32 blockLen)
     578                 : {
     579               0 :   mDecompressBufferUsed = 0;
     580                 : 
     581               0 :   mDownstreamZlib.avail_in = blockLen;
     582               0 :   mDownstreamZlib.next_in = reinterpret_cast<unsigned char *>(blockStart);
     583                 : 
     584               0 :   do {
     585                 :     mDownstreamZlib.next_out =
     586               0 :       reinterpret_cast<unsigned char *>(mDecompressBuffer.get()) +
     587               0 :       mDecompressBufferUsed;
     588               0 :     mDownstreamZlib.avail_out = mDecompressBufferSize - mDecompressBufferUsed;
     589               0 :     int zlib_rv = inflate(&mDownstreamZlib, Z_NO_FLUSH);
     590                 : 
     591               0 :     if (zlib_rv == Z_NEED_DICT)
     592                 :       inflateSetDictionary(&mDownstreamZlib,
     593                 :                            reinterpret_cast<const unsigned char *>
     594                 :                            (SpdyStream::kDictionary),
     595               0 :                            strlen(SpdyStream::kDictionary) + 1);
     596                 :     
     597               0 :     if (zlib_rv == Z_DATA_ERROR || zlib_rv == Z_MEM_ERROR)
     598               0 :       return NS_ERROR_FAILURE;
     599                 : 
     600                 :     mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed -
     601               0 :       mDownstreamZlib.avail_out;
     602                 :     
     603                 :     // When there is no more output room, but input still available then
     604                 :     // increase the output space
     605               0 :     if (zlib_rv == Z_OK &&
     606               0 :         !mDownstreamZlib.avail_out && mDownstreamZlib.avail_in) {
     607               0 :       LOG3(("SpdySession::DownstreamUncompress %p Large Headers - so far %d",
     608                 :             this, mDecompressBufferSize));
     609                 :       EnsureBuffer(mDecompressBuffer,
     610                 :                    mDecompressBufferSize + 4096,
     611                 :                    mDecompressBufferUsed,
     612               0 :                    mDecompressBufferSize);
     613                 :     }
     614                 :   }
     615                 :   while (mDownstreamZlib.avail_in);
     616               0 :   return NS_OK;
     617                 : }
     618                 : 
     619                 : nsresult
     620               0 : SpdySession::FindHeader(nsCString name,
     621                 :                         nsDependentCSubstring &value)
     622                 : {
     623                 :   const unsigned char *nvpair = reinterpret_cast<unsigned char *>
     624               0 :     (mDecompressBuffer.get()) + 2;
     625                 :   const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
     626               0 :     (mDecompressBuffer.get()) + mDecompressBufferUsed;
     627               0 :   if (lastHeaderByte < nvpair)
     628               0 :     return NS_ERROR_ILLEGAL_VALUE;
     629                 :   PRUint16 numPairs =
     630               0 :     PR_ntohs(reinterpret_cast<PRUint16 *>(mDecompressBuffer.get())[0]);
     631               0 :   for (PRUint16 index = 0; index < numPairs; ++index) {
     632               0 :     if (lastHeaderByte < nvpair + 2)
     633               0 :       return NS_ERROR_ILLEGAL_VALUE;
     634               0 :     PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
     635               0 :     if (lastHeaderByte < nvpair + 2 + nameLen)
     636               0 :       return NS_ERROR_ILLEGAL_VALUE;
     637                 :     nsDependentCSubstring nameString =
     638                 :       Substring(reinterpret_cast<const char *>(nvpair) + 2,
     639               0 :                 reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
     640               0 :     if (lastHeaderByte < nvpair + 4 + nameLen)
     641               0 :       return NS_ERROR_ILLEGAL_VALUE;
     642               0 :     PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
     643               0 :     if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
     644               0 :       return NS_ERROR_ILLEGAL_VALUE;
     645               0 :     if (nameString.Equals(name)) {
     646               0 :       value.Assign(((char *)nvpair) + 4 + nameLen, valueLen);
     647               0 :       return NS_OK;
     648                 :     }
     649               0 :     nvpair += 4 + nameLen + valueLen;
     650                 :   }
     651               0 :   return NS_ERROR_NOT_AVAILABLE;
     652                 : }
     653                 : 
     654                 : nsresult
     655               0 : SpdySession::ConvertHeaders(nsDependentCSubstring &status,
     656                 :                             nsDependentCSubstring &version)
     657                 : {
     658               0 :   mFlatHTTPResponseHeaders.Truncate();
     659               0 :   mFlatHTTPResponseHeadersOut = 0;
     660               0 :   mFlatHTTPResponseHeaders.SetCapacity(mDecompressBufferUsed + 64);
     661                 : 
     662                 :   // Connection, Keep-Alive and chunked transfer encodings are to be
     663                 :   // removed.
     664                 : 
     665                 :   // Content-Length is 'advisory'.. we will not strip it because it can
     666                 :   // create UI feedback.
     667                 :   
     668               0 :   mFlatHTTPResponseHeaders.Append(version);
     669               0 :   mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(" "));
     670               0 :   mFlatHTTPResponseHeaders.Append(status);
     671               0 :   mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
     672                 : 
     673                 :   const unsigned char *nvpair = reinterpret_cast<unsigned char *>
     674               0 :     (mDecompressBuffer.get()) + 2;
     675                 :   const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
     676               0 :     (mDecompressBuffer.get()) + mDecompressBufferUsed;
     677                 : 
     678               0 :   if (lastHeaderByte < nvpair)
     679               0 :     return NS_ERROR_ILLEGAL_VALUE;
     680                 : 
     681                 :   PRUint16 numPairs =
     682               0 :     PR_ntohs(reinterpret_cast<PRUint16 *>(mDecompressBuffer.get())[0]);
     683                 : 
     684               0 :   for (PRUint16 index = 0; index < numPairs; ++index) {
     685               0 :     if (lastHeaderByte < nvpair + 2)
     686               0 :       return NS_ERROR_ILLEGAL_VALUE;
     687                 : 
     688               0 :     PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
     689               0 :     if (lastHeaderByte < nvpair + 2 + nameLen)
     690               0 :       return NS_ERROR_ILLEGAL_VALUE;
     691                 : 
     692                 :     nsDependentCSubstring nameString =
     693                 :       Substring(reinterpret_cast<const char *>(nvpair) + 2,
     694               0 :                 reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
     695                 : 
     696               0 :     if (lastHeaderByte < nvpair + 4 + nameLen)
     697               0 :       return NS_ERROR_ILLEGAL_VALUE;
     698                 : 
     699                 :     // Look for illegal characters in the nameString.
     700                 :     // This includes upper case characters and nulls (as they will
     701                 :     // break the fixup-nulls-in-value-string algorithm)
     702                 :     // Look for upper case characters in the name. They are illegal.
     703               0 :     for (char *cPtr = nameString.BeginWriting();
     704               0 :          cPtr && cPtr < nameString.EndWriting();
     705                 :          ++cPtr) {
     706               0 :       if (*cPtr <= 'Z' && *cPtr >= 'A') {
     707               0 :         nsCString toLog(nameString);
     708                 : 
     709               0 :         LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
     710                 :               "upper case response header found. [%s]\n",
     711                 :               this, mInputFrameDataStream, toLog.get()));
     712                 : 
     713               0 :         return NS_ERROR_ILLEGAL_VALUE;
     714                 :       }
     715                 : 
     716                 :       // check for null characters
     717               0 :       if (*cPtr == '\0')
     718               0 :         return NS_ERROR_ILLEGAL_VALUE;
     719                 :     }
     720                 : 
     721                 :     // HTTP Chunked responses are not legal over spdy. We do not need
     722                 :     // to look for chunked specifically because it is the only HTTP
     723                 :     // allowed default encoding and we did not negotiate further encodings
     724                 :     // via TE
     725               0 :     if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) {
     726               0 :       LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
     727                 :             "transfer-encoding found. Chunked is invalid and no TE sent.",
     728                 :             this, mInputFrameDataStream));
     729                 : 
     730               0 :       return NS_ERROR_ILLEGAL_VALUE;
     731                 :     }
     732                 : 
     733               0 :     PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
     734               0 :     if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
     735               0 :       return NS_ERROR_ILLEGAL_VALUE;
     736                 : 
     737               0 :     if (!nameString.Equals(NS_LITERAL_CSTRING("version")) &&
     738               0 :         !nameString.Equals(NS_LITERAL_CSTRING("status")) &&
     739               0 :         !nameString.Equals(NS_LITERAL_CSTRING("connection")) &&
     740               0 :         !nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) {
     741                 :       nsDependentCSubstring valueString =
     742               0 :         Substring(reinterpret_cast<const char *>(nvpair) + 4 + nameLen,
     743                 :                   reinterpret_cast<const char *>(nvpair) + 4 + nameLen +
     744               0 :                   valueLen);
     745                 :       
     746               0 :       mFlatHTTPResponseHeaders.Append(nameString);
     747               0 :       mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(": "));
     748                 : 
     749                 :       // expand NULL bytes in the value string
     750               0 :       for (char *cPtr = valueString.BeginWriting();
     751               0 :            cPtr && cPtr < valueString.EndWriting();
     752                 :            ++cPtr) {
     753               0 :         if (*cPtr != 0) {
     754               0 :           mFlatHTTPResponseHeaders.Append(*cPtr);
     755               0 :           continue;
     756                 :         }
     757                 : 
     758                 :         // NULLs are really "\r\nhdr: "
     759               0 :         mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
     760               0 :         mFlatHTTPResponseHeaders.Append(nameString);
     761               0 :         mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(": "));
     762                 :       }
     763                 : 
     764               0 :       mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
     765                 :     }
     766               0 :     nvpair += 4 + nameLen + valueLen;
     767                 :   }
     768                 : 
     769                 :   mFlatHTTPResponseHeaders.Append(
     770               0 :     NS_LITERAL_CSTRING("X-Firefox-Spdy: 1\r\n\r\n"));
     771               0 :   LOG (("decoded response headers are:\n%s",
     772                 :         mFlatHTTPResponseHeaders.get()));
     773                 :   
     774               0 :   return NS_OK;
     775                 : }
     776                 : 
     777                 : void
     778               0 : SpdySession::GeneratePing(PRUint32 aID)
     779                 : {
     780               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     781               0 :   LOG3(("SpdySession::GeneratePing %p 0x%X\n", this, aID));
     782                 : 
     783                 :   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
     784               0 :                mOutputQueueUsed, mOutputQueueSize);
     785               0 :   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
     786               0 :   mOutputQueueUsed += 12;
     787                 : 
     788               0 :   packet[0] = kFlag_Control;
     789               0 :   packet[1] = 2;                                  /* version 2 */
     790               0 :   packet[2] = 0;
     791               0 :   packet[3] = CONTROL_TYPE_PING;
     792               0 :   packet[4] = 0;                                  /* flags */
     793               0 :   packet[5] = 0;
     794               0 :   packet[6] = 0;
     795               0 :   packet[7] = 4;                                  /* length */
     796                 :   
     797               0 :   aID = PR_htonl(aID);
     798               0 :   memcpy(packet + 8, &aID, 4);
     799                 : 
     800               0 :   FlushOutputQueue();
     801               0 : }
     802                 : 
     803                 : void
     804               0 : SpdySession::GenerateRstStream(PRUint32 aStatusCode, PRUint32 aID)
     805                 : {
     806               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     807               0 :   LOG3(("SpdySession::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
     808                 : 
     809                 :   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16,
     810               0 :                mOutputQueueUsed, mOutputQueueSize);
     811               0 :   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
     812               0 :   mOutputQueueUsed += 16;
     813                 : 
     814               0 :   packet[0] = kFlag_Control;
     815               0 :   packet[1] = 2;                                  /* version 2 */
     816               0 :   packet[2] = 0;
     817               0 :   packet[3] = CONTROL_TYPE_RST_STREAM;
     818               0 :   packet[4] = 0;                                  /* flags */
     819               0 :   packet[5] = 0;
     820               0 :   packet[6] = 0;
     821               0 :   packet[7] = 8;                                  /* length */
     822                 :   
     823               0 :   aID = PR_htonl(aID);
     824               0 :   memcpy(packet + 8, &aID, 4);
     825               0 :   aStatusCode = PR_htonl(aStatusCode);
     826               0 :   memcpy(packet + 12, &aStatusCode, 4);
     827                 : 
     828               0 :   FlushOutputQueue();
     829               0 : }
     830                 : 
     831                 : void
     832               0 : SpdySession::GenerateGoAway()
     833                 : {
     834               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     835               0 :   LOG3(("SpdySession::GenerateGoAway %p\n", this));
     836                 : 
     837                 :   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
     838               0 :                mOutputQueueUsed, mOutputQueueSize);
     839               0 :   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
     840               0 :   mOutputQueueUsed += 12;
     841                 : 
     842               0 :   memset(packet, 0, 12);
     843               0 :   packet[0] = kFlag_Control;
     844               0 :   packet[1] = 2;                                  /* version 2 */
     845               0 :   packet[3] = CONTROL_TYPE_GOAWAY;
     846               0 :   packet[7] = 4;                                  /* data length */
     847                 :   
     848                 :   // last-good-stream-id are bytes 8-11, when we accept server push this will
     849                 :   // need to be set non zero
     850                 : 
     851               0 :   FlushOutputQueue();
     852               0 : }
     853                 : 
     854                 : void
     855               0 : SpdySession::CleanupStream(SpdyStream *aStream, nsresult aResult,
     856                 :                            rstReason aResetCode)
     857                 : {
     858               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     859               0 :   LOG3(("SpdySession::CleanupStream %p %p 0x%x %X\n",
     860                 :         this, aStream, aStream->StreamID(), aResult));
     861                 : 
     862               0 :   if (!aStream->RecvdFin() && aStream->StreamID()) {
     863               0 :     LOG3(("Stream had not processed recv FIN, sending RST code %X\n",
     864                 :           aResetCode));
     865               0 :     GenerateRstStream(aResetCode, aStream->StreamID());
     866               0 :     --mConcurrent;
     867               0 :     ProcessPending();
     868                 :   }
     869                 :   
     870                 :   // Check if partial frame reader
     871               0 :   if (aStream == mInputFrameDataStream) {
     872               0 :     LOG3(("Stream had active partial read frame on close"));
     873               0 :     ChangeDownstreamState(DISCARDING_DATA_FRAME);
     874               0 :     mInputFrameDataStream = nsnull;
     875                 :   }
     876                 : 
     877                 :   // check the streams blocked on write, this is linear but the list
     878                 :   // should be pretty short.
     879               0 :   PRUint32 size = mReadyForWrite.GetSize();
     880               0 :   for (PRUint32 count = 0; count < size; ++count) {
     881               0 :     SpdyStream *stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
     882               0 :     if (stream != aStream)
     883               0 :       mReadyForWrite.Push(stream);
     884                 :   }
     885                 : 
     886                 :   // Check the streams blocked on urgent (i.e. window update) writing.
     887                 :   // This should also be short.
     888               0 :   size = mUrgentForWrite.GetSize();
     889               0 :   for (PRUint32 count = 0; count < size; ++count) {
     890               0 :     SpdyStream *stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
     891               0 :     if (stream != aStream)
     892               0 :       mUrgentForWrite.Push(stream);
     893                 :   }
     894                 : 
     895                 :   // Check the streams queued for activation. Because we normally accept a high
     896                 :   // level of parallelization this should also be short.
     897               0 :   size = mQueuedStreams.GetSize();
     898               0 :   for (PRUint32 count = 0; count < size; ++count) {
     899               0 :     SpdyStream *stream = static_cast<SpdyStream *>(mQueuedStreams.PopFront());
     900               0 :     if (stream != aStream)
     901               0 :       mQueuedStreams.Push(stream);
     902                 :   }
     903                 : 
     904                 :   // Remove the stream from the ID hash table. (this one isn't short, which is
     905                 :   // why it is hashed.)
     906               0 :   mStreamIDHash.Remove(aStream->StreamID());
     907                 : 
     908                 :   // Send the stream the close() indication
     909               0 :   aStream->Close(aResult);
     910                 : 
     911                 :   // removing from the stream transaction hash will
     912                 :   // delete the SpdyStream and drop the reference to
     913                 :   // its transaction
     914               0 :   mStreamTransactionHash.Remove(aStream->Transaction());
     915                 : 
     916               0 :   if (mShouldGoAway && !mStreamTransactionHash.Count())
     917               0 :     Close(NS_OK);
     918               0 : }
     919                 : 
     920                 : nsresult
     921               0 : SpdySession::HandleSynStream(SpdySession *self)
     922                 : {
     923               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_STREAM,
     924                 :                     "wrong control type");
     925                 :   
     926               0 :   if (self->mInputFrameDataSize < 18) {
     927               0 :     LOG3(("SpdySession::HandleSynStream %p SYN_STREAM too short data=%d",
     928                 :           self, self->mInputFrameDataSize));
     929               0 :     return NS_ERROR_ILLEGAL_VALUE;
     930                 :   }
     931                 : 
     932                 :   PRUint32 streamID =
     933               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
     934                 :   PRUint32 associatedID =
     935               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[3]);
     936                 : 
     937               0 :   LOG3(("SpdySession::HandleSynStream %p recv SYN_STREAM (push) "
     938                 :         "for ID 0x%X associated with 0x%X.",
     939                 :         self, streamID, associatedID));
     940                 :     
     941               0 :   if (streamID & 0x01) {                   // test for odd stream ID
     942               0 :     LOG3(("SpdySession::HandleSynStream %p recvd SYN_STREAM id must be even.",
     943                 :           self));
     944               0 :     return NS_ERROR_ILLEGAL_VALUE;
     945                 :   }
     946                 : 
     947               0 :   ++(self->mServerPushedResources);
     948                 : 
     949                 :   // Anytime we start using the high bit of stream ID (either client or server)
     950                 :   // begin to migrate to a new session.
     951               0 :   if (streamID >= kMaxStreamID)
     952               0 :     self->mShouldGoAway = true;
     953                 : 
     954                 :   // Need to decompress the headers even though we aren't using them yet in
     955                 :   // order to keep the compression context consistent for other syn_reply frames
     956               0 :   nsresult rv = self->DownstreamUncompress(self->mInputFrameBuffer + 18,
     957               0 :                                            self->mInputFrameDataSize - 10);
     958               0 :   if (NS_FAILED(rv)) {
     959               0 :     LOG(("SpdySession::HandleSynStream uncompress failed\n"));
     960               0 :     return rv;
     961                 :   }
     962                 : 
     963                 :   // todo populate cache. For now, just reject server push p3
     964               0 :   self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
     965               0 :   self->ResetDownstreamState();
     966               0 :   return NS_OK;
     967                 : }
     968                 : 
     969                 : nsresult
     970               0 : SpdySession::HandleSynReply(SpdySession *self)
     971                 : {
     972               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_REPLY,
     973                 :                     "wrong control type");
     974                 : 
     975               0 :   if (self->mInputFrameDataSize < 8) {
     976               0 :     LOG3(("SpdySession::HandleSynReply %p SYN REPLY too short data=%d",
     977                 :           self, self->mInputFrameDataSize));
     978                 :     // A framing error is a session wide error that cannot be recovered
     979               0 :     return NS_ERROR_ILLEGAL_VALUE;
     980                 :   }
     981                 :   
     982                 :   // Uncompress the headers into mDecompressBuffer, leaving them in
     983                 :   // spdy format for the time being. Make certain to do this
     984                 :   // step before any error handling that might abort the stream but not
     985                 :   // the session becuase the session compression context will become
     986                 :   // inconsistent if all of the compressed data is not processed.
     987               0 :   if (NS_FAILED(self->DownstreamUncompress(self->mInputFrameBuffer + 14,
     988                 :                                            self->mInputFrameDataSize - 6))) {
     989               0 :     LOG(("SpdySession::HandleSynReply uncompress failed\n"));
     990               0 :     return NS_ERROR_FAILURE;
     991                 :   }
     992                 : 
     993                 :   PRUint32 streamID =
     994               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
     995               0 :   self->mInputFrameDataStream = self->mStreamIDHash.Get(streamID);
     996               0 :   if (!self->mInputFrameDataStream) {
     997               0 :     LOG3(("SpdySession::HandleSynReply %p lookup streamID in syn_reply "
     998                 :           "0x%X failed. NextStreamID = 0x%x", self, streamID,
     999                 :           self->mNextStreamID));
    1000               0 :     if (streamID >= self->mNextStreamID)
    1001               0 :       self->GenerateRstStream(RST_INVALID_STREAM, streamID);
    1002                 : 
    1003               0 :     self->ResetDownstreamState();
    1004               0 :     return NS_OK;
    1005                 :   }
    1006                 : 
    1007               0 :   nsresult rv = self->HandleSynReplyForValidStream();
    1008               0 :   if (rv == NS_ERROR_ILLEGAL_VALUE) {
    1009               0 :     LOG3(("SpdySession::HandleSynReply %p PROTOCOL_ERROR detected 0x%X\n",
    1010                 :           self, streamID));
    1011               0 :     self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
    1012               0 :     self->ResetDownstreamState();
    1013               0 :     rv = NS_OK;
    1014                 :   }
    1015                 : 
    1016               0 :   return rv;
    1017                 : }
    1018                 : 
    1019                 : // HandleSynReplyForValidStream() returns NS_ERROR_ILLEGAL_VALUE when the stream
    1020                 : // should be reset with a PROTOCOL_ERROR, NS_OK when the SYN_REPLY was
    1021                 : // fine, and any other error is fatal to the session.
    1022                 : nsresult
    1023               0 : SpdySession::HandleSynReplyForValidStream()
    1024                 : {
    1025               0 :   if (mInputFrameDataStream->GetFullyOpen()) {
    1026                 :     // "If an endpoint receives multiple SYN_REPLY frames for the same active
    1027                 :     // stream ID, it must drop the stream, and send a RST_STREAM for the
    1028                 :     // stream with the error PROTOCOL_ERROR."
    1029                 :     //
    1030                 :     // If the stream is open then just RST_STREAM and move on, otherwise
    1031                 :     // abort the session
    1032               0 :     return mInputFrameDataStream->RecvdFin() ?
    1033               0 :       NS_ERROR_ALREADY_OPENED : NS_ERROR_ILLEGAL_VALUE;
    1034                 :   }
    1035               0 :   mInputFrameDataStream->SetFullyOpen();
    1036                 : 
    1037               0 :   mInputFrameDataLast = mInputFrameBuffer[4] & kFlag_Data_FIN;
    1038                 : 
    1039               0 :   if (mInputFrameBuffer[4] & kFlag_Data_UNI) {
    1040               0 :     LOG3(("SynReply had unidirectional flag set on it - nonsensical"));
    1041               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1042                 :   }
    1043                 : 
    1044               0 :   LOG3(("SpdySession::HandleSynReplyForValidStream %p SYN_REPLY for 0x%X "
    1045                 :         "fin=%d",
    1046                 :         this, mInputFrameDataStream->StreamID(), mInputFrameDataLast));
    1047                 :   
    1048                 :   Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE,
    1049               0 :                         mInputFrameDataSize - 6);
    1050               0 :   if (mDecompressBufferUsed) {
    1051                 :     PRUint32 ratio =
    1052               0 :       (mInputFrameDataSize - 6) * 100 / mDecompressBufferUsed;
    1053               0 :     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
    1054                 :   }
    1055                 : 
    1056                 :   // status and version are required.
    1057               0 :   nsDependentCSubstring status, version;
    1058               0 :   nsresult rv = FindHeader(NS_LITERAL_CSTRING("status"), status);
    1059               0 :   if (NS_FAILED(rv))
    1060               0 :     return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
    1061                 : 
    1062               0 :   rv = FindHeader(NS_LITERAL_CSTRING("version"), version);
    1063               0 :   if (NS_FAILED(rv))
    1064               0 :     return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
    1065                 : 
    1066                 :   // The spdystream needs to see flattened http headers
    1067                 :   // Uncompressed spdy format headers currently live in
    1068                 :   // mDeccompressBuffer - convert that to HTTP format in
    1069                 :   // mFlatHTTPResponseHeaders in ConvertHeaders()
    1070                 : 
    1071               0 :   rv = ConvertHeaders(status, version);
    1072               0 :   if (NS_FAILED(rv))
    1073               0 :     return rv;
    1074                 : 
    1075               0 :   mInputFrameDataStream->UpdateTransportReadEvents(mInputFrameDataSize);
    1076               0 :   mLastDataReadEpoch = mLastReadEpoch;
    1077               0 :   ChangeDownstreamState(PROCESSING_CONTROL_SYN_REPLY);
    1078               0 :   return NS_OK;
    1079                 : }
    1080                 : 
    1081                 : nsresult
    1082               0 : SpdySession::HandleRstStream(SpdySession *self)
    1083                 : {
    1084               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_RST_STREAM,
    1085                 :                     "wrong control type");
    1086                 : 
    1087               0 :   if (self->mInputFrameDataSize != 8) {
    1088               0 :     LOG3(("SpdySession::HandleRstStream %p RST_STREAM wrong length data=%d",
    1089                 :           self, self->mInputFrameDataSize));
    1090               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1091                 :   }
    1092                 : 
    1093               0 :   PRUint8 flags = reinterpret_cast<PRUint8 *>(self->mInputFrameBuffer.get())[4];
    1094                 : 
    1095                 :   PRUint32 streamID =
    1096               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
    1097                 : 
    1098                 :   self->mDownstreamRstReason =
    1099               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[3]);
    1100                 : 
    1101               0 :   LOG3(("SpdySession::HandleRstStream %p RST_STREAM Reason Code %u ID %x "
    1102                 :         "flags %x", self, self->mDownstreamRstReason, streamID, flags));
    1103                 : 
    1104               0 :   if (flags != 0) {
    1105               0 :     LOG3(("SpdySession::HandleRstStream %p RST_STREAM with flags is illegal",
    1106                 :           self));
    1107               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1108                 :   }
    1109                 :   
    1110               0 :   if (self->mDownstreamRstReason == RST_INVALID_STREAM ||
    1111                 :       self->mDownstreamRstReason == RST_FLOW_CONTROL_ERROR) {
    1112                 :     // basically just ignore this
    1113               0 :     self->ResetDownstreamState();
    1114               0 :     return NS_OK;
    1115                 :   }
    1116                 : 
    1117               0 :   self->mInputFrameDataStream = self->mStreamIDHash.Get(streamID);
    1118               0 :   if (!self->mInputFrameDataStream) {
    1119               0 :     LOG3(("SpdySession::HandleRstStream %p lookup streamID for RST Frame "
    1120                 :           "0x%X failed", self, streamID));
    1121               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1122                 :   }
    1123                 : 
    1124               0 :   self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
    1125               0 :   return NS_OK;
    1126                 : }
    1127                 : 
    1128                 : nsresult
    1129               0 : SpdySession::HandleSettings(SpdySession *self)
    1130                 : {
    1131               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SETTINGS,
    1132                 :                     "wrong control type");
    1133                 : 
    1134               0 :   if (self->mInputFrameDataSize < 4) {
    1135               0 :     LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
    1136                 :           self, self->mInputFrameDataSize));
    1137               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1138                 :   }
    1139                 : 
    1140                 :   PRUint32 numEntries =
    1141               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
    1142                 : 
    1143                 :   // Ensure frame is large enough for supplied number of entries
    1144                 :   // Each entry is 8 bytes, frame data is reduced by 4 to account for
    1145                 :   // the NumEntries value.
    1146               0 :   if ((self->mInputFrameDataSize - 4) < (numEntries * 8)) {
    1147               0 :     LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
    1148                 :           self, self->mInputFrameDataSize));
    1149               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1150                 :   }
    1151                 : 
    1152               0 :   LOG3(("SpdySession::HandleSettings %p SETTINGS Control Frame with %d entries",
    1153                 :         self, numEntries));
    1154                 : 
    1155               0 :   for (PRUint32 index = 0; index < numEntries; ++index) {
    1156                 :     // To clarify the v2 spec:
    1157                 :     // Each entry is a 24 bits of a little endian id
    1158                 :     // followed by 8 bits of flags
    1159                 :     // followed by a 32 bit big endian value
    1160                 :     
    1161                 :     unsigned char *setting = reinterpret_cast<unsigned char *>
    1162               0 :       (self->mInputFrameBuffer.get()) + 12 + index * 8;
    1163                 : 
    1164               0 :     PRUint32 id = (setting[2] << 16) + (setting[1] << 8) + setting[0];
    1165               0 :     PRUint32 flags = setting[3];
    1166               0 :     PRUint32 value =  PR_ntohl(reinterpret_cast<PRUint32 *>(setting)[1]);
    1167                 : 
    1168               0 :     LOG3(("Settings ID %d, Flags %X, Value %d", id, flags, value));
    1169                 : 
    1170               0 :     switch (id)
    1171                 :     {
    1172                 :     case SETTINGS_TYPE_UPLOAD_BW:
    1173               0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_UL_BW, value);
    1174               0 :       break;
    1175                 :       
    1176                 :     case SETTINGS_TYPE_DOWNLOAD_BW:
    1177               0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_DL_BW, value);
    1178               0 :       break;
    1179                 :       
    1180                 :     case SETTINGS_TYPE_RTT:
    1181               0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RTT, value);
    1182               0 :       break;
    1183                 :       
    1184                 :     case SETTINGS_TYPE_MAX_CONCURRENT:
    1185               0 :       self->mMaxConcurrent = value;
    1186               0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
    1187               0 :       break;
    1188                 :       
    1189                 :     case SETTINGS_TYPE_CWND:
    1190               0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_CWND, value);
    1191               0 :       break;
    1192                 :       
    1193                 :     case SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE:
    1194               0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RETRANS, value);
    1195               0 :       break;
    1196                 :       
    1197                 :     case SETTINGS_TYPE_INITIAL_WINDOW:
    1198               0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
    1199               0 :       break;
    1200                 :       
    1201                 :     default:
    1202               0 :       break;
    1203                 :     }
    1204                 :     
    1205                 :   }
    1206                 :   
    1207               0 :   self->ResetDownstreamState();
    1208               0 :   return NS_OK;
    1209                 : }
    1210                 : 
    1211                 : nsresult
    1212               0 : SpdySession::HandleNoop(SpdySession *self)
    1213                 : {
    1214               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_NOOP,
    1215                 :                     "wrong control type");
    1216                 : 
    1217               0 :   if (self->mInputFrameDataSize != 0) {
    1218               0 :     LOG3(("SpdySession::HandleNoop %p NOP had data %d",
    1219                 :           self, self->mInputFrameDataSize));
    1220               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1221                 :   }
    1222                 : 
    1223               0 :   LOG3(("SpdySession::HandleNoop %p NOP.", self));
    1224                 : 
    1225               0 :   self->ResetDownstreamState();
    1226               0 :   return NS_OK;
    1227                 : }
    1228                 : 
    1229                 : nsresult
    1230               0 : SpdySession::HandlePing(SpdySession *self)
    1231                 : {
    1232               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_PING,
    1233                 :                     "wrong control type");
    1234                 : 
    1235               0 :   if (self->mInputFrameDataSize != 4) {
    1236               0 :     LOG3(("SpdySession::HandlePing %p PING had wrong amount of data %d",
    1237                 :           self, self->mInputFrameDataSize));
    1238               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1239                 :   }
    1240                 : 
    1241                 :   PRUint32 pingID =
    1242               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
    1243                 : 
    1244               0 :   LOG3(("SpdySession::HandlePing %p PING ID 0x%X.", self, pingID));
    1245                 : 
    1246               0 :   if (pingID & 0x01) {
    1247                 :     // presumably a reply to our timeout ping
    1248               0 :     self->ClearPing(true);
    1249                 :   }
    1250                 :   else {
    1251                 :     // Servers initiate even numbered pings, go ahead and echo it back
    1252               0 :     self->GeneratePing(pingID);
    1253                 :   }
    1254                 :     
    1255               0 :   self->ResetDownstreamState();
    1256               0 :   return NS_OK;
    1257                 : }
    1258                 : 
    1259                 : nsresult
    1260               0 : SpdySession::HandleGoAway(SpdySession *self)
    1261                 : {
    1262               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_GOAWAY,
    1263                 :                     "wrong control type");
    1264                 : 
    1265               0 :   if (self->mInputFrameDataSize != 4) {
    1266               0 :     LOG3(("SpdySession::HandleGoAway %p GOAWAY had wrong amount of data %d",
    1267                 :           self, self->mInputFrameDataSize));
    1268               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1269                 :   }
    1270                 : 
    1271               0 :   self->mShouldGoAway = true;
    1272                 :   self->mGoAwayID =
    1273               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
    1274               0 :   self->mCleanShutdown = true;
    1275                 :   
    1276               0 :   LOG3(("SpdySession::HandleGoAway %p GOAWAY Last-Good-ID 0x%X.",
    1277                 :         self, self->mGoAwayID));
    1278               0 :   self->ResumeRecv();
    1279               0 :   self->ResetDownstreamState();
    1280               0 :   return NS_OK;
    1281                 : }
    1282                 : 
    1283                 : nsresult
    1284               0 : SpdySession::HandleHeaders(SpdySession *self)
    1285                 : {
    1286               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_HEADERS,
    1287                 :                     "wrong control type");
    1288                 : 
    1289               0 :   if (self->mInputFrameDataSize < 10) {
    1290               0 :     LOG3(("SpdySession::HandleHeaders %p HEADERS had wrong amount of data %d",
    1291                 :           self, self->mInputFrameDataSize));
    1292               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1293                 :   }
    1294                 : 
    1295                 :   PRUint32 streamID =
    1296               0 :     PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
    1297                 : 
    1298                 :   // this is actually not legal in the HTTP mapping of SPDY. All
    1299                 :   // headers are in the syn or syn reply. Log and ignore it.
    1300                 : 
    1301                 :   // in v3 this will be legal and we must remember to note
    1302                 :   // NS_NET_STATUS_RECEIVING_FROM from it
    1303                 : 
    1304               0 :   LOG3(("SpdySession::HandleHeaders %p HEADERS for Stream 0x%X. "
    1305                 :         "They are ignored in the HTTP/SPDY mapping.",
    1306                 :         self, streamID));
    1307               0 :   self->mLastDataReadEpoch = self->mLastReadEpoch;
    1308               0 :   self->ResetDownstreamState();
    1309               0 :   return NS_OK;
    1310                 : }
    1311                 : 
    1312                 : nsresult
    1313               0 : SpdySession::HandleWindowUpdate(SpdySession *self)
    1314                 : {
    1315               0 :   NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_WINDOW_UPDATE,
    1316                 :                     "wrong control type");
    1317               0 :   LOG3(("SpdySession::HandleWindowUpdate %p WINDOW UPDATE was "
    1318                 :         "received. WINDOW UPDATE is no longer defined in v2. Ignoring.",
    1319                 :         self));
    1320                 : 
    1321               0 :   self->ResetDownstreamState();
    1322               0 :   return NS_OK;
    1323                 : }
    1324                 : 
    1325                 : //-----------------------------------------------------------------------------
    1326                 : // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
    1327                 : // of these methods
    1328                 : //-----------------------------------------------------------------------------
    1329                 : 
    1330                 : void
    1331               0 : SpdySession::OnTransportStatus(nsITransport* aTransport,
    1332                 :                                nsresult aStatus,
    1333                 :                                PRUint64 aProgress)
    1334                 : {
    1335               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1336                 : 
    1337               0 :   switch (aStatus) {
    1338                 :     // These should appear only once, deliver to the first
    1339                 :     // transaction on the session.
    1340                 :   case NS_NET_STATUS_RESOLVING_HOST:
    1341                 :   case NS_NET_STATUS_RESOLVED_HOST:
    1342                 :   case NS_NET_STATUS_CONNECTING_TO:
    1343                 :   case NS_NET_STATUS_CONNECTED_TO:
    1344                 :   {
    1345               0 :     SpdyStream *target = mStreamIDHash.Get(1);
    1346               0 :     if (target)
    1347               0 :       target->Transaction()->OnTransportStatus(aTransport, aStatus, aProgress);
    1348               0 :     break;
    1349                 :   }
    1350                 : 
    1351                 :   default:
    1352                 :     // The other transport events are ignored here because there is no good
    1353                 :     // way to map them to the right transaction in spdy. Instead, the events
    1354                 :     // are generated again from the spdy code and passed directly to the
    1355                 :     // correct transaction.
    1356                 : 
    1357                 :     // NS_NET_STATUS_SENDING_TO:
    1358                 :     // This is generated by the socket transport when (part) of
    1359                 :     // a transaction is written out
    1360                 :     //
    1361                 :     // There is no good way to map it to the right transaction in spdy,
    1362                 :     // so it is ignored here and generated separately when the SYN_STREAM
    1363                 :     // is sent from SpdyStream::TransmitFrame
    1364                 : 
    1365                 :     // NS_NET_STATUS_WAITING_FOR:
    1366                 :     // Created by nsHttpConnection when the request has been totally sent.
    1367                 :     // There is no good way to map it to the right transaction in spdy,
    1368                 :     // so it is ignored here and generated separately when the same
    1369                 :     // condition is complete in SpdyStream when there is no more
    1370                 :     // request body left to be transmitted.
    1371                 : 
    1372                 :     // NS_NET_STATUS_RECEIVING_FROM
    1373                 :     // Generated in spdysession whenever we read a data frame or a syn_reply
    1374                 :     // that can be attributed to a particular stream/transaction
    1375                 : 
    1376               0 :     break;
    1377                 :   }
    1378               0 : }
    1379                 : 
    1380                 : // ReadSegments() is used to write data to the network. Generally, HTTP
    1381                 : // request data is pulled from the approriate transaction and
    1382                 : // converted to SPDY data. Sometimes control data like window-update are
    1383                 : // generated instead.
    1384                 : 
    1385                 : nsresult
    1386               0 : SpdySession::ReadSegments(nsAHttpSegmentReader *reader,
    1387                 :                           PRUint32 count,
    1388                 :                           PRUint32 *countRead)
    1389                 : {
    1390               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1391                 :   
    1392                 :   nsresult rv;
    1393               0 :   *countRead = 0;
    1394                 : 
    1395                 :   // First priority goes to frames that were writing to the network but were
    1396                 :   // blocked part way through. Then to frames that have no streams (e.g ping
    1397                 :   // reply) and then third to streams marked urgent (generally they have
    1398                 :   // window updates), and finally to streams generally
    1399                 :   // ready to send data frames (http requests).
    1400                 : 
    1401               0 :   LOG3(("SpdySession::ReadSegments %p", this));
    1402                 : 
    1403                 :   SpdyStream *stream;
    1404                 :   
    1405               0 :   stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
    1406               0 :   if (!stream)
    1407               0 :     stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
    1408               0 :   if (!stream) {
    1409               0 :     LOG3(("SpdySession %p could not identify a stream to write; suspending.",
    1410                 :           this));
    1411               0 :     FlushOutputQueue();
    1412               0 :     SetWriteCallbacks();
    1413               0 :     return NS_BASE_STREAM_WOULD_BLOCK;
    1414                 :   }
    1415                 :   
    1416               0 :   LOG3(("SpdySession %p will write from SpdyStream %p", this, stream));
    1417                 : 
    1418               0 :   NS_ABORT_IF_FALSE(!mSegmentReader || !reader || (mSegmentReader == reader),
    1419                 :                     "Inconsistent Write Function Callback");
    1420                 : 
    1421               0 :   if (reader)
    1422               0 :     mSegmentReader = reader;
    1423               0 :   rv = stream->ReadSegments(this, count, countRead);
    1424                 : 
    1425                 :   // Not every permutation of stream->ReadSegents produces data (and therefore
    1426                 :   // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
    1427                 :   // of that. But we might still have old data buffered that would be good
    1428                 :   // to flush.
    1429               0 :   FlushOutputQueue();
    1430                 : 
    1431               0 :   if (stream->RequestBlockedOnRead()) {
    1432                 :     
    1433                 :     // We are blocked waiting for input - either more http headers or
    1434                 :     // any request body data. When more data from the request stream
    1435                 :     // becomes available the httptransaction will call conn->ResumeSend().
    1436                 :     
    1437               0 :     LOG3(("SpdySession::ReadSegments %p dealing with block on read", this));
    1438                 : 
    1439                 :     // call readsegments again if there are other streams ready
    1440                 :     // to run in this session
    1441               0 :     if (GetWriteQueueSize())
    1442               0 :       rv = NS_OK;
    1443                 :     else
    1444               0 :       rv = NS_BASE_STREAM_WOULD_BLOCK;
    1445               0 :     SetWriteCallbacks();
    1446               0 :     return rv;
    1447                 :   }
    1448                 :   
    1449               0 :   if (NS_FAILED(rv)) {
    1450               0 :     LOG3(("SpdySession::ReadSegments %p returning FAIL code %X",
    1451                 :           this, rv));
    1452               0 :     return rv;
    1453                 :   }
    1454                 :   
    1455               0 :   if (*countRead > 0) {
    1456               0 :     LOG3(("SpdySession::ReadSegments %p stream=%p generated end of frame %d",
    1457                 :           this, stream, *countRead));
    1458               0 :     mReadyForWrite.Push(stream);
    1459               0 :     SetWriteCallbacks();
    1460               0 :     return rv;
    1461                 :   }
    1462                 :   
    1463               0 :   LOG3(("SpdySession::ReadSegments %p stream=%p stream send complete",
    1464                 :         this, stream));
    1465                 :   
    1466                 :   /* we now want to recv data */
    1467               0 :   ResumeRecv();
    1468                 : 
    1469                 :   // call readsegments again if there are other streams ready
    1470                 :   // to go in this session
    1471               0 :   SetWriteCallbacks();
    1472                 : 
    1473               0 :   return rv;
    1474                 : }
    1475                 : 
    1476                 : // WriteSegments() is used to read data off the socket. Generally this is
    1477                 : // just the SPDY frame header and from there the appropriate SPDYStream
    1478                 : // is identified from the Stream-ID. The http transaction associated with
    1479                 : // that read then pulls in the data directly, which it will feed to
    1480                 : // OnWriteSegment(). That function will gateway it into http and feed
    1481                 : // it to the appropriate transaction.
    1482                 : 
    1483                 : // we call writer->OnWriteSegment via NetworkRead() to get a spdy header.. 
    1484                 : // and decide if it is data or control.. if it is control, just deal with it.
    1485                 : // if it is data, identify the spdy stream
    1486                 : // call stream->WriteSegemnts which can call this::OnWriteSegment to get the
    1487                 : // data. It always gets full frames if they are part of the stream
    1488                 : 
    1489                 : nsresult
    1490               0 : SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
    1491                 :                            PRUint32 count,
    1492                 :                            PRUint32 *countWritten)
    1493                 : {
    1494               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1495                 :   
    1496                 :   nsresult rv;
    1497               0 :   *countWritten = 0;
    1498                 : 
    1499               0 :   if (mClosed)
    1500               0 :     return NS_ERROR_FAILURE;
    1501                 : 
    1502               0 :   SetWriteCallbacks();
    1503                 :   
    1504                 :   // We buffer all control frames and act on them in this layer.
    1505                 :   // We buffer the first 8 bytes of data frames (the header) but
    1506                 :   // the actual data is passed through unprocessed.
    1507                 :   
    1508               0 :   if (mDownstreamState == BUFFERING_FRAME_HEADER) {
    1509                 :     // The first 8 bytes of every frame is header information that
    1510                 :     // we are going to want to strip before passing to http. That is
    1511                 :     // true of both control and data packets.
    1512                 :     
    1513               0 :     NS_ABORT_IF_FALSE(mInputFrameBufferUsed < 8,
    1514                 :                       "Frame Buffer Used Too Large for State");
    1515                 : 
    1516               0 :     rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
    1517               0 :                      8 - mInputFrameBufferUsed, countWritten);
    1518                 : 
    1519               0 :     if (NS_FAILED(rv)) {
    1520               0 :       LOG3(("SpdySession %p buffering frame header read failure %x\n",
    1521                 :             this, rv));
    1522                 :       // maybe just blocked reading from network
    1523               0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    1524               0 :         ResumeRecv();
    1525               0 :       return rv;
    1526                 :     }
    1527                 : 
    1528                 :     LogIO(this, nsnull, "Reading Frame Header",
    1529               0 :           mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
    1530                 : 
    1531               0 :     mInputFrameBufferUsed += *countWritten;
    1532                 : 
    1533               0 :     if (mInputFrameBufferUsed < 8)
    1534                 :     {
    1535               0 :       LOG3(("SpdySession::WriteSegments %p "
    1536                 :             "BUFFERING FRAME HEADER incomplete size=%d",
    1537                 :             this, mInputFrameBufferUsed));
    1538               0 :       return rv;
    1539                 :     }
    1540                 : 
    1541                 :     // For both control and data frames the second 32 bit word of the header
    1542                 :     // is 8-flags, 24-length. (network byte order)
    1543                 :     mInputFrameDataSize =
    1544               0 :       PR_ntohl(reinterpret_cast<PRUint32 *>(mInputFrameBuffer.get())[1]);
    1545               0 :     mInputFrameDataSize &= 0x00ffffff;
    1546               0 :     mInputFrameDataRead = 0;
    1547                 :     
    1548               0 :     if (mInputFrameBuffer[0] & kFlag_Control) {
    1549                 :       EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
    1550               0 :                    mInputFrameBufferSize);
    1551               0 :       ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
    1552                 :       
    1553                 :       // The first 32 bit word of the header is
    1554                 :       // 1 ctrl - 15 version - 16 type
    1555                 :       PRUint16 version =
    1556               0 :         PR_ntohs(reinterpret_cast<PRUint16 *>(mInputFrameBuffer.get())[0]);
    1557               0 :       version &= 0x7fff;
    1558                 :       
    1559                 :       mFrameControlType =
    1560               0 :         PR_ntohs(reinterpret_cast<PRUint16 *>(mInputFrameBuffer.get())[1]);
    1561                 :       
    1562               0 :       LOG3(("SpdySession::WriteSegments %p - Control Frame Identified "
    1563                 :             "type %d version %d data len %d",
    1564                 :             this, mFrameControlType, version, mInputFrameDataSize));
    1565                 : 
    1566               0 :       if (mFrameControlType >= CONTROL_TYPE_LAST ||
    1567                 :           mFrameControlType <= CONTROL_TYPE_FIRST)
    1568               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1569                 : 
    1570                 :       // The protocol document says this value must be 1 even though this
    1571                 :       // is known as version 2.. Testing interop indicates that is a typo
    1572                 :       // in the protocol document
    1573               0 :       if (version != 2) {
    1574               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1575                 :       }
    1576                 :     }
    1577                 :     else {
    1578               0 :       ChangeDownstreamState(PROCESSING_DATA_FRAME);
    1579                 : 
    1580                 :       PRUint32 streamID =
    1581               0 :         PR_ntohl(reinterpret_cast<PRUint32 *>(mInputFrameBuffer.get())[0]);
    1582               0 :       mInputFrameDataStream = mStreamIDHash.Get(streamID);
    1583               0 :       if (!mInputFrameDataStream) {
    1584               0 :         LOG3(("SpdySession::WriteSegments %p lookup streamID 0x%X failed. "
    1585                 :               "Next = 0x%x", this, streamID, mNextStreamID));
    1586               0 :         if (streamID >= mNextStreamID)
    1587               0 :           GenerateRstStream(RST_INVALID_STREAM, streamID);
    1588               0 :         ChangeDownstreamState(DISCARDING_DATA_FRAME);
    1589                 :       }
    1590               0 :       mInputFrameDataLast = (mInputFrameBuffer[4] & kFlag_Data_FIN);
    1591                 :       Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
    1592               0 :                             mInputFrameDataSize >> 10);
    1593               0 :       LOG3(("Start Processing Data Frame. "
    1594                 :             "Session=%p Stream ID 0x%x Stream Ptr %p Fin=%d Len=%d",
    1595                 :             this, streamID, mInputFrameDataStream, mInputFrameDataLast,
    1596                 :             mInputFrameDataSize));
    1597               0 :       mLastDataReadEpoch = mLastReadEpoch;
    1598                 : 
    1599               0 :       if (mInputFrameBuffer[4] & kFlag_Data_ZLIB) {
    1600               0 :         LOG3(("Data flag has ZLIB flag set which is not valid >=2 spdy"));
    1601               0 :         return NS_ERROR_ILLEGAL_VALUE;
    1602                 :       }
    1603                 :     }
    1604                 :   }
    1605                 : 
    1606               0 :   if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
    1607               0 :     if (mDownstreamRstReason == RST_REFUSED_STREAM)
    1608               0 :       rv = NS_ERROR_NET_RESET;            //we can retry this 100% safely
    1609               0 :     else if (mDownstreamRstReason == RST_CANCEL ||
    1610                 :              mDownstreamRstReason == RST_PROTOCOL_ERROR ||
    1611                 :              mDownstreamRstReason == RST_INTERNAL_ERROR ||
    1612                 :              mDownstreamRstReason == RST_UNSUPPORTED_VERSION)
    1613               0 :       rv = NS_ERROR_NET_INTERRUPT;
    1614                 :     else
    1615               0 :       rv = NS_ERROR_ILLEGAL_VALUE;
    1616                 : 
    1617               0 :     if (mDownstreamRstReason != RST_REFUSED_STREAM &&
    1618                 :         mDownstreamRstReason != RST_CANCEL)
    1619               0 :       mShouldGoAway = true;
    1620                 : 
    1621                 :     // mInputFrameDataStream is reset by ChangeDownstreamState
    1622               0 :     SpdyStream *stream = mInputFrameDataStream;
    1623               0 :     ResetDownstreamState();
    1624               0 :     CleanupStream(stream, rv, RST_CANCEL);
    1625               0 :     return NS_OK;
    1626                 :   }
    1627                 : 
    1628               0 :   if (mDownstreamState == PROCESSING_DATA_FRAME ||
    1629                 :       mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
    1630                 : 
    1631               0 :     mSegmentWriter = writer;
    1632               0 :     rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
    1633               0 :     mSegmentWriter = nsnull;
    1634                 : 
    1635               0 :     mLastDataReadEpoch = mLastReadEpoch;
    1636                 : 
    1637               0 :     if (rv == NS_BASE_STREAM_CLOSED) {
    1638                 :       // This will happen when the transaction figures out it is EOF, generally
    1639                 :       // due to a content-length match being made
    1640               0 :       SpdyStream *stream = mInputFrameDataStream;
    1641               0 :       if (mInputFrameDataRead == mInputFrameDataSize)
    1642               0 :         ResetDownstreamState();
    1643               0 :       CleanupStream(stream, NS_OK, RST_CANCEL);
    1644               0 :       NS_ABORT_IF_FALSE(!mNeedsCleanup, "double cleanup out of data frame");
    1645               0 :       return NS_OK;
    1646                 :     }
    1647                 :     
    1648               0 :     if (mNeedsCleanup) {
    1649               0 :       CleanupStream(mNeedsCleanup, NS_OK, RST_CANCEL);
    1650               0 :       mNeedsCleanup = nsnull;
    1651                 :     }
    1652                 : 
    1653                 :     // In v3 this is where we would generate a window update
    1654                 : 
    1655               0 :     return rv;
    1656                 :   }
    1657                 : 
    1658               0 :   if (mDownstreamState == DISCARDING_DATA_FRAME) {
    1659                 :     char trash[4096];
    1660               0 :     PRUint32 count = NS_MIN(4096U, mInputFrameDataSize - mInputFrameDataRead);
    1661                 : 
    1662               0 :     if (!count) {
    1663               0 :       ResetDownstreamState();
    1664               0 :       ResumeRecv();
    1665               0 :       return NS_BASE_STREAM_WOULD_BLOCK;
    1666                 :     }
    1667                 : 
    1668               0 :     rv = NetworkRead(writer, trash, count, countWritten);
    1669                 : 
    1670               0 :     if (NS_FAILED(rv)) {
    1671               0 :       LOG3(("SpdySession %p discard frame read failure %x\n", this, rv));
    1672                 :       // maybe just blocked reading from network
    1673               0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    1674               0 :         ResumeRecv();
    1675               0 :       return rv;
    1676                 :     }
    1677                 : 
    1678               0 :     LogIO(this, nsnull, "Discarding Frame", trash, *countWritten);
    1679                 : 
    1680               0 :     mInputFrameDataRead += *countWritten;
    1681                 : 
    1682               0 :     if (mInputFrameDataRead == mInputFrameDataSize)
    1683               0 :       ResetDownstreamState();
    1684               0 :     return rv;
    1685                 :   }
    1686                 :   
    1687               0 :   if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
    1688                 :     // this cannot happen
    1689               0 :     NS_ABORT_IF_FALSE(false, "Not in Bufering Control Frame State");
    1690               0 :     return NS_ERROR_UNEXPECTED;
    1691                 :   }
    1692                 : 
    1693               0 :   NS_ABORT_IF_FALSE(mInputFrameBufferUsed == 8,
    1694                 :                     "Frame Buffer Header Not Present");
    1695                 : 
    1696               0 :   rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
    1697               0 :                    mInputFrameDataSize - mInputFrameDataRead, countWritten);
    1698                 : 
    1699               0 :   if (NS_FAILED(rv)) {
    1700               0 :     LOG3(("SpdySession %p buffering control frame read failure %x\n",
    1701                 :           this, rv));
    1702                 :     // maybe just blocked reading from network
    1703               0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    1704               0 :       ResumeRecv();
    1705               0 :     return rv;
    1706                 :   }
    1707                 : 
    1708                 :   LogIO(this, nsnull, "Reading Control Frame",
    1709               0 :         mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
    1710                 : 
    1711               0 :   mInputFrameDataRead += *countWritten;
    1712                 : 
    1713               0 :   if (mInputFrameDataRead != mInputFrameDataSize)
    1714               0 :     return NS_OK;
    1715                 : 
    1716                 :   // This check is actually redundant, the control type was previously
    1717                 :   // checked to make sure it was in range, but we will check it again
    1718                 :   // at time of use to make sure a regression doesn't creep in.
    1719               0 :   if (mFrameControlType >= CONTROL_TYPE_LAST ||
    1720                 :       mFrameControlType <= CONTROL_TYPE_FIRST) 
    1721                 :   {
    1722               0 :     NS_ABORT_IF_FALSE(false, "control type out of range");
    1723               0 :     return NS_ERROR_ILLEGAL_VALUE;
    1724                 :   }
    1725               0 :   rv = sControlFunctions[mFrameControlType](this);
    1726                 : 
    1727               0 :   NS_ABORT_IF_FALSE(NS_FAILED(rv) ||
    1728                 :                     mDownstreamState != BUFFERING_CONTROL_FRAME,
    1729                 :                     "Control Handler returned OK but did not change state");
    1730                 : 
    1731               0 :   if (mShouldGoAway && !mStreamTransactionHash.Count())
    1732               0 :     Close(NS_OK);
    1733               0 :   return rv;
    1734                 : }
    1735                 : 
    1736                 : void
    1737               0 : SpdySession::Close(nsresult aReason)
    1738                 : {
    1739               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1740                 : 
    1741               0 :   if (mClosed)
    1742               0 :     return;
    1743                 : 
    1744               0 :   LOG3(("SpdySession::Close %p %X", this, aReason));
    1745                 : 
    1746               0 :   mClosed = true;
    1747               0 :   mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
    1748               0 :   if (NS_SUCCEEDED(aReason))
    1749               0 :     GenerateGoAway();
    1750               0 :   mConnection = nsnull;
    1751               0 :   mSegmentReader = nsnull;
    1752               0 :   mSegmentWriter = nsnull;
    1753                 : }
    1754                 : 
    1755                 : void
    1756               0 : SpdySession::CloseTransaction(nsAHttpTransaction *aTransaction,
    1757                 :                               nsresult aResult)
    1758                 : {
    1759               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1760               0 :   LOG3(("SpdySession::CloseTransaction %p %p %x", this, aTransaction, aResult));
    1761                 : 
    1762                 :   // Generally this arrives as a cancel event from the connection manager.
    1763                 : 
    1764                 :   // need to find the stream and call CleanupStream() on it.
    1765               0 :   SpdyStream *stream = mStreamTransactionHash.Get(aTransaction);
    1766               0 :   if (!stream) {
    1767               0 :     LOG3(("SpdySession::CloseTransaction %p %p %x - not found.",
    1768                 :           this, aTransaction, aResult));
    1769               0 :     return;
    1770                 :   }
    1771               0 :   LOG3(("SpdySession::CloseTranscation probably a cancel. "
    1772                 :         "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
    1773                 :         this, aTransaction, aResult, stream->StreamID(), stream));
    1774               0 :   CleanupStream(stream, aResult, RST_CANCEL);
    1775               0 :   ResumeRecv();
    1776                 : }
    1777                 : 
    1778                 : 
    1779                 : //-----------------------------------------------------------------------------
    1780                 : // nsAHttpSegmentReader
    1781                 : //-----------------------------------------------------------------------------
    1782                 : 
    1783                 : nsresult
    1784               0 : SpdySession::OnReadSegment(const char *buf,
    1785                 :                            PRUint32 count,
    1786                 :                            PRUint32 *countRead)
    1787                 : {
    1788               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1789                 :   
    1790                 :   nsresult rv;
    1791                 :   
    1792                 :   // If we can release old queued data then we can try and write the new
    1793                 :   // data directly to the network without using the output queue at all
    1794               0 :   if (mOutputQueueUsed)
    1795               0 :     FlushOutputQueue();
    1796                 : 
    1797               0 :   if (!mOutputQueueUsed && mSegmentReader) {
    1798                 :     // try and write directly without output queue
    1799               0 :     rv = mSegmentReader->OnReadSegment(buf, count, countRead);
    1800                 : 
    1801               0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    1802               0 :       *countRead = 0;
    1803               0 :     else if (NS_FAILED(rv))
    1804               0 :       return rv;
    1805                 :     
    1806               0 :     if (*countRead < count) {
    1807               0 :       PRUint32 required = count - *countRead;
    1808                 :       // assuming a commitment() happened, this ensurebuffer is a nop
    1809                 :       // but just in case the queuesize is too small for the required data
    1810                 :       // call ensurebuffer().
    1811               0 :       EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
    1812               0 :       memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
    1813               0 :       mOutputQueueUsed = required;
    1814                 :     }
    1815                 :     
    1816               0 :     *countRead = count;
    1817               0 :     return NS_OK;
    1818                 :   }
    1819                 : 
    1820                 :   // At this point we are going to buffer the new data in the output
    1821                 :   // queue if it fits. By coalescing multiple small submissions into one larger
    1822                 :   // buffer we can get larger writes out to the network later on.
    1823                 : 
    1824                 :   // This routine should not be allowed to fill up the output queue
    1825                 :   // all on its own - at least kQueueReserved bytes are always left
    1826                 :   // for other routines to use - but this is an all-or-nothing function,
    1827                 :   // so if it will not all fit just return WOULD_BLOCK
    1828                 : 
    1829               0 :   if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
    1830               0 :     return NS_BASE_STREAM_WOULD_BLOCK;
    1831                 :   
    1832               0 :   memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
    1833               0 :   mOutputQueueUsed += count;
    1834               0 :   *countRead = count;
    1835                 : 
    1836               0 :   FlushOutputQueue();
    1837                 : 
    1838               0 :   return NS_OK;
    1839                 : }
    1840                 : 
    1841                 : nsresult
    1842               0 : SpdySession::CommitToSegmentSize(PRUint32 count)
    1843                 : {
    1844               0 :   if (mOutputQueueUsed)
    1845               0 :     FlushOutputQueue();
    1846                 : 
    1847                 :   // would there be enough room to buffer this if needed?
    1848               0 :   if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
    1849               0 :     return NS_OK;
    1850                 :   
    1851                 :   // if we are using part of our buffers already, try again later
    1852               0 :   if (mOutputQueueUsed)
    1853               0 :     return NS_BASE_STREAM_WOULD_BLOCK;
    1854                 : 
    1855                 :   // not enough room to buffer even with completely empty buffers.
    1856                 :   // normal frames are max 4kb, so the only case this can really happen
    1857                 :   // is a SYN_STREAM with technically unbounded headers. That is highly
    1858                 :   // unlikely, but possible. Create enough room for it because the buffers
    1859                 :   // will be necessary - SSL does not absorb writes of very large sizes
    1860                 :   // in single sends.
    1861                 : 
    1862               0 :   EnsureBuffer(mOutputQueueBuffer, count + kQueueReserved, 0, mOutputQueueSize);
    1863                 : 
    1864               0 :   NS_ABORT_IF_FALSE((mOutputQueueUsed + count) <=
    1865                 :                     (mOutputQueueSize - kQueueReserved),
    1866                 :                     "buffer not as large as expected");
    1867                 : 
    1868               0 :   return NS_OK;
    1869                 : }
    1870                 : 
    1871                 : //-----------------------------------------------------------------------------
    1872                 : // nsAHttpSegmentWriter
    1873                 : //-----------------------------------------------------------------------------
    1874                 : 
    1875                 : nsresult
    1876               0 : SpdySession::OnWriteSegment(char *buf,
    1877                 :                             PRUint32 count,
    1878                 :                             PRUint32 *countWritten)
    1879                 : {
    1880               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1881                 :   nsresult rv;
    1882                 : 
    1883               0 :   if (!mSegmentWriter) {
    1884                 :     // the only way this could happen would be if Close() were called on the
    1885                 :     // stack with WriteSegments()
    1886               0 :     return NS_ERROR_FAILURE;
    1887                 :   }
    1888                 :   
    1889               0 :   if (mDownstreamState == PROCESSING_DATA_FRAME) {
    1890                 : 
    1891               0 :     if (mInputFrameDataLast &&
    1892                 :         mInputFrameDataRead == mInputFrameDataSize) {
    1893                 :       // This will result in Close() being called
    1894               0 :       NS_ABORT_IF_FALSE(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
    1895               0 :       mNeedsCleanup = mInputFrameDataStream;
    1896                 : 
    1897               0 :       LOG3(("SpdySession::OnWriteSegment %p - recorded downstream fin of "
    1898                 :             "stream %p 0x%X", this, mInputFrameDataStream,
    1899                 :             mInputFrameDataStream->StreamID()));
    1900               0 :       *countWritten = 0;
    1901               0 :       ResetDownstreamState();
    1902               0 :       return NS_BASE_STREAM_CLOSED;
    1903                 :     }
    1904                 :     
    1905               0 :     count = NS_MIN(count, mInputFrameDataSize - mInputFrameDataRead);
    1906               0 :     rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
    1907               0 :     if (NS_FAILED(rv))
    1908               0 :       return rv;
    1909                 : 
    1910                 :     LogIO(this, mInputFrameDataStream, "Reading Data Frame",
    1911               0 :           buf, *countWritten);
    1912                 : 
    1913               0 :     mInputFrameDataRead += *countWritten;
    1914                 :     
    1915               0 :     mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
    1916               0 :     if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameDataLast)
    1917               0 :       ResetDownstreamState();
    1918                 : 
    1919               0 :     return rv;
    1920                 :   }
    1921                 :   
    1922               0 :   if (mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
    1923                 :     
    1924               0 :     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
    1925                 :         mInputFrameDataLast) {
    1926               0 :       *countWritten = 0;
    1927               0 :       ResetDownstreamState();
    1928               0 :       return NS_BASE_STREAM_CLOSED;
    1929                 :     }
    1930                 :       
    1931                 :     count = NS_MIN(count,
    1932               0 :                    mFlatHTTPResponseHeaders.Length() -
    1933               0 :                    mFlatHTTPResponseHeadersOut);
    1934                 :     memcpy(buf,
    1935               0 :            mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
    1936               0 :            count);
    1937               0 :     mFlatHTTPResponseHeadersOut += count;
    1938               0 :     *countWritten = count;
    1939                 : 
    1940               0 :     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
    1941               0 :         !mInputFrameDataLast)
    1942               0 :       ResetDownstreamState();
    1943               0 :     return NS_OK;
    1944                 :   }
    1945                 : 
    1946               0 :   return NS_ERROR_UNEXPECTED;
    1947                 : }
    1948                 : 
    1949                 : //-----------------------------------------------------------------------------
    1950                 : // Modified methods of nsAHttpConnection
    1951                 : //-----------------------------------------------------------------------------
    1952                 : 
    1953                 : nsresult
    1954               0 : SpdySession::ResumeSend()
    1955                 : {
    1956               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1957               0 :   LOG3(("SpdySession::ResumeSend %p", this));
    1958                 : 
    1959               0 :   if (!mConnection)
    1960               0 :     return NS_ERROR_FAILURE;
    1961                 : 
    1962               0 :   return mConnection->ResumeSend();
    1963                 : }
    1964                 : 
    1965                 : void
    1966               0 : SpdySession::TransactionHasDataToWrite(nsAHttpTransaction *caller)
    1967                 : {
    1968               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1969               0 :   LOG3(("SpdySession::TransactionHasDataToWrite %p trans=%p", this, caller));
    1970                 : 
    1971                 :   // a trapped signal from the http transaction to the connection that
    1972                 :   // it is no longer blocked on read.
    1973                 : 
    1974               0 :   SpdyStream *stream = mStreamTransactionHash.Get(caller);
    1975               0 :   if (!stream) {
    1976               0 :     LOG3(("SpdySession::TransactionHasDataToWrite %p caller %p not found",
    1977                 :           this, caller));
    1978               0 :     return;
    1979                 :   }
    1980                 :   
    1981               0 :   LOG3(("SpdySession::TransactionHasDataToWrite %p ID is %x",
    1982                 :         this, stream->StreamID()));
    1983                 : 
    1984               0 :   mReadyForWrite.Push(stream);
    1985                 : }
    1986                 : 
    1987                 : void
    1988               0 : SpdySession::TransactionHasDataToWrite(SpdyStream *stream)
    1989                 : {
    1990               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1991               0 :   LOG3(("SpdySession::TransactionHasDataToWrite %p stream=%p ID=%x",
    1992                 :         this, stream, stream->StreamID()));
    1993                 : 
    1994               0 :   mReadyForWrite.Push(stream);
    1995               0 :   SetWriteCallbacks();
    1996               0 : }
    1997                 : 
    1998                 : nsresult
    1999               0 : SpdySession::ResumeRecv()
    2000                 : {
    2001               0 :   if (!mConnection)
    2002               0 :     return NS_ERROR_FAILURE;
    2003                 : 
    2004               0 :   return mConnection->ResumeRecv();
    2005                 : }
    2006                 : 
    2007                 : bool
    2008               0 : SpdySession::IsPersistent()
    2009                 : {
    2010               0 :   return true;
    2011                 : }
    2012                 : 
    2013                 : nsresult
    2014               0 : SpdySession::TakeTransport(nsISocketTransport **,
    2015                 :                            nsIAsyncInputStream **,
    2016                 :                            nsIAsyncOutputStream **)
    2017                 : {
    2018               0 :   NS_ABORT_IF_FALSE(false, "TakeTransport of SpdySession");
    2019               0 :   return NS_ERROR_UNEXPECTED;
    2020                 : }
    2021                 : 
    2022                 : nsHttpConnection *
    2023               0 : SpdySession::TakeHttpConnection()
    2024                 : {
    2025               0 :   NS_ABORT_IF_FALSE(false, "TakeHttpConnection of SpdySession");
    2026               0 :   return nsnull;
    2027                 : }
    2028                 : 
    2029                 : nsISocketTransport *
    2030               0 : SpdySession::Transport()
    2031                 : {
    2032               0 :   if (!mConnection)
    2033               0 :     return nsnull;
    2034               0 :   return mConnection->Transport();
    2035                 : }
    2036                 : 
    2037                 : //-----------------------------------------------------------------------------
    2038                 : // unused methods of nsAHttpTransaction
    2039                 : // We can be sure of this because SpdySession is only constructed in
    2040                 : // nsHttpConnection and is never passed out of that object
    2041                 : //-----------------------------------------------------------------------------
    2042                 : 
    2043                 : void
    2044               0 : SpdySession::SetConnection(nsAHttpConnection *)
    2045                 : {
    2046                 :   // This is unexpected
    2047               0 :   NS_ABORT_IF_FALSE(false, "SpdySession::SetConnection()");
    2048               0 : }
    2049                 : 
    2050                 : void
    2051               0 : SpdySession::GetSecurityCallbacks(nsIInterfaceRequestor **,
    2052                 :                                   nsIEventTarget **)
    2053                 : {
    2054                 :   // This is unexpected
    2055               0 :   NS_ABORT_IF_FALSE(false, "SpdySession::GetSecurityCallbacks()");
    2056               0 : }
    2057                 : 
    2058                 : void
    2059               0 : SpdySession::SetSSLConnectFailed()
    2060                 : {
    2061               0 :   NS_ABORT_IF_FALSE(false, "SpdySession::SetSSLConnectFailed()");
    2062               0 : }
    2063                 : 
    2064                 : bool
    2065               0 : SpdySession::IsDone()
    2066                 : {
    2067               0 :   NS_ABORT_IF_FALSE(false, "SpdySession::IsDone()");
    2068               0 :   return false;
    2069                 : }
    2070                 : 
    2071                 : nsresult
    2072               0 : SpdySession::Status()
    2073                 : {
    2074               0 :   NS_ABORT_IF_FALSE(false, "SpdySession::Status()");
    2075               0 :   return NS_ERROR_UNEXPECTED;
    2076                 : }
    2077                 : 
    2078                 : PRUint32
    2079               0 : SpdySession::Available()
    2080                 : {
    2081               0 :   NS_ABORT_IF_FALSE(false, "SpdySession::Available()");
    2082               0 :   return 0;
    2083                 : }
    2084                 : 
    2085                 : nsHttpRequestHead *
    2086               0 : SpdySession::RequestHead()
    2087                 : {
    2088               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    2089               0 :   NS_ABORT_IF_FALSE(false,
    2090                 :                     "SpdySession::RequestHead() "
    2091                 :                     "should not be called after SPDY is setup");
    2092               0 :   return NULL;
    2093                 : }
    2094                 : 
    2095                 : PRUint32
    2096               0 : SpdySession::Http1xTransactionCount()
    2097                 : {
    2098               0 :   return 0;
    2099                 : }
    2100                 : 
    2101                 : // used as an enumerator by TakeSubTransactions()
    2102                 : static PLDHashOperator
    2103               0 : TakeStream(nsAHttpTransaction *key,
    2104                 :            nsAutoPtr<SpdyStream> &stream,
    2105                 :            void *closure)
    2106                 : {
    2107                 :   nsTArray<nsRefPtr<nsAHttpTransaction> > *list =
    2108               0 :     static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure);
    2109                 : 
    2110               0 :   list->AppendElement(key);
    2111                 : 
    2112                 :   // removing the stream from the hash will delete the stream
    2113                 :   // and drop the transaction reference the hash held
    2114               0 :   return PL_DHASH_REMOVE;
    2115                 : }
    2116                 : 
    2117                 : nsresult
    2118               0 : SpdySession::TakeSubTransactions(
    2119                 :     nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
    2120                 : {
    2121                 :   // Generally this cannot be done with spdy as transactions are
    2122                 :   // started right away.
    2123                 : 
    2124               0 :   LOG3(("SpdySession::TakeSubTransactions %p\n", this));
    2125                 : 
    2126               0 :   if (mConcurrentHighWater > 0)
    2127               0 :     return NS_ERROR_ALREADY_OPENED;
    2128                 : 
    2129               0 :   LOG3(("   taking %d\n", mStreamTransactionHash.Count()));
    2130                 : 
    2131               0 :   mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
    2132               0 :   return NS_OK;
    2133                 : }
    2134                 : 
    2135                 : //-----------------------------------------------------------------------------
    2136                 : // Pass through methods of nsAHttpConnection
    2137                 : //-----------------------------------------------------------------------------
    2138                 : 
    2139                 : nsAHttpConnection *
    2140               0 : SpdySession::Connection()
    2141                 : {
    2142               0 :   NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    2143               0 :   return mConnection;
    2144                 : }
    2145                 : 
    2146                 : nsresult
    2147               0 : SpdySession::OnHeadersAvailable(nsAHttpTransaction *transaction,
    2148                 :                                 nsHttpRequestHead *requestHead,
    2149                 :                                 nsHttpResponseHead *responseHead,
    2150                 :                                 bool *reset)
    2151                 : {
    2152               0 :   return mConnection->OnHeadersAvailable(transaction,
    2153                 :                                          requestHead,
    2154                 :                                          responseHead,
    2155               0 :                                          reset);
    2156                 : }
    2157                 : 
    2158                 : void
    2159               0 : SpdySession::GetConnectionInfo(nsHttpConnectionInfo **connInfo)
    2160                 : {
    2161               0 :   mConnection->GetConnectionInfo(connInfo);
    2162               0 : }
    2163                 : 
    2164                 : void
    2165               0 : SpdySession::GetSecurityInfo(nsISupports **supports)
    2166                 : {
    2167               0 :   mConnection->GetSecurityInfo(supports);
    2168               0 : }
    2169                 : 
    2170                 : bool
    2171               0 : SpdySession::IsReused()
    2172                 : {
    2173               0 :   return mConnection->IsReused();
    2174                 : }
    2175                 : 
    2176                 : nsresult
    2177               0 : SpdySession::PushBack(const char *buf, PRUint32 len)
    2178                 : {
    2179               0 :   return mConnection->PushBack(buf, len);
    2180                 : }
    2181                 : 
    2182                 : bool
    2183               0 : SpdySession::LastTransactionExpectedNoContent()
    2184                 : {
    2185               0 :   return mConnection->LastTransactionExpectedNoContent();
    2186                 : }
    2187                 : 
    2188                 : void
    2189               0 : SpdySession::SetLastTransactionExpectedNoContent(bool val)
    2190                 : {
    2191               0 :   mConnection->SetLastTransactionExpectedNoContent(val);
    2192               0 : }
    2193                 : 
    2194                 : } // namespace mozilla::net
    2195                 : } // namespace mozilla
    2196                 : 

Generated by: LCOV version 1.7