netwerk/protocol/http/SpdySession3.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // HttpLog.h should generally be included first
     8 #include "HttpLog.h"
    10 // Log on level :5, instead of default :4.
    11 #undef LOG
    12 #define LOG(args) LOG5(args)
    13 #undef LOG_ENABLED
    14 #define LOG_ENABLED() LOG5_ENABLED()
    16 #include "mozilla/Endian.h"
    17 #include "mozilla/Telemetry.h"
    18 #include "nsHttp.h"
    19 #include "nsHttpHandler.h"
    20 #include "nsILoadGroup.h"
    21 #include "prprf.h"
    22 #include "SpdyPush3.h"
    23 #include "SpdySession3.h"
    24 #include "SpdyStream3.h"
    25 #include "PSpdyPush.h"
    26 #include "SpdyZlibReporter.h"
    28 #include <algorithm>
    30 #ifdef DEBUG
    31 // defined by the socket transport service while active
    32 extern PRThread *gSocketThread;
    33 #endif
    35 namespace mozilla {
    36 namespace net {
    38 // SpdySession3 has multiple inheritance of things that implement
    39 // nsISupports, so this magic is taken from nsHttpPipeline that
    40 // implements some of the same abstract classes.
    41 NS_IMPL_ADDREF(SpdySession3)
    42 NS_IMPL_RELEASE(SpdySession3)
    43 NS_INTERFACE_MAP_BEGIN(SpdySession3)
    44     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
    45 NS_INTERFACE_MAP_END
    47 SpdySession3::SpdySession3(nsAHttpTransaction *aHttpTransaction,
    48                          nsISocketTransport *aSocketTransport,
    49                          int32_t firstPriority)
    50   : mSocketTransport(aSocketTransport),
    51     mSegmentReader(nullptr),
    52     mSegmentWriter(nullptr),
    53     mNextStreamID(1),
    54     mConcurrentHighWater(0),
    55     mDownstreamState(BUFFERING_FRAME_HEADER),
    56     mInputFrameBufferSize(kDefaultBufferSize),
    57     mInputFrameBufferUsed(0),
    58     mInputFrameDataLast(false),
    59     mInputFrameDataStream(nullptr),
    60     mNeedsCleanup(nullptr),
    61     mShouldGoAway(false),
    62     mClosed(false),
    63     mCleanShutdown(false),
    64     mDataPending(false),
    65     mGoAwayID(0),
    66     mMaxConcurrent(kDefaultMaxConcurrent),
    67     mConcurrent(0),
    68     mServerPushedResources(0),
    69     mServerInitialWindow(kDefaultServerRwin),
    70     mOutputQueueSize(kDefaultQueueSize),
    71     mOutputQueueUsed(0),
    72     mOutputQueueSent(0),
    73     mLastReadEpoch(PR_IntervalNow()),
    74     mPingSentEpoch(0),
    75     mNextPingID(1)
    76 {
    77   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
    79   static uint64_t sSerial;
    80   mSerial = ++sSerial;
    82   LOG3(("SpdySession3::SpdySession3 %p transaction 1 = %p serial=0x%X\n",
    83         this, aHttpTransaction, mSerial));
    85   mConnection = aHttpTransaction->Connection();
    86   mInputFrameBuffer = new char[mInputFrameBufferSize];
    87   mOutputQueueBuffer = new char[mOutputQueueSize];
    88   zlibInit();
    90   mPushAllowance = gHttpHandler->SpdyPushAllowance();
    91   mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
    92   GenerateSettings();
    94   if (!aHttpTransaction->IsNullTransaction())
    95     AddStream(aHttpTransaction, firstPriority);
    96   mLastDataReadEpoch = mLastReadEpoch;
    98   mPingThreshold = gHttpHandler->SpdyPingThreshold();
    99 }
   101 PLDHashOperator
   102 SpdySession3::ShutdownEnumerator(nsAHttpTransaction *key,
   103                                 nsAutoPtr<SpdyStream3> &stream,
   104                                 void *closure)
   105 {
   106   SpdySession3 *self = static_cast<SpdySession3 *>(closure);
   108   // On a clean server hangup the server sets the GoAwayID to be the ID of
   109   // the last transaction it processed. If the ID of stream in the
   110   // local stream is greater than that it can safely be restarted because the
   111   // server guarantees it was not partially processed. Streams that have not
   112   // registered an ID haven't actually been sent yet so they can always be
   113   // restarted.
   114   if (self->mCleanShutdown &&
   115       (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID()))
   116     self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted
   117   else
   118     self->CloseStream(stream, NS_ERROR_ABORT);
   120   return PL_DHASH_NEXT;
   121 }
   123 PLDHashOperator
   124 SpdySession3::GoAwayEnumerator(nsAHttpTransaction *key,
   125                                nsAutoPtr<SpdyStream3> &stream,
   126                                void *closure)
   127 {
   128   SpdySession3 *self = static_cast<SpdySession3 *>(closure);
   130   // these streams were not processed by the server and can be restarted.
   131   // Do that after the enumerator completes to avoid the risk of
   132   // a restart event re-entrantly modifying this hash. Be sure not to restart
   133   // a pushed (even numbered) stream
   134   if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) ||
   135       !stream->HasRegisteredID()) {
   136     self->mGoAwayStreamsToRestart.Push(stream);
   137   }
   139   return PL_DHASH_NEXT;
   140 }
   142 SpdySession3::~SpdySession3()
   143 {
   144   LOG3(("SpdySession3::~SpdySession3 %p mDownstreamState=%X",
   145         this, mDownstreamState));
   147   inflateEnd(&mDownstreamZlib);
   148   deflateEnd(&mUpstreamZlib);
   150   mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
   151   Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
   152   Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
   153   Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
   154                         mServerPushedResources);
   155 }
   157 void
   158 SpdySession3::LogIO(SpdySession3 *self, SpdyStream3 *stream, const char *label,
   159                    const char *data, uint32_t datalen)
   160 {
   161   if (!LOG4_ENABLED())
   162     return;
   164   LOG4(("SpdySession3::LogIO %p stream=%p id=0x%X [%s]",
   165         self, stream, stream ? stream->StreamID() : 0, label));
   167   // Max line is (16 * 3) + 10(prefix) + newline + null
   168   char linebuf[128];
   169   uint32_t index;
   170   char *line = linebuf;
   172   linebuf[127] = 0;
   174   for (index = 0; index < datalen; ++index) {
   175     if (!(index % 16)) {
   176       if (index) {
   177         *line = 0;
   178         LOG4(("%s", linebuf));
   179       }
   180       line = linebuf;
   181       PR_snprintf(line, 128, "%08X: ", index);
   182       line += 10;
   183     }
   184     PR_snprintf(line, 128 - (line - linebuf), "%02X ",
   185                 ((unsigned char *)data)[index]);
   186     line += 3;
   187   }
   188   if (index) {
   189     *line = 0;
   190     LOG4(("%s", linebuf));
   191   }
   192 }
   194 bool
   195 SpdySession3::RoomForMoreConcurrent()
   196 {
   197   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   199   return (mConcurrent < mMaxConcurrent);
   200 }
   202 bool
   203 SpdySession3::RoomForMoreStreams()
   204 {
   205   if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
   206     return false;
   208   return !mShouldGoAway;
   209 }
   211 PRIntervalTime
   212 SpdySession3::IdleTime()
   213 {
   214   return PR_IntervalNow() - mLastDataReadEpoch;
   215 }
   217 uint32_t
   218 SpdySession3::ReadTimeoutTick(PRIntervalTime now)
   219 {
   220     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   221     MOZ_ASSERT(mNextPingID & 1, "Ping Counter Not Odd");
   223     LOG(("SpdySession3::ReadTimeoutTick %p delta since last read %ds\n",
   224          this, PR_IntervalToSeconds(now - mLastReadEpoch)));
   226     if (!mPingThreshold)
   227       return UINT32_MAX;
   229     if ((now - mLastReadEpoch) < mPingThreshold) {
   230       // recent activity means ping is not an issue
   231       if (mPingSentEpoch)
   232         mPingSentEpoch = 0;
   234       return PR_IntervalToSeconds(mPingThreshold) -
   235         PR_IntervalToSeconds(now - mLastReadEpoch);
   236     }
   238     if (mPingSentEpoch) {
   239       LOG(("SpdySession3::ReadTimeoutTick %p handle outstanding ping\n"));
   240       if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
   241         LOG(("SpdySession3::ReadTimeoutTick %p Ping Timer Exhaustion\n",
   242              this));
   243         mPingSentEpoch = 0;
   244         Close(NS_ERROR_NET_TIMEOUT);
   245         return UINT32_MAX;
   246       }
   247       return 1; // run the tick aggressively while ping is outstanding
   248     }
   250     LOG(("SpdySession3::ReadTimeoutTick %p generating ping 0x%X\n",
   251          this, mNextPingID));
   253     if (mNextPingID == 0xffffffff) {
   254       LOG(("SpdySession3::ReadTimeoutTick %p cannot form ping - ids exhausted\n",
   255            this));
   256       return UINT32_MAX;
   257     }
   259     mPingSentEpoch = PR_IntervalNow();
   260     if (!mPingSentEpoch)
   261       mPingSentEpoch = 1; // avoid the 0 sentinel value
   262     GeneratePing(mNextPingID);
   263     mNextPingID += 2;
   264     ResumeRecv(); // read the ping reply
   266     // Check for orphaned push streams. This looks expensive, but generally the
   267     // list is empty.
   268     SpdyPushedStream3 *deleteMe;
   269     TimeStamp timestampNow;
   270     do {
   271       deleteMe = nullptr;
   273       for (uint32_t index = mPushedStreams.Length();
   274            index > 0 ; --index) {
   275         SpdyPushedStream3 *pushedStream = mPushedStreams[index - 1];
   277         if (timestampNow.IsNull())
   278           timestampNow = TimeStamp::Now(); // lazy initializer
   280         // if spdy finished, but not connected, and its been like that for too long..
   281         // cleanup the stream..
   282         if (pushedStream->IsOrphaned(timestampNow))
   283         {
   284           LOG3(("SpdySession3 Timeout Pushed Stream %p 0x%X\n",
   285                 this, pushedStream->StreamID()));
   286           deleteMe = pushedStream;
   287           break; // don't CleanupStream() while iterating this vector
   288         }
   289       }
   290       if (deleteMe)
   291         CleanupStream(deleteMe, NS_ERROR_ABORT, RST_CANCEL);
   293     } while (deleteMe);
   295     if (mNextPingID == 0xffffffff) {
   296       LOG(("SpdySession3::ReadTimeoutTick %p "
   297            "ping ids exhausted marking goaway\n", this));
   298       mShouldGoAway = true;
   299     }
   300     return 1; // run the tick aggressively while ping is outstanding
   301 }
   303 uint32_t
   304 SpdySession3::RegisterStreamID(SpdyStream3 *stream, uint32_t aNewID)
   305 {
   306   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   308   MOZ_ASSERT(mNextStreamID < 0xfffffff0,
   309              "should have stopped admitting streams");
   311   MOZ_ASSERT(!(aNewID & 1),
   312              "0 for autoassign pull, otherwise explicit even push assignment");
   313   if (!aNewID) {
   314     // auto generate a new pull stream ID
   315     aNewID = mNextStreamID;
   316     MOZ_ASSERT(aNewID & 1, "pull ID must be odd.");
   317     mNextStreamID += 2;
   318   }
   320   LOG3(("SpdySession3::RegisterStreamID session=%p stream=%p id=0x%X "
   321         "concurrent=%d",this, stream, aNewID, mConcurrent));
   323   // We've used up plenty of ID's on this session. Start
   324   // moving to a new one before there is a crunch involving
   325   // server push streams or concurrent non-registered submits
   326   if (aNewID >= kMaxStreamID)
   327     mShouldGoAway = true;
   329   // integrity check
   330   if (mStreamIDHash.Get(aNewID)) {
   331     LOG3(("   New ID already present\n"));
   332     MOZ_ASSERT(false, "New ID already present in mStreamIDHash");
   333     mShouldGoAway = true;
   334     return kDeadStreamID;
   335   }
   337   mStreamIDHash.Put(aNewID, stream);
   338   return aNewID;
   339 }
   341 bool
   342 SpdySession3::AddStream(nsAHttpTransaction *aHttpTransaction,
   343                        int32_t aPriority)
   344 {
   345   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   347   // integrity check
   348   if (mStreamTransactionHash.Get(aHttpTransaction)) {
   349     LOG3(("   New transaction already present\n"));
   350     MOZ_ASSERT(false, "AddStream duplicate transaction pointer");
   351     return false;
   352   }
   354   aHttpTransaction->SetConnection(this);
   355   SpdyStream3 *stream = new SpdyStream3(aHttpTransaction, this, aPriority);
   357   LOG3(("SpdySession3::AddStream session=%p stream=%p NextID=0x%X (tentative)",
   358         this, stream, mNextStreamID));
   360   mStreamTransactionHash.Put(aHttpTransaction, stream);
   362   if (RoomForMoreConcurrent()) {
   363     LOG3(("SpdySession3::AddStream %p stream %p activated immediately.",
   364           this, stream));
   365     ActivateStream(stream);
   366   }
   367   else {
   368     LOG3(("SpdySession3::AddStream %p stream %p queued.", this, stream));
   369     mQueuedStreams.Push(stream);
   370   }
   372   if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) {
   373     LOG3(("SpdySession3::AddStream %p transaction %p forces keep-alive off.\n",
   374           this, aHttpTransaction));
   375     DontReuse();
   376   }
   377   return true;
   378 }
   380 void
   381 SpdySession3::ActivateStream(SpdyStream3 *stream)
   382 {
   383   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   384   MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
   385              "Do not activate pushed streams");
   387   ++mConcurrent;
   388   if (mConcurrent > mConcurrentHighWater)
   389     mConcurrentHighWater = mConcurrent;
   390   LOG3(("SpdySession3::AddStream %p activating stream %p Currently %d "
   391         "streams in session, high water mark is %d",
   392         this, stream, mConcurrent, mConcurrentHighWater));
   394   mReadyForWrite.Push(stream);
   395   SetWriteCallbacks();
   397   // Kick off the SYN transmit without waiting for the poll loop
   398   // This won't work for stream id=1 because there is no segment reader
   399   // yet.
   400   if (mSegmentReader) {
   401     uint32_t countRead;
   402     ReadSegments(nullptr, kDefaultBufferSize, &countRead);
   403   }
   404 }
   406 void
   407 SpdySession3::ProcessPending()
   408 {
   409   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   411   while (RoomForMoreConcurrent()) {
   412     SpdyStream3 *stream = static_cast<SpdyStream3 *>(mQueuedStreams.PopFront());
   413     if (!stream)
   414       return;
   415     LOG3(("SpdySession3::ProcessPending %p stream %p activated from queue.",
   416           this, stream));
   417     ActivateStream(stream);
   418   }
   419 }
   421 nsresult
   422 SpdySession3::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
   423                          uint32_t count, uint32_t *countWritten)
   424 {
   425   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   427   if (!count) {
   428     *countWritten = 0;
   429     return NS_OK;
   430   }
   432   nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
   433   if (NS_SUCCEEDED(rv) && *countWritten > 0)
   434     mLastReadEpoch = PR_IntervalNow();
   435   return rv;
   436 }
   438 void
   439 SpdySession3::SetWriteCallbacks()
   440 {
   441   if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
   442       mConnection->ResumeSend();
   443 }
   445 void
   446 SpdySession3::RealignOutputQueue()
   447 {
   448   mOutputQueueUsed -= mOutputQueueSent;
   449   memmove(mOutputQueueBuffer.get(),
   450           mOutputQueueBuffer.get() + mOutputQueueSent,
   451           mOutputQueueUsed);
   452   mOutputQueueSent = 0;
   453 }
   455 void
   456 SpdySession3::FlushOutputQueue()
   457 {
   458   if (!mSegmentReader || !mOutputQueueUsed)
   459     return;
   461   nsresult rv;
   462   uint32_t countRead;
   463   uint32_t avail = mOutputQueueUsed - mOutputQueueSent;
   465   rv = mSegmentReader->
   466     OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
   467                                      &countRead);
   468   LOG3(("SpdySession3::FlushOutputQueue %p sz=%d rv=%x actual=%d",
   469         this, avail, rv, countRead));
   471   // Dont worry about errors on write, we will pick this up as a read error too
   472   if (NS_FAILED(rv))
   473     return;
   475   if (countRead == avail) {
   476     mOutputQueueUsed = 0;
   477     mOutputQueueSent = 0;
   478     return;
   479   }
   481   mOutputQueueSent += countRead;
   483   // If the output queue is close to filling up and we have sent out a good
   484   // chunk of data from the beginning then realign it.
   486   if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
   487       ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
   488     RealignOutputQueue();
   489   }
   490 }
   492 void
   493 SpdySession3::DontReuse()
   494 {
   495   mShouldGoAway = true;
   496   if (!mStreamTransactionHash.Count())
   497     Close(NS_OK);
   498 }
   500 uint32_t
   501 SpdySession3::GetWriteQueueSize()
   502 {
   503   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   505   return mReadyForWrite.GetSize();
   506 }
   508 void
   509 SpdySession3::ChangeDownstreamState(enum stateType newState)
   510 {
   511   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   513   LOG3(("SpdyStream3::ChangeDownstreamState() %p from %X to %X",
   514         this, mDownstreamState, newState));
   515   mDownstreamState = newState;
   516 }
   518 void
   519 SpdySession3::ResetDownstreamState()
   520 {
   521   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   523   LOG3(("SpdyStream3::ResetDownstreamState() %p", this));
   524   ChangeDownstreamState(BUFFERING_FRAME_HEADER);
   526   if (mInputFrameDataLast && mInputFrameDataStream) {
   527     mInputFrameDataLast = false;
   528     if (!mInputFrameDataStream->RecvdFin()) {
   529       LOG3(("  SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID()));
   530       mInputFrameDataStream->SetRecvdFin(true);
   531       DecrementConcurrent(mInputFrameDataStream);
   532     }
   533   }
   534   mInputFrameBufferUsed = 0;
   535   mInputFrameDataStream = nullptr;
   536 }
   538 template<typename T> void
   539 SpdySession3::EnsureBuffer(nsAutoArrayPtr<T> &buf,
   540                           uint32_t newSize,
   541                           uint32_t preserve,
   542                           uint32_t &objSize)
   543 {
   544   if (objSize >= newSize)
   545       return;
   547   // Leave a little slop on the new allocation - add 2KB to
   548   // what we need and then round the result up to a 4KB (page)
   549   // boundary.
   551   objSize = (newSize + 2048 + 4095) & ~4095;
   553   static_assert(sizeof(T) == 1, "sizeof(T) must be 1");
   554   nsAutoArrayPtr<T> tmp(new T[objSize]);
   555   memcpy(tmp, buf, preserve);
   556   buf = tmp;
   557 }
   559 // Instantiate supported templates explicitly.
   560 template void
   561 SpdySession3::EnsureBuffer(nsAutoArrayPtr<char> &buf,
   562                            uint32_t newSize,
   563                            uint32_t preserve,
   564                            uint32_t &objSize);
   566 template void
   567 SpdySession3::EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf,
   568                            uint32_t newSize,
   569                            uint32_t preserve,
   570                            uint32_t &objSize);
   572 void
   573 SpdySession3::DecrementConcurrent(SpdyStream3 *aStream)
   574 {
   575   uint32_t id = aStream->StreamID();
   577   if (id && !(id & 0x1))
   578     return; // pushed streams aren't counted in concurrent limit
   580   MOZ_ASSERT(mConcurrent);
   581   --mConcurrent;
   582   LOG3(("DecrementConcurrent %p id=0x%X concurrent=%d\n",
   583         this, id, mConcurrent));
   584   ProcessPending();
   585 }
   587 void
   588 SpdySession3::zlibInit()
   589 {
   590   mDownstreamZlib.zalloc = SpdyZlibReporter::Alloc;
   591   mDownstreamZlib.zfree = SpdyZlibReporter::Free;
   592   mDownstreamZlib.opaque = Z_NULL;
   594   inflateInit(&mDownstreamZlib);
   596   mUpstreamZlib.zalloc = SpdyZlibReporter::Alloc;
   597   mUpstreamZlib.zfree = SpdyZlibReporter::Free;
   598   mUpstreamZlib.opaque = Z_NULL;
   600   // mixing carte blanche compression with tls subjects us to traffic
   601   // analysis attacks
   602   deflateInit(&mUpstreamZlib, Z_NO_COMPRESSION);
   603   deflateSetDictionary(&mUpstreamZlib,
   604                        SpdyStream3::kDictionary,
   605                        sizeof(SpdyStream3::kDictionary));
   606 }
   608 // Need to decompress some data in order to keep the compression
   609 // context correct, but we really don't care what the result is
   610 nsresult
   611 SpdySession3::UncompressAndDiscard(uint32_t offset,
   612                                    uint32_t blockLen)
   613 {
   614   char *blockStart = mInputFrameBuffer + offset;
   615   unsigned char trash[2048];
   616   mDownstreamZlib.avail_in = blockLen;
   617   mDownstreamZlib.next_in = reinterpret_cast<unsigned char *>(blockStart);
   618   bool triedDictionary = false;
   620   do {
   621     mDownstreamZlib.next_out = trash;
   622     mDownstreamZlib.avail_out = sizeof(trash);
   623     int zlib_rv = inflate(&mDownstreamZlib, Z_NO_FLUSH);
   625     if (zlib_rv == Z_NEED_DICT) {
   626       if (triedDictionary) {
   627         LOG3(("SpdySession3::UncompressAndDiscard %p Dictionary Error\n", this));
   628         return NS_ERROR_ILLEGAL_VALUE;
   629       }
   631       triedDictionary = true;
   632       inflateSetDictionary(&mDownstreamZlib, SpdyStream3::kDictionary,
   633                            sizeof(SpdyStream3::kDictionary));
   634     }
   636     if (zlib_rv == Z_DATA_ERROR)
   637       return NS_ERROR_ILLEGAL_VALUE;
   639     if (zlib_rv == Z_MEM_ERROR)
   640       return NS_ERROR_FAILURE;
   641   }
   642   while (mDownstreamZlib.avail_in);
   643   return NS_OK;
   644 }
   646 void
   647 SpdySession3::GeneratePing(uint32_t aID)
   648 {
   649   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   650   LOG3(("SpdySession3::GeneratePing %p 0x%X\n", this, aID));
   652   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
   653                mOutputQueueUsed, mOutputQueueSize);
   654   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
   655   mOutputQueueUsed += 12;
   657   packet[0] = kFlag_Control;
   658   packet[1] = kVersion;
   659   packet[2] = 0;
   660   packet[3] = CONTROL_TYPE_PING;
   661   packet[4] = 0;                                  /* flags */
   662   packet[5] = 0;
   663   packet[6] = 0;
   664   packet[7] = 4;                                  /* length */
   666   NetworkEndian::writeUint32(packet + 8, aID);
   668   LogIO(this, nullptr, "Generate Ping", packet, 12);
   669   FlushOutputQueue();
   670 }
   672 void
   673 SpdySession3::GenerateRstStream(uint32_t aStatusCode, uint32_t aID)
   674 {
   675   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   676   LOG3(("SpdySession3::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
   678   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16,
   679                mOutputQueueUsed, mOutputQueueSize);
   680   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
   681   mOutputQueueUsed += 16;
   683   packet[0] = kFlag_Control;
   684   packet[1] = kVersion;
   685   packet[2] = 0;
   686   packet[3] = CONTROL_TYPE_RST_STREAM;
   687   packet[4] = 0;                                  /* flags */
   688   packet[5] = 0;
   689   packet[6] = 0;
   690   packet[7] = 8;                                  /* length */
   692   NetworkEndian::writeUint32(packet + 8, aID);
   693   NetworkEndian::writeUint32(packet + 12, aStatusCode);
   695   LogIO(this, nullptr, "Generate Reset", packet, 16);
   696   FlushOutputQueue();
   697 }
   699 void
   700 SpdySession3::GenerateGoAway(uint32_t aStatusCode)
   701 {
   702   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   703   LOG3(("SpdySession3::GenerateGoAway %p code=%X\n", this, aStatusCode));
   705   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16,
   706                mOutputQueueUsed, mOutputQueueSize);
   707   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
   708   mOutputQueueUsed += 16;
   710   memset(packet, 0, 16);
   711   packet[0] = kFlag_Control;
   712   packet[1] = kVersion;
   713   packet[3] = CONTROL_TYPE_GOAWAY;
   714   packet[7] = 8;                                  /* data length */
   716   // last-good-stream-id are bytes 8-11, when we accept server push this will
   717   // need to be set non zero
   719   // bytes 12-15 are the status code.
   720   NetworkEndian::writeUint32(packet + 12, aStatusCode);
   722   LogIO(this, nullptr, "Generate GoAway", packet, 16);
   723   FlushOutputQueue();
   724 }
   726 void
   727 SpdySession3::GenerateSettings()
   728 {
   729   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   730   LOG3(("SpdySession3::GenerateSettings %p\n", this));
   732   static const uint32_t maxDataLen = 4 + 3 * 8; // sized for 3 settings
   733   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 8 + maxDataLen,
   734                mOutputQueueUsed, mOutputQueueSize);
   735   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
   737   memset(packet, 0, 8 + maxDataLen);
   738   packet[0] = kFlag_Control;
   739   packet[1] = kVersion;
   740   packet[3] = CONTROL_TYPE_SETTINGS;
   742   uint8_t numberOfEntries = 0;
   744   // entries need to be listed in order by ID
   745   // 1st entry is bytes 12 to 19
   746   // 2nd entry is bytes 20 to 27
   747   // 3rd entry is bytes 28 to 35
   749   if (!gHttpHandler->AllowPush()) {
   750   // announcing that we accept 0 incoming streams is done to
   751   // disable server push
   752     packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT;
   753     // The value portion of the setting pair is already initialized to 0
   754     numberOfEntries++;
   755   }
   757   nsRefPtr<nsHttpConnectionInfo> ci;
   758   uint32_t cwnd = 0;
   759   GetConnectionInfo(getter_AddRefs(ci));
   760   if (ci)
   761     cwnd = gHttpHandler->ConnMgr()->GetSpdyCWNDSetting(ci);
   762   if (cwnd) {
   763     packet[12 + 8 * numberOfEntries] = PERSISTED_VALUE;
   764     packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_CWND;
   765     LOG(("SpdySession3::GenerateSettings %p sending CWND %u\n", this, cwnd));
   766     NetworkEndian::writeUint32(packet + 16 + 8 * numberOfEntries, cwnd);
   767     numberOfEntries++;
   768   }
   770   // Advertise the Push RWIN and on each client SYN_STREAM pipeline
   771   // a window update with it in order to use larger initial windows with pulled
   772   // streams.
   773   packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_INITIAL_WINDOW;
   774   NetworkEndian::writeUint32(packet + 16 + 8 * numberOfEntries, mPushAllowance);
   775   numberOfEntries++;
   777   uint32_t dataLen = 4 + 8 * numberOfEntries;
   778   mOutputQueueUsed += 8 + dataLen;
   779   packet[7] = dataLen;
   780   packet[11] = numberOfEntries;
   782   LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen);
   783   FlushOutputQueue();
   784 }
   786 // perform a bunch of integrity checks on the stream.
   787 // returns true if passed, false (plus LOG and ABORT) if failed.
   788 bool
   789 SpdySession3::VerifyStream(SpdyStream3 *aStream, uint32_t aOptionalID = 0)
   790 {
   791   // This is annoying, but at least it is O(1)
   792   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   794 #ifndef DEBUG
   795   // Only do the real verification in debug builds
   796   return true;
   797 #endif
   799   if (!aStream)
   800     return true;
   802   uint32_t test = 0;
   804   do {
   805     if (aStream->StreamID() == kDeadStreamID)
   806       break;
   808     nsAHttpTransaction *trans = aStream->Transaction();
   810     test++;
   811     if (!trans)
   812       break;
   814     test++;
   815     if (mStreamTransactionHash.Get(trans) != aStream)
   816       break;
   818     if (aStream->StreamID()) {
   819       SpdyStream3 *idStream = mStreamIDHash.Get(aStream->StreamID());
   821       test++;
   822       if (idStream != aStream)
   823         break;
   825       if (aOptionalID) {
   826         test++;
   827         if (idStream->StreamID() != aOptionalID)
   828           break;
   829       }
   830     }
   832     // tests passed
   833     return true;
   834   } while (0);
   836   LOG(("SpdySession3 %p VerifyStream Failure %p stream->id=0x%X "
   837        "optionalID=0x%X trans=%p test=%d\n",
   838        this, aStream, aStream->StreamID(),
   839        aOptionalID, aStream->Transaction(), test));
   841   MOZ_ASSERT(false, "VerifyStream");
   842   return false;
   843 }
   845 void
   846 SpdySession3::CleanupStream(SpdyStream3 *aStream, nsresult aResult,
   847                            rstReason aResetCode)
   848 {
   849   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   850   LOG3(("SpdySession3::CleanupStream %p %p 0x%X %X\n",
   851         this, aStream, aStream ? aStream->StreamID() : 0, aResult));
   852   if (!aStream) {
   853     return;
   854   }
   856   SpdyPushedStream3 *pushSource = nullptr;
   858   if (NS_SUCCEEDED(aResult) && aStream->DeferCleanupOnSuccess()) {
   859     LOG(("SpdySession3::CleanupStream 0x%X deferred\n", aStream->StreamID()));
   860     return;
   861   }
   863   if (!VerifyStream(aStream)) {
   864     LOG(("SpdySession3::CleanupStream failed to verify stream\n"));
   865     return;
   866   }
   868   pushSource = aStream->PushSource();
   870   if (!aStream->RecvdFin() && aStream->StreamID()) {
   871     LOG3(("Stream had not processed recv FIN, sending RST code %X\n",
   872           aResetCode));
   873     GenerateRstStream(aResetCode, aStream->StreamID());
   874     DecrementConcurrent(aStream);
   875   }
   877   CloseStream(aStream, aResult);
   879   // Remove the stream from the ID hash table and, if an even id, the pushed
   880   // table too.
   881   uint32_t id = aStream->StreamID();
   882   if (id > 0) {
   883     mStreamIDHash.Remove(id);
   884     if (!(id & 1))
   885       mPushedStreams.RemoveElement(aStream);
   886   }
   888   RemoveStreamFromQueues(aStream);
   890   // removing from the stream transaction hash will
   891   // delete the SpdyStream3 and drop the reference to
   892   // its transaction
   893   mStreamTransactionHash.Remove(aStream->Transaction());
   895   if (mShouldGoAway && !mStreamTransactionHash.Count())
   896     Close(NS_OK);
   898   if (pushSource) {
   899     pushSource->SetDeferCleanupOnSuccess(false);
   900     CleanupStream(pushSource, aResult, aResetCode);
   901   }
   902 }
   904 static void RemoveStreamFromQueue(SpdyStream3 *aStream, nsDeque &queue)
   905 {
   906   uint32_t size = queue.GetSize();
   907   for (uint32_t count = 0; count < size; ++count) {
   908     SpdyStream3 *stream = static_cast<SpdyStream3 *>(queue.PopFront());
   909     if (stream != aStream)
   910       queue.Push(stream);
   911   }
   912 }
   914 void
   915 SpdySession3::RemoveStreamFromQueues(SpdyStream3 *aStream)
   916 {
   917   RemoveStreamFromQueue(aStream, mReadyForWrite);
   918   RemoveStreamFromQueue(aStream, mQueuedStreams);
   919   RemoveStreamFromQueue(aStream, mReadyForRead);
   920 }
   922 void
   923 SpdySession3::CloseStream(SpdyStream3 *aStream, nsresult aResult)
   924 {
   925   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   926   LOG3(("SpdySession3::CloseStream %p %p 0x%x %X\n",
   927         this, aStream, aStream->StreamID(), aResult));
   929   // Check if partial frame reader
   930   if (aStream == mInputFrameDataStream) {
   931     LOG3(("Stream had active partial read frame on close"));
   932     ChangeDownstreamState(DISCARDING_DATA_FRAME);
   933     mInputFrameDataStream = nullptr;
   934   }
   936   RemoveStreamFromQueues(aStream);
   938   // Send the stream the close() indication
   939   aStream->Close(aResult);
   940 }
   942 nsresult
   943 SpdySession3::HandleSynStream(SpdySession3 *self)
   944 {
   945   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SYN_STREAM);
   947   if (self->mInputFrameDataSize < 18) {
   948     LOG3(("SpdySession3::HandleSynStream %p SYN_STREAM too short data=%d",
   949           self, self->mInputFrameDataSize));
   950     return NS_ERROR_ILLEGAL_VALUE;
   951   }
   953   uint32_t streamID =
   954     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
   955   uint32_t associatedID =
   956     NetworkEndian::readUint32(self->mInputFrameBuffer + 3 * sizeof(uint32_t));
   957   uint8_t flags = reinterpret_cast<uint8_t *>(self->mInputFrameBuffer.get())[4];
   959   LOG3(("SpdySession3::HandleSynStream %p recv SYN_STREAM (push) "
   960         "for ID 0x%X associated with 0x%X.\n",
   961         self, streamID, associatedID));
   963   if (streamID & 0x01) {                   // test for odd stream ID
   964     LOG3(("SpdySession3::HandleSynStream %p recvd SYN_STREAM id must be even.",
   965           self));
   966     return NS_ERROR_ILLEGAL_VALUE;
   967   }
   969   // confirm associated-to
   970   nsresult rv = self->SetInputFrameDataStream(associatedID);
   971   if (NS_FAILED(rv))
   972     return rv;
   973   SpdyStream3 *associatedStream = self->mInputFrameDataStream;
   975   ++(self->mServerPushedResources);
   977   // Anytime we start using the high bit of stream ID (either client or server)
   978   // begin to migrate to a new session.
   979   if (streamID >= kMaxStreamID)
   980     self->mShouldGoAway = true;
   982   bool resetStream = true;
   983   SpdyPushCache *cache = nullptr;
   985   if (!(flags & kFlag_Data_UNI)) {
   986     // pushed streams require UNIDIRECTIONAL flag
   987     LOG3(("SpdySession3::HandleSynStream %p ID %0x%X associated ID 0x%X failed.\n",
   988           self, streamID, associatedID));
   989     self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
   991   } else if (!associatedID) {
   992     // associated stream 0 will never find a match, but the spec requires a
   993     // PROTOCOL_ERROR in this specific case
   994     LOG3(("SpdySession3::HandleSynStream %p associated ID of 0 failed.\n", self));
   995     self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
   997   } else if (!gHttpHandler->AllowPush()) {
   998     // MAX_CONCURRENT_STREAMS of 0 in settings should have disabled push,
   999     // but some servers are buggy about that.. or the config could have
  1000     // been updated after the settings frame was sent. In both cases just
  1001     // reject the pushed stream as refused
  1002     LOG3(("SpdySession3::HandleSynStream Push Recevied when Disabled\n"));
  1003     self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
  1005   } else if (!associatedStream) {
  1006     LOG3(("SpdySession3::HandleSynStream %p lookup associated ID failed.\n", self));
  1007     self->GenerateRstStream(RST_INVALID_STREAM, streamID);
  1009   } else {
  1010     nsILoadGroupConnectionInfo *loadGroupCI = associatedStream->LoadGroupConnectionInfo();
  1011     if (loadGroupCI) {
  1012       loadGroupCI->GetSpdyPushCache(&cache);
  1013       if (!cache) {
  1014         cache = new SpdyPushCache();
  1015         if (!cache || NS_FAILED(loadGroupCI->SetSpdyPushCache(cache))) {
  1016           delete cache;
  1017           cache = nullptr;
  1021     if (!cache) {
  1022       // this is unexpected, but we can handle it just be refusing the push
  1023       LOG3(("SpdySession3::HandleSynStream Push Recevied without loadgroup cache\n"));
  1024       self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
  1026     else {
  1027       resetStream = false;
  1031   if (resetStream) {
  1032     // Need to decompress the headers even though we aren't using them yet in
  1033     // order to keep the compression context consistent for other syn_reply frames
  1034     rv = self->UncompressAndDiscard(18, self->mInputFrameDataSize - 10);
  1035     if (NS_FAILED(rv)) {
  1036       LOG(("SpdySession3::HandleSynStream uncompress failed\n"));
  1037       return rv;
  1039     self->ResetDownstreamState();
  1040     return NS_OK;
  1043   // Create the buffering transaction and push stream
  1044   nsRefPtr<SpdyPush3TransactionBuffer> transactionBuffer =
  1045     new SpdyPush3TransactionBuffer();
  1046   transactionBuffer->SetConnection(self);
  1047   SpdyPushedStream3 *pushedStream =
  1048     new SpdyPushedStream3(transactionBuffer, self,
  1049                           associatedStream, streamID);
  1051   // ownership of the pushed stream is by the transaction hash, just as it
  1052   // is for a client initiated stream. Errors that aren't fatal to the
  1053   // whole session must call cleanupStream() after this point in order
  1054   // to remove the stream from that hash.
  1055   self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
  1056   self->mPushedStreams.AppendElement(pushedStream);
  1058   // The pushed stream is unidirectional so it is fully open immediately
  1059   pushedStream->SetFullyOpen();
  1061   // Uncompress the response headers into a stream specific buffer, leaving them
  1062   // in spdy format for the time being.
  1063   rv = pushedStream->Uncompress(&self->mDownstreamZlib,
  1064                                 self->mInputFrameBuffer + 18,
  1065                                 self->mInputFrameDataSize - 10);
  1066   if (NS_FAILED(rv)) {
  1067     LOG(("SpdySession3::HandleSynStream uncompress failed\n"));
  1068     return rv;
  1071   if (self->RegisterStreamID(pushedStream, streamID) == kDeadStreamID) {
  1072     LOG(("SpdySession3::HandleSynStream registerstreamid failed\n"));
  1073     return NS_ERROR_FAILURE;
  1076   // Fake the request side of the pushed HTTP transaction. Sets up hash
  1077   // key and origin
  1078   uint32_t notUsed;
  1079   pushedStream->ReadSegments(nullptr, 1, &notUsed);
  1081   nsAutoCString key;
  1082   if (!pushedStream->GetHashKey(key)) {
  1083     LOG(("SpdySession3::HandleSynStream one of :host :scheme :path missing from push\n"));
  1084     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM);
  1085     self->ResetDownstreamState();
  1086     return NS_OK;
  1089   if (!associatedStream->Origin().Equals(pushedStream->Origin())) {
  1090     LOG(("SpdySession3::HandleSynStream pushed stream mismatched origin\n"));
  1091     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM);
  1092     self->ResetDownstreamState();
  1093     return NS_OK;
  1096   if (!cache->RegisterPushedStreamSpdy3(key, pushedStream)) {
  1097     LOG(("SpdySession3::HandleSynStream registerPushedStream Failed\n"));
  1098     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM);
  1099     self->ResetDownstreamState();
  1100     return NS_OK;
  1103   self->ResetDownstreamState();
  1104   return NS_OK;
  1107 nsresult
  1108 SpdySession3::SetInputFrameDataStream(uint32_t streamID)
  1110   mInputFrameDataStream = mStreamIDHash.Get(streamID);
  1111   if (VerifyStream(mInputFrameDataStream, streamID))
  1112     return NS_OK;
  1114   LOG(("SpdySession3::SetInputFrameDataStream failed to verify 0x%X\n",
  1115        streamID));
  1116   mInputFrameDataStream = nullptr;
  1117   return NS_ERROR_UNEXPECTED;
  1120 nsresult
  1121 SpdySession3::HandleSynReply(SpdySession3 *self)
  1123   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SYN_REPLY);
  1125   if (self->mInputFrameDataSize < 4) {
  1126     LOG3(("SpdySession3::HandleSynReply %p SYN REPLY too short data=%d",
  1127           self, self->mInputFrameDataSize));
  1128     // A framing error is a session wide error that cannot be recovered
  1129     return NS_ERROR_ILLEGAL_VALUE;
  1132   LOG3(("SpdySession3::HandleSynReply %p lookup via streamID in syn_reply.\n",
  1133         self));
  1134   uint32_t streamID =
  1135     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
  1136   nsresult rv = self->SetInputFrameDataStream(streamID);
  1137   if (NS_FAILED(rv))
  1138     return rv;
  1140   if (!self->mInputFrameDataStream) {
  1141     // Cannot find stream. We can continue the SPDY session, but we need to
  1142     // uncompress the header block to maintain the correct compression context
  1144     LOG3(("SpdySession3::HandleSynReply %p lookup streamID in syn_reply "
  1145           "0x%X failed. NextStreamID = 0x%X\n",
  1146           self, streamID, self->mNextStreamID));
  1148     if (streamID >= self->mNextStreamID)
  1149       self->GenerateRstStream(RST_INVALID_STREAM, streamID);
  1151     rv = self->UncompressAndDiscard(12, self->mInputFrameDataSize - 4);
  1152     if (NS_FAILED(rv)) {
  1153       LOG(("SpdySession3::HandleSynReply uncompress failed\n"));
  1154       // this is fatal to the session
  1155       return rv;
  1158     self->ResetDownstreamState();
  1159     return NS_OK;
  1162   // Uncompress the headers into a stream specific buffer, leaving them in
  1163   // spdy format for the time being. Make certain to do this
  1164   // step before any error handling that might abort the stream but not
  1165   // the session becuase the session compression context will become
  1166   // inconsistent if all of the compressed data is not processed.
  1167   rv = self->mInputFrameDataStream->Uncompress(&self->mDownstreamZlib,
  1168                                                self->mInputFrameBuffer + 12,
  1169                                                self->mInputFrameDataSize - 4);
  1171   if (NS_FAILED(rv)) {
  1172     LOG(("SpdySession3::HandleSynReply uncompress failed\n"));
  1173     return rv;
  1176   if (self->mInputFrameDataStream->GetFullyOpen()) {
  1177     // "If an endpoint receives multiple SYN_REPLY frames for the same active
  1178     // stream ID, it MUST issue a stream error (Section 2.4.2) with the error
  1179     // code STREAM_IN_USE."
  1180     //
  1181     // "STREAM_ALREADY_CLOSED. The endpoint received a data or SYN_REPLY
  1182     // frame for a stream which is half closed."
  1183     //
  1184     // If the stream is open then just RST_STREAM with STREAM_IN_USE
  1185     // If the stream is half closed then RST_STREAM with STREAM_ALREADY_CLOSED
  1186     // abort the session
  1187     //
  1188     LOG3(("SpdySession3::HandleSynReply %p dup SYN_REPLY for 0x%X"
  1189           " recvdfin=%d", self, self->mInputFrameDataStream->StreamID(),
  1190           self->mInputFrameDataStream->RecvdFin()));
  1192     self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ALREADY_OPENED,
  1193                         self->mInputFrameDataStream->RecvdFin() ?
  1194                         RST_STREAM_ALREADY_CLOSED : RST_STREAM_IN_USE);
  1195     self->ResetDownstreamState();
  1196     return NS_OK;
  1198   self->mInputFrameDataStream->SetFullyOpen();
  1200   self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN;
  1201   self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
  1202   self->mLastDataReadEpoch = self->mLastReadEpoch;
  1204   if (self->mInputFrameBuffer[4] & ~kFlag_Data_FIN) {
  1205     LOG3(("SynReply %p had undefined flag set 0x%X\n", self, streamID));
  1206     self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
  1207                         RST_PROTOCOL_ERROR);
  1208     self->ResetDownstreamState();
  1209     return NS_OK;
  1212   if (!self->mInputFrameDataLast) {
  1213     // don't process the headers yet as there could be more coming from HEADERS
  1214     // frames
  1215     self->ResetDownstreamState();
  1216     return NS_OK;
  1219   rv = self->ResponseHeadersComplete();
  1220   if (rv == NS_ERROR_ILLEGAL_VALUE) {
  1221     LOG3(("SpdySession3::HandleSynReply %p PROTOCOL_ERROR detected 0x%X\n",
  1222           self, streamID));
  1223     self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
  1224     self->ResetDownstreamState();
  1225     rv = NS_OK;
  1227   return rv;
  1230 // ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream
  1231 // should be reset with a PROTOCOL_ERROR, NS_OK when the SYN_REPLY was
  1232 // fine, and any other error is fatal to the session.
  1233 nsresult
  1234 SpdySession3::ResponseHeadersComplete()
  1236   LOG3(("SpdySession3::ResponseHeadersComplete %p for 0x%X fin=%d",
  1237         this, mInputFrameDataStream->StreamID(), mInputFrameDataLast));
  1239   // The spdystream needs to see flattened http headers
  1240   // Uncompressed spdy format headers currently live in
  1241   // SpdyStream3::mDecompressBuffer - convert that to HTTP format in
  1242   // mFlatHTTPResponseHeaders via ConvertHeaders()
  1244   mFlatHTTPResponseHeadersOut = 0;
  1245   nsresult rv = mInputFrameDataStream->ConvertHeaders(mFlatHTTPResponseHeaders);
  1246   if (NS_FAILED(rv))
  1247     return rv;
  1249   ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
  1250   return NS_OK;
  1253 nsresult
  1254 SpdySession3::HandleRstStream(SpdySession3 *self)
  1256   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_RST_STREAM);
  1258   if (self->mInputFrameDataSize != 8) {
  1259     LOG3(("SpdySession3::HandleRstStream %p RST_STREAM wrong length data=%d",
  1260           self, self->mInputFrameDataSize));
  1261     return NS_ERROR_ILLEGAL_VALUE;
  1264   uint8_t flags = reinterpret_cast<uint8_t *>(self->mInputFrameBuffer.get())[4];
  1266   uint32_t streamID =
  1267     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
  1269   self->mDownstreamRstReason =
  1270     NetworkEndian::readUint32(self->mInputFrameBuffer + 3 * sizeof(uint32_t));
  1272   LOG3(("SpdySession3::HandleRstStream %p RST_STREAM Reason Code %u ID %x "
  1273         "flags %x", self, self->mDownstreamRstReason, streamID, flags));
  1275   if (flags != 0) {
  1276     LOG3(("SpdySession3::HandleRstStream %p RST_STREAM with flags is illegal",
  1277           self));
  1278     return NS_ERROR_ILLEGAL_VALUE;
  1281   if (self->mDownstreamRstReason == RST_INVALID_STREAM ||
  1282       self->mDownstreamRstReason == RST_STREAM_IN_USE ||
  1283       self->mDownstreamRstReason == RST_FLOW_CONTROL_ERROR) {
  1284     // basically just ignore this
  1285     LOG3(("SpdySession3::HandleRstStream %p No Reset Processing Needed.\n"));
  1286     self->ResetDownstreamState();
  1287     return NS_OK;
  1290   nsresult rv = self->SetInputFrameDataStream(streamID);
  1292   if (!self->mInputFrameDataStream) {
  1293     if (NS_FAILED(rv))
  1294       LOG(("SpdySession3::HandleRstStream %p lookup streamID for RST Frame "
  1295            "0x%X failed reason = %d :: VerifyStream Failed\n", self, streamID,
  1296            self->mDownstreamRstReason));
  1298     LOG3(("SpdySession3::HandleRstStream %p lookup streamID for RST Frame "
  1299           "0x%X failed reason = %d", self, streamID,
  1300           self->mDownstreamRstReason));
  1301     return NS_ERROR_ILLEGAL_VALUE;
  1304   self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
  1305   return NS_OK;
  1308 PLDHashOperator
  1309 SpdySession3::UpdateServerRwinEnumerator(nsAHttpTransaction *key,
  1310                                          nsAutoPtr<SpdyStream3> &stream,
  1311                                          void *closure)
  1313   int32_t delta = *(static_cast<int32_t *>(closure));
  1314   stream->UpdateRemoteWindow(delta);
  1315   return PL_DHASH_NEXT;
  1318 nsresult
  1319 SpdySession3::HandleSettings(SpdySession3 *self)
  1321   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SETTINGS);
  1323   if (self->mInputFrameDataSize < 4) {
  1324     LOG3(("SpdySession3::HandleSettings %p SETTINGS wrong length data=%d",
  1325           self, self->mInputFrameDataSize));
  1326     return NS_ERROR_ILLEGAL_VALUE;
  1329   uint32_t numEntries =
  1330     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
  1332   // Ensure frame is large enough for supplied number of entries
  1333   // Each entry is 8 bytes, frame data is reduced by 4 to account for
  1334   // the NumEntries value.
  1335   if ((self->mInputFrameDataSize - 4) < (numEntries * 8)) {
  1336     LOG3(("SpdySession3::HandleSettings %p SETTINGS wrong length data=%d",
  1337           self, self->mInputFrameDataSize));
  1338     return NS_ERROR_ILLEGAL_VALUE;
  1341   LOG3(("SpdySession3::HandleSettings %p SETTINGS Control Frame with %d entries",
  1342         self, numEntries));
  1344   for (uint32_t index = 0; index < numEntries; ++index) {
  1345     unsigned char *setting = reinterpret_cast<unsigned char *>
  1346       (self->mInputFrameBuffer.get()) + 12 + index * 8;
  1348     uint32_t flags = setting[0];
  1349     uint32_t id = NetworkEndian::readUint32(setting) & 0xffffff;
  1350     uint32_t value = NetworkEndian::readUint32(setting + 1 * sizeof(uint32_t));
  1352     LOG3(("Settings ID %d, Flags %X, Value %d", id, flags, value));
  1354     switch (id)
  1356     case SETTINGS_TYPE_UPLOAD_BW:
  1357       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_UL_BW, value);
  1358       break;
  1360     case SETTINGS_TYPE_DOWNLOAD_BW:
  1361       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_DL_BW, value);
  1362       break;
  1364     case SETTINGS_TYPE_RTT:
  1365       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RTT, value);
  1366       break;
  1368     case SETTINGS_TYPE_MAX_CONCURRENT:
  1369       self->mMaxConcurrent = value;
  1370       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
  1371       break;
  1373     case SETTINGS_TYPE_CWND:
  1374       if (flags & PERSIST_VALUE)
  1376         nsRefPtr<nsHttpConnectionInfo> ci;
  1377         self->GetConnectionInfo(getter_AddRefs(ci));
  1378         if (ci)
  1379           gHttpHandler->ConnMgr()->ReportSpdyCWNDSetting(ci, value);
  1381       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_CWND, value);
  1382       break;
  1384     case SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE:
  1385       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RETRANS, value);
  1386       break;
  1388     case SETTINGS_TYPE_INITIAL_WINDOW:
  1389       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
  1391         int32_t delta = value - self->mServerInitialWindow;
  1392         self->mServerInitialWindow = value;
  1394         // we need to add the delta to all open streams (delta can be negative)
  1395         self->mStreamTransactionHash.Enumerate(UpdateServerRwinEnumerator,
  1396                                                &delta);
  1398       break;
  1400     default:
  1401       break;
  1406   self->ResetDownstreamState();
  1407   return NS_OK;
  1410 nsresult
  1411 SpdySession3::HandleNoop(SpdySession3 *self)
  1413   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_NOOP);
  1415   // Should not be receiving noop frames in spdy/3, so we'll just
  1416   // make a log and ignore it
  1418   LOG3(("SpdySession3::HandleNoop %p NOP.", self));
  1420   self->ResetDownstreamState();
  1421   return NS_OK;
  1424 nsresult
  1425 SpdySession3::HandlePing(SpdySession3 *self)
  1427   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_PING);
  1429   if (self->mInputFrameDataSize != 4) {
  1430     LOG3(("SpdySession3::HandlePing %p PING had wrong amount of data %d",
  1431           self, self->mInputFrameDataSize));
  1432     return NS_ERROR_ILLEGAL_VALUE;
  1435   uint32_t pingID =
  1436     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
  1438   LOG3(("SpdySession3::HandlePing %p PING ID 0x%X.", self, pingID));
  1440   if (pingID & 0x01) {
  1441     // presumably a reply to our timeout ping
  1442     self->mPingSentEpoch = 0;
  1444   else {
  1445     // Servers initiate even numbered pings, go ahead and echo it back
  1446     self->GeneratePing(pingID);
  1449   self->ResetDownstreamState();
  1450   return NS_OK;
  1453 nsresult
  1454 SpdySession3::HandleGoAway(SpdySession3 *self)
  1456   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_GOAWAY);
  1458   if (self->mInputFrameDataSize != 8) {
  1459     LOG3(("SpdySession3::HandleGoAway %p GOAWAY had wrong amount of data %d",
  1460           self, self->mInputFrameDataSize));
  1461     return NS_ERROR_ILLEGAL_VALUE;
  1464   self->mShouldGoAway = true;
  1465   self->mGoAwayID =
  1466     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
  1467   self->mCleanShutdown = true;
  1469   // Find streams greater than the last-good ID and mark them for deletion
  1470   // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. The
  1471   // underlying transaction can be restarted.
  1472   self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self);
  1474   // Process the streams marked for deletion and restart.
  1475   uint32_t size = self->mGoAwayStreamsToRestart.GetSize();
  1476   for (uint32_t count = 0; count < size; ++count) {
  1477     SpdyStream3 *stream =
  1478       static_cast<SpdyStream3 *>(self->mGoAwayStreamsToRestart.PopFront());
  1480     self->CloseStream(stream, NS_ERROR_NET_RESET);
  1481     if (stream->HasRegisteredID())
  1482       self->mStreamIDHash.Remove(stream->StreamID());
  1483     self->mStreamTransactionHash.Remove(stream->Transaction());
  1486   // Queued streams can also be deleted from this session and restarted
  1487   // in another one. (they were never sent on the network so they implicitly
  1488   // are not covered by the last-good id.
  1489   size = self->mQueuedStreams.GetSize();
  1490   for (uint32_t count = 0; count < size; ++count) {
  1491     SpdyStream3 *stream =
  1492       static_cast<SpdyStream3 *>(self->mQueuedStreams.PopFront());
  1493     self->CloseStream(stream, NS_ERROR_NET_RESET);
  1494     self->mStreamTransactionHash.Remove(stream->Transaction());
  1497   LOG3(("SpdySession3::HandleGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X "
  1498         "live streams=%d\n", self, self->mGoAwayID,
  1499         NetworkEndian::readUint32(self->mInputFrameBuffer +
  1500                                   3 * sizeof(uint32_t)),
  1501         self->mStreamTransactionHash.Count()));
  1503   self->ResetDownstreamState();
  1504   return NS_OK;
  1507 nsresult
  1508 SpdySession3::HandleHeaders(SpdySession3 *self)
  1510   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_HEADERS);
  1512   if (self->mInputFrameDataSize < 4) {
  1513     LOG3(("SpdySession3::HandleHeaders %p HEADERS had wrong amount of data %d",
  1514           self, self->mInputFrameDataSize));
  1515     return NS_ERROR_ILLEGAL_VALUE;
  1518   uint32_t streamID =
  1519     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
  1520   LOG3(("SpdySession3::HandleHeaders %p HEADERS for Stream 0x%X.\n",
  1521         self, streamID));
  1522   nsresult rv = self->SetInputFrameDataStream(streamID);
  1523   if (NS_FAILED(rv))
  1524     return rv;
  1526   if (!self->mInputFrameDataStream) {
  1527     LOG3(("SpdySession3::HandleHeaders %p lookup streamID 0x%X failed.\n",
  1528           self, streamID));
  1529     if (streamID >= self->mNextStreamID)
  1530       self->GenerateRstStream(RST_INVALID_STREAM, streamID);
  1532     rv = self->UncompressAndDiscard(12, self->mInputFrameDataSize - 4);
  1533     if (NS_FAILED(rv)) {
  1534       LOG(("SpdySession3::HandleHeaders uncompress failed\n"));
  1535       // this is fatal to the session
  1536       return rv;
  1538     self->ResetDownstreamState();
  1539     return NS_OK;
  1542   // Uncompress the headers into local buffers in the SpdyStream, leaving
  1543   // them in spdy format for the time being. Make certain to do this
  1544   // step before any error handling that might abort the stream but not
  1545   // the session becuase the session compression context will become
  1546   // inconsistent if all of the compressed data is not processed.
  1547   rv = self->mInputFrameDataStream->Uncompress(&self->mDownstreamZlib,
  1548                                                self->mInputFrameBuffer + 12,
  1549                                                self->mInputFrameDataSize - 4);
  1550   if (NS_FAILED(rv)) {
  1551     LOG(("SpdySession3::HandleHeaders uncompress failed\n"));
  1552     return rv;
  1555   self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN;
  1556   self->mInputFrameDataStream->
  1557     UpdateTransportReadEvents(self->mInputFrameDataSize);
  1558   self->mLastDataReadEpoch = self->mLastReadEpoch;
  1560   if (self->mInputFrameBuffer[4] & ~kFlag_Data_FIN) {
  1561     LOG3(("Headers %p had undefined flag set 0x%X\n", self, streamID));
  1562     self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
  1563                         RST_PROTOCOL_ERROR);
  1564     self->ResetDownstreamState();
  1565     return NS_OK;
  1568   if (!self->mInputFrameDataLast) {
  1569     // don't process the headers yet as there could be more HEADERS frames
  1570     self->ResetDownstreamState();
  1571     return NS_OK;
  1574   rv = self->ResponseHeadersComplete();
  1575   if (rv == NS_ERROR_ILLEGAL_VALUE) {
  1576     LOG3(("SpdySession3::HanndleHeaders %p PROTOCOL_ERROR detected 0x%X\n",
  1577           self, streamID));
  1578     self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
  1579     self->ResetDownstreamState();
  1580     rv = NS_OK;
  1582   return rv;
  1585 nsresult
  1586 SpdySession3::HandleWindowUpdate(SpdySession3 *self)
  1588   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_WINDOW_UPDATE);
  1590   if (self->mInputFrameDataSize < 8) {
  1591     LOG3(("SpdySession3::HandleWindowUpdate %p Window Update wrong length %d\n",
  1592           self, self->mInputFrameDataSize));
  1593     return NS_ERROR_ILLEGAL_VALUE;
  1596   uint32_t delta =
  1597     NetworkEndian::readUint32(self->mInputFrameBuffer + 3 * sizeof(uint32_t));
  1598   delta &= 0x7fffffff;
  1599   uint32_t streamID =
  1600     NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t));
  1601   streamID &= 0x7fffffff;
  1603   LOG3(("SpdySession3::HandleWindowUpdate %p len=%d for Stream 0x%X.\n",
  1604         self, delta, streamID));
  1605   nsresult rv = self->SetInputFrameDataStream(streamID);
  1606   if (NS_FAILED(rv))
  1607     return rv;
  1609   if (!self->mInputFrameDataStream) {
  1610     LOG3(("SpdySession3::HandleWindowUpdate %p lookup streamID 0x%X failed.\n",
  1611           self, streamID));
  1612     if (streamID >= self->mNextStreamID)
  1613       self->GenerateRstStream(RST_INVALID_STREAM, streamID);
  1614     self->ResetDownstreamState();
  1615     return NS_OK;
  1618   self->mInputFrameDataStream->UpdateRemoteWindow(delta);
  1619   self->ResetDownstreamState();
  1620   return NS_OK;
  1623 nsresult
  1624 SpdySession3::HandleCredential(SpdySession3 *self)
  1626   MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_CREDENTIAL);
  1628   // These aren't used yet. Just ignore the frame.
  1630   LOG3(("SpdySession3::HandleCredential %p NOP.", self));
  1632   self->ResetDownstreamState();
  1633   return NS_OK;
  1636 //-----------------------------------------------------------------------------
  1637 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
  1638 // of these methods
  1639 //-----------------------------------------------------------------------------
  1641 void
  1642 SpdySession3::OnTransportStatus(nsITransport* aTransport,
  1643                                nsresult aStatus,
  1644                                uint64_t aProgress)
  1646   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1648   switch (aStatus) {
  1649     // These should appear only once, deliver to the first
  1650     // transaction on the session.
  1651   case NS_NET_STATUS_RESOLVING_HOST:
  1652   case NS_NET_STATUS_RESOLVED_HOST:
  1653   case NS_NET_STATUS_CONNECTING_TO:
  1654   case NS_NET_STATUS_CONNECTED_TO:
  1656     SpdyStream3 *target = mStreamIDHash.Get(1);
  1657     nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
  1658     if (transaction)
  1659       transaction->OnTransportStatus(aTransport, aStatus, aProgress);
  1660     break;
  1663   default:
  1664     // The other transport events are ignored here because there is no good
  1665     // way to map them to the right transaction in spdy. Instead, the events
  1666     // are generated again from the spdy code and passed directly to the
  1667     // correct transaction.
  1669     // NS_NET_STATUS_SENDING_TO:
  1670     // This is generated by the socket transport when (part) of
  1671     // a transaction is written out
  1672     //
  1673     // There is no good way to map it to the right transaction in spdy,
  1674     // so it is ignored here and generated separately when the SYN_STREAM
  1675     // is sent from SpdyStream3::TransmitFrame
  1677     // NS_NET_STATUS_WAITING_FOR:
  1678     // Created by nsHttpConnection when the request has been totally sent.
  1679     // There is no good way to map it to the right transaction in spdy,
  1680     // so it is ignored here and generated separately when the same
  1681     // condition is complete in SpdyStream3 when there is no more
  1682     // request body left to be transmitted.
  1684     // NS_NET_STATUS_RECEIVING_FROM
  1685     // Generated in spdysession whenever we read a data frame or a syn_reply
  1686     // that can be attributed to a particular stream/transaction
  1688     break;
  1692 // ReadSegments() is used to write data to the network. Generally, HTTP
  1693 // request data is pulled from the approriate transaction and
  1694 // converted to SPDY data. Sometimes control data like window-update are
  1695 // generated instead.
  1697 nsresult
  1698 SpdySession3::ReadSegments(nsAHttpSegmentReader *reader,
  1699                           uint32_t count,
  1700                           uint32_t *countRead)
  1702   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1704   MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader),
  1705              "Inconsistent Write Function Callback");
  1707   if (reader)
  1708     mSegmentReader = reader;
  1710   nsresult rv;
  1711   *countRead = 0;
  1713   LOG3(("SpdySession3::ReadSegments %p", this));
  1715   SpdyStream3 *stream = static_cast<SpdyStream3 *>(mReadyForWrite.PopFront());
  1716   if (!stream) {
  1717     LOG3(("SpdySession3 %p could not identify a stream to write; suspending.",
  1718           this));
  1719     FlushOutputQueue();
  1720     SetWriteCallbacks();
  1721     return NS_BASE_STREAM_WOULD_BLOCK;
  1724   LOG3(("SpdySession3 %p will write from SpdyStream3 %p 0x%X "
  1725         "block-input=%d block-output=%d\n", this, stream, stream->StreamID(),
  1726         stream->RequestBlockedOnRead(), stream->BlockedOnRwin()));
  1728   rv = stream->ReadSegments(this, count, countRead);
  1730   // Not every permutation of stream->ReadSegents produces data (and therefore
  1731   // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
  1732   // of that. But we might still have old data buffered that would be good
  1733   // to flush.
  1734   FlushOutputQueue();
  1736   // Allow new server reads - that might be data or control information
  1737   // (e.g. window updates or http replies) that are responses to these writes
  1738   ResumeRecv();
  1740   if (stream->RequestBlockedOnRead()) {
  1742     // We are blocked waiting for input - either more http headers or
  1743     // any request body data. When more data from the request stream
  1744     // becomes available the httptransaction will call conn->ResumeSend().
  1746     LOG3(("SpdySession3::ReadSegments %p dealing with block on read", this));
  1748     // call readsegments again if there are other streams ready
  1749     // to run in this session
  1750     if (GetWriteQueueSize())
  1751       rv = NS_OK;
  1752     else
  1753       rv = NS_BASE_STREAM_WOULD_BLOCK;
  1754     SetWriteCallbacks();
  1755     return rv;
  1758   if (NS_FAILED(rv)) {
  1759     LOG3(("SpdySession3::ReadSegments %p returning FAIL code %X",
  1760           this, rv));
  1761     if (rv != NS_BASE_STREAM_WOULD_BLOCK)
  1762       CleanupStream(stream, rv, RST_CANCEL);
  1763     return rv;
  1766   if (*countRead > 0) {
  1767     LOG3(("SpdySession3::ReadSegments %p stream=%p countread=%d",
  1768           this, stream, *countRead));
  1769     mReadyForWrite.Push(stream);
  1770     SetWriteCallbacks();
  1771     return rv;
  1774   if (stream->BlockedOnRwin()) {
  1775     LOG3(("SpdySession3 %p will stream %p 0x%X suspended for flow control\n",
  1776           this, stream, stream->StreamID()));
  1777     return NS_BASE_STREAM_WOULD_BLOCK;
  1780   LOG3(("SpdySession3::ReadSegments %p stream=%p stream send complete",
  1781         this, stream));
  1783   // call readsegments again if there are other streams ready
  1784   // to go in this session
  1785   SetWriteCallbacks();
  1787   return rv;
  1790 // WriteSegments() is used to read data off the socket. Generally this is
  1791 // just the SPDY frame header and from there the appropriate SPDYStream
  1792 // is identified from the Stream-ID. The http transaction associated with
  1793 // that read then pulls in the data directly, which it will feed to
  1794 // OnWriteSegment(). That function will gateway it into http and feed
  1795 // it to the appropriate transaction.
  1797 // we call writer->OnWriteSegment via NetworkRead() to get a spdy header..
  1798 // and decide if it is data or control.. if it is control, just deal with it.
  1799 // if it is data, identify the spdy stream
  1800 // call stream->WriteSegments which can call this::OnWriteSegment to get the
  1801 // data. It always gets full frames if they are part of the stream
  1803 nsresult
  1804 SpdySession3::WriteSegments(nsAHttpSegmentWriter *writer,
  1805                            uint32_t count,
  1806                            uint32_t *countWritten)
  1808   typedef nsresult  (*Control_FX) (SpdySession3 *self);
  1809   static const Control_FX sControlFunctions[] =
  1811     nullptr,
  1812     SpdySession3::HandleSynStream,
  1813     SpdySession3::HandleSynReply,
  1814     SpdySession3::HandleRstStream,
  1815     SpdySession3::HandleSettings,
  1816     SpdySession3::HandleNoop,
  1817     SpdySession3::HandlePing,
  1818     SpdySession3::HandleGoAway,
  1819     SpdySession3::HandleHeaders,
  1820     SpdySession3::HandleWindowUpdate,
  1821     SpdySession3::HandleCredential
  1822   };
  1824   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1826   nsresult rv;
  1827   *countWritten = 0;
  1829   if (mClosed)
  1830     return NS_ERROR_FAILURE;
  1832   SetWriteCallbacks();
  1834   // If there are http transactions attached to a push stream with filled buffers
  1835   // trigger that data pump here. This only reads from buffers (not the network)
  1836   // so mDownstreamState doesn't matter.
  1837   SpdyStream3 *pushConnectedStream =
  1838     static_cast<SpdyStream3 *>(mReadyForRead.PopFront());
  1839   if (pushConnectedStream) {
  1840     LOG3(("SpdySession3::WriteSegments %p processing pushed stream 0x%X\n",
  1841           this, pushConnectedStream->StreamID()));
  1842     mSegmentWriter = writer;
  1843     rv = pushConnectedStream->WriteSegments(this, count, countWritten);
  1844     mSegmentWriter = nullptr;
  1846     // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
  1847     // so we need this check to determine the truth.
  1848     if (NS_SUCCEEDED(rv) && !*countWritten &&
  1849         pushConnectedStream->PushSource() &&
  1850         pushConnectedStream->PushSource()->GetPushComplete()) {
  1851       rv = NS_BASE_STREAM_CLOSED;
  1854     if (rv == NS_BASE_STREAM_CLOSED) {
  1855       CleanupStream(pushConnectedStream, NS_OK, RST_CANCEL);
  1856       rv = NS_OK;
  1859     // if we return OK to nsHttpConnection it will use mSocketInCondition
  1860     // to determine whether to schedule more reads, incorrectly
  1861     // assuming that nsHttpConnection::OnSocketWrite() was called.
  1862     if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
  1863       rv = NS_BASE_STREAM_WOULD_BLOCK;
  1864       ResumeRecv();
  1867     return rv;
  1870   // We buffer all control frames and act on them in this layer.
  1871   // We buffer the first 8 bytes of data frames (the header) but
  1872   // the actual data is passed through unprocessed.
  1874   if (mDownstreamState == BUFFERING_FRAME_HEADER) {
  1875     // The first 8 bytes of every frame is header information that
  1876     // we are going to want to strip before passing to http. That is
  1877     // true of both control and data packets.
  1879     MOZ_ASSERT(mInputFrameBufferUsed < 8,
  1880                "Frame Buffer Used Too Large for State");
  1882     rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
  1883                      8 - mInputFrameBufferUsed, countWritten);
  1885     if (NS_FAILED(rv)) {
  1886       LOG3(("SpdySession3 %p buffering frame header read failure %x\n",
  1887             this, rv));
  1888       // maybe just blocked reading from network
  1889       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  1890         rv = NS_OK;
  1891       return rv;
  1894     LogIO(this, nullptr, "Reading Frame Header",
  1895           mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
  1897     mInputFrameBufferUsed += *countWritten;
  1899     if (mInputFrameBufferUsed < 8)
  1901       LOG3(("SpdySession3::WriteSegments %p "
  1902             "BUFFERING FRAME HEADER incomplete size=%d",
  1903             this, mInputFrameBufferUsed));
  1904       return rv;
  1907     // For both control and data frames the second 32 bit word of the header
  1908     // is 8-flags, 24-length. (network byte order)
  1909     mInputFrameDataSize =
  1910       NetworkEndian::readUint32(mInputFrameBuffer + 1 * sizeof(uint32_t));
  1911     mInputFrameDataSize &= 0x00ffffff;
  1912     mInputFrameDataRead = 0;
  1914     if (mInputFrameBuffer[0] & kFlag_Control) {
  1915       EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
  1916                    mInputFrameBufferSize);
  1917       ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
  1919       // The first 32 bit word of the header is
  1920       // 1 ctrl - 15 version - 16 type
  1921       uint16_t version = NetworkEndian::readUint16(mInputFrameBuffer);
  1922       version &= 0x7fff;
  1924       mFrameControlType =
  1925         NetworkEndian::readUint16(mInputFrameBuffer + sizeof(uint16_t));
  1927       LOG3(("SpdySession3::WriteSegments %p - Control Frame Identified "
  1928             "type %d version %d data len %d",
  1929             this, mFrameControlType, version, mInputFrameDataSize));
  1931       if (mFrameControlType >= CONTROL_TYPE_LAST ||
  1932           mFrameControlType <= CONTROL_TYPE_FIRST)
  1933         return NS_ERROR_ILLEGAL_VALUE;
  1935       if (version != kVersion)
  1936         return NS_ERROR_ILLEGAL_VALUE;
  1938     else {
  1939       ChangeDownstreamState(PROCESSING_DATA_FRAME);
  1941       Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
  1942                             mInputFrameDataSize >> 10);
  1943       mLastDataReadEpoch = mLastReadEpoch;
  1945       uint32_t streamID = NetworkEndian::readUint32(mInputFrameBuffer);
  1946       rv = SetInputFrameDataStream(streamID);
  1947       if (NS_FAILED(rv)) {
  1948         LOG(("SpdySession3::WriteSegments %p lookup streamID 0x%X failed. "
  1949               "probably due to verification.\n", this, streamID));
  1950         return rv;
  1952       if (!mInputFrameDataStream) {
  1953         LOG3(("SpdySession3::WriteSegments %p lookup streamID 0x%X failed. "
  1954               "Next = 0x%X", this, streamID, mNextStreamID));
  1955         if (streamID >= mNextStreamID)
  1956           GenerateRstStream(RST_INVALID_STREAM, streamID);
  1957         ChangeDownstreamState(DISCARDING_DATA_FRAME);
  1959       else if (mInputFrameDataStream->RecvdFin()) {
  1960         LOG3(("SpdySession3::WriteSegments %p streamID 0x%X "
  1961               "Data arrived for already server closed stream.\n",
  1962               this, streamID));
  1963         GenerateRstStream(RST_STREAM_ALREADY_CLOSED, streamID);
  1964         ChangeDownstreamState(DISCARDING_DATA_FRAME);
  1966       else if (!mInputFrameDataStream->RecvdData()) {
  1967         LOG3(("SpdySession3 %p First Data Frame Flushes Headers stream 0x%X\n",
  1968               this, streamID));
  1970         mInputFrameDataStream->SetRecvdData(true);
  1971         rv = ResponseHeadersComplete();
  1972         if (rv == NS_ERROR_ILLEGAL_VALUE) {
  1973           LOG3(("SpdySession3 %p PROTOCOL_ERROR detected 0x%X\n",
  1974                 this, streamID));
  1975           CleanupStream(mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
  1976           ChangeDownstreamState(DISCARDING_DATA_FRAME);
  1978         else {
  1979           mDataPending = true;
  1983       mInputFrameDataLast = (mInputFrameBuffer[4] & kFlag_Data_FIN);
  1984       LOG3(("Start Processing Data Frame. "
  1985             "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d",
  1986             this, streamID, mInputFrameDataStream, mInputFrameDataLast,
  1987             mInputFrameDataSize));
  1988       UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize);
  1992   if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
  1993     if (mDownstreamRstReason == RST_REFUSED_STREAM)
  1994       rv = NS_ERROR_NET_RESET;            //we can retry this 100% safely
  1995     else if (mDownstreamRstReason == RST_CANCEL ||
  1996              mDownstreamRstReason == RST_PROTOCOL_ERROR ||
  1997              mDownstreamRstReason == RST_INTERNAL_ERROR ||
  1998              mDownstreamRstReason == RST_UNSUPPORTED_VERSION)
  1999       rv = NS_ERROR_NET_INTERRUPT;
  2000     else if (mDownstreamRstReason == RST_FRAME_TOO_LARGE)
  2001       rv = NS_ERROR_FILE_TOO_BIG;
  2002     else
  2003       rv = NS_ERROR_ILLEGAL_VALUE;
  2005     if (mDownstreamRstReason != RST_REFUSED_STREAM &&
  2006         mDownstreamRstReason != RST_CANCEL)
  2007       mShouldGoAway = true;
  2009     // mInputFrameDataStream is reset by ChangeDownstreamState
  2010     SpdyStream3 *stream = mInputFrameDataStream;
  2011     ResetDownstreamState();
  2012     LOG3(("SpdySession3::WriteSegments cleanup stream on recv of rst "
  2013           "session=%p stream=%p 0x%X\n", this, stream,
  2014           stream ? stream->StreamID() : 0));
  2015     CleanupStream(stream, rv, RST_CANCEL);
  2016     return NS_OK;
  2019   if (mDownstreamState == PROCESSING_DATA_FRAME ||
  2020       mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
  2022     // The cleanup stream should only be set while stream->WriteSegments is
  2023     // on the stack and then cleaned up in this code block afterwards.
  2024     MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
  2025     mNeedsCleanup = nullptr;                     /* just in case */
  2027     mSegmentWriter = writer;
  2028     rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
  2029     mSegmentWriter = nullptr;
  2031     mLastDataReadEpoch = mLastReadEpoch;
  2033     if (SoftStreamError(rv)) {
  2034       // This will happen when the transaction figures out it is EOF, generally
  2035       // due to a content-length match being made. Return OK from this function
  2036       // otherwise the whole session would be torn down.
  2037       SpdyStream3 *stream = mInputFrameDataStream;
  2039       // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
  2040       // back to PROCESSING_DATA_FRAME where we came from
  2041       mDownstreamState = PROCESSING_DATA_FRAME;
  2043       if (mInputFrameDataRead == mInputFrameDataSize)
  2044         ResetDownstreamState();
  2045       LOG3(("SpdySession3::WriteSegments session=%p stream=%p 0x%X "
  2046             "needscleanup=%p. cleanup stream based on "
  2047             "stream->writeSegments returning code %X\n",
  2048             this, stream, stream ? stream->StreamID() : 0,
  2049             mNeedsCleanup, rv));
  2050       CleanupStream(stream, NS_OK, RST_CANCEL);
  2051       MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame");
  2052       mNeedsCleanup = nullptr;                     /* just in case */
  2053       return NS_OK;
  2056     if (mNeedsCleanup) {
  2057       LOG3(("SpdySession3::WriteSegments session=%p stream=%p 0x%X "
  2058             "cleanup stream based on mNeedsCleanup.\n",
  2059             this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0));
  2060       CleanupStream(mNeedsCleanup, NS_OK, RST_CANCEL);
  2061       mNeedsCleanup = nullptr;
  2064     if (NS_FAILED(rv)) {
  2065       LOG3(("SpdySession3 %p data frame read failure %x\n", this, rv));
  2066       // maybe just blocked reading from network
  2067       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2068         rv = NS_OK;
  2071     return rv;
  2074   if (mDownstreamState == DISCARDING_DATA_FRAME) {
  2075     char trash[4096];
  2076     uint32_t count = std::min(4096U, mInputFrameDataSize - mInputFrameDataRead);
  2078     if (!count) {
  2079       ResetDownstreamState();
  2080       ResumeRecv();
  2081       return NS_BASE_STREAM_WOULD_BLOCK;
  2084     rv = NetworkRead(writer, trash, count, countWritten);
  2086     if (NS_FAILED(rv)) {
  2087       LOG3(("SpdySession3 %p discard frame read failure %x\n", this, rv));
  2088       // maybe just blocked reading from network
  2089       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2090         rv = NS_OK;
  2091       return rv;
  2094     LogIO(this, nullptr, "Discarding Frame", trash, *countWritten);
  2096     mInputFrameDataRead += *countWritten;
  2098     if (mInputFrameDataRead == mInputFrameDataSize)
  2099       ResetDownstreamState();
  2100     return rv;
  2103   MOZ_ASSERT(mDownstreamState == BUFFERING_CONTROL_FRAME);
  2104   if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
  2105     // this cannot happen
  2106     return NS_ERROR_UNEXPECTED;
  2109   MOZ_ASSERT(mInputFrameBufferUsed == 8,
  2110              "Frame Buffer Header Not Present");
  2112   rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
  2113                    mInputFrameDataSize - mInputFrameDataRead, countWritten);
  2115   if (NS_FAILED(rv)) {
  2116     LOG3(("SpdySession3 %p buffering control frame read failure %x\n",
  2117           this, rv));
  2118     // maybe just blocked reading from network
  2119     if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2120       rv = NS_OK;
  2121     return rv;
  2124   LogIO(this, nullptr, "Reading Control Frame",
  2125         mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
  2127   mInputFrameDataRead += *countWritten;
  2129   if (mInputFrameDataRead != mInputFrameDataSize)
  2130     return NS_OK;
  2132   // This check is actually redundant, the control type was previously
  2133   // checked to make sure it was in range, but we will check it again
  2134   // at time of use to make sure a regression doesn't creep in.
  2135   if (mFrameControlType >= CONTROL_TYPE_LAST ||
  2136       mFrameControlType <= CONTROL_TYPE_FIRST)
  2138     MOZ_ASSERT(false, "control type out of range");
  2139     return NS_ERROR_ILLEGAL_VALUE;
  2141   rv = sControlFunctions[mFrameControlType](this);
  2143   MOZ_ASSERT(NS_FAILED(rv) ||
  2144              mDownstreamState != BUFFERING_CONTROL_FRAME,
  2145              "Control Handler returned OK but did not change state");
  2147   if (mShouldGoAway && !mStreamTransactionHash.Count())
  2148     Close(NS_OK);
  2149   return rv;
  2152 void
  2153 SpdySession3::UpdateLocalRwin(SpdyStream3 *stream,
  2154                               uint32_t bytes)
  2156   // If this data packet was not for a valid or live stream then there
  2157   // is no reason to mess with the flow control
  2158   if (!stream || stream->RecvdFin())
  2159     return;
  2161   stream->DecrementLocalWindow(bytes);
  2163   // Don't necessarily ack every data packet. Only do it
  2164   // after a significant amount of data.
  2165   uint64_t unacked = stream->LocalUnAcked();
  2166   int64_t  localWindow = stream->LocalWindow();
  2168   LOG3(("SpdySession3::UpdateLocalRwin this=%p id=0x%X newbytes=%u "
  2169         "unacked=%llu localWindow=%lld\n",
  2170         this, stream->StreamID(), bytes, unacked, localWindow));
  2172   if (!unacked)
  2173     return;
  2175   if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold))
  2176     return;
  2178   if (!stream->HasSink()) {
  2179     LOG3(("SpdySession3::UpdateLocalRwin %p 0x%X Pushed Stream Has No Sink\n",
  2180           this, stream->StreamID()));
  2181     return;
  2184   // Generate window updates directly out of spdysession instead of the stream
  2185   // in order to avoid queue delays in getting the 'ACK' out.
  2186   uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU;
  2188   LOG3(("SpdySession3::UpdateLocalRwin Ack this=%p id=0x%X acksize=%d\n",
  2189         this, stream->StreamID(), toack));
  2190   stream->IncrementLocalWindow(toack);
  2192   static const uint32_t dataLen = 8;
  2193   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 8 + dataLen,
  2194                mOutputQueueUsed, mOutputQueueSize);
  2195   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
  2196   mOutputQueueUsed += 8 + dataLen;
  2198   memset(packet, 0, 8 + dataLen);
  2199   packet[0] = kFlag_Control;
  2200   packet[1] = kVersion;
  2201   packet[3] = CONTROL_TYPE_WINDOW_UPDATE;
  2202   packet[7] = dataLen;
  2204   NetworkEndian::writeUint32(packet + 8, stream->StreamID());
  2205   NetworkEndian::writeUint32(packet + 12, toack);
  2207   LogIO(this, stream, "Window Update", packet, 8 + dataLen);
  2208   FlushOutputQueue();
  2211 void
  2212 SpdySession3::Close(nsresult aReason)
  2214   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2216   if (mClosed)
  2217     return;
  2219   LOG3(("SpdySession3::Close %p %X", this, aReason));
  2221   mClosed = true;
  2223   mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
  2224   mStreamIDHash.Clear();
  2225   mStreamTransactionHash.Clear();
  2227   uint32_t goAwayReason;
  2228   if (NS_SUCCEEDED(aReason)) {
  2229     goAwayReason = OK;
  2230   } else if (aReason == NS_ERROR_ILLEGAL_VALUE) {
  2231     goAwayReason = PROTOCOL_ERROR;
  2232   } else {
  2233     goAwayReason = INTERNAL_ERROR;
  2235   GenerateGoAway(goAwayReason);
  2236   mConnection = nullptr;
  2237   mSegmentReader = nullptr;
  2238   mSegmentWriter = nullptr;
  2241 void
  2242 SpdySession3::CloseTransaction(nsAHttpTransaction *aTransaction,
  2243                               nsresult aResult)
  2245   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2246   LOG3(("SpdySession3::CloseTransaction %p %p %x", this, aTransaction, aResult));
  2248   // Generally this arrives as a cancel event from the connection manager.
  2250   // need to find the stream and call CleanupStream() on it.
  2251   SpdyStream3 *stream = mStreamTransactionHash.Get(aTransaction);
  2252   if (!stream) {
  2253     LOG3(("SpdySession3::CloseTransaction %p %p %x - not found.",
  2254           this, aTransaction, aResult));
  2255     return;
  2257   LOG3(("SpdySession3::CloseTranscation probably a cancel. "
  2258         "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
  2259         this, aTransaction, aResult, stream->StreamID(), stream));
  2260   CleanupStream(stream, aResult, RST_CANCEL);
  2261   ResumeRecv();
  2265 //-----------------------------------------------------------------------------
  2266 // nsAHttpSegmentReader
  2267 //-----------------------------------------------------------------------------
  2269 nsresult
  2270 SpdySession3::OnReadSegment(const char *buf,
  2271                            uint32_t count,
  2272                            uint32_t *countRead)
  2274   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2276   nsresult rv;
  2278   // If we can release old queued data then we can try and write the new
  2279   // data directly to the network without using the output queue at all
  2280   if (mOutputQueueUsed)
  2281     FlushOutputQueue();
  2283   if (!mOutputQueueUsed && mSegmentReader) {
  2284     // try and write directly without output queue
  2285     rv = mSegmentReader->OnReadSegment(buf, count, countRead);
  2287     if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2288       *countRead = 0;
  2289     else if (NS_FAILED(rv))
  2290       return rv;
  2292     if (*countRead < count) {
  2293       uint32_t required = count - *countRead;
  2294       // assuming a commitment() happened, this ensurebuffer is a nop
  2295       // but just in case the queuesize is too small for the required data
  2296       // call ensurebuffer().
  2297       EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
  2298       memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
  2299       mOutputQueueUsed = required;
  2302     *countRead = count;
  2303     return NS_OK;
  2306   // At this point we are going to buffer the new data in the output
  2307   // queue if it fits. By coalescing multiple small submissions into one larger
  2308   // buffer we can get larger writes out to the network later on.
  2310   // This routine should not be allowed to fill up the output queue
  2311   // all on its own - at least kQueueReserved bytes are always left
  2312   // for other routines to use - but this is an all-or-nothing function,
  2313   // so if it will not all fit just return WOULD_BLOCK
  2315   if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
  2316     return NS_BASE_STREAM_WOULD_BLOCK;
  2318   memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
  2319   mOutputQueueUsed += count;
  2320   *countRead = count;
  2322   FlushOutputQueue();
  2324   return NS_OK;
  2327 nsresult
  2328 SpdySession3::CommitToSegmentSize(uint32_t count, bool forceCommitment)
  2330   if (mOutputQueueUsed)
  2331     FlushOutputQueue();
  2333   // would there be enough room to buffer this if needed?
  2334   if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
  2335     return NS_OK;
  2337   // if we are using part of our buffers already, try again later unless
  2338   // forceCommitment is set.
  2339   if (mOutputQueueUsed && !forceCommitment)
  2340     return NS_BASE_STREAM_WOULD_BLOCK;
  2342   if (mOutputQueueUsed) {
  2343     // normally we avoid the memmove of RealignOutputQueue, but we'll try
  2344     // it if forceCommitment is set before growing the buffer.
  2345     RealignOutputQueue();
  2347     // is there enough room now?
  2348     if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
  2349       return NS_OK;
  2352   // resize the buffers as needed
  2353   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + count + kQueueReserved,
  2354                mOutputQueueUsed, mOutputQueueSize);
  2356   MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved),
  2357              "buffer not as large as expected");
  2359   return NS_OK;
  2362 //-----------------------------------------------------------------------------
  2363 // nsAHttpSegmentWriter
  2364 //-----------------------------------------------------------------------------
  2366 nsresult
  2367 SpdySession3::OnWriteSegment(char *buf,
  2368                             uint32_t count,
  2369                             uint32_t *countWritten)
  2371   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2372   nsresult rv;
  2374   if (!mSegmentWriter) {
  2375     // the only way this could happen would be if Close() were called on the
  2376     // stack with WriteSegments()
  2377     return NS_ERROR_FAILURE;
  2380   if (mDownstreamState == PROCESSING_DATA_FRAME) {
  2382     if (mInputFrameDataLast &&
  2383         mInputFrameDataRead == mInputFrameDataSize) {
  2384       *countWritten = 0;
  2385       SetNeedsCleanup();
  2386       return NS_BASE_STREAM_CLOSED;
  2389     count = std::min(count, mInputFrameDataSize - mInputFrameDataRead);
  2390     rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
  2391     if (NS_FAILED(rv))
  2392       return rv;
  2394     LogIO(this, mInputFrameDataStream, "Reading Data Frame",
  2395           buf, *countWritten);
  2397     mInputFrameDataRead += *countWritten;
  2399     mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
  2400     if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameDataLast)
  2401       ResetDownstreamState();
  2403     return rv;
  2406   if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
  2408     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
  2409         mInputFrameDataLast) {
  2410       *countWritten = 0;
  2411       SetNeedsCleanup();
  2412       return NS_BASE_STREAM_CLOSED;
  2415     count = std::min(count,
  2416                    mFlatHTTPResponseHeaders.Length() -
  2417                    mFlatHTTPResponseHeadersOut);
  2418     memcpy(buf,
  2419            mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
  2420            count);
  2421     mFlatHTTPResponseHeadersOut += count;
  2422     *countWritten = count;
  2424     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) {
  2425       if (mDataPending) {
  2426         // Now ready to process data frames - pop PROCESING_DATA_FRAME back onto
  2427         // the stack because receipt of that first data frame triggered the
  2428         // response header processing
  2429         mDataPending = false;
  2430         ChangeDownstreamState(PROCESSING_DATA_FRAME);
  2432       else if (!mInputFrameDataLast) {
  2433         // If more frames are expected in this stream, then reset the state so they can be
  2434         // handled. Otherwise (e.g. a 0 length response with the fin on the SYN_REPLY)
  2435         // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can
  2436         // cleanup the stream.
  2437         ResetDownstreamState();
  2441     return NS_OK;
  2444   return NS_ERROR_UNEXPECTED;
  2447 void
  2448 SpdySession3::SetNeedsCleanup()
  2450   LOG3(("SpdySession3::SetNeedsCleanup %p - recorded downstream fin of "
  2451         "stream %p 0x%X", this, mInputFrameDataStream,
  2452         mInputFrameDataStream->StreamID()));
  2454   // This will result in Close() being called
  2455   MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
  2456   mNeedsCleanup = mInputFrameDataStream;
  2457   ResetDownstreamState();
  2460 void
  2461 SpdySession3::ConnectPushedStream(SpdyStream3 *stream)
  2463   mReadyForRead.Push(stream);
  2464   ForceRecv();
  2467 //-----------------------------------------------------------------------------
  2468 // Modified methods of nsAHttpConnection
  2469 //-----------------------------------------------------------------------------
  2471 void
  2472 SpdySession3::TransactionHasDataToWrite(nsAHttpTransaction *caller)
  2474   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2475   LOG3(("SpdySession3::TransactionHasDataToWrite %p trans=%p", this, caller));
  2477   // a trapped signal from the http transaction to the connection that
  2478   // it is no longer blocked on read.
  2480   SpdyStream3 *stream = mStreamTransactionHash.Get(caller);
  2481   if (!stream || !VerifyStream(stream)) {
  2482     LOG3(("SpdySession3::TransactionHasDataToWrite %p caller %p not found",
  2483           this, caller));
  2484     return;
  2487   LOG3(("SpdySession3::TransactionHasDataToWrite %p ID is 0x%X\n",
  2488         this, stream->StreamID()));
  2490   mReadyForWrite.Push(stream);
  2493 void
  2494 SpdySession3::TransactionHasDataToWrite(SpdyStream3 *stream)
  2496   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2497   LOG3(("SpdySession3::TransactionHasDataToWrite %p stream=%p ID=%x",
  2498         this, stream, stream->StreamID()));
  2500   mReadyForWrite.Push(stream);
  2501   SetWriteCallbacks();
  2504 bool
  2505 SpdySession3::IsPersistent()
  2507   return true;
  2510 nsresult
  2511 SpdySession3::TakeTransport(nsISocketTransport **,
  2512                            nsIAsyncInputStream **,
  2513                            nsIAsyncOutputStream **)
  2515   MOZ_ASSERT(false, "TakeTransport of SpdySession3");
  2516   return NS_ERROR_UNEXPECTED;
  2519 nsHttpConnection *
  2520 SpdySession3::TakeHttpConnection()
  2522   MOZ_ASSERT(false, "TakeHttpConnection of SpdySession3");
  2523   return nullptr;
  2526 uint32_t
  2527 SpdySession3::CancelPipeline(nsresult reason)
  2529   // we don't pipeline inside spdy, so this isn't an issue
  2530   return 0;
  2533 nsAHttpTransaction::Classifier
  2534 SpdySession3::Classification()
  2536   if (!mConnection)
  2537     return nsAHttpTransaction::CLASS_GENERAL;
  2538   return mConnection->Classification();
  2541 //-----------------------------------------------------------------------------
  2542 // unused methods of nsAHttpTransaction
  2543 // We can be sure of this because SpdySession3 is only constructed in
  2544 // nsHttpConnection and is never passed out of that object
  2545 //-----------------------------------------------------------------------------
  2547 void
  2548 SpdySession3::SetConnection(nsAHttpConnection *)
  2550   // This is unexpected
  2551   MOZ_ASSERT(false, "SpdySession3::SetConnection()");
  2554 void
  2555 SpdySession3::GetSecurityCallbacks(nsIInterfaceRequestor **)
  2557   // This is unexpected
  2558   MOZ_ASSERT(false, "SpdySession3::GetSecurityCallbacks()");
  2561 void
  2562 SpdySession3::SetProxyConnectFailed()
  2564   MOZ_ASSERT(false, "SpdySession3::SetProxyConnectFailed()");
  2567 bool
  2568 SpdySession3::IsDone()
  2570   return !mStreamTransactionHash.Count();
  2573 nsresult
  2574 SpdySession3::Status()
  2576   MOZ_ASSERT(false, "SpdySession3::Status()");
  2577   return NS_ERROR_UNEXPECTED;
  2580 uint32_t
  2581 SpdySession3::Caps()
  2583   MOZ_ASSERT(false, "SpdySession3::Caps()");
  2584   return 0;
  2587 void
  2588 SpdySession3::SetDNSWasRefreshed()
  2592 uint64_t
  2593 SpdySession3::Available()
  2595   MOZ_ASSERT(false, "SpdySession3::Available()");
  2596   return 0;
  2599 nsHttpRequestHead *
  2600 SpdySession3::RequestHead()
  2602   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2603   MOZ_ASSERT(false,
  2604              "SpdySession3::RequestHead() "
  2605              "should not be called after SPDY is setup");
  2606   return nullptr;
  2609 uint32_t
  2610 SpdySession3::Http1xTransactionCount()
  2612   return 0;
  2615 // used as an enumerator by TakeSubTransactions()
  2616 static PLDHashOperator
  2617 TakeStream(nsAHttpTransaction *key,
  2618            nsAutoPtr<SpdyStream3> &stream,
  2619            void *closure)
  2621   nsTArray<nsRefPtr<nsAHttpTransaction> > *list =
  2622     static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure);
  2624   list->AppendElement(key);
  2626   // removing the stream from the hash will delete the stream
  2627   // and drop the transaction reference the hash held
  2628   return PL_DHASH_REMOVE;
  2631 nsresult
  2632 SpdySession3::TakeSubTransactions(
  2633     nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
  2635   // Generally this cannot be done with spdy as transactions are
  2636   // started right away.
  2638   LOG3(("SpdySession3::TakeSubTransactions %p\n", this));
  2640   if (mConcurrentHighWater > 0)
  2641     return NS_ERROR_ALREADY_OPENED;
  2643   LOG3(("   taking %d\n", mStreamTransactionHash.Count()));
  2645   mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
  2646   return NS_OK;
  2649 nsresult
  2650 SpdySession3::AddTransaction(nsAHttpTransaction *)
  2652   // This API is meant for pipelining, SpdySession3's should be
  2653   // extended with AddStream()
  2655   MOZ_ASSERT(false,
  2656              "SpdySession3::AddTransaction() should not be called");
  2658   return NS_ERROR_NOT_IMPLEMENTED;
  2661 uint32_t
  2662 SpdySession3::PipelineDepth()
  2664   return IsDone() ? 0 : 1;
  2667 nsresult
  2668 SpdySession3::SetPipelinePosition(int32_t position)
  2670   // This API is meant for pipelining, SpdySession3's should be
  2671   // extended with AddStream()
  2673   MOZ_ASSERT(false,
  2674              "SpdySession3::SetPipelinePosition() should not be called");
  2676   return NS_ERROR_NOT_IMPLEMENTED;
  2679 int32_t
  2680 SpdySession3::PipelinePosition()
  2682     return 0;
  2685 //-----------------------------------------------------------------------------
  2686 // Pass through methods of nsAHttpConnection
  2687 //-----------------------------------------------------------------------------
  2689 nsAHttpConnection *
  2690 SpdySession3::Connection()
  2692   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2693   return mConnection;
  2696 nsresult
  2697 SpdySession3::OnHeadersAvailable(nsAHttpTransaction *transaction,
  2698                                 nsHttpRequestHead *requestHead,
  2699                                 nsHttpResponseHead *responseHead,
  2700                                 bool *reset)
  2702   return mConnection->OnHeadersAvailable(transaction,
  2703                                          requestHead,
  2704                                          responseHead,
  2705                                          reset);
  2708 bool
  2709 SpdySession3::IsReused()
  2711   return mConnection->IsReused();
  2714 nsresult
  2715 SpdySession3::PushBack(const char *buf, uint32_t len)
  2717   return mConnection->PushBack(buf, len);
  2720 } // namespace mozilla::net
  2721 } // namespace mozilla

mercurial