LCOV - code coverage report
Current view: top level - netwerk/protocol/http - Http2Session.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 2083 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 122 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : // HttpLog.h should generally be included first
       8             : #include "HttpLog.h"
       9             : 
      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()
      15             : 
      16             : #include <algorithm>
      17             : 
      18             : #include "Http2Session.h"
      19             : #include "Http2Stream.h"
      20             : #include "Http2Push.h"
      21             : 
      22             : #include "mozilla/EndianUtils.h"
      23             : #include "mozilla/Telemetry.h"
      24             : #include "mozilla/Preferences.h"
      25             : #include "nsHttp.h"
      26             : #include "nsHttpHandler.h"
      27             : #include "nsHttpConnection.h"
      28             : #include "nsIRequestContext.h"
      29             : #include "nsISSLSocketControl.h"
      30             : #include "nsISSLStatus.h"
      31             : #include "nsISSLStatusProvider.h"
      32             : #include "nsISupportsPriority.h"
      33             : #include "nsStandardURL.h"
      34             : #include "nsURLHelper.h"
      35             : #include "prnetdb.h"
      36             : #include "sslt.h"
      37             : #include "mozilla/Sprintf.h"
      38             : #include "nsSocketTransportService2.h"
      39             : #include "nsNetUtil.h"
      40             : 
      41             : namespace mozilla {
      42             : namespace net {
      43             : 
      44             : // Http2Session has multiple inheritance of things that implement nsISupports
      45           0 : NS_IMPL_ADDREF(Http2Session)
      46           0 : NS_IMPL_RELEASE(Http2Session)
      47           0 : NS_INTERFACE_MAP_BEGIN(Http2Session)
      48           0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
      49           0 : NS_INTERFACE_MAP_END
      50             : 
      51             : // "magic" refers to the string that preceeds HTTP/2 on the wire
      52             : // to help find any intermediaries speaking an older version of HTTP
      53             : const uint8_t Http2Session::kMagicHello[] = {
      54             :   0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
      55             :   0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
      56             :   0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
      57             : };
      58             : 
      59             : #define RETURN_SESSION_ERROR(o,x)  \
      60             : do {                             \
      61             :   (o)->mGoAwayReason = (x);      \
      62             :   return NS_ERROR_ILLEGAL_VALUE; \
      63             :   } while (0)
      64             : 
      65           0 : Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t version, bool attemptingEarlyData)
      66             :   : mSocketTransport(aSocketTransport)
      67             :   , mSegmentReader(nullptr)
      68             :   , mSegmentWriter(nullptr)
      69             :   , mNextStreamID(3) // 1 is reserved for Updgrade handshakes
      70             :   , mLastPushedID(0)
      71             :   , mConcurrentHighWater(0)
      72             :   , mDownstreamState(BUFFERING_OPENING_SETTINGS)
      73             :   , mInputFrameBufferSize(kDefaultBufferSize)
      74             :   , mInputFrameBufferUsed(0)
      75             :   , mInputFrameDataSize(0)
      76             :   , mInputFrameDataRead(0)
      77             :   , mInputFrameFinal(false)
      78             :   , mInputFrameType(0)
      79             :   , mInputFrameFlags(0)
      80             :   , mInputFrameID(0)
      81             :   , mPaddingLength(0)
      82             :   , mInputFrameDataStream(nullptr)
      83             :   , mNeedsCleanup(nullptr)
      84             :   , mDownstreamRstReason(NO_HTTP_ERROR)
      85             :   , mExpectedHeaderID(0)
      86             :   , mExpectedPushPromiseID(0)
      87             :   , mContinuedPromiseStream(0)
      88             :   , mFlatHTTPResponseHeadersOut(0)
      89             :   , mShouldGoAway(false)
      90             :   , mClosed(false)
      91             :   , mCleanShutdown(false)
      92             :   , mReceivedSettings(false)
      93             :   , mTLSProfileConfirmed(false)
      94             :   , mGoAwayReason(NO_HTTP_ERROR)
      95             :   , mClientGoAwayReason(UNASSIGNED)
      96             :   , mPeerGoAwayReason(UNASSIGNED)
      97             :   , mGoAwayID(0)
      98             :   , mOutgoingGoAwayID(0)
      99             :   , mConcurrent(0)
     100             :   , mServerPushedResources(0)
     101             :   , mServerInitialStreamWindow(kDefaultRwin)
     102             :   , mLocalSessionWindow(kDefaultRwin)
     103             :   , mServerSessionWindow(kDefaultRwin)
     104             :   , mInitialRwin(ASpdySession::kInitialRwin)
     105             :   , mOutputQueueSize(kDefaultQueueSize)
     106             :   , mOutputQueueUsed(0)
     107             :   , mOutputQueueSent(0)
     108           0 :   , mLastReadEpoch(PR_IntervalNow())
     109             :   , mPingSentEpoch(0)
     110             :   , mPreviousUsed(false)
     111             :   , mWaitingForSettingsAck(false)
     112             :   , mGoAwayOnPush(false)
     113             :   , mUseH2Deps(false)
     114             :   , mAttemptingEarlyData(attemptingEarlyData)
     115           0 :   , mOriginFrameActivated(false)
     116             : {
     117           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     118             : 
     119             :   static uint64_t sSerial;
     120           0 :   mSerial = ++sSerial;
     121             : 
     122           0 :   LOG3(("Http2Session::Http2Session %p serial=0x%" PRIX64 "\n", this, mSerial));
     123             : 
     124           0 :   mInputFrameBuffer = MakeUnique<char[]>(mInputFrameBufferSize);
     125           0 :   mOutputQueueBuffer = MakeUnique<char[]>(mOutputQueueSize);
     126           0 :   mDecompressBuffer.SetCapacity(kDefaultBufferSize);
     127             : 
     128           0 :   mPushAllowance = gHttpHandler->SpdyPushAllowance();
     129           0 :   mInitialRwin = std::max(gHttpHandler->SpdyPullAllowance(), mPushAllowance);
     130           0 :   mMaxConcurrent = gHttpHandler->DefaultSpdyConcurrent();
     131           0 :   mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
     132           0 :   SendHello();
     133             : 
     134           0 :   mLastDataReadEpoch = mLastReadEpoch;
     135             : 
     136           0 :   mPingThreshold = gHttpHandler->SpdyPingThreshold();
     137           0 :   mPreviousPingThreshold = mPingThreshold;
     138           0 : }
     139             : 
     140             : void
     141           0 : Http2Session::Shutdown()
     142             : {
     143           0 :   for (auto iter = mStreamTransactionHash.Iter(); !iter.Done(); iter.Next()) {
     144           0 :     nsAutoPtr<Http2Stream> &stream = iter.Data();
     145             : 
     146             :     // On a clean server hangup the server sets the GoAwayID to be the ID of
     147             :     // the last transaction it processed. If the ID of stream in the
     148             :     // local stream is greater than that it can safely be restarted because the
     149             :     // server guarantees it was not partially processed. Streams that have not
     150             :     // registered an ID haven't actually been sent yet so they can always be
     151             :     // restarted.
     152           0 :     if (mCleanShutdown &&
     153           0 :         (stream->StreamID() > mGoAwayID || !stream->HasRegisteredID())) {
     154           0 :       CloseStream(stream, NS_ERROR_NET_RESET);  // can be restarted
     155           0 :     } else if (stream->RecvdData()) {
     156           0 :       CloseStream(stream, NS_ERROR_NET_PARTIAL_TRANSFER);
     157           0 :     } else if (mGoAwayReason == INADEQUATE_SECURITY) {
     158           0 :       CloseStream(stream, NS_ERROR_NET_INADEQUATE_SECURITY);
     159             :     } else {
     160           0 :       CloseStream(stream, NS_ERROR_ABORT);
     161             :     }
     162             :   }
     163           0 : }
     164             : 
     165           0 : Http2Session::~Http2Session()
     166             : {
     167           0 :   LOG3(("Http2Session::~Http2Session %p mDownstreamState=%X",
     168             :         this, mDownstreamState));
     169             : 
     170           0 :   Shutdown();
     171             : 
     172           0 :   Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
     173           0 :   Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
     174           0 :   Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
     175           0 :                         mServerPushedResources);
     176           0 :   Telemetry::Accumulate(Telemetry::SPDY_GOAWAY_LOCAL, mClientGoAwayReason);
     177           0 :   Telemetry::Accumulate(Telemetry::SPDY_GOAWAY_PEER, mPeerGoAwayReason);
     178           0 : }
     179             : 
     180             : void
     181           0 : Http2Session::LogIO(Http2Session *self, Http2Stream *stream,
     182             :                     const char *label,
     183             :                     const char *data, uint32_t datalen)
     184             : {
     185           0 :   if (!LOG5_ENABLED())
     186           0 :     return;
     187             : 
     188           0 :   LOG5(("Http2Session::LogIO %p stream=%p id=0x%X [%s]",
     189             :         self, stream, stream ? stream->StreamID() : 0, label));
     190             : 
     191             :   // Max line is (16 * 3) + 10(prefix) + newline + null
     192             :   char linebuf[128];
     193             :   uint32_t index;
     194           0 :   char *line = linebuf;
     195             : 
     196           0 :   linebuf[127] = 0;
     197             : 
     198           0 :   for (index = 0; index < datalen; ++index) {
     199           0 :     if (!(index % 16)) {
     200           0 :       if (index) {
     201           0 :         *line = 0;
     202           0 :         LOG5(("%s", linebuf));
     203             :       }
     204           0 :       line = linebuf;
     205           0 :       snprintf(line, 128, "%08X: ", index);
     206           0 :       line += 10;
     207             :     }
     208           0 :     snprintf(line, 128 - (line - linebuf), "%02X ", (reinterpret_cast<const uint8_t *>(data))[index]);
     209           0 :     line += 3;
     210             :   }
     211           0 :   if (index) {
     212           0 :     *line = 0;
     213           0 :     LOG5(("%s", linebuf));
     214             :   }
     215             : }
     216             : 
     217             : typedef nsresult (*Http2ControlFx) (Http2Session *self);
     218             : static Http2ControlFx sControlFunctions[] = {
     219             :   nullptr, // type 0 data is not a control function
     220             :   Http2Session::RecvHeaders,
     221             :   Http2Session::RecvPriority,
     222             :   Http2Session::RecvRstStream,
     223             :   Http2Session::RecvSettings,
     224             :   Http2Session::RecvPushPromise,
     225             :   Http2Session::RecvPing,
     226             :   Http2Session::RecvGoAway,
     227             :   Http2Session::RecvWindowUpdate,
     228             :   Http2Session::RecvContinuation,
     229             :   Http2Session::RecvAltSvc, // extension for type 0x0A
     230             :   Http2Session::RecvUnused, // 0x0B was BLOCKED still radioactive
     231             :   Http2Session::RecvOrigin  // extension for type 0x0C
     232             : };
     233             : 
     234             : bool
     235           0 : Http2Session::RoomForMoreConcurrent()
     236             : {
     237           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     238           0 :   return (mConcurrent < mMaxConcurrent);
     239             : }
     240             : 
     241             : bool
     242           0 : Http2Session::RoomForMoreStreams()
     243             : {
     244           0 :   if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
     245           0 :     return false;
     246             : 
     247           0 :   return !mShouldGoAway;
     248             : }
     249             : 
     250             : PRIntervalTime
     251           0 : Http2Session::IdleTime()
     252             : {
     253           0 :   return PR_IntervalNow() - mLastDataReadEpoch;
     254             : }
     255             : 
     256             : uint32_t
     257           0 : Http2Session::ReadTimeoutTick(PRIntervalTime now)
     258             : {
     259           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     260             : 
     261           0 :   LOG3(("Http2Session::ReadTimeoutTick %p delta since last read %ds\n",
     262             :        this, PR_IntervalToSeconds(now - mLastReadEpoch)));
     263             : 
     264           0 :   if (!mPingThreshold)
     265           0 :     return UINT32_MAX;
     266             : 
     267           0 :   if ((now - mLastReadEpoch) < mPingThreshold) {
     268             :     // recent activity means ping is not an issue
     269           0 :     if (mPingSentEpoch) {
     270           0 :       mPingSentEpoch = 0;
     271           0 :       if (mPreviousUsed) {
     272             :         // restore the former value
     273           0 :         mPingThreshold = mPreviousPingThreshold;
     274           0 :         mPreviousUsed = false;
     275             :       }
     276             :     }
     277             : 
     278           0 :     return PR_IntervalToSeconds(mPingThreshold) -
     279           0 :       PR_IntervalToSeconds(now - mLastReadEpoch);
     280             :   }
     281             : 
     282           0 :   if (mPingSentEpoch) {
     283           0 :     LOG3(("Http2Session::ReadTimeoutTick %p handle outstanding ping\n", this));
     284           0 :     if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
     285           0 :       LOG3(("Http2Session::ReadTimeoutTick %p Ping Timer Exhaustion\n", this));
     286           0 :       mPingSentEpoch = 0;
     287           0 :       Close(NS_ERROR_NET_TIMEOUT);
     288           0 :       return UINT32_MAX;
     289             :     }
     290           0 :     return 1; // run the tick aggressively while ping is outstanding
     291             :   }
     292             : 
     293           0 :   LOG3(("Http2Session::ReadTimeoutTick %p generating ping\n", this));
     294             : 
     295           0 :   mPingSentEpoch = PR_IntervalNow();
     296           0 :   if (!mPingSentEpoch) {
     297           0 :     mPingSentEpoch = 1; // avoid the 0 sentinel value
     298             :   }
     299           0 :   GeneratePing(false);
     300           0 :   Unused << ResumeRecv(); // read the ping reply
     301             : 
     302             :   // Check for orphaned push streams. This looks expensive, but generally the
     303             :   // list is empty.
     304             :   Http2PushedStream *deleteMe;
     305           0 :   TimeStamp timestampNow;
     306           0 :   do {
     307           0 :     deleteMe = nullptr;
     308             : 
     309           0 :     for (uint32_t index = mPushedStreams.Length();
     310           0 :          index > 0 ; --index) {
     311           0 :       Http2PushedStream *pushedStream = mPushedStreams[index - 1];
     312             : 
     313           0 :       if (timestampNow.IsNull())
     314           0 :         timestampNow = TimeStamp::Now(); // lazy initializer
     315             : 
     316             :       // if stream finished, but is not connected, and its been like that for
     317             :       // long then cleanup the stream.
     318           0 :       if (pushedStream->IsOrphaned(timestampNow))
     319             :       {
     320           0 :         LOG3(("Http2Session Timeout Pushed Stream %p 0x%X\n",
     321             :               this, pushedStream->StreamID()));
     322           0 :         deleteMe = pushedStream;
     323           0 :         break; // don't CleanupStream() while iterating this vector
     324             :       }
     325             :     }
     326           0 :     if (deleteMe)
     327           0 :       CleanupStream(deleteMe, NS_ERROR_ABORT, CANCEL_ERROR);
     328             : 
     329           0 :   } while (deleteMe);
     330             : 
     331           0 :   return 1; // run the tick aggressively while ping is outstanding
     332             : }
     333             : 
     334             : uint32_t
     335           0 : Http2Session::RegisterStreamID(Http2Stream *stream, uint32_t aNewID)
     336             : {
     337           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     338           0 :   MOZ_ASSERT(mNextStreamID < 0xfffffff0,
     339             :              "should have stopped admitting streams");
     340           0 :   MOZ_ASSERT(!(aNewID & 1),
     341             :              "0 for autoassign pull, otherwise explicit even push assignment");
     342             : 
     343           0 :   if (!aNewID) {
     344             :     // auto generate a new pull stream ID
     345           0 :     aNewID = mNextStreamID;
     346           0 :     MOZ_ASSERT(aNewID & 1, "pull ID must be odd.");
     347           0 :     mNextStreamID += 2;
     348             :   }
     349             : 
     350           0 :   LOG3(("Http2Session::RegisterStreamID session=%p stream=%p id=0x%X "
     351             :         "concurrent=%d",this, stream, aNewID, mConcurrent));
     352             : 
     353             :   // We've used up plenty of ID's on this session. Start
     354             :   // moving to a new one before there is a crunch involving
     355             :   // server push streams or concurrent non-registered submits
     356           0 :   if (aNewID >= kMaxStreamID)
     357           0 :     mShouldGoAway = true;
     358             : 
     359             :   // integrity check
     360           0 :   if (mStreamIDHash.Get(aNewID)) {
     361           0 :     LOG3(("   New ID already present\n"));
     362           0 :     MOZ_ASSERT(false, "New ID already present in mStreamIDHash");
     363             :     mShouldGoAway = true;
     364             :     return kDeadStreamID;
     365             :   }
     366             : 
     367           0 :   mStreamIDHash.Put(aNewID, stream);
     368           0 :   return aNewID;
     369             : }
     370             : 
     371             : bool
     372           0 : Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
     373             :                         int32_t aPriority,
     374             :                         bool aUseTunnel,
     375             :                         nsIInterfaceRequestor *aCallbacks)
     376             : {
     377           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     378             : 
     379             :   // integrity check
     380           0 :   if (mStreamTransactionHash.Get(aHttpTransaction)) {
     381           0 :     LOG3(("   New transaction already present\n"));
     382           0 :     MOZ_ASSERT(false, "AddStream duplicate transaction pointer");
     383             :     return false;
     384             :   }
     385             : 
     386           0 :   if (!mConnection) {
     387           0 :     mConnection = aHttpTransaction->Connection();
     388             :   }
     389             : 
     390           0 :   if (mClosed || mShouldGoAway) {
     391           0 :     nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
     392           0 :     if (trans && !trans->GetPushedStream()) {
     393           0 :       LOG3(("Http2Session::AddStream %p atrans=%p trans=%p session unusable - resched.\n",
     394             :             this, aHttpTransaction, trans));
     395           0 :       aHttpTransaction->SetConnection(nullptr);
     396           0 :       nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
     397           0 :       if (NS_FAILED(rv)) {
     398           0 :         LOG3(("Http2Session::AddStream %p atrans=%p trans=%p failed to initiate "
     399             :               "transaction (%08x).\n", this, aHttpTransaction, trans,
     400             :               static_cast<uint32_t>(rv)));
     401             :       }
     402           0 :       return true;
     403             :     }
     404             :   }
     405             : 
     406           0 :   aHttpTransaction->SetConnection(this);
     407           0 :   aHttpTransaction->OnActivated(true);
     408             : 
     409           0 :   if (aUseTunnel) {
     410           0 :     LOG3(("Http2Session::AddStream session=%p trans=%p OnTunnel",
     411             :           this, aHttpTransaction));
     412           0 :     DispatchOnTunnel(aHttpTransaction, aCallbacks);
     413           0 :     return true;
     414             :   }
     415             : 
     416           0 :   Http2Stream *stream = new Http2Stream(aHttpTransaction, this, aPriority);
     417             : 
     418           0 :   LOG3(("Http2Session::AddStream session=%p stream=%p serial=%" PRIu64 " "
     419             :         "NextID=0x%X (tentative)", this, stream, mSerial, mNextStreamID));
     420             : 
     421           0 :   mStreamTransactionHash.Put(aHttpTransaction, stream);
     422             : 
     423           0 :   mReadyForWrite.Push(stream);
     424           0 :   SetWriteCallbacks();
     425             : 
     426             :   // Kick off the SYN transmit without waiting for the poll loop
     427             :   // This won't work for the first stream because there is no segment reader
     428             :   // yet.
     429           0 :   if (mSegmentReader) {
     430             :     uint32_t countRead;
     431           0 :     Unused << ReadSegments(nullptr, kDefaultBufferSize, &countRead);
     432             :   }
     433             : 
     434           0 :   if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
     435           0 :       !aHttpTransaction->IsNullTransaction()) {
     436           0 :     LOG3(("Http2Session::AddStream %p transaction %p forces keep-alive off.\n",
     437             :           this, aHttpTransaction));
     438           0 :     DontReuse();
     439             :   }
     440             : 
     441           0 :   return true;
     442             : }
     443             : 
     444             : void
     445           0 : Http2Session::QueueStream(Http2Stream *stream)
     446             : {
     447             :   // will be removed via processpending or a shutdown path
     448           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     449           0 :   MOZ_ASSERT(!stream->CountAsActive());
     450           0 :   MOZ_ASSERT(!stream->Queued());
     451             : 
     452           0 :   LOG3(("Http2Session::QueueStream %p stream %p queued.", this, stream));
     453             : 
     454             : #ifdef DEBUG
     455           0 :   int32_t qsize = mQueuedStreams.GetSize();
     456           0 :   for (int32_t i = 0; i < qsize; i++) {
     457           0 :     Http2Stream *qStream = static_cast<Http2Stream *>(mQueuedStreams.ObjectAt(i));
     458           0 :     MOZ_ASSERT(qStream != stream);
     459           0 :     MOZ_ASSERT(qStream->Queued());
     460             :   }
     461             : #endif
     462             : 
     463           0 :   stream->SetQueued(true);
     464           0 :   mQueuedStreams.Push(stream);
     465           0 : }
     466             : 
     467             : void
     468           0 : Http2Session::ProcessPending()
     469             : {
     470           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     471             : 
     472             :   Http2Stream*stream;
     473           0 :   while (RoomForMoreConcurrent() &&
     474           0 :          (stream = static_cast<Http2Stream *>(mQueuedStreams.PopFront()))) {
     475             : 
     476           0 :     LOG3(("Http2Session::ProcessPending %p stream %p woken from queue.",
     477             :           this, stream));
     478           0 :     MOZ_ASSERT(!stream->CountAsActive());
     479           0 :     MOZ_ASSERT(stream->Queued());
     480           0 :     stream->SetQueued(false);
     481           0 :     mReadyForWrite.Push(stream);
     482           0 :     SetWriteCallbacks();
     483             :   }
     484           0 : }
     485             : 
     486             : nsresult
     487           0 : Http2Session::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
     488             :                           uint32_t count, uint32_t *countWritten)
     489             : {
     490           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     491             : 
     492           0 :   if (!count) {
     493           0 :     *countWritten = 0;
     494           0 :     return NS_OK;
     495             :   }
     496             : 
     497           0 :   nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
     498           0 :   if (NS_SUCCEEDED(rv) && *countWritten > 0)
     499           0 :     mLastReadEpoch = PR_IntervalNow();
     500           0 :   return rv;
     501             : }
     502             : 
     503             : void
     504           0 : Http2Session::SetWriteCallbacks()
     505             : {
     506           0 :   if (mConnection &&
     507           0 :       (GetWriteQueueSize() || (mOutputQueueUsed > mOutputQueueSent))) {
     508           0 :     Unused << mConnection->ResumeSend();
     509             :   }
     510           0 : }
     511             : 
     512             : void
     513           0 : Http2Session::RealignOutputQueue()
     514             : {
     515           0 :   if (mAttemptingEarlyData) {
     516             :     // We can't realign right now, because we may need what's in there if early
     517             :     // data fails.
     518           0 :     return;
     519             :   }
     520             : 
     521           0 :   mOutputQueueUsed -= mOutputQueueSent;
     522           0 :   memmove(mOutputQueueBuffer.get(),
     523           0 :           mOutputQueueBuffer.get() + mOutputQueueSent,
     524           0 :           mOutputQueueUsed);
     525           0 :   mOutputQueueSent = 0;
     526             : }
     527             : 
     528             : void
     529           0 : Http2Session::FlushOutputQueue()
     530             : {
     531           0 :   if (!mSegmentReader || !mOutputQueueUsed)
     532           0 :     return;
     533             : 
     534             :   nsresult rv;
     535             :   uint32_t countRead;
     536           0 :   uint32_t avail = mOutputQueueUsed - mOutputQueueSent;
     537             : 
     538           0 :   if (!avail && mAttemptingEarlyData) {
     539             :     // This is kind of a hack, but there are cases where we'll have already
     540             :     // written the data we want whlie doing early data, but we get called again
     541             :     // with a reader, and we need to avoid calling the reader when there's
     542             :     // nothing for it to read.
     543           0 :     return;
     544             :   }
     545             : 
     546           0 :   rv = mSegmentReader->
     547           0 :     OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
     548           0 :                   &countRead);
     549           0 :   LOG3(("Http2Session::FlushOutputQueue %p sz=%d rv=%" PRIx32 " actual=%d",
     550             :         this, avail, static_cast<uint32_t>(rv), countRead));
     551             : 
     552             :   // Dont worry about errors on write, we will pick this up as a read error too
     553           0 :   if (NS_FAILED(rv))
     554           0 :     return;
     555             : 
     556           0 :   mOutputQueueSent += countRead;
     557             : 
     558           0 :   if (mAttemptingEarlyData) {
     559           0 :     return;
     560             :   }
     561             : 
     562           0 :   if (countRead == avail) {
     563           0 :     mOutputQueueUsed = 0;
     564           0 :     mOutputQueueSent = 0;
     565           0 :     return;
     566             :   }
     567             : 
     568             :   // If the output queue is close to filling up and we have sent out a good
     569             :   // chunk of data from the beginning then realign it.
     570             : 
     571           0 :   if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
     572           0 :       ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
     573           0 :     RealignOutputQueue();
     574             :   }
     575             : }
     576             : 
     577             : void
     578           0 : Http2Session::DontReuse()
     579             : {
     580           0 :   LOG3(("Http2Session::DontReuse %p\n", this));
     581           0 :   mShouldGoAway = true;
     582           0 :   if (!mStreamTransactionHash.Count())
     583           0 :     Close(NS_OK);
     584           0 : }
     585             : 
     586             : uint32_t
     587           0 : Http2Session::SpdyVersion()
     588             : {
     589           0 :   return HTTP_VERSION_2;
     590             : }
     591             : 
     592             : uint32_t
     593           0 : Http2Session::GetWriteQueueSize()
     594             : {
     595           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     596             : 
     597           0 :   return mReadyForWrite.GetSize();
     598             : }
     599             : 
     600             : void
     601           0 : Http2Session::ChangeDownstreamState(enum internalStateType newState)
     602             : {
     603           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     604             : 
     605           0 :   LOG3(("Http2Session::ChangeDownstreamState() %p from %X to %X",
     606             :         this, mDownstreamState, newState));
     607           0 :   mDownstreamState = newState;
     608           0 : }
     609             : 
     610             : void
     611           0 : Http2Session::ResetDownstreamState()
     612             : {
     613           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     614             : 
     615           0 :   LOG3(("Http2Session::ResetDownstreamState() %p", this));
     616           0 :   ChangeDownstreamState(BUFFERING_FRAME_HEADER);
     617             : 
     618           0 :   if (mInputFrameFinal && mInputFrameDataStream) {
     619           0 :     mInputFrameFinal = false;
     620           0 :     LOG3(("  SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID()));
     621           0 :     mInputFrameDataStream->SetRecvdFin(true);
     622           0 :     MaybeDecrementConcurrent(mInputFrameDataStream);
     623             :   }
     624           0 :   mInputFrameFinal = false;
     625           0 :   mInputFrameBufferUsed = 0;
     626           0 :   mInputFrameDataStream = nullptr;
     627           0 : }
     628             : 
     629             : // return true if activated (and counted against max)
     630             : // otherwise return false and queue
     631             : bool
     632           0 : Http2Session::TryToActivate(Http2Stream *aStream)
     633             : {
     634           0 :   if (aStream->Queued()) {
     635           0 :     LOG3(("Http2Session::TryToActivate %p stream=%p already queued.\n", this, aStream));
     636           0 :     return false;
     637             :   }
     638             : 
     639           0 :   if (!RoomForMoreConcurrent()) {
     640           0 :     LOG3(("Http2Session::TryToActivate %p stream=%p no room for more concurrent "
     641             :           "streams\n", this, aStream));
     642           0 :     QueueStream(aStream);
     643           0 :     return false;
     644             :   }
     645             : 
     646           0 :   LOG3(("Http2Session::TryToActivate %p stream=%p\n", this, aStream));
     647           0 :   IncrementConcurrent(aStream);
     648           0 :   return true;
     649             : }
     650             : 
     651             : void
     652           0 : Http2Session::IncrementConcurrent(Http2Stream *stream)
     653             : {
     654           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     655           0 :   MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
     656             :              "Do not activate pushed streams");
     657             : 
     658           0 :   nsAHttpTransaction *trans = stream->Transaction();
     659           0 :   if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) {
     660             : 
     661           0 :     MOZ_ASSERT(!stream->CountAsActive());
     662           0 :     stream->SetCountAsActive(true);
     663           0 :     ++mConcurrent;
     664             : 
     665           0 :     if (mConcurrent > mConcurrentHighWater) {
     666           0 :       mConcurrentHighWater = mConcurrent;
     667             :     }
     668           0 :     LOG3(("Http2Session::IncrementCounter %p counting stream %p Currently %d "
     669             :           "streams in session, high water mark is %d\n",
     670             :           this, stream, mConcurrent, mConcurrentHighWater));
     671             :   }
     672           0 : }
     673             : 
     674             : // call with data length (i.e. 0 for 0 data bytes - ignore 9 byte header)
     675             : // dest must have 9 bytes of allocated space
     676             : template<typename charType> void
     677           0 : Http2Session::CreateFrameHeader(charType dest, uint16_t frameLength,
     678             :                                 uint8_t frameType, uint8_t frameFlags,
     679             :                                 uint32_t streamID)
     680             : {
     681           0 :   MOZ_ASSERT(frameLength <= kMaxFrameData, "framelength too large");
     682           0 :   MOZ_ASSERT(!(streamID & 0x80000000));
     683           0 :   MOZ_ASSERT(!frameFlags ||
     684             :              (frameType != FRAME_TYPE_PRIORITY &&
     685             :               frameType != FRAME_TYPE_RST_STREAM &&
     686             :               frameType != FRAME_TYPE_GOAWAY &&
     687             :               frameType != FRAME_TYPE_WINDOW_UPDATE));
     688             : 
     689           0 :   dest[0] = 0x00;
     690           0 :   NetworkEndian::writeUint16(dest + 1, frameLength);
     691           0 :   dest[3] = frameType;
     692           0 :   dest[4] = frameFlags;
     693           0 :   NetworkEndian::writeUint32(dest + 5, streamID);
     694           0 : }
     695             : 
     696             : char *
     697           0 : Http2Session::EnsureOutputBuffer(uint32_t spaceNeeded)
     698             : {
     699             :   // this is an infallible allocation (if an allocation is
     700             :   // needed, which is probably isn't)
     701           0 :   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + spaceNeeded,
     702           0 :                mOutputQueueUsed, mOutputQueueSize);
     703           0 :   return mOutputQueueBuffer.get() + mOutputQueueUsed;
     704             : }
     705             : 
     706             : template void
     707             : Http2Session::CreateFrameHeader(char *dest, uint16_t frameLength,
     708             :                                 uint8_t frameType, uint8_t frameFlags,
     709             :                                 uint32_t streamID);
     710             : 
     711             : template void
     712             : Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength,
     713             :                                 uint8_t frameType, uint8_t frameFlags,
     714             :                                 uint32_t streamID);
     715             : 
     716             : void
     717           0 : Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream)
     718             : {
     719           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     720           0 :   LOG3(("MaybeDecrementConcurrent %p id=0x%X concurrent=%d active=%d\n",
     721             :         this, aStream->StreamID(), mConcurrent, aStream->CountAsActive()));
     722             : 
     723           0 :   if (!aStream->CountAsActive())
     724           0 :     return;
     725             : 
     726           0 :   MOZ_ASSERT(mConcurrent);
     727           0 :   aStream->SetCountAsActive(false);
     728           0 :   --mConcurrent;
     729           0 :   ProcessPending();
     730             : }
     731             : 
     732             : // Need to decompress some data in order to keep the compression
     733             : // context correct, but we really don't care what the result is
     734             : nsresult
     735           0 : Http2Session::UncompressAndDiscard(bool isPush)
     736             : {
     737             :   nsresult rv;
     738           0 :   nsAutoCString trash;
     739             : 
     740           0 :   rv = mDecompressor.DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(mDecompressBuffer.BeginReading()),
     741           0 :                                        mDecompressBuffer.Length(), trash, isPush);
     742           0 :   mDecompressBuffer.Truncate();
     743           0 :   if (NS_FAILED(rv)) {
     744           0 :     LOG3(("Http2Session::UncompressAndDiscard %p Compression Error\n",
     745             :           this));
     746           0 :     mGoAwayReason = COMPRESSION_ERROR;
     747           0 :     return rv;
     748             :   }
     749           0 :   return NS_OK;
     750             : }
     751             : 
     752             : void
     753           0 : Http2Session::GeneratePing(bool isAck)
     754             : {
     755           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     756           0 :   LOG3(("Http2Session::GeneratePing %p isAck=%d\n", this, isAck));
     757             : 
     758           0 :   char *packet = EnsureOutputBuffer(kFrameHeaderBytes + 8);
     759           0 :   mOutputQueueUsed += kFrameHeaderBytes + 8;
     760             : 
     761           0 :   if (isAck) {
     762           0 :     CreateFrameHeader(packet, 8, FRAME_TYPE_PING, kFlag_ACK, 0);
     763           0 :     memcpy(packet + kFrameHeaderBytes,
     764           0 :            mInputFrameBuffer.get() + kFrameHeaderBytes, 8);
     765             :   } else {
     766           0 :     CreateFrameHeader(packet, 8, FRAME_TYPE_PING, 0, 0);
     767           0 :     memset(packet + kFrameHeaderBytes, 0, 8);
     768             :   }
     769             : 
     770           0 :   LogIO(this, nullptr, "Generate Ping", packet, kFrameHeaderBytes + 8);
     771           0 :   FlushOutputQueue();
     772           0 : }
     773             : 
     774             : void
     775           0 : Http2Session::GenerateSettingsAck()
     776             : {
     777             :   // need to generate ack of this settings frame
     778           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     779           0 :   LOG3(("Http2Session::GenerateSettingsAck %p\n", this));
     780             : 
     781           0 :   char *packet = EnsureOutputBuffer(kFrameHeaderBytes);
     782           0 :   mOutputQueueUsed += kFrameHeaderBytes;
     783           0 :   CreateFrameHeader(packet, 0, FRAME_TYPE_SETTINGS, kFlag_ACK, 0);
     784           0 :   LogIO(this, nullptr, "Generate Settings ACK", packet, kFrameHeaderBytes);
     785           0 :   FlushOutputQueue();
     786           0 : }
     787             : 
     788             : void
     789           0 : Http2Session::GeneratePriority(uint32_t aID, uint8_t aPriorityWeight)
     790             : {
     791           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     792           0 :   LOG3(("Http2Session::GeneratePriority %p %X %X\n",
     793             :         this, aID, aPriorityWeight));
     794             : 
     795           0 :   uint32_t frameSize = kFrameHeaderBytes + 5;
     796           0 :   char *packet = EnsureOutputBuffer(frameSize);
     797           0 :   mOutputQueueUsed += frameSize;
     798             : 
     799           0 :   CreateFrameHeader(packet, 5, FRAME_TYPE_PRIORITY, 0, aID);
     800           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes, 0);
     801           0 :   memcpy(packet + frameSize - 1, &aPriorityWeight, 1);
     802           0 :   LogIO(this, nullptr, "Generate Priority", packet, frameSize);
     803           0 :   FlushOutputQueue();
     804           0 : }
     805             : 
     806             : void
     807           0 : Http2Session::GenerateRstStream(uint32_t aStatusCode, uint32_t aID)
     808             : {
     809           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     810             : 
     811             :   // make sure we don't do this twice for the same stream (at least if we
     812             :   // have a stream entry for it)
     813           0 :   Http2Stream *stream = mStreamIDHash.Get(aID);
     814           0 :   if (stream) {
     815           0 :     if (stream->SentReset())
     816           0 :       return;
     817           0 :     stream->SetSentReset(true);
     818             :   }
     819             : 
     820           0 :   LOG3(("Http2Session::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
     821             : 
     822           0 :   uint32_t frameSize = kFrameHeaderBytes + 4;
     823           0 :   char *packet = EnsureOutputBuffer(frameSize);
     824           0 :   mOutputQueueUsed += frameSize;
     825           0 :   CreateFrameHeader(packet, 4, FRAME_TYPE_RST_STREAM, 0, aID);
     826             : 
     827           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes, aStatusCode);
     828             : 
     829           0 :   LogIO(this, nullptr, "Generate Reset", packet, frameSize);
     830           0 :   FlushOutputQueue();
     831             : }
     832             : 
     833             : void
     834           0 : Http2Session::GenerateGoAway(uint32_t aStatusCode)
     835             : {
     836           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     837           0 :   LOG3(("Http2Session::GenerateGoAway %p code=%X\n", this, aStatusCode));
     838             : 
     839           0 :   mClientGoAwayReason = aStatusCode;
     840           0 :   uint32_t frameSize = kFrameHeaderBytes + 8;
     841           0 :   char *packet = EnsureOutputBuffer(frameSize);
     842           0 :   mOutputQueueUsed += frameSize;
     843             : 
     844           0 :   CreateFrameHeader(packet, 8, FRAME_TYPE_GOAWAY, 0, 0);
     845             : 
     846             :   // last-good-stream-id are bytes 9-12 reflecting pushes
     847           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes, mOutgoingGoAwayID);
     848             : 
     849             :   // bytes 13-16 are the status code.
     850           0 :   NetworkEndian::writeUint32(packet + frameSize - 4, aStatusCode);
     851             : 
     852           0 :   LogIO(this, nullptr, "Generate GoAway", packet, frameSize);
     853           0 :   FlushOutputQueue();
     854           0 : }
     855             : 
     856             : // The Hello is comprised of
     857             : // 1] 24 octets of magic, which are designed to
     858             : // flush out silent but broken intermediaries
     859             : // 2] a settings frame which sets a small flow control window for pushes
     860             : // 3] a window update frame which creates a large session flow control window
     861             : // 4] 5 priority frames for streams which will never be opened with headers
     862             : //    these streams (3, 5, 7, 9, b) build a dependency tree that all other
     863             : //    streams will be direct leaves of.
     864             : void
     865           0 : Http2Session::SendHello()
     866             : {
     867           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     868           0 :   LOG3(("Http2Session::SendHello %p\n", this));
     869             : 
     870             :   // sized for magic + 5 settings and a session window update and 5 priority frames
     871             :   // 24 magic, 33 for settings (9 header + 4 settings @6), 13 for window update,
     872             :   // 5 priority frames at 14 (9 + 5) each
     873             :   static const uint32_t maxSettings = 5;
     874             :   static const uint32_t prioritySize = 5 * (kFrameHeaderBytes + 5);
     875             :   static const uint32_t maxDataLen = 24 + kFrameHeaderBytes + maxSettings * 6 + 13 + prioritySize;
     876           0 :   char *packet = EnsureOutputBuffer(maxDataLen);
     877           0 :   memcpy(packet, kMagicHello, 24);
     878           0 :   mOutputQueueUsed += 24;
     879           0 :   LogIO(this, nullptr, "Magic Connection Header", packet, 24);
     880             : 
     881           0 :   packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
     882           0 :   memset(packet, 0, maxDataLen - 24);
     883             : 
     884             :   // frame header will be filled in after we know how long the frame is
     885           0 :   uint8_t numberOfEntries = 0;
     886             : 
     887             :   // entries need to be listed in order by ID
     888             :   // 1st entry is bytes 9 to 14
     889             :   // 2nd entry is bytes 15 to 20
     890             :   // 3rd entry is bytes 21 to 26
     891             :   // 4th entry is bytes 27 to 32
     892             :   // 5th entry is bytes 33 to 38
     893             : 
     894             :   // Let the other endpoint know about our default HPACK decompress table size
     895           0 :   uint32_t maxHpackBufferSize = gHttpHandler->DefaultHpackBuffer();
     896           0 :   mDecompressor.SetInitialMaxBufferSize(maxHpackBufferSize);
     897           0 :   NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_HEADER_TABLE_SIZE);
     898           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, maxHpackBufferSize);
     899           0 :   numberOfEntries++;
     900             : 
     901           0 :   if (!gHttpHandler->AllowPush()) {
     902             :     // If we don't support push then set MAX_CONCURRENT to 0 and also
     903             :     // set ENABLE_PUSH to 0
     904           0 :     NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_ENABLE_PUSH);
     905             :     // The value portion of the setting pair is already initialized to 0
     906           0 :     numberOfEntries++;
     907             : 
     908           0 :     NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_MAX_CONCURRENT);
     909             :     // The value portion of the setting pair is already initialized to 0
     910           0 :     numberOfEntries++;
     911             : 
     912           0 :     mWaitingForSettingsAck = true;
     913             :   }
     914             : 
     915             :   // Advertise the Push RWIN for the session, and on each new pull stream
     916             :   // send a window update
     917           0 :   NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_INITIAL_WINDOW);
     918           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, mPushAllowance);
     919           0 :   numberOfEntries++;
     920             : 
     921             :   // Make sure the other endpoint knows that we're sticking to the default max
     922             :   // frame size
     923           0 :   NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_MAX_FRAME_SIZE);
     924           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, kMaxFrameData);
     925           0 :   numberOfEntries++;
     926             : 
     927           0 :   MOZ_ASSERT(numberOfEntries <= maxSettings);
     928           0 :   uint32_t dataLen = 6 * numberOfEntries;
     929           0 :   CreateFrameHeader(packet, dataLen, FRAME_TYPE_SETTINGS, 0, 0);
     930           0 :   mOutputQueueUsed += kFrameHeaderBytes + dataLen;
     931             : 
     932           0 :   LogIO(this, nullptr, "Generate Settings", packet, kFrameHeaderBytes + dataLen);
     933             : 
     934             :   // now bump the local session window from 64KB
     935           0 :   uint32_t sessionWindowBump = mInitialRwin - kDefaultRwin;
     936           0 :   if (kDefaultRwin < mInitialRwin) {
     937             :     // send a window update for the session (Stream 0) for something large
     938           0 :     mLocalSessionWindow = mInitialRwin;
     939             : 
     940           0 :     packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
     941           0 :     CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
     942           0 :     mOutputQueueUsed += kFrameHeaderBytes + 4;
     943           0 :     NetworkEndian::writeUint32(packet + kFrameHeaderBytes, sessionWindowBump);
     944             : 
     945           0 :     LOG3(("Session Window increase at start of session %p %u\n",
     946             :           this, sessionWindowBump));
     947           0 :     LogIO(this, nullptr, "Session Window Bump ", packet, kFrameHeaderBytes + 4);
     948             :   }
     949             : 
     950           0 :   if (gHttpHandler->UseH2Deps() && gHttpHandler->CriticalRequestPrioritization()) {
     951           0 :     mUseH2Deps = true;
     952           0 :     MOZ_ASSERT(mNextStreamID == kLeaderGroupID);
     953           0 :     CreatePriorityNode(kLeaderGroupID, 0, 200, "leader");
     954           0 :     mNextStreamID += 2;
     955           0 :     MOZ_ASSERT(mNextStreamID == kOtherGroupID);
     956           0 :     CreatePriorityNode(kOtherGroupID, 0, 100, "other");
     957           0 :     mNextStreamID += 2;
     958           0 :     MOZ_ASSERT(mNextStreamID == kBackgroundGroupID);
     959           0 :     CreatePriorityNode(kBackgroundGroupID, 0, 0, "background");
     960           0 :     mNextStreamID += 2;
     961           0 :     MOZ_ASSERT(mNextStreamID == kSpeculativeGroupID);
     962           0 :     CreatePriorityNode(kSpeculativeGroupID, kBackgroundGroupID, 0, "speculative");
     963           0 :     mNextStreamID += 2;
     964           0 :     MOZ_ASSERT(mNextStreamID == kFollowerGroupID);
     965           0 :     CreatePriorityNode(kFollowerGroupID, kLeaderGroupID, 0, "follower");
     966           0 :     mNextStreamID += 2;
     967           0 :     MOZ_ASSERT(mNextStreamID == kUrgentStartGroupID);
     968           0 :     CreatePriorityNode(kUrgentStartGroupID, 0, 240, "urgentStart");
     969           0 :     mNextStreamID += 2;
     970             :     // Hey, you! YES YOU! If you add/remove any groups here, you almost
     971             :     // certainly need to change the lookup of the stream/ID hash in
     972             :     // Http2Session::OnTransportStatus. Yeah, that's right. YOU!
     973             :   }
     974             : 
     975           0 :   FlushOutputQueue();
     976           0 : }
     977             : 
     978             : void
     979           0 : Http2Session::CreatePriorityNode(uint32_t streamID, uint32_t dependsOn, uint8_t weight,
     980             :                                  const char *label)
     981             : {
     982           0 :   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
     983           0 :   CreateFrameHeader(packet, 5, FRAME_TYPE_PRIORITY, 0, streamID);
     984           0 :   mOutputQueueUsed += kFrameHeaderBytes + 5;
     985           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes, dependsOn); // depends on
     986           0 :   packet[kFrameHeaderBytes + 4] = weight; // weight
     987             : 
     988           0 :   LOG3(("Http2Session %p generate Priority Frame 0x%X depends on 0x%X "
     989             :         "weight %d for %s class\n", this, streamID, dependsOn, weight, label));
     990           0 :   LogIO(this, nullptr, "Priority dep node", packet, kFrameHeaderBytes + 5);
     991           0 : }
     992             : 
     993             : // perform a bunch of integrity checks on the stream.
     994             : // returns true if passed, false (plus LOG and ABORT) if failed.
     995             : bool
     996           0 : Http2Session::VerifyStream(Http2Stream *aStream, uint32_t aOptionalID = 0)
     997             : {
     998             :   // This is annoying, but at least it is O(1)
     999           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1000             : 
    1001             : #ifndef DEBUG
    1002             :   // Only do the real verification in debug builds
    1003             :   return true;
    1004             : #else //DEBUG
    1005             : 
    1006           0 :   if (!aStream)
    1007           0 :     return true;
    1008             : 
    1009           0 :   uint32_t test = 0;
    1010             : 
    1011             :   do {
    1012           0 :     if (aStream->StreamID() == kDeadStreamID)
    1013           0 :       break;
    1014             : 
    1015           0 :     nsAHttpTransaction *trans = aStream->Transaction();
    1016             : 
    1017           0 :     test++;
    1018           0 :     if (!trans)
    1019           0 :       break;
    1020             : 
    1021           0 :     test++;
    1022           0 :     if (mStreamTransactionHash.Get(trans) != aStream)
    1023           0 :       break;
    1024             : 
    1025           0 :     if (aStream->StreamID()) {
    1026           0 :       Http2Stream *idStream = mStreamIDHash.Get(aStream->StreamID());
    1027             : 
    1028           0 :       test++;
    1029           0 :       if (idStream != aStream)
    1030           0 :         break;
    1031             : 
    1032           0 :       if (aOptionalID) {
    1033           0 :         test++;
    1034           0 :         if (idStream->StreamID() != aOptionalID)
    1035           0 :           break;
    1036             :       }
    1037             :     }
    1038             : 
    1039             :     // tests passed
    1040           0 :     return true;
    1041             :   } while (0);
    1042             : 
    1043           0 :   LOG3(("Http2Session %p VerifyStream Failure %p stream->id=0x%X "
    1044             :        "optionalID=0x%X trans=%p test=%d\n",
    1045             :        this, aStream, aStream->StreamID(),
    1046             :        aOptionalID, aStream->Transaction(), test));
    1047             : 
    1048           0 :   MOZ_ASSERT(false, "VerifyStream");
    1049             :   return false;
    1050             : #endif //DEBUG
    1051             : }
    1052             : 
    1053             : void
    1054           0 : Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
    1055             :                             errorType aResetCode)
    1056             : {
    1057           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1058           0 :   LOG3(("Http2Session::CleanupStream %p %p 0x%X %" PRIX32 "\n",
    1059             :         this, aStream, aStream ? aStream->StreamID() : 0, static_cast<uint32_t>(aResult)));
    1060           0 :   if (!aStream) {
    1061           0 :     return;
    1062             :   }
    1063             : 
    1064           0 :   if (aStream->DeferCleanup(aResult)) {
    1065           0 :     LOG3(("Http2Session::CleanupStream 0x%X deferred\n", aStream->StreamID()));
    1066           0 :     return;
    1067             :   }
    1068             : 
    1069           0 :   if (!VerifyStream(aStream)) {
    1070           0 :     LOG3(("Http2Session::CleanupStream failed to verify stream\n"));
    1071           0 :     return;
    1072             :   }
    1073             : 
    1074           0 :   Http2PushedStream *pushSource = aStream->PushSource();
    1075           0 :   if (pushSource) {
    1076             :     // aStream is a synthetic  attached to an even push
    1077           0 :     MOZ_ASSERT(pushSource->GetConsumerStream() == aStream);
    1078           0 :     MOZ_ASSERT(!aStream->StreamID());
    1079           0 :     MOZ_ASSERT(!(pushSource->StreamID() & 0x1));
    1080           0 :     pushSource->SetConsumerStream(nullptr);
    1081             :   }
    1082             : 
    1083             :   // don't reset a stream that has recevied a fin or rst
    1084           0 :   if (!aStream->RecvdFin() && !aStream->RecvdReset() && aStream->StreamID() &&
    1085           0 :       !(mInputFrameFinal && (aStream == mInputFrameDataStream))) { // !(recvdfin with mark pending)
    1086           0 :     LOG3(("Stream 0x%X had not processed recv FIN, sending RST code %X\n", aStream->StreamID(), aResetCode));
    1087           0 :     GenerateRstStream(aResetCode, aStream->StreamID());
    1088             :   }
    1089             : 
    1090           0 :   CloseStream(aStream, aResult);
    1091             : 
    1092             :   // Remove the stream from the ID hash table and, if an even id, the pushed
    1093             :   // table too.
    1094           0 :   uint32_t id = aStream->StreamID();
    1095           0 :   if (id > 0) {
    1096           0 :     mStreamIDHash.Remove(id);
    1097           0 :     if (!(id & 1)) {
    1098           0 :       mPushedStreams.RemoveElement(aStream);
    1099           0 :       Http2PushedStream *pushStream = static_cast<Http2PushedStream *>(aStream);
    1100           0 :       nsAutoCString hashKey;
    1101           0 :       DebugOnly<bool> rv = pushStream->GetHashKey(hashKey);
    1102           0 :       MOZ_ASSERT(rv);
    1103           0 :       nsIRequestContext *requestContext = aStream->RequestContext();
    1104           0 :       if (requestContext) {
    1105           0 :         SpdyPushCache *cache = nullptr;
    1106           0 :         requestContext->GetSpdyPushCache(&cache);
    1107           0 :         if (cache) {
    1108             :           // Make sure the id of the stream in the push cache is the same
    1109             :           // as the id of the stream we're cleaning up! See bug 1368080.
    1110           0 :           Http2PushedStream *trash = cache->RemovePushedStreamHttp2ByID(hashKey, aStream->StreamID());
    1111           0 :           LOG3(("Http2Session::CleanupStream %p aStream=%p pushStream=%p trash=%p",
    1112             :                 this, aStream, pushStream, trash));
    1113             :         }
    1114             :       }
    1115             :     }
    1116             :   }
    1117             : 
    1118           0 :   RemoveStreamFromQueues(aStream);
    1119             : 
    1120             :   // removing from the stream transaction hash will
    1121             :   // delete the Http2Stream and drop the reference to
    1122             :   // its transaction
    1123           0 :   mStreamTransactionHash.Remove(aStream->Transaction());
    1124             : 
    1125           0 :   if (mShouldGoAway && !mStreamTransactionHash.Count())
    1126           0 :     Close(NS_OK);
    1127             : 
    1128           0 :   if (pushSource) {
    1129           0 :     pushSource->SetDeferCleanupOnSuccess(false);
    1130           0 :     CleanupStream(pushSource, aResult, aResetCode);
    1131             :   }
    1132             : }
    1133             : 
    1134             : void
    1135           0 : Http2Session::CleanupStream(uint32_t aID, nsresult aResult, errorType aResetCode)
    1136             : {
    1137           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1138           0 :   Http2Stream *stream = mStreamIDHash.Get(aID);
    1139           0 :   LOG3(("Http2Session::CleanupStream %p by ID 0x%X to stream %p\n",
    1140             :         this, aID, stream));
    1141           0 :   if (!stream) {
    1142           0 :     return;
    1143             :   }
    1144           0 :   CleanupStream(stream, aResult, aResetCode);
    1145             : }
    1146             : 
    1147           0 : static void RemoveStreamFromQueue(Http2Stream *aStream, nsDeque &queue)
    1148             : {
    1149           0 :   size_t size = queue.GetSize();
    1150           0 :   for (size_t count = 0; count < size; ++count) {
    1151           0 :     Http2Stream *stream = static_cast<Http2Stream *>(queue.PopFront());
    1152           0 :     if (stream != aStream)
    1153           0 :       queue.Push(stream);
    1154             :   }
    1155           0 : }
    1156             : 
    1157             : void
    1158           0 : Http2Session::RemoveStreamFromQueues(Http2Stream *aStream)
    1159             : {
    1160           0 :   RemoveStreamFromQueue(aStream, mReadyForWrite);
    1161           0 :   RemoveStreamFromQueue(aStream, mQueuedStreams);
    1162           0 :   RemoveStreamFromQueue(aStream, mPushesReadyForRead);
    1163           0 :   RemoveStreamFromQueue(aStream, mSlowConsumersReadyForRead);
    1164           0 : }
    1165             : 
    1166             : void
    1167           0 : Http2Session::CloseStream(Http2Stream *aStream, nsresult aResult)
    1168             : {
    1169           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1170           0 :   LOG3(("Http2Session::CloseStream %p %p 0x%x %" PRIX32 "\n",
    1171             :         this, aStream, aStream->StreamID(), static_cast<uint32_t>(aResult)));
    1172             : 
    1173           0 :   MaybeDecrementConcurrent(aStream);
    1174             : 
    1175             :   // Check if partial frame reader
    1176           0 :   if (aStream == mInputFrameDataStream) {
    1177           0 :     LOG3(("Stream had active partial read frame on close"));
    1178           0 :     ChangeDownstreamState(DISCARDING_DATA_FRAME);
    1179           0 :     mInputFrameDataStream = nullptr;
    1180             :   }
    1181             : 
    1182           0 :   RemoveStreamFromQueues(aStream);
    1183             : 
    1184           0 :   if (aStream->IsTunnel()) {
    1185           0 :     UnRegisterTunnel(aStream);
    1186             :   }
    1187             : 
    1188             :   // Send the stream the close() indication
    1189           0 :   aStream->Close(aResult);
    1190           0 : }
    1191             : 
    1192             : nsresult
    1193           0 : Http2Session::SetInputFrameDataStream(uint32_t streamID)
    1194             : {
    1195           0 :   mInputFrameDataStream = mStreamIDHash.Get(streamID);
    1196           0 :   if (VerifyStream(mInputFrameDataStream, streamID))
    1197           0 :     return NS_OK;
    1198             : 
    1199           0 :   LOG3(("Http2Session::SetInputFrameDataStream failed to verify 0x%X\n",
    1200             :        streamID));
    1201           0 :   mInputFrameDataStream = nullptr;
    1202           0 :   return NS_ERROR_UNEXPECTED;
    1203             : }
    1204             : 
    1205             : nsresult
    1206           0 : Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength)
    1207             : {
    1208           0 :   if (mInputFrameFlags & kFlag_PADDED) {
    1209           0 :     paddingLength = *reinterpret_cast<uint8_t *>(&mInputFrameBuffer[kFrameHeaderBytes]);
    1210           0 :     paddingControlBytes = 1;
    1211             :   } else {
    1212           0 :     paddingLength = 0;
    1213           0 :     paddingControlBytes = 0;
    1214             :   }
    1215             : 
    1216           0 :   if (static_cast<uint32_t>(paddingLength + paddingControlBytes) > mInputFrameDataSize) {
    1217             :     // This is fatal to the session
    1218           0 :     LOG3(("Http2Session::ParsePadding %p stream 0x%x PROTOCOL_ERROR "
    1219             :           "paddingLength %d > frame size %d\n",
    1220             :           this, mInputFrameID, paddingLength, mInputFrameDataSize));
    1221           0 :     RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
    1222             :   }
    1223             : 
    1224           0 :   return NS_OK;
    1225             : }
    1226             : 
    1227             : nsresult
    1228           0 : Http2Session::RecvHeaders(Http2Session *self)
    1229             : {
    1230           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_HEADERS ||
    1231             :              self->mInputFrameType == FRAME_TYPE_CONTINUATION);
    1232             : 
    1233           0 :   bool isContinuation = self->mExpectedHeaderID != 0;
    1234             : 
    1235             :   // If this doesn't have END_HEADERS set on it then require the next
    1236             :   // frame to be HEADERS of the same ID
    1237           0 :   bool endHeadersFlag = self->mInputFrameFlags & kFlag_END_HEADERS;
    1238             : 
    1239           0 :   if (endHeadersFlag)
    1240           0 :     self->mExpectedHeaderID = 0;
    1241             :   else
    1242           0 :     self->mExpectedHeaderID = self->mInputFrameID;
    1243             : 
    1244           0 :   uint32_t priorityLen = 0;
    1245           0 :   if (self->mInputFrameFlags & kFlag_PRIORITY) {
    1246           0 :     priorityLen = 5;
    1247             :   }
    1248           0 :   nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
    1249           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    1250             : 
    1251             :   // Find out how much padding this frame has, so we can only extract the real
    1252             :   // header data from the frame.
    1253           0 :   uint16_t paddingLength = 0;
    1254           0 :   uint8_t paddingControlBytes = 0;
    1255             : 
    1256           0 :   if (!isContinuation) {
    1257           0 :     self->mDecompressBuffer.Truncate();
    1258           0 :     rv = self->ParsePadding(paddingControlBytes, paddingLength);
    1259           0 :     if (NS_FAILED(rv)) {
    1260           0 :       return rv;
    1261             :     }
    1262             :   }
    1263             : 
    1264           0 :   LOG3(("Http2Session::RecvHeaders %p stream 0x%X priorityLen=%d stream=%p "
    1265             :         "end_stream=%d end_headers=%d priority_group=%d "
    1266             :         "paddingLength=%d padded=%d\n",
    1267             :         self, self->mInputFrameID, priorityLen, self->mInputFrameDataStream,
    1268             :         self->mInputFrameFlags & kFlag_END_STREAM,
    1269             :         self->mInputFrameFlags & kFlag_END_HEADERS,
    1270             :         self->mInputFrameFlags & kFlag_PRIORITY,
    1271             :         paddingLength,
    1272             :         self->mInputFrameFlags & kFlag_PADDED));
    1273             : 
    1274           0 :   if ((paddingControlBytes + priorityLen + paddingLength) > self->mInputFrameDataSize) {
    1275             :     // This is fatal to the session
    1276           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1277             :   }
    1278             : 
    1279           0 :   if (!self->mInputFrameDataStream) {
    1280             :     // Cannot find stream. We can continue the session, but we need to
    1281             :     // uncompress the header block to maintain the correct compression context
    1282             : 
    1283           0 :     LOG3(("Http2Session::RecvHeaders %p lookup mInputFrameID stream "
    1284             :           "0x%X failed. NextStreamID = 0x%X\n",
    1285             :           self, self->mInputFrameID, self->mNextStreamID));
    1286             : 
    1287           0 :     if (self->mInputFrameID >= self->mNextStreamID)
    1288           0 :       self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
    1289             : 
    1290           0 :     self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + priorityLen],
    1291           0 :                                    self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
    1292             : 
    1293           0 :     if (self->mInputFrameFlags & kFlag_END_HEADERS) {
    1294           0 :       rv = self->UncompressAndDiscard(false);
    1295           0 :       if (NS_FAILED(rv)) {
    1296           0 :         LOG3(("Http2Session::RecvHeaders uncompress failed\n"));
    1297             :         // this is fatal to the session
    1298           0 :         self->mGoAwayReason = COMPRESSION_ERROR;
    1299           0 :         return rv;
    1300             :       }
    1301             :     }
    1302             : 
    1303           0 :     self->ResetDownstreamState();
    1304           0 :     return NS_OK;
    1305             :   }
    1306             : 
    1307             :   // make sure this is either the first headers or a trailer
    1308           0 :   if (self->mInputFrameDataStream->AllHeadersReceived() &&
    1309           0 :       !(self->mInputFrameFlags & kFlag_END_STREAM)) {
    1310             :     // Any header block after the first that does *not* end the stream is
    1311             :     // illegal.
    1312           0 :     LOG3(("Http2Session::Illegal Extra HeaderBlock %p 0x%X\n", self, self->mInputFrameID));
    1313           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1314             :   }
    1315             : 
    1316             :   // queue up any compression bytes
    1317           0 :   self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + priorityLen],
    1318           0 :                                  self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
    1319             : 
    1320           0 :   self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
    1321           0 :   self->mLastDataReadEpoch = self->mLastReadEpoch;
    1322             : 
    1323           0 :   if (!isContinuation) {
    1324           0 :     self->mAggregatedHeaderSize = self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength;
    1325             :   } else {
    1326           0 :     self->mAggregatedHeaderSize += self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength;
    1327             :   }
    1328             : 
    1329           0 :   if (!endHeadersFlag) { // more are coming - don't process yet
    1330           0 :     self->ResetDownstreamState();
    1331           0 :     return NS_OK;
    1332             :   }
    1333             : 
    1334           0 :   if (isContinuation) {
    1335           0 :     Telemetry::Accumulate(Telemetry::SPDY_CONTINUED_HEADERS, self->mAggregatedHeaderSize);
    1336             :   }
    1337             : 
    1338           0 :   rv = self->ResponseHeadersComplete();
    1339           0 :   if (rv == NS_ERROR_ILLEGAL_VALUE) {
    1340           0 :     LOG3(("Http2Session::RecvHeaders %p PROTOCOL_ERROR detected stream 0x%X\n",
    1341             :           self, self->mInputFrameID));
    1342           0 :     self->CleanupStream(self->mInputFrameDataStream, rv, PROTOCOL_ERROR);
    1343           0 :     self->ResetDownstreamState();
    1344           0 :     rv = NS_OK;
    1345           0 :   } else if (NS_FAILED(rv)) {
    1346             :     // This is fatal to the session.
    1347           0 :     self->mGoAwayReason = COMPRESSION_ERROR;
    1348             :   }
    1349           0 :   return rv;
    1350             : }
    1351             : 
    1352             : // ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream
    1353             : // should be reset with a PROTOCOL_ERROR, NS_OK when the response headers were
    1354             : // fine, and any other error is fatal to the session.
    1355             : nsresult
    1356           0 : Http2Session::ResponseHeadersComplete()
    1357             : {
    1358           0 :   LOG3(("Http2Session::ResponseHeadersComplete %p for 0x%X fin=%d",
    1359             :         this, mInputFrameDataStream->StreamID(), mInputFrameFinal));
    1360             : 
    1361             :   // only interpret headers once, afterwards ignore as trailers
    1362           0 :   if (mInputFrameDataStream->AllHeadersReceived()) {
    1363           0 :     LOG3(("Http2Session::ResponseHeadersComplete extra headers"));
    1364           0 :     MOZ_ASSERT(mInputFrameFlags & kFlag_END_STREAM);
    1365           0 :     nsresult rv = UncompressAndDiscard(false);
    1366           0 :     if (NS_FAILED(rv)) {
    1367           0 :       LOG3(("Http2Session::ResponseHeadersComplete extra uncompress failed\n"));
    1368           0 :       return rv;
    1369             :     }
    1370           0 :     mFlatHTTPResponseHeadersOut = 0;
    1371           0 :     mFlatHTTPResponseHeaders.Truncate();
    1372           0 :     if (mInputFrameFinal) {
    1373             :       // need to process the fin
    1374           0 :       ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
    1375             :     } else {
    1376           0 :       ResetDownstreamState();
    1377             :     }
    1378             : 
    1379           0 :     return NS_OK;
    1380             :   }
    1381             : 
    1382             :   // if this turns out to be a 1xx response code we have to
    1383             :   // undo the headers received bit that we are setting here.
    1384           0 :   bool didFirstSetAllRecvd = !mInputFrameDataStream->AllHeadersReceived();
    1385           0 :   mInputFrameDataStream->SetAllHeadersReceived();
    1386             : 
    1387             :   // The stream needs to see flattened http headers
    1388             :   // Uncompressed http/2 format headers currently live in
    1389             :   // Http2Stream::mDecompressBuffer - convert that to HTTP format in
    1390             :   // mFlatHTTPResponseHeaders via ConvertHeaders()
    1391             : 
    1392             :   nsresult rv;
    1393             :   int32_t httpResponseCode; // out param to ConvertResponseHeaders
    1394           0 :   mFlatHTTPResponseHeadersOut = 0;
    1395           0 :   rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor,
    1396             :                                                      mDecompressBuffer,
    1397             :                                                      mFlatHTTPResponseHeaders,
    1398           0 :                                                      httpResponseCode);
    1399           0 :   if (rv == NS_ERROR_NET_RESET) {
    1400           0 :     LOG(("Http2Session::ResponseHeadersComplete %p ConvertResponseHeaders reset\n", this));
    1401             :     // This means the stream found connection-oriented auth. Treat this like we
    1402             :     // got a reset with HTTP_1_1_REQUIRED.
    1403           0 :     mInputFrameDataStream->Transaction()->DisableSpdy();
    1404           0 :     CleanupStream(mInputFrameDataStream, NS_ERROR_NET_RESET, CANCEL_ERROR);
    1405           0 :     ResetDownstreamState();
    1406           0 :     return NS_OK;
    1407           0 :   } else if (NS_FAILED(rv)) {
    1408           0 :     return rv;
    1409             :   }
    1410             : 
    1411             :   // allow more headers in the case of 1xx
    1412           0 :   if (((httpResponseCode / 100) == 1) && didFirstSetAllRecvd) {
    1413           0 :     mInputFrameDataStream->UnsetAllHeadersReceived();
    1414             :   }
    1415             : 
    1416           0 :   ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
    1417           0 :   return NS_OK;
    1418             : }
    1419             : 
    1420             : nsresult
    1421           0 : Http2Session::RecvPriority(Http2Session *self)
    1422             : {
    1423           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PRIORITY);
    1424             : 
    1425           0 :   if (self->mInputFrameDataSize != 5) {
    1426           0 :     LOG3(("Http2Session::RecvPriority %p wrong length data=%d\n",
    1427             :           self, self->mInputFrameDataSize));
    1428           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1429             :   }
    1430             : 
    1431           0 :   if (!self->mInputFrameID) {
    1432           0 :     LOG3(("Http2Session::RecvPriority %p stream ID of 0.\n", self));
    1433           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1434             :   }
    1435             : 
    1436           0 :   nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
    1437           0 :   if (NS_FAILED(rv))
    1438           0 :     return rv;
    1439             : 
    1440             :   uint32_t newPriorityDependency = NetworkEndian::readUint32(
    1441           0 :       self->mInputFrameBuffer.get() + kFrameHeaderBytes);
    1442           0 :   bool exclusive = !!(newPriorityDependency & 0x80000000);
    1443           0 :   newPriorityDependency &= 0x7fffffff;
    1444           0 :   uint8_t newPriorityWeight = *(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4);
    1445           0 :   if (self->mInputFrameDataStream) {
    1446           0 :     self->mInputFrameDataStream->SetPriorityDependency(newPriorityDependency,
    1447             :                                                        newPriorityWeight,
    1448           0 :                                                        exclusive);
    1449             :   }
    1450             : 
    1451           0 :   self->ResetDownstreamState();
    1452           0 :   return NS_OK;
    1453             : }
    1454             : 
    1455             : nsresult
    1456           0 : Http2Session::RecvRstStream(Http2Session *self)
    1457             : {
    1458           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_RST_STREAM);
    1459             : 
    1460           0 :   if (self->mInputFrameDataSize != 4) {
    1461           0 :     LOG3(("Http2Session::RecvRstStream %p RST_STREAM wrong length data=%d",
    1462             :           self, self->mInputFrameDataSize));
    1463           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1464             :   }
    1465             : 
    1466           0 :   if (!self->mInputFrameID) {
    1467           0 :     LOG3(("Http2Session::RecvRstStream %p stream ID of 0.\n", self));
    1468           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1469             :   }
    1470             : 
    1471           0 :   self->mDownstreamRstReason = NetworkEndian::readUint32(
    1472           0 :       self->mInputFrameBuffer.get() + kFrameHeaderBytes);
    1473             : 
    1474           0 :   LOG3(("Http2Session::RecvRstStream %p RST_STREAM Reason Code %u ID %x\n",
    1475             :         self, self->mDownstreamRstReason, self->mInputFrameID));
    1476             : 
    1477           0 :   DebugOnly<nsresult> rv = self->SetInputFrameDataStream(self->mInputFrameID);
    1478           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    1479           0 :   if (!self->mInputFrameDataStream) {
    1480             :     // if we can't find the stream just ignore it (4.2 closed)
    1481           0 :     self->ResetDownstreamState();
    1482           0 :     return NS_OK;
    1483             :   }
    1484             : 
    1485           0 :   self->mInputFrameDataStream->SetRecvdReset(true);
    1486           0 :   self->MaybeDecrementConcurrent(self->mInputFrameDataStream);
    1487           0 :   self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
    1488           0 :   return NS_OK;
    1489             : }
    1490             : 
    1491             : nsresult
    1492           0 : Http2Session::RecvSettings(Http2Session *self)
    1493             : {
    1494           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_SETTINGS);
    1495             : 
    1496           0 :   if (self->mInputFrameID) {
    1497           0 :     LOG3(("Http2Session::RecvSettings %p needs stream ID of 0. 0x%X\n",
    1498             :           self, self->mInputFrameID));
    1499           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1500             :   }
    1501             : 
    1502           0 :   if (self->mInputFrameDataSize % 6) {
    1503             :     // Number of Settings is determined by dividing by each 6 byte setting
    1504             :     // entry. So the payload must be a multiple of 6.
    1505           0 :     LOG3(("Http2Session::RecvSettings %p SETTINGS wrong length data=%d",
    1506             :           self, self->mInputFrameDataSize));
    1507           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1508             :   }
    1509             : 
    1510           0 :   self->mReceivedSettings = true;
    1511             : 
    1512           0 :   uint32_t numEntries = self->mInputFrameDataSize / 6;
    1513           0 :   LOG3(("Http2Session::RecvSettings %p SETTINGS Control Frame "
    1514             :         "with %d entries ack=%X", self, numEntries,
    1515             :         self->mInputFrameFlags & kFlag_ACK));
    1516             : 
    1517           0 :   if ((self->mInputFrameFlags & kFlag_ACK) && self->mInputFrameDataSize) {
    1518           0 :     LOG3(("Http2Session::RecvSettings %p ACK with non zero payload is err\n", self));
    1519           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1520             :   }
    1521             : 
    1522           0 :   for (uint32_t index = 0; index < numEntries; ++index) {
    1523             :     uint8_t *setting = reinterpret_cast<uint8_t *>
    1524           0 :       (self->mInputFrameBuffer.get()) + kFrameHeaderBytes + index * 6;
    1525             : 
    1526           0 :     uint16_t id = NetworkEndian::readUint16(setting);
    1527           0 :     uint32_t value = NetworkEndian::readUint32(setting + 2);
    1528           0 :     LOG3(("Settings ID %u, Value %u", id, value));
    1529             : 
    1530           0 :     switch (id)
    1531             :     {
    1532             :     case SETTINGS_TYPE_HEADER_TABLE_SIZE:
    1533           0 :       LOG3(("Compression header table setting received: %d\n", value));
    1534           0 :       self->mCompressor.SetMaxBufferSize(value);
    1535           0 :       break;
    1536             : 
    1537             :     case SETTINGS_TYPE_ENABLE_PUSH:
    1538           0 :       LOG3(("Client received an ENABLE Push SETTING. Odd.\n"));
    1539             :       // nop
    1540           0 :       break;
    1541             : 
    1542             :     case SETTINGS_TYPE_MAX_CONCURRENT:
    1543           0 :       self->mMaxConcurrent = value;
    1544           0 :       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
    1545           0 :       self->ProcessPending();
    1546           0 :       break;
    1547             : 
    1548             :     case SETTINGS_TYPE_INITIAL_WINDOW:
    1549             :       {
    1550           0 :         Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
    1551           0 :         int32_t delta = value - self->mServerInitialStreamWindow;
    1552           0 :         self->mServerInitialStreamWindow = value;
    1553             : 
    1554             :         // SETTINGS only adjusts stream windows. Leave the session window alone.
    1555             :         // We need to add the delta to all open streams (delta can be negative)
    1556           0 :         for (auto iter = self->mStreamTransactionHash.Iter();
    1557           0 :              !iter.Done();
    1558           0 :              iter.Next()) {
    1559           0 :           iter.Data()->UpdateServerReceiveWindow(delta);
    1560             :         }
    1561             :       }
    1562           0 :       break;
    1563             : 
    1564             :     case SETTINGS_TYPE_MAX_FRAME_SIZE:
    1565             :       {
    1566           0 :         if ((value < kMaxFrameData) || (value >= 0x01000000)) {
    1567           0 :           LOG3(("Received invalid max frame size 0x%X", value));
    1568           0 :           RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1569             :         }
    1570             :         // We stick to the default for simplicity's sake, so nothing to change
    1571             :       }
    1572           0 :       break;
    1573             : 
    1574             :     default:
    1575           0 :       break;
    1576             :     }
    1577             :   }
    1578             : 
    1579           0 :   self->ResetDownstreamState();
    1580             : 
    1581           0 :   if (!(self->mInputFrameFlags & kFlag_ACK)) {
    1582           0 :     self->GenerateSettingsAck();
    1583           0 :   } else if (self->mWaitingForSettingsAck) {
    1584           0 :     self->mGoAwayOnPush = true;
    1585             :   }
    1586             : 
    1587           0 :   return NS_OK;
    1588             : }
    1589             : 
    1590             : nsresult
    1591           0 : Http2Session::RecvPushPromise(Http2Session *self)
    1592             : {
    1593           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PUSH_PROMISE ||
    1594             :              self->mInputFrameType == FRAME_TYPE_CONTINUATION);
    1595             : 
    1596             :   // Find out how much padding this frame has, so we can only extract the real
    1597             :   // header data from the frame.
    1598           0 :   uint16_t paddingLength = 0;
    1599           0 :   uint8_t paddingControlBytes = 0;
    1600             : 
    1601             :   // If this doesn't have END_PUSH_PROMISE set on it then require the next
    1602             :   // frame to be PUSH_PROMISE of the same ID
    1603             :   uint32_t promiseLen;
    1604             :   uint32_t promisedID;
    1605             : 
    1606           0 :   if (self->mExpectedPushPromiseID) {
    1607           0 :     promiseLen = 0; // really a continuation frame
    1608           0 :     promisedID = self->mContinuedPromiseStream;
    1609             :   } else {
    1610           0 :     self->mDecompressBuffer.Truncate();
    1611           0 :     nsresult rv = self->ParsePadding(paddingControlBytes, paddingLength);
    1612           0 :     if (NS_FAILED(rv)) {
    1613           0 :       return rv;
    1614             :     }
    1615           0 :     promiseLen = 4;
    1616             :     promisedID = NetworkEndian::readUint32(
    1617           0 :         self->mInputFrameBuffer.get() + kFrameHeaderBytes + paddingControlBytes);
    1618           0 :     promisedID &= 0x7fffffff;
    1619           0 :     if (promisedID <= self->mLastPushedID) {
    1620           0 :       LOG3(("Http2Session::RecvPushPromise %p ID too low %u expected > %u.\n",
    1621             :             self, promisedID, self->mLastPushedID));
    1622           0 :       RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1623             :     }
    1624           0 :     self->mLastPushedID = promisedID;
    1625             :   }
    1626             : 
    1627           0 :   uint32_t associatedID = self->mInputFrameID;
    1628             : 
    1629           0 :   if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
    1630           0 :     self->mExpectedPushPromiseID = 0;
    1631           0 :     self->mContinuedPromiseStream = 0;
    1632             :   } else {
    1633           0 :     self->mExpectedPushPromiseID = self->mInputFrameID;
    1634           0 :     self->mContinuedPromiseStream = promisedID;
    1635             :   }
    1636             : 
    1637           0 :   if ((paddingControlBytes + promiseLen + paddingLength) > self->mInputFrameDataSize) {
    1638             :     // This is fatal to the session
    1639           0 :     LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X "
    1640             :           "PROTOCOL_ERROR extra %d > frame size %d\n",
    1641             :           self, promisedID, associatedID, (paddingControlBytes + promiseLen + paddingLength),
    1642             :           self->mInputFrameDataSize));
    1643           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1644             :   }
    1645             : 
    1646           0 :   LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X "
    1647             :         "paddingLength %d padded %d\n",
    1648             :         self, promisedID, associatedID, paddingLength,
    1649             :         self->mInputFrameFlags & kFlag_PADDED));
    1650             : 
    1651           0 :   if (!associatedID || !promisedID || (promisedID & 1)) {
    1652           0 :     LOG3(("Http2Session::RecvPushPromise %p ID invalid.\n", self));
    1653           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1654             :   }
    1655             : 
    1656             :   // confirm associated-to
    1657           0 :   nsresult rv = self->SetInputFrameDataStream(associatedID);
    1658           0 :   if (NS_FAILED(rv))
    1659           0 :     return rv;
    1660             : 
    1661           0 :   Http2Stream *associatedStream = self->mInputFrameDataStream;
    1662           0 :   ++(self->mServerPushedResources);
    1663             : 
    1664             :   // Anytime we start using the high bit of stream ID (either client or server)
    1665             :   // begin to migrate to a new session.
    1666           0 :   if (promisedID >= kMaxStreamID)
    1667           0 :     self->mShouldGoAway = true;
    1668             : 
    1669           0 :   bool resetStream = true;
    1670           0 :   SpdyPushCache *cache = nullptr;
    1671             : 
    1672           0 :   if (self->mShouldGoAway && !Http2PushedStream::TestOnPush(associatedStream)) {
    1673           0 :     LOG3(("Http2Session::RecvPushPromise %p cache push while in GoAway "
    1674             :           "mode refused.\n", self));
    1675           0 :     self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
    1676           0 :   } else if (!gHttpHandler->AllowPush()) {
    1677             :     // ENABLE_PUSH and MAX_CONCURRENT_STREAMS of 0 in settings disabled push
    1678           0 :     LOG3(("Http2Session::RecvPushPromise Push Recevied when Disabled\n"));
    1679           0 :     if (self->mGoAwayOnPush) {
    1680           0 :       LOG3(("Http2Session::RecvPushPromise sending GOAWAY"));
    1681           0 :       RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1682             :     }
    1683           0 :     self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
    1684           0 :   } else if (!(associatedID & 1)) {
    1685           0 :     LOG3(("Http2Session::RecvPushPromise %p assocated=0x%X on pushed (even) stream not allowed\n",
    1686             :           self, associatedID));
    1687           0 :     self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
    1688           0 :   } else if (!associatedStream) {
    1689           0 :     LOG3(("Http2Session::RecvPushPromise %p lookup associated ID failed.\n", self));
    1690           0 :     self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
    1691             :   } else {
    1692           0 :     nsIRequestContext *requestContext = associatedStream->RequestContext();
    1693           0 :     if (requestContext) {
    1694           0 :       requestContext->GetSpdyPushCache(&cache);
    1695           0 :       if (!cache) {
    1696           0 :         cache = new SpdyPushCache();
    1697           0 :         if (!cache || NS_FAILED(requestContext->SetSpdyPushCache(cache))) {
    1698           0 :           delete cache;
    1699           0 :           cache = nullptr;
    1700             :         }
    1701             :       }
    1702             :     }
    1703           0 :     if (!cache) {
    1704             :       // this is unexpected, but we can handle it just by refusing the push
    1705           0 :       LOG3(("Http2Session::RecvPushPromise Push Recevied without push cache\n"));
    1706           0 :       self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
    1707             :     } else {
    1708           0 :       resetStream = false;
    1709             :     }
    1710             :   }
    1711             : 
    1712           0 :   if (resetStream) {
    1713             :     // Need to decompress the headers even though we aren't using them yet in
    1714             :     // order to keep the compression context consistent for other frames
    1715           0 :     self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + promiseLen],
    1716           0 :                                    self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
    1717           0 :     if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
    1718           0 :       rv = self->UncompressAndDiscard(true);
    1719           0 :       if (NS_FAILED(rv)) {
    1720           0 :         LOG3(("Http2Session::RecvPushPromise uncompress failed\n"));
    1721           0 :         self->mGoAwayReason = COMPRESSION_ERROR;
    1722           0 :         return rv;
    1723             :       }
    1724             :     }
    1725           0 :     self->ResetDownstreamState();
    1726           0 :     return NS_OK;
    1727             :   }
    1728             : 
    1729           0 :   self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + promiseLen],
    1730           0 :                                  self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
    1731             : 
    1732           0 :   if (self->mInputFrameType != FRAME_TYPE_CONTINUATION) {
    1733           0 :     self->mAggregatedHeaderSize = self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength;
    1734             :   } else {
    1735           0 :     self->mAggregatedHeaderSize += self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength;
    1736             :   }
    1737             : 
    1738           0 :   if (!(self->mInputFrameFlags & kFlag_END_PUSH_PROMISE)) {
    1739           0 :     LOG3(("Http2Session::RecvPushPromise not finishing processing for multi-frame push\n"));
    1740           0 :     self->ResetDownstreamState();
    1741           0 :     return NS_OK;
    1742             :   }
    1743             : 
    1744           0 :   if (self->mInputFrameType == FRAME_TYPE_CONTINUATION) {
    1745           0 :     Telemetry::Accumulate(Telemetry::SPDY_CONTINUED_HEADERS, self->mAggregatedHeaderSize);
    1746             :   }
    1747             : 
    1748             :   // Create the buffering transaction and push stream
    1749             :   RefPtr<Http2PushTransactionBuffer> transactionBuffer =
    1750           0 :     new Http2PushTransactionBuffer();
    1751           0 :   transactionBuffer->SetConnection(self);
    1752             :   Http2PushedStream *pushedStream =
    1753           0 :     new Http2PushedStream(transactionBuffer, self, associatedStream, promisedID);
    1754             : 
    1755           0 :   rv = pushedStream->ConvertPushHeaders(&self->mDecompressor,
    1756             :                                         self->mDecompressBuffer,
    1757           0 :                                         pushedStream->GetRequestString());
    1758             : 
    1759           0 :   if (rv == NS_ERROR_NOT_IMPLEMENTED) {
    1760           0 :     LOG3(("Http2Session::PushPromise Semantics not Implemented\n"));
    1761           0 :     self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
    1762           0 :     delete pushedStream;
    1763           0 :     self->ResetDownstreamState();
    1764           0 :     return NS_OK;
    1765             :   }
    1766             : 
    1767           0 :   if (rv == NS_ERROR_ILLEGAL_VALUE) {
    1768             :     // This means the decompression completed ok, but there was a problem with
    1769             :     // the decoded headers. Reset the stream and go away.
    1770           0 :     self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
    1771           0 :     delete pushedStream;
    1772           0 :     self->ResetDownstreamState();
    1773           0 :     return NS_OK;
    1774           0 :   } else if (NS_FAILED(rv)) {
    1775             :     // This is fatal to the session.
    1776           0 :     self->mGoAwayReason = COMPRESSION_ERROR;
    1777           0 :     return rv;
    1778             :   }
    1779             : 
    1780             :   // Ownership of the pushed stream is by the transaction hash, just as it
    1781             :   // is for a client initiated stream. Errors that aren't fatal to the
    1782             :   // whole session must call cleanupStream() after this point in order
    1783             :   // to remove the stream from that hash.
    1784           0 :   self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
    1785           0 :   self->mPushedStreams.AppendElement(pushedStream);
    1786             : 
    1787           0 :   if (self->RegisterStreamID(pushedStream, promisedID) == kDeadStreamID) {
    1788           0 :     LOG3(("Http2Session::RecvPushPromise registerstreamid failed\n"));
    1789           0 :     self->mGoAwayReason = INTERNAL_ERROR;
    1790           0 :     return NS_ERROR_FAILURE;
    1791             :   }
    1792             : 
    1793           0 :   if (promisedID > self->mOutgoingGoAwayID)
    1794           0 :     self->mOutgoingGoAwayID = promisedID;
    1795             : 
    1796             :   // Fake the request side of the pushed HTTP transaction. Sets up hash
    1797             :   // key and origin
    1798             :   uint32_t notUsed;
    1799           0 :   Unused << pushedStream->ReadSegments(nullptr, 1, &notUsed);
    1800             : 
    1801           0 :   nsAutoCString key;
    1802           0 :   if (!pushedStream->GetHashKey(key)) {
    1803           0 :     LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n"));
    1804           0 :     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR);
    1805           0 :     self->ResetDownstreamState();
    1806           0 :     return NS_OK;
    1807             :   }
    1808             : 
    1809             :   // does the pushed origin belong on this connection?
    1810           0 :   LOG3(("Http2Session::RecvPushPromise %p origin check %s", self,
    1811             :         pushedStream->Origin().get()));
    1812           0 :   RefPtr<nsStandardURL> pushedURL;
    1813           0 :   rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
    1814           0 :   nsAutoCString pushedHostName;
    1815           0 :   int32_t pushedPort = -1;
    1816           0 :   if (NS_SUCCEEDED(rv)) {
    1817           0 :     rv = pushedURL->GetHost(pushedHostName);
    1818             :   }
    1819           0 :   if (NS_SUCCEEDED(rv)) {
    1820           0 :     rv = pushedURL->GetPort(&pushedPort);
    1821             :   }
    1822           0 :   if (NS_FAILED(rv) ||
    1823           0 :       !self->TestJoinConnection(pushedHostName, pushedPort)) {
    1824           0 :     LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin %s\n",
    1825             :           self, pushedStream->Origin().get()));
    1826           0 :     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
    1827           0 :     self->ResetDownstreamState();
    1828           0 :     return NS_OK;
    1829             :   }
    1830             : 
    1831           0 :   if (pushedStream->TryOnPush()) {
    1832           0 :     LOG3(("Http2Session::RecvPushPromise %p channel implements nsIHttpPushListener "
    1833             :           "stream %p will not be placed into session cache.\n", self, pushedStream));
    1834             :   } else {
    1835           0 :     LOG3(("Http2Session::RecvPushPromise %p place stream into session cache\n", self));
    1836           0 :     if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) {
    1837             :       // This only happens if they've already pushed us this item.
    1838           0 :       LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n"));
    1839           0 :       self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
    1840           0 :       self->ResetDownstreamState();
    1841           0 :       return NS_OK;
    1842             :     }
    1843             :   }
    1844             : 
    1845           0 :   pushedStream->SetHTTPState(Http2Stream::RESERVED_BY_REMOTE);
    1846             :   static_assert(Http2Stream::kWorstPriority >= 0,
    1847             :                 "kWorstPriority out of range");
    1848             :   uint8_t priorityWeight = (nsISupportsPriority::PRIORITY_LOWEST + 1) -
    1849           0 :     (Http2Stream::kWorstPriority - Http2Stream::kNormalPriority);
    1850           0 :   pushedStream->SetPriority(Http2Stream::kWorstPriority);
    1851           0 :   self->GeneratePriority(promisedID, priorityWeight);
    1852           0 :   self->ResetDownstreamState();
    1853           0 :   return NS_OK;
    1854             : }
    1855             : 
    1856             : nsresult
    1857           0 : Http2Session::RecvPing(Http2Session *self)
    1858             : {
    1859           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PING);
    1860             : 
    1861           0 :   LOG3(("Http2Session::RecvPing %p PING Flags 0x%X.", self,
    1862             :         self->mInputFrameFlags));
    1863             : 
    1864           0 :   if (self->mInputFrameDataSize != 8) {
    1865           0 :     LOG3(("Http2Session::RecvPing %p PING had wrong amount of data %d",
    1866             :           self, self->mInputFrameDataSize));
    1867           0 :     RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR);
    1868             :   }
    1869             : 
    1870           0 :   if (self->mInputFrameID) {
    1871           0 :     LOG3(("Http2Session::RecvPing %p PING needs stream ID of 0. 0x%X\n",
    1872             :           self, self->mInputFrameID));
    1873           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1874             :   }
    1875             : 
    1876           0 :   if (self->mInputFrameFlags & kFlag_ACK) {
    1877             :     // presumably a reply to our timeout ping.. don't reply to it
    1878           0 :     self->mPingSentEpoch = 0;
    1879             :   } else {
    1880             :     // reply with a ack'd ping
    1881           0 :     self->GeneratePing(true);
    1882             :   }
    1883             : 
    1884           0 :   self->ResetDownstreamState();
    1885           0 :   return NS_OK;
    1886             : }
    1887             : 
    1888             : nsresult
    1889           0 : Http2Session::RecvGoAway(Http2Session *self)
    1890             : {
    1891           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_GOAWAY);
    1892             : 
    1893           0 :   if (self->mInputFrameDataSize < 8) {
    1894             :     // data > 8 is an opaque token that we can't interpret. NSPR Logs will
    1895             :     // have the hex of all packets so there is no point in separately logging.
    1896           0 :     LOG3(("Http2Session::RecvGoAway %p GOAWAY had wrong amount of data %d",
    1897             :           self, self->mInputFrameDataSize));
    1898           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1899             :   }
    1900             : 
    1901           0 :   if (self->mInputFrameID) {
    1902           0 :     LOG3(("Http2Session::RecvGoAway %p GOAWAY had non zero stream ID 0x%X\n",
    1903             :           self, self->mInputFrameID));
    1904           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1905             :   }
    1906             : 
    1907           0 :   self->mShouldGoAway = true;
    1908           0 :   self->mGoAwayID = NetworkEndian::readUint32(
    1909           0 :       self->mInputFrameBuffer.get() + kFrameHeaderBytes);
    1910           0 :   self->mGoAwayID &= 0x7fffffff;
    1911           0 :   self->mCleanShutdown = true;
    1912           0 :   self->mPeerGoAwayReason = NetworkEndian::readUint32(
    1913           0 :       self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4);
    1914             : 
    1915             :   // Find streams greater than the last-good ID and mark them for deletion
    1916             :   // in the mGoAwayStreamsToRestart queue. The underlying transaction can be
    1917             :   // restarted.
    1918           0 :   for (auto iter = self->mStreamTransactionHash.Iter();
    1919           0 :        !iter.Done();
    1920           0 :        iter.Next()) {
    1921             :     // these streams were not processed by the server and can be restarted.
    1922             :     // Do that after the enumerator completes to avoid the risk of
    1923             :     // a restart event re-entrantly modifying this hash. Be sure not to restart
    1924             :     // a pushed (even numbered) stream
    1925           0 :     nsAutoPtr<Http2Stream>& stream = iter.Data();
    1926           0 :     if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) ||
    1927           0 :         !stream->HasRegisteredID()) {
    1928           0 :       self->mGoAwayStreamsToRestart.Push(stream);
    1929             :     }
    1930             :   }
    1931             : 
    1932             :   // Process the streams marked for deletion and restart.
    1933           0 :   size_t size = self->mGoAwayStreamsToRestart.GetSize();
    1934           0 :   for (size_t count = 0; count < size; ++count) {
    1935             :     Http2Stream *stream =
    1936           0 :       static_cast<Http2Stream *>(self->mGoAwayStreamsToRestart.PopFront());
    1937             : 
    1938           0 :     if (self->mPeerGoAwayReason == HTTP_1_1_REQUIRED) {
    1939           0 :       stream->Transaction()->DisableSpdy();
    1940             :     }
    1941           0 :     self->CloseStream(stream, NS_ERROR_NET_RESET);
    1942           0 :     if (stream->HasRegisteredID())
    1943           0 :       self->mStreamIDHash.Remove(stream->StreamID());
    1944           0 :     self->mStreamTransactionHash.Remove(stream->Transaction());
    1945             :   }
    1946             : 
    1947             :   // Queued streams can also be deleted from this session and restarted
    1948             :   // in another one. (they were never sent on the network so they implicitly
    1949             :   // are not covered by the last-good id.
    1950           0 :   size = self->mQueuedStreams.GetSize();
    1951           0 :   for (size_t count = 0; count < size; ++count) {
    1952             :     Http2Stream *stream =
    1953           0 :       static_cast<Http2Stream *>(self->mQueuedStreams.PopFront());
    1954           0 :     MOZ_ASSERT(stream->Queued());
    1955           0 :     stream->SetQueued(false);
    1956           0 :     if (self->mPeerGoAwayReason == HTTP_1_1_REQUIRED) {
    1957           0 :       stream->Transaction()->DisableSpdy();
    1958             :     }
    1959           0 :     self->CloseStream(stream, NS_ERROR_NET_RESET);
    1960           0 :     self->mStreamTransactionHash.Remove(stream->Transaction());
    1961             :   }
    1962             : 
    1963           0 :   LOG3(("Http2Session::RecvGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X "
    1964             :         "live streams=%d\n", self, self->mGoAwayID, self->mPeerGoAwayReason,
    1965             :         self->mStreamTransactionHash.Count()));
    1966             : 
    1967           0 :   self->ResetDownstreamState();
    1968           0 :   return NS_OK;
    1969             : }
    1970             : 
    1971             : nsresult
    1972           0 : Http2Session::RecvWindowUpdate(Http2Session *self)
    1973             : {
    1974           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_WINDOW_UPDATE);
    1975             : 
    1976           0 :   if (self->mInputFrameDataSize != 4) {
    1977           0 :     LOG3(("Http2Session::RecvWindowUpdate %p Window Update wrong length %d\n",
    1978             :           self, self->mInputFrameDataSize));
    1979           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    1980             :   }
    1981             : 
    1982             :   uint32_t delta = NetworkEndian::readUint32(
    1983           0 :       self->mInputFrameBuffer.get() + kFrameHeaderBytes);
    1984           0 :   delta &= 0x7fffffff;
    1985             : 
    1986           0 :   LOG3(("Http2Session::RecvWindowUpdate %p len=%d Stream 0x%X.\n",
    1987             :         self, delta, self->mInputFrameID));
    1988             : 
    1989           0 :   if (self->mInputFrameID) { // stream window
    1990           0 :     nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
    1991           0 :     if (NS_FAILED(rv))
    1992           0 :       return rv;
    1993             : 
    1994           0 :     if (!self->mInputFrameDataStream) {
    1995           0 :       LOG3(("Http2Session::RecvWindowUpdate %p lookup streamID 0x%X failed.\n",
    1996             :             self, self->mInputFrameID));
    1997             :       // only resest the session if the ID is one we haven't ever opened
    1998           0 :       if (self->mInputFrameID >= self->mNextStreamID)
    1999           0 :         self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
    2000           0 :       self->ResetDownstreamState();
    2001           0 :       return NS_OK;
    2002             :     }
    2003             : 
    2004           0 :     if (delta == 0) {
    2005           0 :       LOG3(("Http2Session::RecvWindowUpdate %p received 0 stream window update",
    2006             :             self));
    2007           0 :       self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
    2008           0 :                           PROTOCOL_ERROR);
    2009           0 :       self->ResetDownstreamState();
    2010           0 :       return NS_OK;
    2011             :     }
    2012             : 
    2013           0 :     int64_t oldRemoteWindow = self->mInputFrameDataStream->ServerReceiveWindow();
    2014           0 :     self->mInputFrameDataStream->UpdateServerReceiveWindow(delta);
    2015           0 :     if (self->mInputFrameDataStream->ServerReceiveWindow() >= 0x80000000) {
    2016             :       // a window cannot reach 2^31 and be in compliance. Our calculations
    2017             :       // are 64 bit safe though.
    2018           0 :       LOG3(("Http2Session::RecvWindowUpdate %p stream window "
    2019             :             "exceeds 2^31 - 1\n", self));
    2020           0 :       self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
    2021           0 :                           FLOW_CONTROL_ERROR);
    2022           0 :       self->ResetDownstreamState();
    2023           0 :       return NS_OK;
    2024             :     }
    2025             : 
    2026           0 :     LOG3(("Http2Session::RecvWindowUpdate %p stream 0x%X window "
    2027             :           "%" PRId64 " increased by %" PRIu32 " now %" PRId64 ".\n",
    2028             :           self, self->mInputFrameID, oldRemoteWindow, delta, oldRemoteWindow + delta));
    2029             : 
    2030             :   } else { // session window update
    2031           0 :     if (delta == 0) {
    2032           0 :       LOG3(("Http2Session::RecvWindowUpdate %p received 0 session window update",
    2033             :             self));
    2034           0 :       RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    2035             :     }
    2036             : 
    2037           0 :     int64_t oldRemoteWindow = self->mServerSessionWindow;
    2038           0 :     self->mServerSessionWindow += delta;
    2039             : 
    2040           0 :     if (self->mServerSessionWindow >= 0x80000000) {
    2041             :       // a window cannot reach 2^31 and be in compliance. Our calculations
    2042             :       // are 64 bit safe though.
    2043           0 :       LOG3(("Http2Session::RecvWindowUpdate %p session window "
    2044             :             "exceeds 2^31 - 1\n", self));
    2045           0 :       RETURN_SESSION_ERROR(self, FLOW_CONTROL_ERROR);
    2046             :     }
    2047             : 
    2048           0 :     if ((oldRemoteWindow <= 0) && (self->mServerSessionWindow > 0)) {
    2049           0 :       LOG3(("Http2Session::RecvWindowUpdate %p restart session window\n",
    2050             :             self));
    2051           0 :       for (auto iter = self->mStreamTransactionHash.Iter();
    2052           0 :            !iter.Done();
    2053           0 :            iter.Next()) {
    2054           0 :         MOZ_ASSERT(self->mServerSessionWindow > 0);
    2055             : 
    2056           0 :         nsAutoPtr<Http2Stream>& stream = iter.Data();
    2057           0 :         if (!stream->BlockedOnRwin() || stream->ServerReceiveWindow() <= 0) {
    2058           0 :           continue;
    2059             :         }
    2060             : 
    2061           0 :         self->mReadyForWrite.Push(stream);
    2062           0 :         self->SetWriteCallbacks();
    2063             :       }
    2064             :     }
    2065           0 :     LOG3(("Http2Session::RecvWindowUpdate %p session window "
    2066             :           "%" PRId64 " increased by %d now %" PRId64 ".\n", self,
    2067             :           oldRemoteWindow, delta, oldRemoteWindow + delta));
    2068             :   }
    2069             : 
    2070           0 :   self->ResetDownstreamState();
    2071           0 :   return NS_OK;
    2072             : }
    2073             : 
    2074             : nsresult
    2075           0 : Http2Session::RecvContinuation(Http2Session *self)
    2076             : {
    2077           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_CONTINUATION);
    2078           0 :   MOZ_ASSERT(self->mInputFrameID);
    2079           0 :   MOZ_ASSERT(self->mExpectedPushPromiseID || self->mExpectedHeaderID);
    2080           0 :   MOZ_ASSERT(!(self->mExpectedPushPromiseID && self->mExpectedHeaderID));
    2081             : 
    2082           0 :   LOG3(("Http2Session::RecvContinuation %p Flags 0x%X id 0x%X "
    2083             :         "promise id 0x%X header id 0x%X\n",
    2084             :         self, self->mInputFrameFlags, self->mInputFrameID,
    2085             :         self->mExpectedPushPromiseID, self->mExpectedHeaderID));
    2086             : 
    2087           0 :   DebugOnly<nsresult> rv = self->SetInputFrameDataStream(self->mInputFrameID);
    2088           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2089             : 
    2090           0 :   if (!self->mInputFrameDataStream) {
    2091           0 :     LOG3(("Http2Session::RecvContination stream ID 0x%X not found.",
    2092             :           self->mInputFrameID));
    2093           0 :     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
    2094             :   }
    2095             : 
    2096             :   // continued headers
    2097           0 :   if (self->mExpectedHeaderID) {
    2098           0 :     self->mInputFrameFlags &= ~kFlag_PRIORITY;
    2099           0 :     return RecvHeaders(self);
    2100             :   }
    2101             : 
    2102             :   // continued push promise
    2103           0 :   if (self->mInputFrameFlags & kFlag_END_HEADERS) {
    2104           0 :     self->mInputFrameFlags &= ~kFlag_END_HEADERS;
    2105           0 :     self->mInputFrameFlags |= kFlag_END_PUSH_PROMISE;
    2106             :   }
    2107           0 :   return RecvPushPromise(self);
    2108             : }
    2109             : 
    2110           0 : class UpdateAltSvcEvent : public Runnable
    2111             : {
    2112             : public:
    2113           0 :   UpdateAltSvcEvent(const nsCString& header,
    2114             :                     const nsCString& aOrigin,
    2115             :                     nsHttpConnectionInfo* aCI,
    2116             :                     nsIInterfaceRequestor* callbacks)
    2117           0 :     : Runnable("net::UpdateAltSvcEvent")
    2118             :     , mHeader(header)
    2119             :     , mOrigin(aOrigin)
    2120             :     , mCI(aCI)
    2121           0 :     , mCallbacks(callbacks)
    2122             :   {
    2123           0 :   }
    2124             : 
    2125           0 :   NS_IMETHOD Run() override
    2126             :   {
    2127           0 :     MOZ_ASSERT(NS_IsMainThread());
    2128             : 
    2129           0 :     nsCString originScheme;
    2130           0 :     nsCString originHost;
    2131           0 :     int32_t originPort = -1;
    2132             : 
    2133           0 :     nsCOMPtr<nsIURI> uri;
    2134           0 :     if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), mOrigin))) {
    2135           0 :       LOG(("UpdateAltSvcEvent origin does not parse %s\n",
    2136             :            mOrigin.get()));
    2137           0 :       return NS_OK;
    2138             :     }
    2139           0 :     uri->GetScheme(originScheme);
    2140           0 :     uri->GetHost(originHost);
    2141           0 :     uri->GetPort(&originPort);
    2142             : 
    2143           0 :     AltSvcMapping::ProcessHeader(mHeader, originScheme, originHost, originPort,
    2144           0 :                                  mCI->GetUsername(), mCI->GetPrivate(), mCallbacks,
    2145           0 :                                  mCI->ProxyInfo(), 0, mCI->GetOriginAttributes());
    2146           0 :     return NS_OK;
    2147             :   }
    2148             : 
    2149             : private:
    2150             :   nsCString mHeader;
    2151             :   nsCString mOrigin;
    2152             :   RefPtr<nsHttpConnectionInfo> mCI;
    2153             :   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
    2154             : };
    2155             : 
    2156             : // defined as an http2 extension - alt-svc
    2157             : // defines receipt of frame type 0x0A.. See AlternateSevices.h at least draft -06 sec 4
    2158             : // as this is an extension, never generate protocol error - just ignore problems
    2159             : nsresult
    2160           0 : Http2Session::RecvAltSvc(Http2Session *self)
    2161             : {
    2162           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ALTSVC);
    2163           0 :   LOG3(("Http2Session::RecvAltSvc %p Flags 0x%X id 0x%X\n", self,
    2164             :         self->mInputFrameFlags, self->mInputFrameID));
    2165             : 
    2166           0 :   if (self->mInputFrameDataSize < 2) {
    2167           0 :     LOG3(("Http2Session::RecvAltSvc %p frame too small", self));
    2168           0 :     self->ResetDownstreamState();
    2169           0 :     return NS_OK;
    2170             :   }
    2171             : 
    2172             :   uint16_t originLen = NetworkEndian::readUint16(
    2173           0 :       self->mInputFrameBuffer.get() + kFrameHeaderBytes);
    2174           0 :   if (originLen + 2U > self->mInputFrameDataSize) {
    2175           0 :     LOG3(("Http2Session::RecvAltSvc %p origin len too big for frame", self));
    2176           0 :     self->ResetDownstreamState();
    2177           0 :     return NS_OK;
    2178             :   }
    2179             : 
    2180           0 :   if (!gHttpHandler->AllowAltSvc()) {
    2181           0 :     LOG3(("Http2Session::RecvAltSvc %p frame alt service pref'd off", self));
    2182           0 :     self->ResetDownstreamState();
    2183           0 :     return NS_OK;
    2184             :   }
    2185             : 
    2186           0 :   uint16_t altSvcFieldValueLen = static_cast<uint16_t>(self->mInputFrameDataSize) - 2U - originLen;
    2187           0 :   LOG3(("Http2Session::RecvAltSvc %p frame originLen=%u altSvcFieldValueLen=%u\n",
    2188             :         self, originLen, altSvcFieldValueLen));
    2189             : 
    2190           0 :   if (self->mInputFrameDataSize > 2000) {
    2191           0 :     LOG3(("Http2Session::RecvAltSvc %p frame too large to parse sensibly", self));
    2192           0 :     self->ResetDownstreamState();
    2193           0 :     return NS_OK;
    2194             :   }
    2195             : 
    2196           0 :   nsAutoCString origin;
    2197           0 :   bool impliedOrigin = true;
    2198           0 :   if (originLen) {
    2199           0 :     origin.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 2, originLen);
    2200           0 :     impliedOrigin = false;
    2201             :   }
    2202             : 
    2203           0 :   nsAutoCString altSvcFieldValue;
    2204           0 :   if (altSvcFieldValueLen) {
    2205           0 :     altSvcFieldValue.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 2 + originLen,
    2206           0 :                             altSvcFieldValueLen);
    2207             :   }
    2208             : 
    2209           0 :   if (altSvcFieldValue.IsEmpty() || !nsHttp::IsReasonableHeaderValue(altSvcFieldValue)) {
    2210           0 :     LOG(("Http2Session %p Alt-Svc Response Header seems unreasonable - skipping\n", self));
    2211           0 :     self->ResetDownstreamState();
    2212           0 :     return NS_OK;
    2213             :   }
    2214             : 
    2215           0 :   if (self->mInputFrameID & 1) {
    2216             :     // pulled streams apply to the origin of the pulled stream.
    2217             :     // If the origin field is filled in the frame, the frame should be ignored
    2218           0 :     if (!origin.IsEmpty()) {
    2219           0 :       LOG(("Http2Session %p Alt-Svc pulled stream has non empty origin\n", self));
    2220           0 :       self->ResetDownstreamState();
    2221           0 :       return NS_OK;
    2222             :     }
    2223             : 
    2224           0 :     if (NS_FAILED(self->SetInputFrameDataStream(self->mInputFrameID)) ||
    2225           0 :         !self->mInputFrameDataStream->Transaction() ||
    2226           0 :         !self->mInputFrameDataStream->Transaction()->RequestHead()) {
    2227           0 :       LOG3(("Http2Session::RecvAltSvc %p got frame w/o origin on invalid stream", self));
    2228           0 :       self->ResetDownstreamState();
    2229           0 :       return NS_OK;
    2230             :     }
    2231             : 
    2232           0 :     self->mInputFrameDataStream->Transaction()->RequestHead()->Origin(origin);
    2233           0 :   } else if (!self->mInputFrameID) {
    2234             :     // ID 0 streams must supply their own origin
    2235           0 :     if (origin.IsEmpty()) {
    2236           0 :       LOG(("Http2Session %p Alt-Svc Stream 0 has empty origin\n", self));
    2237           0 :       self->ResetDownstreamState();
    2238           0 :       return NS_OK;
    2239             :     }
    2240             :   } else {
    2241             :     // handling of push streams is not defined. Let's ignore it
    2242           0 :     LOG(("Http2Session %p Alt-Svc received on pushed stream - ignoring\n", self));
    2243           0 :     self->ResetDownstreamState();
    2244           0 :     return NS_OK;
    2245             :   }
    2246             : 
    2247           0 :   RefPtr<nsHttpConnectionInfo> ci(self->ConnectionInfo());
    2248           0 :   if (!self->mConnection || !ci) {
    2249           0 :     LOG3(("Http2Session::RecvAltSvc %p no connection or conninfo for %d", self,
    2250             :           self->mInputFrameID));
    2251           0 :     self->ResetDownstreamState();
    2252           0 :     return NS_OK;
    2253             :   }
    2254             : 
    2255           0 :   if (!impliedOrigin) {
    2256           0 :     bool okToReroute = true;
    2257           0 :     nsCOMPtr<nsISupports> securityInfo;
    2258           0 :     self->mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
    2259           0 :     nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
    2260           0 :     if (!ssl) {
    2261           0 :       okToReroute = false;
    2262             :     }
    2263             : 
    2264             :     // a little off main thread origin parser. This is a non critical function because
    2265             :     // any alternate route created has to be verified anyhow
    2266           0 :     nsAutoCString specifiedOriginHost;
    2267           0 :     if (origin.EqualsIgnoreCase("https://", 8)) {
    2268           0 :       specifiedOriginHost.Assign(origin.get() + 8, origin.Length() - 8);
    2269           0 :     } else if (origin.EqualsIgnoreCase("http://", 7)) {
    2270           0 :       specifiedOriginHost.Assign(origin.get() + 7, origin.Length() - 7);
    2271             :     }
    2272             : 
    2273           0 :     int32_t colonOffset = specifiedOriginHost.FindCharInSet(":", 0);
    2274           0 :     if (colonOffset != kNotFound) {
    2275           0 :       specifiedOriginHost.Truncate(colonOffset);
    2276             :     }
    2277             : 
    2278           0 :     if (okToReroute) {
    2279           0 :       ssl->IsAcceptableForHost(specifiedOriginHost, &okToReroute);
    2280             :     }
    2281             : 
    2282           0 :     if (!okToReroute) {
    2283           0 :       LOG3(("Http2Session::RecvAltSvc %p can't reroute non-authoritative origin %s",
    2284             :             self, origin.BeginReading()));
    2285           0 :       self->ResetDownstreamState();
    2286           0 :       return NS_OK;
    2287             :     }
    2288             :   }
    2289             : 
    2290           0 :   nsCOMPtr<nsISupports> callbacks;
    2291           0 :   self->mConnection->GetSecurityInfo(getter_AddRefs(callbacks));
    2292           0 :   nsCOMPtr<nsIInterfaceRequestor> irCallbacks = do_QueryInterface(callbacks);
    2293             : 
    2294             :   RefPtr<UpdateAltSvcEvent> event =
    2295           0 :     new UpdateAltSvcEvent(altSvcFieldValue, origin, ci, irCallbacks);
    2296           0 :   NS_DispatchToMainThread(event);
    2297           0 :   self->ResetDownstreamState();
    2298           0 :   return NS_OK;
    2299             : }
    2300             : 
    2301             : void
    2302           0 : Http2Session::Received421(nsHttpConnectionInfo *ci)
    2303             : {
    2304           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2305           0 :   LOG3(("Http2Session::Recevied421 %p %d\n", this, mOriginFrameActivated));
    2306           0 :   if (!mOriginFrameActivated || !ci) {
    2307           0 :     return;
    2308             :   }
    2309             : 
    2310           0 :   nsAutoCString key(ci->GetOrigin());
    2311           0 :   key.Append(':');
    2312           0 :   key.AppendInt(ci->OriginPort());
    2313           0 :   mOriginFrame.Remove(key);
    2314           0 :   LOG3(("Http2Session::Received421 %p key %s removed\n", this, key.get()));
    2315             : }
    2316             : 
    2317             : nsresult
    2318           0 : Http2Session::RecvUnused(Http2Session *self)
    2319             : {
    2320           0 :   LOG3(("Http2Session %p unknown frame type %x ignored\n",
    2321             :         self, self->mInputFrameType));
    2322           0 :   self->ResetDownstreamState();
    2323           0 :   return NS_OK;
    2324             : }
    2325             : 
    2326             : // defined as an http2 extension - origin
    2327             : // defines receipt of frame type 0x0b.. http://httpwg.org/http-extensions/origin-frame.html
    2328             : // as this is an extension, never generate protocol error - just ignore problems
    2329             : nsresult
    2330           0 : Http2Session::RecvOrigin(Http2Session *self)
    2331             : {
    2332           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2333           0 :   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ORIGIN);
    2334           0 :   LOG3(("Http2Session::RecvOrigin %p Flags 0x%X id 0x%X\n", self,
    2335             :         self->mInputFrameFlags, self->mInputFrameID));
    2336             : 
    2337           0 :   if (self->mInputFrameFlags & 0x0F) {
    2338           0 :     LOG3(("Http2Session::RecvOrigin %p leading flags must be 0", self));
    2339           0 :     self->ResetDownstreamState();
    2340           0 :     return NS_OK;
    2341             :   }
    2342             : 
    2343           0 :   if (self->mInputFrameID) {
    2344           0 :     LOG3(("Http2Session::RecvOrigin %p not stream 0", self));
    2345           0 :     self->ResetDownstreamState();
    2346           0 :     return NS_OK;
    2347             :   }
    2348             : 
    2349           0 :   if (self->ConnectionInfo()->UsingProxy()) {
    2350           0 :     LOG3(("Http2Session::RecvOrigin %p must not use proxy", self));
    2351           0 :     self->ResetDownstreamState();
    2352           0 :     return NS_OK;
    2353             :   }
    2354             : 
    2355           0 :   if (!gHttpHandler->AllowOriginExtension()) {
    2356           0 :     LOG3(("Http2Session::RecvOrigin %p origin extension pref'd off", self));
    2357           0 :     self->ResetDownstreamState();
    2358           0 :     return NS_OK;
    2359             :   }
    2360             : 
    2361           0 :   uint32_t offset = 0;
    2362           0 :   self->mOriginFrameActivated = true;
    2363             : 
    2364           0 :   while (self->mInputFrameDataSize >= (offset + 2U)) {
    2365             : 
    2366             :     uint16_t originLen = NetworkEndian::readUint16(
    2367           0 :       self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset);
    2368           0 :     LOG3(("Http2Session::RecvOrigin %p origin extension defined as %d bytes\n", self, originLen));
    2369           0 :     if (originLen + 2U + offset > self->mInputFrameDataSize) {
    2370           0 :       LOG3(("Http2Session::RecvOrigin %p origin len too big for frame", self));
    2371           0 :       break;
    2372             :     }
    2373             : 
    2374           0 :     nsAutoCString originString;
    2375           0 :     RefPtr<nsStandardURL> originURL;
    2376           0 :     originString.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset + 2, originLen);
    2377           0 :     offset += originLen + 2;
    2378           0 :     if (NS_FAILED(Http2Stream::MakeOriginURL(originString, originURL))){
    2379           0 :       LOG3(("Http2Session::RecvOrigin %p origin frame string %s failed to parse\n", self, originString.get()));
    2380           0 :       continue;
    2381             :     }
    2382             : 
    2383           0 :     LOG3(("Http2Session::RecvOrigin %p origin frame string %s parsed OK\n", self, originString.get()));
    2384           0 :     bool isHttps = false;
    2385           0 :     if (NS_FAILED(originURL->SchemeIs("https", &isHttps)) || !isHttps) {
    2386           0 :       LOG3(("Http2Session::RecvOrigin %p origin frame not https\n", self));
    2387           0 :       continue;
    2388             :     }
    2389             : 
    2390           0 :     int32_t port = -1;
    2391           0 :     originURL->GetPort(&port);
    2392           0 :     if (port == -1) {
    2393           0 :       port = 443;
    2394             :     }
    2395             :     // dont use ->GetHostPort because we want explicit 443
    2396           0 :     nsAutoCString host;
    2397           0 :     originURL->GetHost(host);
    2398           0 :     nsAutoCString key(host);
    2399           0 :     key.Append(':');
    2400           0 :     key.AppendInt(port);
    2401           0 :     if (!self->mOriginFrame.Get(key)) {
    2402           0 :       self->mOriginFrame.Put(key, true);
    2403           0 :       RefPtr<nsHttpConnection> conn(self->HttpConnection());
    2404           0 :       MOZ_ASSERT(conn.get());
    2405           0 :       gHttpHandler->ConnMgr()->RegisterOriginCoalescingKey(conn, host, port);
    2406             :     } else {
    2407           0 :       LOG3(("Http2Session::RecvOrigin %p origin frame already in set\n", self));
    2408             :     }
    2409             :   }
    2410             : 
    2411           0 :   self->ResetDownstreamState();
    2412           0 :   return NS_OK;
    2413             : }
    2414             : 
    2415             : //-----------------------------------------------------------------------------
    2416             : // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
    2417             : // of these methods
    2418             : //-----------------------------------------------------------------------------
    2419             : 
    2420             : void
    2421           0 : Http2Session::OnTransportStatus(nsITransport* aTransport,
    2422             :                                 nsresult aStatus, int64_t aProgress)
    2423             : {
    2424           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2425             : 
    2426           0 :   switch (aStatus) {
    2427             :     // These should appear only once, deliver to the first
    2428             :     // transaction on the session.
    2429             :   case NS_NET_STATUS_RESOLVING_HOST:
    2430             :   case NS_NET_STATUS_RESOLVED_HOST:
    2431             :   case NS_NET_STATUS_CONNECTING_TO:
    2432             :   case NS_NET_STATUS_CONNECTED_TO:
    2433             :   case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
    2434             :   case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
    2435             :   {
    2436           0 :     Http2Stream *target = mStreamIDHash.Get(mUseH2Deps ? 0xF : 0x3);
    2437           0 :     nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
    2438           0 :     if (transaction)
    2439           0 :       transaction->OnTransportStatus(aTransport, aStatus, aProgress);
    2440           0 :     break;
    2441             :   }
    2442             : 
    2443             :   default:
    2444             :     // The other transport events are ignored here because there is no good
    2445             :     // way to map them to the right transaction in http/2. Instead, the events
    2446             :     // are generated again from the http/2 code and passed directly to the
    2447             :     // correct transaction.
    2448             : 
    2449             :     // NS_NET_STATUS_SENDING_TO:
    2450             :     // This is generated by the socket transport when (part) of
    2451             :     // a transaction is written out
    2452             :     //
    2453             :     // There is no good way to map it to the right transaction in http/2,
    2454             :     // so it is ignored here and generated separately when the request
    2455             :     // is sent from Http2Stream::TransmitFrame
    2456             : 
    2457             :     // NS_NET_STATUS_WAITING_FOR:
    2458             :     // Created by nsHttpConnection when the request has been totally sent.
    2459             :     // There is no good way to map it to the right transaction in http/2,
    2460             :     // so it is ignored here and generated separately when the same
    2461             :     // condition is complete in Http2Stream when there is no more
    2462             :     // request body left to be transmitted.
    2463             : 
    2464             :     // NS_NET_STATUS_RECEIVING_FROM
    2465             :     // Generated in session whenever we read a data frame or a HEADERS
    2466             :     // that can be attributed to a particular stream/transaction
    2467             : 
    2468           0 :     break;
    2469             :   }
    2470           0 : }
    2471             : 
    2472             : // ReadSegments() is used to write data to the network. Generally, HTTP
    2473             : // request data is pulled from the approriate transaction and
    2474             : // converted to http/2 data. Sometimes control data like window-update are
    2475             : // generated instead.
    2476             : 
    2477             : nsresult
    2478           0 : Http2Session::ReadSegmentsAgain(nsAHttpSegmentReader *reader,
    2479             :                                 uint32_t count, uint32_t *countRead, bool *again)
    2480             : {
    2481           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2482             : 
    2483           0 :   MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader),
    2484             :              "Inconsistent Write Function Callback");
    2485             : 
    2486           0 :   nsresult rv = ConfirmTLSProfile();
    2487           0 :   if (NS_FAILED(rv)) {
    2488           0 :     if (mGoAwayReason == INADEQUATE_SECURITY) {
    2489           0 :       LOG3(("Http2Session::ReadSegments %p returning INADEQUATE_SECURITY %" PRIx32,
    2490             :             this, static_cast<uint32_t>(NS_ERROR_NET_INADEQUATE_SECURITY)));
    2491           0 :       rv = NS_ERROR_NET_INADEQUATE_SECURITY;
    2492             :     }
    2493           0 :     return rv;
    2494             :   }
    2495             : 
    2496           0 :   if (reader)
    2497           0 :     mSegmentReader = reader;
    2498             : 
    2499           0 :   *countRead = 0;
    2500             : 
    2501           0 :   LOG3(("Http2Session::ReadSegments %p", this));
    2502             : 
    2503           0 :   Http2Stream *stream = static_cast<Http2Stream *>(mReadyForWrite.PopFront());
    2504           0 :   if (!stream) {
    2505           0 :     LOG3(("Http2Session %p could not identify a stream to write; suspending.",
    2506             :           this));
    2507           0 :     uint32_t availBeforeFlush = mOutputQueueUsed - mOutputQueueSent;
    2508           0 :     FlushOutputQueue();
    2509           0 :     uint32_t availAfterFlush = mOutputQueueUsed - mOutputQueueSent;
    2510           0 :     if (availBeforeFlush != availAfterFlush) {
    2511           0 :       LOG3(("Http2Session %p ResumeRecv After early flush in ReadSegments", this));
    2512           0 :       Unused << ResumeRecv();
    2513             :     }
    2514           0 :     SetWriteCallbacks();
    2515           0 :     if (mAttemptingEarlyData) {
    2516             :       // We can still try to send our preamble as early-data
    2517           0 :       *countRead = mOutputQueueUsed - mOutputQueueSent;
    2518             :     }
    2519           0 :     return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
    2520             :   }
    2521             : 
    2522           0 :   uint32_t earlyDataUsed = 0;
    2523           0 :   if (mAttemptingEarlyData) {
    2524           0 :     if (!stream->Do0RTT()) {
    2525           0 :       LOG3(("Http2Session %p will not get early data from Http2Stream %p 0x%X",
    2526             :             this, stream, stream->StreamID()));
    2527           0 :       FlushOutputQueue();
    2528           0 :       SetWriteCallbacks();
    2529             :       // We can still send our preamble
    2530           0 :       *countRead = mOutputQueueUsed - mOutputQueueSent;
    2531           0 :       return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
    2532             :     }
    2533             : 
    2534             :     // Need to adjust this to only take as much as we can fit in with the
    2535             :     // preamble/settings/priority stuff
    2536           0 :     count -= (mOutputQueueUsed - mOutputQueueSent);
    2537             : 
    2538             :     // Keep track of this to add it into countRead later, as
    2539             :     // stream->ReadSegments will likely change the value of mOutputQueueUsed.
    2540           0 :     earlyDataUsed = mOutputQueueUsed - mOutputQueueSent;
    2541             :   }
    2542             : 
    2543           0 :   LOG3(("Http2Session %p will write from Http2Stream %p 0x%X "
    2544             :         "block-input=%d block-output=%d\n", this, stream, stream->StreamID(),
    2545             :         stream->RequestBlockedOnRead(), stream->BlockedOnRwin()));
    2546             : 
    2547           0 :   rv = stream->ReadSegments(this, count, countRead);
    2548             : 
    2549           0 :   if (earlyDataUsed) {
    2550             :     // Do this here because countRead could get reset somewhere down the rabbit
    2551             :     // hole of stream->ReadSegments, and we want to make sure we return the
    2552             :     // proper value to our caller.
    2553           0 :     *countRead += earlyDataUsed;
    2554             :   }
    2555             : 
    2556           0 :   if (mAttemptingEarlyData && !m0RTTStreams.Contains(stream)) {
    2557           0 :     LOG3(("Http2Session::ReadSegmentsAgain adding stream %d to m0RTTStreams\n",
    2558             :           stream->StreamID()));
    2559           0 :     m0RTTStreams.AppendElement(stream);
    2560             :   }
    2561             : 
    2562             :   // Not every permutation of stream->ReadSegents produces data (and therefore
    2563             :   // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
    2564             :   // of that. But we might still have old data buffered that would be good
    2565             :   // to flush.
    2566           0 :   FlushOutputQueue();
    2567             : 
    2568             :   // Allow new server reads - that might be data or control information
    2569             :   // (e.g. window updates or http replies) that are responses to these writes
    2570           0 :   Unused << ResumeRecv();
    2571             : 
    2572           0 :   if (stream->RequestBlockedOnRead()) {
    2573             : 
    2574             :     // We are blocked waiting for input - either more http headers or
    2575             :     // any request body data. When more data from the request stream
    2576             :     // becomes available the httptransaction will call conn->ResumeSend().
    2577             : 
    2578           0 :     LOG3(("Http2Session::ReadSegments %p dealing with block on read", this));
    2579             : 
    2580             :     // call readsegments again if there are other streams ready
    2581             :     // to run in this session
    2582           0 :     if (GetWriteQueueSize()) {
    2583           0 :       rv = NS_OK;
    2584             :     } else {
    2585           0 :       rv = NS_BASE_STREAM_WOULD_BLOCK;
    2586             :     }
    2587           0 :     SetWriteCallbacks();
    2588           0 :     return rv;
    2589             :   }
    2590             : 
    2591           0 :   if (NS_FAILED(rv)) {
    2592           0 :     LOG3(("Http2Session::ReadSegments %p may return FAIL code %" PRIX32,
    2593             :           this, static_cast<uint32_t>(rv)));
    2594           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    2595           0 :       return rv;
    2596             :     }
    2597             : 
    2598           0 :     CleanupStream(stream, rv, CANCEL_ERROR);
    2599           0 :     if (SoftStreamError(rv)) {
    2600           0 :       LOG3(("Http2Session::ReadSegments %p soft error override\n", this));
    2601           0 :       *again = false;
    2602           0 :       SetWriteCallbacks();
    2603           0 :       rv = NS_OK;
    2604             :     }
    2605           0 :     return rv;
    2606             :   }
    2607             : 
    2608           0 :   if (*countRead > 0) {
    2609           0 :     LOG3(("Http2Session::ReadSegments %p stream=%p countread=%d",
    2610             :           this, stream, *countRead));
    2611           0 :     mReadyForWrite.Push(stream);
    2612           0 :     SetWriteCallbacks();
    2613           0 :     return rv;
    2614             :   }
    2615             : 
    2616           0 :   if (stream->BlockedOnRwin()) {
    2617           0 :     LOG3(("Http2Session %p will stream %p 0x%X suspended for flow control\n",
    2618             :           this, stream, stream->StreamID()));
    2619           0 :     return NS_BASE_STREAM_WOULD_BLOCK;
    2620             :   }
    2621             : 
    2622           0 :   LOG3(("Http2Session::ReadSegments %p stream=%p stream send complete",
    2623             :         this, stream));
    2624             : 
    2625             :   // call readsegments again if there are other streams ready
    2626             :   // to go in this session
    2627           0 :   SetWriteCallbacks();
    2628             : 
    2629           0 :   return rv;
    2630             : }
    2631             : 
    2632             : nsresult
    2633           0 : Http2Session::ReadSegments(nsAHttpSegmentReader *reader,
    2634             :                            uint32_t count, uint32_t *countRead)
    2635             : {
    2636           0 :   bool again = false;
    2637           0 :   return ReadSegmentsAgain(reader, count, countRead, &again);
    2638             : }
    2639             : 
    2640             : nsresult
    2641           0 : Http2Session::ReadyToProcessDataFrame(enum internalStateType newState)
    2642             : {
    2643           0 :   MOZ_ASSERT(newState == PROCESSING_DATA_FRAME ||
    2644             :              newState == DISCARDING_DATA_FRAME_PADDING);
    2645           0 :   ChangeDownstreamState(newState);
    2646             : 
    2647           0 :   Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
    2648           0 :                         mInputFrameDataSize >> 10);
    2649           0 :   mLastDataReadEpoch = mLastReadEpoch;
    2650             : 
    2651           0 :   if (!mInputFrameID) {
    2652           0 :     LOG3(("Http2Session::ReadyToProcessDataFrame %p data frame stream 0\n",
    2653             :           this));
    2654           0 :     RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
    2655             :   }
    2656             : 
    2657           0 :   nsresult rv = SetInputFrameDataStream(mInputFrameID);
    2658           0 :   if (NS_FAILED(rv)) {
    2659           0 :     LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X "
    2660             :           "failed. probably due to verification.\n", this, mInputFrameID));
    2661           0 :     return rv;
    2662             :   }
    2663           0 :   if (!mInputFrameDataStream) {
    2664           0 :     LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X "
    2665             :           "failed. Next = 0x%X", this, mInputFrameID, mNextStreamID));
    2666           0 :     if (mInputFrameID >= mNextStreamID)
    2667           0 :       GenerateRstStream(PROTOCOL_ERROR, mInputFrameID);
    2668           0 :     ChangeDownstreamState(DISCARDING_DATA_FRAME);
    2669           0 :   } else if (mInputFrameDataStream->RecvdFin() ||
    2670           0 :             mInputFrameDataStream->RecvdReset() ||
    2671           0 :             mInputFrameDataStream->SentReset()) {
    2672           0 :     LOG3(("Http2Session::ReadyToProcessDataFrame %p streamID 0x%X "
    2673             :           "Data arrived for already server closed stream.\n",
    2674             :           this, mInputFrameID));
    2675           0 :     if (mInputFrameDataStream->RecvdFin() || mInputFrameDataStream->RecvdReset())
    2676           0 :       GenerateRstStream(STREAM_CLOSED_ERROR, mInputFrameID);
    2677           0 :     ChangeDownstreamState(DISCARDING_DATA_FRAME);
    2678           0 :   } else if (mInputFrameDataSize == 0 && !mInputFrameFinal) {
    2679             :     // Only if non-final because the stream properly handles final frames of any
    2680             :     // size, and we want the stream to be able to notice its own end flag.
    2681           0 :     LOG3(("Http2Session::ReadyToProcessDataFrame %p streamID 0x%X "
    2682             :           "Ignoring 0-length non-terminal data frame.", this, mInputFrameID));
    2683           0 :     ChangeDownstreamState(DISCARDING_DATA_FRAME);
    2684             :   }
    2685             : 
    2686           0 :   LOG3(("Start Processing Data Frame. "
    2687             :         "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d",
    2688             :         this, mInputFrameID, mInputFrameDataStream, mInputFrameFinal,
    2689             :         mInputFrameDataSize));
    2690           0 :   UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize);
    2691             : 
    2692           0 :   if (mInputFrameDataStream) {
    2693           0 :     mInputFrameDataStream->SetRecvdData(true);
    2694             :   }
    2695             : 
    2696           0 :   return NS_OK;
    2697             : }
    2698             : 
    2699             : // WriteSegments() is used to read data off the socket. Generally this is
    2700             : // just the http2 frame header and from there the appropriate *Stream
    2701             : // is identified from the Stream-ID. The http transaction associated with
    2702             : // that read then pulls in the data directly, which it will feed to
    2703             : // OnWriteSegment(). That function will gateway it into http and feed
    2704             : // it to the appropriate transaction.
    2705             : 
    2706             : // we call writer->OnWriteSegment via NetworkRead() to get a http2 header..
    2707             : // and decide if it is data or control.. if it is control, just deal with it.
    2708             : // if it is data, identify the stream
    2709             : // call stream->WriteSegments which can call this::OnWriteSegment to get the
    2710             : // data. It always gets full frames if they are part of the stream
    2711             : 
    2712             : nsresult
    2713           0 : Http2Session::WriteSegmentsAgain(nsAHttpSegmentWriter *writer,
    2714             :                                  uint32_t count, uint32_t *countWritten,
    2715             :                                  bool *again)
    2716             : {
    2717           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2718             : 
    2719           0 :   LOG3(("Http2Session::WriteSegments %p InternalState %X\n",
    2720             :         this, mDownstreamState));
    2721             : 
    2722           0 :   *countWritten = 0;
    2723             : 
    2724           0 :   if (mClosed)
    2725           0 :     return NS_ERROR_FAILURE;
    2726             : 
    2727           0 :   nsresult rv = ConfirmTLSProfile();
    2728           0 :   if (NS_FAILED(rv))
    2729           0 :     return rv;
    2730             : 
    2731           0 :   SetWriteCallbacks();
    2732             : 
    2733             :   // If there are http transactions attached to a push stream with filled buffers
    2734             :   // trigger that data pump here. This only reads from buffers (not the network)
    2735             :   // so mDownstreamState doesn't matter.
    2736             :   Http2Stream *pushConnectedStream =
    2737           0 :     static_cast<Http2Stream *>(mPushesReadyForRead.PopFront());
    2738           0 :   if (pushConnectedStream) {
    2739           0 :     return ProcessConnectedPush(pushConnectedStream, writer, count, countWritten);
    2740             :   }
    2741             : 
    2742             :   // feed gecko channels that previously stopped consuming data
    2743             :   // only take data from stored buffers
    2744             :   Http2Stream *slowConsumer =
    2745           0 :     static_cast<Http2Stream *>(mSlowConsumersReadyForRead.PopFront());
    2746           0 :   if (slowConsumer) {
    2747           0 :     internalStateType savedState = mDownstreamState;
    2748           0 :     mDownstreamState = NOT_USING_NETWORK;
    2749           0 :     rv = ProcessSlowConsumer(slowConsumer, writer, count, countWritten);
    2750           0 :     mDownstreamState = savedState;
    2751           0 :     return rv;
    2752             :   }
    2753             : 
    2754             :   // The BUFFERING_OPENING_SETTINGS state is just like any BUFFERING_FRAME_HEADER
    2755             :   // except the only frame type it will allow is SETTINGS
    2756             : 
    2757             :   // The session layer buffers the leading 8 byte header of every frame.
    2758             :   // Non-Data frames are then buffered for their full length, but data
    2759             :   // frames (type 0) are passed through to the http stack unprocessed
    2760             : 
    2761           0 :   if (mDownstreamState == BUFFERING_OPENING_SETTINGS ||
    2762           0 :       mDownstreamState == BUFFERING_FRAME_HEADER) {
    2763             :     // The first 9 bytes of every frame is header information that
    2764             :     // we are going to want to strip before passing to http. That is
    2765             :     // true of both control and data packets.
    2766             : 
    2767           0 :     MOZ_ASSERT(mInputFrameBufferUsed < kFrameHeaderBytes,
    2768             :                "Frame Buffer Used Too Large for State");
    2769             : 
    2770           0 :     rv = NetworkRead(writer, &mInputFrameBuffer[mInputFrameBufferUsed],
    2771           0 :                      kFrameHeaderBytes - mInputFrameBufferUsed, countWritten);
    2772             : 
    2773           0 :     if (NS_FAILED(rv)) {
    2774           0 :       LOG3(("Http2Session %p buffering frame header read failure %" PRIx32 "\n",
    2775             :             this, static_cast<uint32_t>(rv)));
    2776             :       // maybe just blocked reading from network
    2777           0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    2778           0 :         rv = NS_OK;
    2779           0 :       return rv;
    2780             :     }
    2781             : 
    2782           0 :     LogIO(this, nullptr, "Reading Frame Header",
    2783           0 :           &mInputFrameBuffer[mInputFrameBufferUsed], *countWritten);
    2784             : 
    2785           0 :     mInputFrameBufferUsed += *countWritten;
    2786             : 
    2787           0 :     if (mInputFrameBufferUsed < kFrameHeaderBytes)
    2788             :     {
    2789           0 :       LOG3(("Http2Session::WriteSegments %p "
    2790             :             "BUFFERING FRAME HEADER incomplete size=%d",
    2791             :             this, mInputFrameBufferUsed));
    2792           0 :       return rv;
    2793             :     }
    2794             : 
    2795             :     // 3 bytes of length, 1 type byte, 1 flag byte, 1 unused bit, 31 bits of ID
    2796           0 :     uint8_t totallyWastedByte = mInputFrameBuffer.get()[0];
    2797           0 :     mInputFrameDataSize = NetworkEndian::readUint16(
    2798           0 :         mInputFrameBuffer.get() + 1);
    2799           0 :     if (totallyWastedByte || (mInputFrameDataSize > kMaxFrameData)) {
    2800           0 :       LOG3(("Got frame too large 0x%02X%04X", totallyWastedByte, mInputFrameDataSize));
    2801           0 :       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
    2802             :     }
    2803           0 :     mInputFrameType = *reinterpret_cast<uint8_t *>(mInputFrameBuffer.get() + kFrameLengthBytes);
    2804           0 :     mInputFrameFlags = *reinterpret_cast<uint8_t *>(mInputFrameBuffer.get() + kFrameLengthBytes + kFrameTypeBytes);
    2805           0 :     mInputFrameID = NetworkEndian::readUint32(
    2806           0 :         mInputFrameBuffer.get() + kFrameLengthBytes + kFrameTypeBytes + kFrameFlagBytes);
    2807           0 :     mInputFrameID &= 0x7fffffff;
    2808           0 :     mInputFrameDataRead = 0;
    2809             : 
    2810           0 :     if (mInputFrameType == FRAME_TYPE_DATA || mInputFrameType == FRAME_TYPE_HEADERS)  {
    2811           0 :       mInputFrameFinal = mInputFrameFlags & kFlag_END_STREAM;
    2812             :     } else {
    2813           0 :       mInputFrameFinal = 0;
    2814             :     }
    2815             : 
    2816           0 :     mPaddingLength = 0;
    2817             : 
    2818           0 :     LOG3(("Http2Session::WriteSegments[%p::%" PRIu64 "] Frame Header Read "
    2819             :           "type %X data len %u flags %x id 0x%X",
    2820             :           this, mSerial, mInputFrameType, mInputFrameDataSize, mInputFrameFlags,
    2821             :           mInputFrameID));
    2822             : 
    2823             :     // if mExpectedHeaderID is non 0, it means this frame must be a CONTINUATION of
    2824             :     // a HEADERS frame with a matching ID (section 6.2)
    2825           0 :     if (mExpectedHeaderID &&
    2826           0 :         ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
    2827           0 :          (mExpectedHeaderID != mInputFrameID))) {
    2828           0 :       LOG3(("Expected CONINUATION OF HEADERS for ID 0x%X\n", mExpectedHeaderID));
    2829           0 :       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
    2830             :     }
    2831             : 
    2832             :     // if mExpectedPushPromiseID is non 0, it means this frame must be a
    2833             :     // CONTINUATION of a PUSH_PROMISE with a matching ID (section 6.2)
    2834           0 :     if (mExpectedPushPromiseID &&
    2835           0 :         ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
    2836           0 :          (mExpectedPushPromiseID != mInputFrameID))) {
    2837           0 :       LOG3(("Expected CONTINUATION of PUSH PROMISE for ID 0x%X\n",
    2838             :             mExpectedPushPromiseID));
    2839           0 :       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
    2840             :     }
    2841             : 
    2842           0 :     if (mDownstreamState == BUFFERING_OPENING_SETTINGS &&
    2843           0 :         mInputFrameType != FRAME_TYPE_SETTINGS) {
    2844           0 :       LOG3(("First Frame Type Must Be Settings\n"));
    2845           0 :       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
    2846             :     }
    2847             : 
    2848           0 :     if (mInputFrameType != FRAME_TYPE_DATA) { // control frame
    2849           0 :       EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + kFrameHeaderBytes,
    2850           0 :                    kFrameHeaderBytes, mInputFrameBufferSize);
    2851           0 :       ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
    2852           0 :     } else if (mInputFrameFlags & kFlag_PADDED) {
    2853           0 :       ChangeDownstreamState(PROCESSING_DATA_FRAME_PADDING_CONTROL);
    2854             :     } else {
    2855           0 :       rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME);
    2856           0 :       if (NS_FAILED(rv)) {
    2857           0 :         return rv;
    2858             :       }
    2859             :     }
    2860             :   }
    2861             : 
    2862           0 :   if (mDownstreamState == PROCESSING_DATA_FRAME_PADDING_CONTROL) {
    2863           0 :     MOZ_ASSERT(mInputFrameFlags & kFlag_PADDED,
    2864             :                "Processing padding control on unpadded frame");
    2865             : 
    2866           0 :     MOZ_ASSERT(mInputFrameBufferUsed < (kFrameHeaderBytes + 1),
    2867             :                "Frame buffer used too large for state");
    2868             : 
    2869           0 :     rv = NetworkRead(writer, &mInputFrameBuffer[mInputFrameBufferUsed],
    2870           0 :                      (kFrameHeaderBytes + 1) - mInputFrameBufferUsed,
    2871           0 :                      countWritten);
    2872             : 
    2873           0 :     if (NS_FAILED(rv)) {
    2874           0 :       LOG3(("Http2Session %p buffering data frame padding control read failure %" PRIx32 "\n",
    2875             :             this, static_cast<uint32_t>(rv)));
    2876             :       // maybe just blocked reading from network
    2877           0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    2878           0 :         rv = NS_OK;
    2879           0 :       return rv;
    2880             :     }
    2881             : 
    2882           0 :     LogIO(this, nullptr, "Reading Data Frame Padding Control",
    2883           0 :           &mInputFrameBuffer[mInputFrameBufferUsed], *countWritten);
    2884             : 
    2885           0 :     mInputFrameBufferUsed += *countWritten;
    2886             : 
    2887           0 :     if (mInputFrameBufferUsed - kFrameHeaderBytes < 1) {
    2888           0 :       LOG3(("Http2Session::WriteSegments %p "
    2889             :             "BUFFERING DATA FRAME CONTROL PADDING incomplete size=%d",
    2890             :             this, mInputFrameBufferUsed - 8));
    2891           0 :       return rv;
    2892             :     }
    2893             : 
    2894           0 :     ++mInputFrameDataRead;
    2895             : 
    2896           0 :     char *control = &mInputFrameBuffer[kFrameHeaderBytes];
    2897           0 :     mPaddingLength = static_cast<uint8_t>(*control);
    2898             : 
    2899           0 :     LOG3(("Http2Session::WriteSegments %p stream 0x%X mPaddingLength=%d", this,
    2900             :           mInputFrameID, mPaddingLength));
    2901             : 
    2902           0 :     if (1U + mPaddingLength > mInputFrameDataSize) {
    2903           0 :       LOG3(("Http2Session::WriteSegments %p stream 0x%X padding too large for "
    2904             :             "frame", this, mInputFrameID));
    2905           0 :       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
    2906           0 :     } else if (1U + mPaddingLength == mInputFrameDataSize) {
    2907             :       // This frame consists entirely of padding, we can just discard it
    2908           0 :       LOG3(("Http2Session::WriteSegments %p stream 0x%X frame with only padding",
    2909             :             this, mInputFrameID));
    2910           0 :       rv = ReadyToProcessDataFrame(DISCARDING_DATA_FRAME_PADDING);
    2911           0 :       if (NS_FAILED(rv)) {
    2912           0 :         return rv;
    2913             :       }
    2914             :     } else {
    2915           0 :       LOG3(("Http2Session::WriteSegments %p stream 0x%X ready to read HTTP data",
    2916             :             this, mInputFrameID));
    2917           0 :       rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME);
    2918           0 :       if (NS_FAILED(rv)) {
    2919           0 :         return rv;
    2920             :       }
    2921             :     }
    2922             :   }
    2923             : 
    2924           0 :   if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
    2925             :     nsresult streamCleanupCode;
    2926             : 
    2927             :     // There is no bounds checking on the error code.. we provide special
    2928             :     // handling for a couple of cases and all others (including unknown) are
    2929             :     // equivalent to cancel.
    2930           0 :     if (mDownstreamRstReason == REFUSED_STREAM_ERROR) {
    2931           0 :       streamCleanupCode = NS_ERROR_NET_RESET;      // can retry this 100% safely
    2932           0 :       mInputFrameDataStream->Transaction()->ReuseConnectionOnRestartOK(true);
    2933           0 :     } else if (mDownstreamRstReason == HTTP_1_1_REQUIRED) {
    2934           0 :       streamCleanupCode = NS_ERROR_NET_RESET;
    2935           0 :       mInputFrameDataStream->Transaction()->ReuseConnectionOnRestartOK(true);
    2936           0 :       mInputFrameDataStream->Transaction()->DisableSpdy();
    2937             :     } else {
    2938           0 :       streamCleanupCode = mInputFrameDataStream->RecvdData() ?
    2939             :         NS_ERROR_NET_PARTIAL_TRANSFER :
    2940             :         NS_ERROR_NET_INTERRUPT;
    2941             :     }
    2942             : 
    2943           0 :     if (mDownstreamRstReason == COMPRESSION_ERROR) {
    2944           0 :       mShouldGoAway = true;
    2945             :     }
    2946             : 
    2947             :     // mInputFrameDataStream is reset by ChangeDownstreamState
    2948           0 :     Http2Stream *stream = mInputFrameDataStream;
    2949           0 :     ResetDownstreamState();
    2950           0 :     LOG3(("Http2Session::WriteSegments cleanup stream on recv of rst "
    2951             :           "session=%p stream=%p 0x%X\n", this, stream,
    2952             :           stream ? stream->StreamID() : 0));
    2953           0 :     CleanupStream(stream, streamCleanupCode, CANCEL_ERROR);
    2954           0 :     return NS_OK;
    2955             :   }
    2956             : 
    2957           0 :   if (mDownstreamState == PROCESSING_DATA_FRAME ||
    2958           0 :       mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
    2959             : 
    2960             :     // The cleanup stream should only be set while stream->WriteSegments is
    2961             :     // on the stack and then cleaned up in this code block afterwards.
    2962           0 :     MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
    2963           0 :     mNeedsCleanup = nullptr;                     /* just in case */
    2964             : 
    2965           0 :     uint32_t streamID = mInputFrameDataStream->StreamID();
    2966           0 :     mSegmentWriter = writer;
    2967           0 :     rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
    2968           0 :     mSegmentWriter = nullptr;
    2969             : 
    2970           0 :     mLastDataReadEpoch = mLastReadEpoch;
    2971             : 
    2972           0 :     if (SoftStreamError(rv)) {
    2973             :       // This will happen when the transaction figures out it is EOF, generally
    2974             :       // due to a content-length match being made. Return OK from this function
    2975             :       // otherwise the whole session would be torn down.
    2976             : 
    2977             :       // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
    2978             :       // back to PROCESSING_DATA_FRAME where we came from
    2979           0 :       mDownstreamState = PROCESSING_DATA_FRAME;
    2980             : 
    2981           0 :       if (mInputFrameDataRead == mInputFrameDataSize)
    2982           0 :         ResetDownstreamState();
    2983           0 :       LOG3(("Http2Session::WriteSegments session=%p id 0x%X "
    2984             :             "needscleanup=%p. cleanup stream based on "
    2985             :             "stream->writeSegments returning code %" PRIx32 "\n",
    2986             :             this, streamID, mNeedsCleanup, static_cast<uint32_t>(rv)));
    2987           0 :       MOZ_ASSERT(!mNeedsCleanup || mNeedsCleanup->StreamID() == streamID);
    2988           0 :       CleanupStream(streamID, NS_OK, CANCEL_ERROR);
    2989           0 :       mNeedsCleanup = nullptr;
    2990           0 :       *again = false;
    2991           0 :       rv = ResumeRecv();
    2992           0 :       if (NS_FAILED(rv)) {
    2993           0 :         LOG3(("ResumeRecv returned code %x", static_cast<uint32_t>(rv)));
    2994             :       }
    2995           0 :       return NS_OK;
    2996             :     }
    2997             : 
    2998           0 :     if (mNeedsCleanup) {
    2999           0 :       LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
    3000             :             "cleanup stream based on mNeedsCleanup.\n",
    3001             :             this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0));
    3002           0 :       CleanupStream(mNeedsCleanup, NS_OK, CANCEL_ERROR);
    3003           0 :       mNeedsCleanup = nullptr;
    3004             :     }
    3005             : 
    3006           0 :     if (NS_FAILED(rv)) {
    3007           0 :       LOG3(("Http2Session %p data frame read failure %" PRIx32 "\n", this,
    3008             :             static_cast<uint32_t>(rv)));
    3009             :       // maybe just blocked reading from network
    3010           0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    3011           0 :         rv = NS_OK;
    3012             :     }
    3013             : 
    3014           0 :     return rv;
    3015             :   }
    3016             : 
    3017           0 :   if (mDownstreamState == DISCARDING_DATA_FRAME ||
    3018           0 :       mDownstreamState == DISCARDING_DATA_FRAME_PADDING) {
    3019             :     char trash[4096];
    3020           0 :     uint32_t discardCount = std::min(mInputFrameDataSize - mInputFrameDataRead,
    3021           0 :                                      4096U);
    3022           0 :     LOG3(("Http2Session::WriteSegments %p trying to discard %d bytes of data",
    3023             :           this, discardCount));
    3024             : 
    3025           0 :     if (!discardCount) {
    3026           0 :       ResetDownstreamState();
    3027           0 :       Unused << ResumeRecv();
    3028           0 :       return NS_BASE_STREAM_WOULD_BLOCK;
    3029             :     }
    3030             : 
    3031           0 :     rv = NetworkRead(writer, trash, discardCount, countWritten);
    3032             : 
    3033           0 :     if (NS_FAILED(rv)) {
    3034           0 :       LOG3(("Http2Session %p discard frame read failure %" PRIx32 "\n", this,
    3035             :             static_cast<uint32_t>(rv)));
    3036             :       // maybe just blocked reading from network
    3037           0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    3038           0 :         rv = NS_OK;
    3039           0 :       return rv;
    3040             :     }
    3041             : 
    3042           0 :     LogIO(this, nullptr, "Discarding Frame", trash, *countWritten);
    3043             : 
    3044           0 :     mInputFrameDataRead += *countWritten;
    3045             : 
    3046           0 :     if (mInputFrameDataRead == mInputFrameDataSize) {
    3047           0 :       Http2Stream *streamToCleanup = nullptr;
    3048           0 :       if (mInputFrameFinal) {
    3049           0 :         streamToCleanup = mInputFrameDataStream;
    3050             :       }
    3051             : 
    3052           0 :       ResetDownstreamState();
    3053             : 
    3054           0 :       if (streamToCleanup) {
    3055           0 :         CleanupStream(streamToCleanup, NS_OK, CANCEL_ERROR);
    3056             :       }
    3057             :     }
    3058           0 :     return rv;
    3059             :   }
    3060             : 
    3061           0 :   if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
    3062           0 :     MOZ_ASSERT(false); // this cannot happen
    3063             :     return NS_ERROR_UNEXPECTED;
    3064             :   }
    3065             : 
    3066           0 :   MOZ_ASSERT(mInputFrameBufferUsed == kFrameHeaderBytes, "Frame Buffer Header Not Present");
    3067           0 :   MOZ_ASSERT(mInputFrameDataSize + kFrameHeaderBytes <= mInputFrameBufferSize,
    3068             :              "allocation for control frame insufficient");
    3069             : 
    3070           0 :   rv = NetworkRead(writer, &mInputFrameBuffer[kFrameHeaderBytes + mInputFrameDataRead],
    3071           0 :                    mInputFrameDataSize - mInputFrameDataRead, countWritten);
    3072             : 
    3073           0 :   if (NS_FAILED(rv)) {
    3074           0 :     LOG3(("Http2Session %p buffering control frame read failure %" PRIx32 "\n",
    3075             :           this, static_cast<uint32_t>(rv)));
    3076             :     // maybe just blocked reading from network
    3077           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    3078           0 :       rv = NS_OK;
    3079           0 :     return rv;
    3080             :   }
    3081             : 
    3082           0 :   LogIO(this, nullptr, "Reading Control Frame",
    3083           0 :         &mInputFrameBuffer[kFrameHeaderBytes + mInputFrameDataRead], *countWritten);
    3084             : 
    3085           0 :   mInputFrameDataRead += *countWritten;
    3086             : 
    3087           0 :   if (mInputFrameDataRead != mInputFrameDataSize)
    3088           0 :     return NS_OK;
    3089             : 
    3090           0 :   MOZ_ASSERT(mInputFrameType != FRAME_TYPE_DATA);
    3091           0 :   if (mInputFrameType < FRAME_TYPE_LAST) {
    3092           0 :     rv = sControlFunctions[mInputFrameType](this);
    3093             :   } else {
    3094             :     // Section 4.1 requires this to be ignored; though protocol_error would
    3095             :     // be better
    3096           0 :     LOG3(("Http2Session %p unknown frame type %x ignored\n",
    3097             :           this, mInputFrameType));
    3098           0 :     ResetDownstreamState();
    3099           0 :     rv = NS_OK;
    3100             :   }
    3101             : 
    3102           0 :   MOZ_ASSERT(NS_FAILED(rv) ||
    3103             :              mDownstreamState != BUFFERING_CONTROL_FRAME,
    3104             :              "Control Handler returned OK but did not change state");
    3105             : 
    3106           0 :   if (mShouldGoAway && !mStreamTransactionHash.Count())
    3107           0 :     Close(NS_OK);
    3108           0 :   return rv;
    3109             : }
    3110             : 
    3111             : nsresult
    3112           0 : Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
    3113             :                             uint32_t count, uint32_t *countWritten)
    3114             : {
    3115           0 :   bool again = false;
    3116           0 :   return WriteSegmentsAgain(writer, count, countWritten, &again);
    3117             : }
    3118             : 
    3119             : nsresult
    3120           0 : Http2Session::Finish0RTT(bool aRestart, bool aAlpnChanged)
    3121             : {
    3122           0 :   MOZ_ASSERT(mAttemptingEarlyData);
    3123           0 :   LOG3(("Http2Session::Finish0RTT %p aRestart=%d aAlpnChanged=%d", this,
    3124             :         aRestart, aAlpnChanged));
    3125             : 
    3126           0 :   for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
    3127           0 :     if (m0RTTStreams[i]) {
    3128           0 :       m0RTTStreams[i]->Finish0RTT(aRestart, aAlpnChanged);
    3129             :     }
    3130             :   }
    3131             : 
    3132           0 :   if (aRestart) {
    3133             :     // 0RTT failed
    3134           0 :     if (aAlpnChanged) {
    3135             :       // This is a slightly more involved case - we need to get all our streams/
    3136             :       // transactions back in the queue so they can restart as http/1
    3137             : 
    3138             :       // These must be set this way to ensure we gracefully restart all streams
    3139           0 :       mGoAwayID = 0;
    3140           0 :       mCleanShutdown = true;
    3141             : 
    3142             :       // Close takes care of the rest of our work for us. The reason code here
    3143             :       // doesn't matter, as we aren't actually going to send a GOAWAY frame, but
    3144             :       // we use NS_ERROR_NET_RESET as it's closest to the truth.
    3145           0 :       Close(NS_ERROR_NET_RESET);
    3146             :     } else {
    3147             :       // This is the easy case - early data failed, but we're speaking h2, so
    3148             :       // we just need to rewind to the beginning of the preamble and try again.
    3149           0 :       mOutputQueueSent = 0;
    3150             :     }
    3151             :   } else {
    3152             :     // 0RTT succeeded
    3153             :     // Make sure we look for any incoming data in repsonse to our early data.
    3154           0 :     Unused << ResumeRecv();
    3155             :   }
    3156             : 
    3157           0 :   mAttemptingEarlyData = false;
    3158           0 :   m0RTTStreams.Clear();
    3159           0 :   RealignOutputQueue();
    3160             : 
    3161           0 :   return NS_OK;
    3162             : }
    3163             : 
    3164             : void
    3165           0 : Http2Session::SetFastOpenStatus(uint8_t aStatus)
    3166             : {
    3167           0 :   LOG3(("Http2Session::SetFastOpenStatus %d [this=%p]",
    3168             :         aStatus, this));
    3169             : 
    3170           0 :   for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
    3171           0 :     if (m0RTTStreams[i]) {
    3172           0 :       m0RTTStreams[i]->Transaction()->SetFastOpenStatus(aStatus);
    3173             :     }
    3174             :   }
    3175           0 : }
    3176             : 
    3177             : nsresult
    3178           0 : Http2Session::ProcessConnectedPush(Http2Stream *pushConnectedStream,
    3179             :                                    nsAHttpSegmentWriter * writer,
    3180             :                                    uint32_t count, uint32_t *countWritten)
    3181             : {
    3182           0 :   LOG3(("Http2Session::ProcessConnectedPush %p 0x%X\n",
    3183             :         this, pushConnectedStream->StreamID()));
    3184           0 :   mSegmentWriter = writer;
    3185           0 :   nsresult rv = pushConnectedStream->WriteSegments(this, count, countWritten);
    3186           0 :   mSegmentWriter = nullptr;
    3187             : 
    3188             :   // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
    3189             :   // so we need this check to determine the truth.
    3190           0 :   if (NS_SUCCEEDED(rv) && !*countWritten &&
    3191           0 :       pushConnectedStream->PushSource() &&
    3192           0 :       pushConnectedStream->PushSource()->GetPushComplete()) {
    3193           0 :     rv = NS_BASE_STREAM_CLOSED;
    3194             :   }
    3195             : 
    3196           0 :   if (rv == NS_BASE_STREAM_CLOSED) {
    3197           0 :     CleanupStream(pushConnectedStream, NS_OK, CANCEL_ERROR);
    3198           0 :     rv = NS_OK;
    3199             :   }
    3200             : 
    3201             :   // if we return OK to nsHttpConnection it will use mSocketInCondition
    3202             :   // to determine whether to schedule more reads, incorrectly
    3203             :   // assuming that nsHttpConnection::OnSocketWrite() was called.
    3204           0 :   if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
    3205           0 :     rv = NS_BASE_STREAM_WOULD_BLOCK;
    3206           0 :     Unused << ResumeRecv();
    3207             :   }
    3208           0 :   return rv;
    3209             : }
    3210             : 
    3211             : nsresult
    3212           0 : Http2Session::ProcessSlowConsumer(Http2Stream *slowConsumer,
    3213             :                                   nsAHttpSegmentWriter * writer,
    3214             :                                   uint32_t count, uint32_t *countWritten)
    3215             : {
    3216           0 :   LOG3(("Http2Session::ProcessSlowConsumer %p 0x%X\n",
    3217             :         this, slowConsumer->StreamID()));
    3218           0 :   mSegmentWriter = writer;
    3219           0 :   nsresult rv = slowConsumer->WriteSegments(this, count, countWritten);
    3220           0 :   mSegmentWriter = nullptr;
    3221           0 :   LOG3(("Http2Session::ProcessSlowConsumer Writesegments %p 0x%X rv %" PRIX32 " %d\n",
    3222             :         this, slowConsumer->StreamID(), static_cast<uint32_t>(rv), *countWritten));
    3223           0 :   if (NS_SUCCEEDED(rv) && !*countWritten && slowConsumer->RecvdFin()) {
    3224           0 :     rv = NS_BASE_STREAM_CLOSED;
    3225             :   }
    3226             : 
    3227           0 :   if (NS_SUCCEEDED(rv) && (*countWritten > 0)) {
    3228             :     // There have been buffered bytes successfully fed into the
    3229             :     // formerly blocked consumer. Repeat until buffer empty or
    3230             :     // consumer is blocked again.
    3231           0 :     UpdateLocalRwin(slowConsumer, 0);
    3232           0 :     ConnectSlowConsumer(slowConsumer);
    3233             :   }
    3234             : 
    3235           0 :   if (rv == NS_BASE_STREAM_CLOSED) {
    3236           0 :     CleanupStream(slowConsumer, NS_OK, CANCEL_ERROR);
    3237           0 :     rv = NS_OK;
    3238             :   }
    3239             : 
    3240           0 :   return rv;
    3241             : }
    3242             : 
    3243             : void
    3244           0 : Http2Session::UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes)
    3245             : {
    3246           0 :   if (!stream) // this is ok - it means there was a data frame for a rst stream
    3247           0 :     return;
    3248             : 
    3249             :   // If this data packet was not for a valid or live stream then there
    3250             :   // is no reason to mess with the flow control
    3251           0 :   if (!stream || stream->RecvdFin() || stream->RecvdReset() ||
    3252           0 :       mInputFrameFinal) {
    3253           0 :     return;
    3254             :   }
    3255             : 
    3256           0 :   stream->DecrementClientReceiveWindow(bytes);
    3257             : 
    3258             :   // Don't necessarily ack every data packet. Only do it
    3259             :   // after a significant amount of data.
    3260           0 :   uint64_t unacked = stream->LocalUnAcked();
    3261           0 :   int64_t  localWindow = stream->ClientReceiveWindow();
    3262             : 
    3263           0 :   LOG3(("Http2Session::UpdateLocalStreamWindow this=%p id=0x%X newbytes=%u "
    3264             :         "unacked=%" PRIu64 " localWindow=%" PRId64 "\n",
    3265             :         this, stream->StreamID(), bytes, unacked, localWindow));
    3266             : 
    3267           0 :   if (!unacked)
    3268           0 :     return;
    3269             : 
    3270           0 :   if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold))
    3271           0 :     return;
    3272             : 
    3273           0 :   if (!stream->HasSink()) {
    3274           0 :     LOG3(("Http2Session::UpdateLocalStreamWindow %p 0x%X Pushed Stream Has No Sink\n",
    3275             :           this, stream->StreamID()));
    3276           0 :     return;
    3277             :   }
    3278             : 
    3279             :   // Generate window updates directly out of session instead of the stream
    3280             :   // in order to avoid queue delays in getting the 'ACK' out.
    3281           0 :   uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU;
    3282             : 
    3283           0 :   LOG3(("Http2Session::UpdateLocalStreamWindow Ack this=%p id=0x%X acksize=%d\n",
    3284             :         this, stream->StreamID(), toack));
    3285           0 :   stream->IncrementClientReceiveWindow(toack);
    3286           0 :   if (toack == 0) {
    3287             :     // Ensure we never send an illegal 0 window update
    3288           0 :     return;
    3289             :   }
    3290             : 
    3291             :   // room for this packet needs to be ensured before calling this function
    3292           0 :   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
    3293           0 :   mOutputQueueUsed += kFrameHeaderBytes + 4;
    3294           0 :   MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
    3295             : 
    3296           0 :   CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, stream->StreamID());
    3297           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes, toack);
    3298             : 
    3299           0 :   LogIO(this, stream, "Stream Window Update", packet, kFrameHeaderBytes + 4);
    3300             :   // dont flush here, this write can commonly be coalesced with a
    3301             :   // session window update to immediately follow.
    3302             : }
    3303             : 
    3304             : void
    3305           0 : Http2Session::UpdateLocalSessionWindow(uint32_t bytes)
    3306             : {
    3307           0 :   if (!bytes)
    3308           0 :     return;
    3309             : 
    3310           0 :   mLocalSessionWindow -= bytes;
    3311             : 
    3312           0 :   LOG3(("Http2Session::UpdateLocalSessionWindow this=%p newbytes=%u "
    3313             :         "localWindow=%" PRId64 "\n", this, bytes, mLocalSessionWindow));
    3314             : 
    3315             :   // Don't necessarily ack every data packet. Only do it
    3316             :   // after a significant amount of data.
    3317           0 :   if ((mLocalSessionWindow > (mInitialRwin - kMinimumToAck)) &&
    3318           0 :       (mLocalSessionWindow > kEmergencyWindowThreshold))
    3319           0 :     return;
    3320             : 
    3321             :   // Only send max  bits of window updates at a time.
    3322           0 :   uint64_t toack64 = mInitialRwin - mLocalSessionWindow;
    3323           0 :   uint32_t toack = (toack64 <= 0x7fffffffU) ? toack64 : 0x7fffffffU;
    3324             : 
    3325           0 :   LOG3(("Http2Session::UpdateLocalSessionWindow Ack this=%p acksize=%u\n",
    3326             :         this, toack));
    3327           0 :   mLocalSessionWindow += toack;
    3328             : 
    3329           0 :   if (toack == 0) {
    3330             :     // Ensure we never send an illegal 0 window update
    3331           0 :     return;
    3332             :   }
    3333             : 
    3334             :   // room for this packet needs to be ensured before calling this function
    3335           0 :   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
    3336           0 :   mOutputQueueUsed += kFrameHeaderBytes + 4;
    3337           0 :   MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
    3338             : 
    3339           0 :   CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
    3340           0 :   NetworkEndian::writeUint32(packet + kFrameHeaderBytes, toack);
    3341             : 
    3342           0 :   LogIO(this, nullptr, "Session Window Update", packet, kFrameHeaderBytes + 4);
    3343             :   // dont flush here, this write can commonly be coalesced with others
    3344             : }
    3345             : 
    3346             : void
    3347           0 : Http2Session::UpdateLocalRwin(Http2Stream *stream, uint32_t bytes)
    3348             : {
    3349             :   // make sure there is room for 2 window updates even though
    3350             :   // we may not generate any.
    3351           0 :   EnsureOutputBuffer(2 * (kFrameHeaderBytes + 4));
    3352             : 
    3353           0 :   UpdateLocalStreamWindow(stream, bytes);
    3354           0 :   UpdateLocalSessionWindow(bytes);
    3355           0 :   FlushOutputQueue();
    3356           0 : }
    3357             : 
    3358             : void
    3359           0 : Http2Session::Close(nsresult aReason)
    3360             : {
    3361           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3362             : 
    3363           0 :   if (mClosed)
    3364           0 :     return;
    3365             : 
    3366           0 :   LOG3(("Http2Session::Close %p %" PRIX32, this, static_cast<uint32_t>(aReason)));
    3367             : 
    3368           0 :   mClosed = true;
    3369             : 
    3370           0 :   Shutdown();
    3371             : 
    3372           0 :   mStreamIDHash.Clear();
    3373           0 :   mStreamTransactionHash.Clear();
    3374             : 
    3375             :   uint32_t goAwayReason;
    3376           0 :   if (mGoAwayReason != NO_HTTP_ERROR) {
    3377           0 :     goAwayReason = mGoAwayReason;
    3378           0 :   } else if (NS_SUCCEEDED(aReason)) {
    3379           0 :     goAwayReason = NO_HTTP_ERROR;
    3380           0 :   } else if (aReason == NS_ERROR_ILLEGAL_VALUE) {
    3381           0 :     goAwayReason = PROTOCOL_ERROR;
    3382             :   } else {
    3383           0 :     goAwayReason = INTERNAL_ERROR;
    3384             :   }
    3385           0 :   if (!mAttemptingEarlyData) {
    3386           0 :     GenerateGoAway(goAwayReason);
    3387             :   }
    3388           0 :   mConnection = nullptr;
    3389           0 :   mSegmentReader = nullptr;
    3390           0 :   mSegmentWriter = nullptr;
    3391             : }
    3392             : 
    3393             : nsHttpConnectionInfo *
    3394           0 : Http2Session::ConnectionInfo()
    3395             : {
    3396           0 :   RefPtr<nsHttpConnectionInfo> ci;
    3397           0 :   GetConnectionInfo(getter_AddRefs(ci));
    3398           0 :   return ci.get();
    3399             : }
    3400             : 
    3401             : void
    3402           0 : Http2Session::CloseTransaction(nsAHttpTransaction *aTransaction,
    3403             :                                nsresult aResult)
    3404             : {
    3405           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3406           0 :   LOG3(("Http2Session::CloseTransaction %p %p %" PRIx32, this, aTransaction,
    3407             :         static_cast<uint32_t>(aResult)));
    3408             : 
    3409             :   // Generally this arrives as a cancel event from the connection manager.
    3410             : 
    3411             :   // need to find the stream and call CleanupStream() on it.
    3412           0 :   Http2Stream *stream = mStreamTransactionHash.Get(aTransaction);
    3413           0 :   if (!stream) {
    3414           0 :     LOG3(("Http2Session::CloseTransaction %p %p %" PRIx32 " - not found.",
    3415             :           this, aTransaction, static_cast<uint32_t>(aResult)));
    3416           0 :     return;
    3417             :   }
    3418           0 :   LOG3(("Http2Session::CloseTransaction probably a cancel. "
    3419             :         "this=%p, trans=%p, result=%" PRIx32 ", streamID=0x%X stream=%p",
    3420             :         this, aTransaction, static_cast<uint32_t>(aResult), stream->StreamID(), stream));
    3421           0 :   CleanupStream(stream, aResult, CANCEL_ERROR);
    3422           0 :   nsresult rv = ResumeRecv();
    3423           0 :   if (NS_FAILED(rv)) {
    3424           0 :     LOG3(("Http2Session::CloseTransaction %p %p %x ResumeRecv returned %x",
    3425             :           this, aTransaction, static_cast<uint32_t>(aResult),
    3426             :           static_cast<uint32_t>(rv)));
    3427             :   }
    3428             : }
    3429             : 
    3430             : //-----------------------------------------------------------------------------
    3431             : // nsAHttpSegmentReader
    3432             : //-----------------------------------------------------------------------------
    3433             : 
    3434             : nsresult
    3435           0 : Http2Session::OnReadSegment(const char *buf,
    3436             :                             uint32_t count, uint32_t *countRead)
    3437             : {
    3438           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3439             :   nsresult rv;
    3440             : 
    3441             :   // If we can release old queued data then we can try and write the new
    3442             :   // data directly to the network without using the output queue at all
    3443           0 :   if (mOutputQueueUsed)
    3444           0 :     FlushOutputQueue();
    3445             : 
    3446           0 :   if (!mOutputQueueUsed && mSegmentReader) {
    3447             :     // try and write directly without output queue
    3448           0 :     rv = mSegmentReader->OnReadSegment(buf, count, countRead);
    3449             : 
    3450           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    3451           0 :       *countRead = 0;
    3452           0 :     } else if (NS_FAILED(rv)) {
    3453           0 :       return rv;
    3454             :     }
    3455             : 
    3456           0 :     if (*countRead < count) {
    3457           0 :       uint32_t required = count - *countRead;
    3458             :       // assuming a commitment() happened, this ensurebuffer is a nop
    3459             :       // but just in case the queuesize is too small for the required data
    3460             :       // call ensurebuffer().
    3461           0 :       EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
    3462           0 :       memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
    3463           0 :       mOutputQueueUsed = required;
    3464             :     }
    3465             : 
    3466           0 :     *countRead = count;
    3467           0 :     return NS_OK;
    3468             :   }
    3469             : 
    3470             :   // At this point we are going to buffer the new data in the output
    3471             :   // queue if it fits. By coalescing multiple small submissions into one larger
    3472             :   // buffer we can get larger writes out to the network later on.
    3473             : 
    3474             :   // This routine should not be allowed to fill up the output queue
    3475             :   // all on its own - at least kQueueReserved bytes are always left
    3476             :   // for other routines to use - but this is an all-or-nothing function,
    3477             :   // so if it will not all fit just return WOULD_BLOCK
    3478             : 
    3479           0 :   if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
    3480           0 :     return NS_BASE_STREAM_WOULD_BLOCK;
    3481             : 
    3482           0 :   memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
    3483           0 :   mOutputQueueUsed += count;
    3484           0 :   *countRead = count;
    3485             : 
    3486           0 :   FlushOutputQueue();
    3487             : 
    3488           0 :   return NS_OK;
    3489             : }
    3490             : 
    3491             : nsresult
    3492           0 : Http2Session::CommitToSegmentSize(uint32_t count, bool forceCommitment)
    3493             : {
    3494           0 :   if (mOutputQueueUsed && !mAttemptingEarlyData)
    3495           0 :     FlushOutputQueue();
    3496             : 
    3497             :   // would there be enough room to buffer this if needed?
    3498           0 :   if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
    3499           0 :     return NS_OK;
    3500             : 
    3501             :   // if we are using part of our buffers already, try again later unless
    3502             :   // forceCommitment is set.
    3503           0 :   if (mOutputQueueUsed && !forceCommitment)
    3504           0 :     return NS_BASE_STREAM_WOULD_BLOCK;
    3505             : 
    3506           0 :   if (mOutputQueueUsed) {
    3507             :     // normally we avoid the memmove of RealignOutputQueue, but we'll try
    3508             :     // it if forceCommitment is set before growing the buffer.
    3509           0 :     RealignOutputQueue();
    3510             : 
    3511             :     // is there enough room now?
    3512           0 :     if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
    3513           0 :       return NS_OK;
    3514             :   }
    3515             : 
    3516             :   // resize the buffers as needed
    3517           0 :   EnsureOutputBuffer(count + kQueueReserved);
    3518             : 
    3519           0 :   MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved),
    3520             :              "buffer not as large as expected");
    3521             : 
    3522           0 :   return NS_OK;
    3523             : }
    3524             : 
    3525             : //-----------------------------------------------------------------------------
    3526             : // nsAHttpSegmentWriter
    3527             : //-----------------------------------------------------------------------------
    3528             : 
    3529             : nsresult
    3530           0 : Http2Session::OnWriteSegment(char *buf,
    3531             :                              uint32_t count, uint32_t *countWritten)
    3532             : {
    3533           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3534             :   nsresult rv;
    3535             : 
    3536           0 :   if (!mSegmentWriter) {
    3537             :     // the only way this could happen would be if Close() were called on the
    3538             :     // stack with WriteSegments()
    3539           0 :     return NS_ERROR_FAILURE;
    3540             :   }
    3541             : 
    3542           0 :   if (mDownstreamState == NOT_USING_NETWORK ||
    3543           0 :       mDownstreamState == BUFFERING_FRAME_HEADER ||
    3544           0 :       mDownstreamState == DISCARDING_DATA_FRAME_PADDING) {
    3545           0 :     return NS_BASE_STREAM_WOULD_BLOCK;
    3546             :   }
    3547             : 
    3548           0 :   if (mDownstreamState == PROCESSING_DATA_FRAME) {
    3549             : 
    3550           0 :     if (mInputFrameFinal &&
    3551           0 :         mInputFrameDataRead == mInputFrameDataSize) {
    3552           0 :       *countWritten = 0;
    3553           0 :       SetNeedsCleanup();
    3554           0 :       return NS_BASE_STREAM_CLOSED;
    3555             :     }
    3556             : 
    3557           0 :     count = std::min(count, mInputFrameDataSize - mInputFrameDataRead);
    3558           0 :     rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
    3559           0 :     if (NS_FAILED(rv))
    3560           0 :       return rv;
    3561             : 
    3562           0 :     LogIO(this, mInputFrameDataStream, "Reading Data Frame",
    3563           0 :           buf, *countWritten);
    3564             : 
    3565           0 :     mInputFrameDataRead += *countWritten;
    3566           0 :     if (mPaddingLength && (mInputFrameDataSize - mInputFrameDataRead <= mPaddingLength)) {
    3567             :       // We are crossing from real HTTP data into the realm of padding. If
    3568             :       // we've actually crossed the line, we need to munge countWritten for the
    3569             :       // sake of goodness and sanity. No matter what, any future calls to
    3570             :       // WriteSegments need to just discard data until we reach the end of this
    3571             :       // frame.
    3572           0 :       if (mInputFrameDataSize != mInputFrameDataRead) {
    3573             :         // Only change state if we still have padding to read. If we don't do
    3574             :         // this, we can end up hanging on frames that combine real data,
    3575             :         // padding, and END_STREAM (see bug 1019921)
    3576           0 :         ChangeDownstreamState(DISCARDING_DATA_FRAME_PADDING);
    3577             :       }
    3578           0 :       uint32_t paddingRead = mPaddingLength - (mInputFrameDataSize - mInputFrameDataRead);
    3579           0 :       LOG3(("Http2Session::OnWriteSegment %p stream 0x%X len=%d read=%d "
    3580             :             "crossed from HTTP data into padding (%d of %d) countWritten=%d",
    3581             :             this, mInputFrameID, mInputFrameDataSize, mInputFrameDataRead,
    3582             :             paddingRead, mPaddingLength, *countWritten));
    3583           0 :       *countWritten -= paddingRead;
    3584           0 :       LOG3(("Http2Session::OnWriteSegment %p stream 0x%X new countWritten=%d",
    3585             :             this, mInputFrameID, *countWritten));
    3586             :     }
    3587             : 
    3588           0 :     mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
    3589           0 :     if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameFinal)
    3590           0 :       ResetDownstreamState();
    3591             : 
    3592           0 :     return rv;
    3593             :   }
    3594             : 
    3595           0 :   if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
    3596             : 
    3597           0 :     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
    3598           0 :         mInputFrameFinal) {
    3599           0 :       *countWritten = 0;
    3600           0 :       SetNeedsCleanup();
    3601           0 :       return NS_BASE_STREAM_CLOSED;
    3602             :     }
    3603             : 
    3604           0 :     count = std::min(count,
    3605           0 :                      mFlatHTTPResponseHeaders.Length() -
    3606           0 :                      mFlatHTTPResponseHeadersOut);
    3607           0 :     memcpy(buf,
    3608           0 :            mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
    3609           0 :            count);
    3610           0 :     mFlatHTTPResponseHeadersOut += count;
    3611           0 :     *countWritten = count;
    3612             : 
    3613           0 :     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) {
    3614           0 :       if (!mInputFrameFinal) {
    3615             :         // If more frames are expected in this stream, then reset the state so they can be
    3616             :         // handled. Otherwise (e.g. a 0 length response with the fin on the incoming headers)
    3617             :         // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can
    3618             :         // cleanup the stream.
    3619           0 :         ResetDownstreamState();
    3620             :       }
    3621             :     }
    3622             : 
    3623           0 :     return NS_OK;
    3624             :   }
    3625             : 
    3626           0 :   MOZ_ASSERT(false);
    3627             :   return NS_ERROR_UNEXPECTED;
    3628             : }
    3629             : 
    3630             : void
    3631           0 : Http2Session::SetNeedsCleanup()
    3632             : {
    3633           0 :   LOG3(("Http2Session::SetNeedsCleanup %p - recorded downstream fin of "
    3634             :         "stream %p 0x%X", this, mInputFrameDataStream,
    3635             :         mInputFrameDataStream->StreamID()));
    3636             : 
    3637             :   // This will result in Close() being called
    3638           0 :   MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
    3639           0 :   mInputFrameDataStream->SetResponseIsComplete();
    3640           0 :   mNeedsCleanup = mInputFrameDataStream;
    3641           0 :   ResetDownstreamState();
    3642           0 : }
    3643             : 
    3644             : void
    3645           0 : Http2Session::ConnectPushedStream(Http2Stream *stream)
    3646             : {
    3647           0 :   mPushesReadyForRead.Push(stream);
    3648           0 :   Unused << ForceRecv();
    3649           0 : }
    3650             : 
    3651             : void
    3652           0 : Http2Session::ConnectSlowConsumer(Http2Stream *stream)
    3653             : {
    3654           0 :   LOG3(("Http2Session::ConnectSlowConsumer %p 0x%X\n",
    3655             :         this, stream->StreamID()));
    3656           0 :   mSlowConsumersReadyForRead.Push(stream);
    3657           0 :   Unused << ForceRecv();
    3658           0 : }
    3659             : 
    3660             : uint32_t
    3661           0 : Http2Session::FindTunnelCount(nsHttpConnectionInfo *aConnInfo)
    3662             : {
    3663           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3664           0 :   uint32_t rv = 0;
    3665           0 :   mTunnelHash.Get(aConnInfo->HashKey(), &rv);
    3666           0 :   return rv;
    3667             : }
    3668             : 
    3669             : void
    3670           0 : Http2Session::RegisterTunnel(Http2Stream *aTunnel)
    3671             : {
    3672           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3673           0 :   nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
    3674           0 :   uint32_t newcount = FindTunnelCount(ci) + 1;
    3675           0 :   mTunnelHash.Remove(ci->HashKey());
    3676           0 :   mTunnelHash.Put(ci->HashKey(), newcount);
    3677           0 :   LOG3(("Http2Stream::RegisterTunnel %p stream=%p tunnels=%d [%s]",
    3678             :         this, aTunnel, newcount, ci->HashKey().get()));
    3679           0 : }
    3680             : 
    3681             : void
    3682           0 : Http2Session::UnRegisterTunnel(Http2Stream *aTunnel)
    3683             : {
    3684           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3685           0 :   nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
    3686           0 :   MOZ_ASSERT(FindTunnelCount(ci));
    3687           0 :   uint32_t newcount = FindTunnelCount(ci) - 1;
    3688           0 :   mTunnelHash.Remove(ci->HashKey());
    3689           0 :   if (newcount) {
    3690           0 :     mTunnelHash.Put(ci->HashKey(), newcount);
    3691             :   }
    3692           0 :   LOG3(("Http2Session::UnRegisterTunnel %p stream=%p tunnels=%d [%s]",
    3693             :         this, aTunnel, newcount, ci->HashKey().get()));
    3694           0 : }
    3695             : 
    3696             : void
    3697           0 : Http2Session::CreateTunnel(nsHttpTransaction *trans,
    3698             :                            nsHttpConnectionInfo *ci,
    3699             :                            nsIInterfaceRequestor *aCallbacks)
    3700             : {
    3701           0 :   LOG(("Http2Session::CreateTunnel %p %p make new tunnel\n", this, trans));
    3702             :   // The connect transaction will hold onto the underlying http
    3703             :   // transaction so that an auth created by the connect can be mappped
    3704             :   // to the correct security callbacks
    3705             : 
    3706             :   RefPtr<SpdyConnectTransaction> connectTrans =
    3707           0 :     new SpdyConnectTransaction(ci, aCallbacks, trans->Caps(), trans, this);
    3708           0 :   DebugOnly<bool> rv = AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL, false, nullptr);
    3709           0 :   MOZ_ASSERT(rv);
    3710           0 :   Http2Stream *tunnel = mStreamTransactionHash.Get(connectTrans);
    3711           0 :   MOZ_ASSERT(tunnel);
    3712           0 :   RegisterTunnel(tunnel);
    3713           0 : }
    3714             : 
    3715             : void
    3716           0 : Http2Session::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
    3717             :                                nsIInterfaceRequestor *aCallbacks)
    3718             : {
    3719           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3720           0 :   nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
    3721           0 :   nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
    3722           0 :   MOZ_ASSERT(trans);
    3723             : 
    3724           0 :   LOG3(("Http2Session::DispatchOnTunnel %p trans=%p", this, trans));
    3725             : 
    3726           0 :   aHttpTransaction->SetConnection(nullptr);
    3727             : 
    3728             :   // this transaction has done its work of setting up a tunnel, let
    3729             :   // the connection manager queue it if necessary
    3730           0 :   trans->SetTunnelProvider(this);
    3731           0 :   trans->EnableKeepAlive();
    3732             : 
    3733           0 :   if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
    3734           0 :     LOG3(("Http2Session::DispatchOnTunnel %p create on new tunnel %s",
    3735             :           this, ci->HashKey().get()));
    3736           0 :     CreateTunnel(trans, ci, aCallbacks);
    3737             :   } else {
    3738             :     // requeue it. The connection manager is responsible for actually putting
    3739             :     // this on the tunnel connection with the specific ci. If that can't
    3740             :     // happen the cmgr checks with us via MaybeReTunnel() to see if it should
    3741             :     // make a new tunnel or just wait longer.
    3742           0 :     LOG3(("Http2Session::DispatchOnTunnel %p trans=%p queue in connection manager",
    3743             :           this, trans));
    3744           0 :     nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
    3745           0 :     if (NS_FAILED(rv)) {
    3746           0 :       LOG3(("Http2Session::DispatchOnTunnel %p trans=%p failed to initiate "
    3747             :             "transaction (%08x)", this, trans, static_cast<uint32_t>(rv)));
    3748             :     }
    3749             :   }
    3750           0 : }
    3751             : 
    3752             : // From ASpdySession
    3753             : bool
    3754           0 : Http2Session::MaybeReTunnel(nsAHttpTransaction *aHttpTransaction)
    3755             : {
    3756           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3757           0 :   nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
    3758           0 :   LOG(("Http2Session::MaybeReTunnel %p trans=%p\n", this, trans));
    3759           0 :   if (!trans || trans->TunnelProvider() != this) {
    3760             :     // this isn't really one of our transactions.
    3761           0 :     return false;
    3762             :   }
    3763             : 
    3764           0 :   if (mClosed || mShouldGoAway) {
    3765           0 :     LOG(("Http2Session::MaybeReTunnel %p %p session closed - requeue\n", this, trans));
    3766           0 :     trans->SetTunnelProvider(nullptr);
    3767           0 :     nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
    3768           0 :     if (NS_FAILED(rv)) {
    3769           0 :       LOG3(("Http2Session::MaybeReTunnel %p trans=%p failed to initiate "
    3770             :             "transaction (%08x)", this, trans, static_cast<uint32_t>(rv)));
    3771             :     }
    3772           0 :     return true;
    3773             :   }
    3774             : 
    3775           0 :   nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
    3776           0 :   LOG(("Http2Session:MaybeReTunnel %p %p count=%d limit %d\n",
    3777             :        this, trans, FindTunnelCount(ci), gHttpHandler->MaxConnectionsPerOrigin()));
    3778           0 :   if (FindTunnelCount(ci) >= gHttpHandler->MaxConnectionsPerOrigin()) {
    3779             :     // patience - a tunnel will open up.
    3780           0 :     return false;
    3781             :   }
    3782             : 
    3783           0 :   LOG(("Http2Session::MaybeReTunnel %p %p make new tunnel\n", this, trans));
    3784           0 :   CreateTunnel(trans, ci, trans->SecurityCallbacks());
    3785           0 :   return true;
    3786             : }
    3787             : 
    3788             : nsresult
    3789           0 : Http2Session::BufferOutput(const char *buf,
    3790             :                            uint32_t count,
    3791             :                            uint32_t *countRead)
    3792             : {
    3793           0 :   nsAHttpSegmentReader *old = mSegmentReader;
    3794           0 :   mSegmentReader = nullptr;
    3795           0 :   nsresult rv = OnReadSegment(buf, count, countRead);
    3796           0 :   mSegmentReader = old;
    3797           0 :   return rv;
    3798             : }
    3799             : 
    3800             : bool // static
    3801           0 : Http2Session::ALPNCallback(nsISupports *securityInfo)
    3802             : {
    3803           0 :   if (!gHttpHandler->IsH2MandatorySuiteEnabled()) {
    3804           0 :     LOG3(("Http2Session::ALPNCallback Mandatory Cipher Suite Unavailable\n"));
    3805           0 :     return false;
    3806             :   }
    3807             : 
    3808           0 :   nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
    3809           0 :   LOG3(("Http2Session::ALPNCallback sslsocketcontrol=%p\n", ssl.get()));
    3810           0 :   if (ssl) {
    3811           0 :     int16_t version = ssl->GetSSLVersionOffered();
    3812           0 :     LOG3(("Http2Session::ALPNCallback version=%x\n", version));
    3813           0 :     if (version >= nsISSLSocketControl::TLS_VERSION_1_2) {
    3814           0 :       return true;
    3815             :     }
    3816             :   }
    3817           0 :   return false;
    3818             : }
    3819             : 
    3820             : nsresult
    3821           0 : Http2Session::ConfirmTLSProfile()
    3822             : {
    3823           0 :   if (mTLSProfileConfirmed) {
    3824           0 :     return NS_OK;
    3825             :   }
    3826             : 
    3827           0 :   LOG3(("Http2Session::ConfirmTLSProfile %p mConnection=%p\n",
    3828             :         this, mConnection.get()));
    3829             : 
    3830           0 :   if (mAttemptingEarlyData) {
    3831           0 :     LOG3(("Http2Session::ConfirmTLSProfile %p temporarily passing due to early data\n", this));
    3832           0 :     return NS_OK;
    3833             :   }
    3834             : 
    3835           0 :   if (!gHttpHandler->EnforceHttp2TlsProfile()) {
    3836           0 :     LOG3(("Http2Session::ConfirmTLSProfile %p passed due to configuration bypass\n", this));
    3837           0 :     mTLSProfileConfirmed = true;
    3838           0 :     return NS_OK;
    3839             :   }
    3840             : 
    3841           0 :   if (!mConnection)
    3842           0 :     return NS_ERROR_FAILURE;
    3843             : 
    3844           0 :   nsCOMPtr<nsISupports> securityInfo;
    3845           0 :   mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
    3846           0 :   nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
    3847           0 :   LOG3(("Http2Session::ConfirmTLSProfile %p sslsocketcontrol=%p\n", this, ssl.get()));
    3848           0 :   if (!ssl)
    3849           0 :     return NS_ERROR_FAILURE;
    3850             : 
    3851           0 :   int16_t version = ssl->GetSSLVersionUsed();
    3852           0 :   LOG3(("Http2Session::ConfirmTLSProfile %p version=%x\n", this, version));
    3853           0 :   if (version < nsISSLSocketControl::TLS_VERSION_1_2) {
    3854           0 :     LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of TLS1.2\n", this));
    3855           0 :     RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
    3856             :   }
    3857             : 
    3858           0 :   uint16_t kea = ssl->GetKEAUsed();
    3859           0 :   if (kea != ssl_kea_dh && kea != ssl_kea_ecdh) {
    3860           0 :     LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to invalid KEA %d\n",
    3861             :           this, kea));
    3862           0 :     RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
    3863             :   }
    3864             : 
    3865           0 :   uint32_t keybits = ssl->GetKEAKeyBits();
    3866           0 :   if (kea == ssl_kea_dh && keybits < 2048) {
    3867           0 :     LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to DH %d < 2048\n",
    3868             :           this, keybits));
    3869           0 :     RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
    3870           0 :   } else if (kea == ssl_kea_ecdh && keybits < 224) { // see rfc7540 9.2.1.
    3871           0 :     LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to ECDH %d < 224\n",
    3872             :           this, keybits));
    3873           0 :     RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
    3874             :   }
    3875             : 
    3876           0 :   int16_t macAlgorithm = ssl->GetMACAlgorithmUsed();
    3877           0 :   LOG3(("Http2Session::ConfirmTLSProfile %p MAC Algortihm (aead==6) %d\n",
    3878             :         this, macAlgorithm));
    3879           0 :   if (macAlgorithm != nsISSLSocketControl::SSL_MAC_AEAD) {
    3880           0 :     LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of AEAD\n", this));
    3881           0 :     RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
    3882             :   }
    3883             : 
    3884             :   /* We are required to send SNI. We do that already, so no check is done
    3885             :    * here to make sure we did. */
    3886             : 
    3887             :   /* We really should check to ensure TLS compression isn't enabled on
    3888             :    * this connection. However, we never enable TLS compression on our end,
    3889             :    * anyway, so it'll never be on. All the same, see https://bugzil.la/965881
    3890             :    * for the possibility for an interface to ensure it never gets turned on. */
    3891             : 
    3892           0 :   mTLSProfileConfirmed = true;
    3893           0 :   return NS_OK;
    3894             : }
    3895             : 
    3896             : 
    3897             : //-----------------------------------------------------------------------------
    3898             : // Modified methods of nsAHttpConnection
    3899             : //-----------------------------------------------------------------------------
    3900             : 
    3901             : void
    3902           0 : Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller)
    3903             : {
    3904           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3905           0 :   LOG3(("Http2Session::TransactionHasDataToWrite %p trans=%p", this, caller));
    3906             : 
    3907             :   // a trapped signal from the http transaction to the connection that
    3908             :   // it is no longer blocked on read.
    3909             : 
    3910           0 :   Http2Stream *stream = mStreamTransactionHash.Get(caller);
    3911           0 :   if (!stream || !VerifyStream(stream)) {
    3912           0 :     LOG3(("Http2Session::TransactionHasDataToWrite %p caller %p not found",
    3913             :           this, caller));
    3914           0 :     return;
    3915             :   }
    3916             : 
    3917           0 :   LOG3(("Http2Session::TransactionHasDataToWrite %p ID is 0x%X\n",
    3918             :         this, stream->StreamID()));
    3919             : 
    3920           0 :   if (!mClosed) {
    3921           0 :     mReadyForWrite.Push(stream);
    3922           0 :     SetWriteCallbacks();
    3923             :   } else {
    3924           0 :     LOG3(("Http2Session::TransactionHasDataToWrite %p closed so not setting Ready4Write\n",
    3925             :           this));
    3926             :   }
    3927             : 
    3928             :   // NSPR poll will not poll the network if there are non system PR_FileDesc's
    3929             :   // that are ready - so we can get into a deadlock waiting for the system IO
    3930             :   // to come back here if we don't force the send loop manually.
    3931           0 :   Unused << ForceSend();
    3932             : }
    3933             : 
    3934             : void
    3935           0 : Http2Session::TransactionHasDataToRecv(nsAHttpTransaction *caller)
    3936             : {
    3937           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3938           0 :   LOG3(("Http2Session::TransactionHasDataToRecv %p trans=%p", this, caller));
    3939             : 
    3940             :   // a signal from the http transaction to the connection that it will consume more
    3941           0 :   Http2Stream *stream = mStreamTransactionHash.Get(caller);
    3942           0 :   if (!stream || !VerifyStream(stream)) {
    3943           0 :     LOG3(("Http2Session::TransactionHasDataToRecv %p caller %p not found",
    3944             :           this, caller));
    3945           0 :     return;
    3946             :   }
    3947             : 
    3948           0 :   LOG3(("Http2Session::TransactionHasDataToRecv %p ID is 0x%X\n",
    3949             :         this, stream->StreamID()));
    3950           0 :   ConnectSlowConsumer(stream);
    3951             : }
    3952             : 
    3953             : void
    3954           0 : Http2Session::TransactionHasDataToWrite(Http2Stream *stream)
    3955             : {
    3956           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3957           0 :   LOG3(("Http2Session::TransactionHasDataToWrite %p stream=%p ID=0x%x",
    3958             :         this, stream, stream->StreamID()));
    3959             : 
    3960           0 :   mReadyForWrite.Push(stream);
    3961           0 :   SetWriteCallbacks();
    3962           0 :   Unused << ForceSend();
    3963           0 : }
    3964             : 
    3965             : bool
    3966           0 : Http2Session::IsPersistent()
    3967             : {
    3968           0 :   return true;
    3969             : }
    3970             : 
    3971             : nsresult
    3972           0 : Http2Session::TakeTransport(nsISocketTransport **,
    3973             :                             nsIAsyncInputStream **, nsIAsyncOutputStream **)
    3974             : {
    3975           0 :   MOZ_ASSERT(false, "TakeTransport of Http2Session");
    3976             :   return NS_ERROR_UNEXPECTED;
    3977             : }
    3978             : 
    3979             : already_AddRefed<nsHttpConnection>
    3980           0 : Http2Session::TakeHttpConnection()
    3981             : {
    3982           0 :   MOZ_ASSERT(false, "TakeHttpConnection of Http2Session");
    3983             :   return nullptr;
    3984             : }
    3985             : 
    3986             : already_AddRefed<nsHttpConnection>
    3987           0 : Http2Session::HttpConnection()
    3988             : {
    3989           0 :   if (mConnection) {
    3990           0 :     return mConnection->HttpConnection();
    3991             :   }
    3992           0 :   return nullptr;
    3993             : }
    3994             : 
    3995             : void
    3996           0 : Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
    3997             : {
    3998           0 :   *aOut = nullptr;
    3999           0 : }
    4000             : 
    4001             : //-----------------------------------------------------------------------------
    4002             : // unused methods of nsAHttpTransaction
    4003             : // We can be sure of this because Http2Session is only constructed in
    4004             : // nsHttpConnection and is never passed out of that object or a TLSFilterTransaction
    4005             : // TLS tunnel
    4006             : //-----------------------------------------------------------------------------
    4007             : 
    4008             : void
    4009           0 : Http2Session::SetConnection(nsAHttpConnection *)
    4010             : {
    4011             :   // This is unexpected
    4012           0 :   MOZ_ASSERT(false, "Http2Session::SetConnection()");
    4013             : }
    4014             : 
    4015             : void
    4016           0 : Http2Session::SetProxyConnectFailed()
    4017             : {
    4018           0 :   MOZ_ASSERT(false, "Http2Session::SetProxyConnectFailed()");
    4019             : }
    4020             : 
    4021             : bool
    4022           0 : Http2Session::IsDone()
    4023             : {
    4024           0 :   return !mStreamTransactionHash.Count();
    4025             : }
    4026             : 
    4027             : nsresult
    4028           0 : Http2Session::Status()
    4029             : {
    4030           0 :   MOZ_ASSERT(false, "Http2Session::Status()");
    4031             :   return NS_ERROR_UNEXPECTED;
    4032             : }
    4033             : 
    4034             : uint32_t
    4035           0 : Http2Session::Caps()
    4036             : {
    4037           0 :   MOZ_ASSERT(false, "Http2Session::Caps()");
    4038             :   return 0;
    4039             : }
    4040             : 
    4041             : void
    4042           0 : Http2Session::SetDNSWasRefreshed()
    4043             : {
    4044           0 :   MOZ_ASSERT(false, "Http2Session::SetDNSWasRefreshed()");
    4045             : }
    4046             : 
    4047             : nsHttpRequestHead *
    4048           0 : Http2Session::RequestHead()
    4049             : {
    4050           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4051           0 :   MOZ_ASSERT(false,
    4052             :              "Http2Session::RequestHead() "
    4053             :              "should not be called after http/2 is setup");
    4054             :   return NULL;
    4055             : }
    4056             : 
    4057             : uint32_t
    4058           0 : Http2Session::Http1xTransactionCount()
    4059             : {
    4060           0 :   return 0;
    4061             : }
    4062             : 
    4063             : nsresult
    4064           0 : Http2Session::TakeSubTransactions(
    4065             :   nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
    4066             : {
    4067             :   // Generally this cannot be done with http/2 as transactions are
    4068             :   // started right away.
    4069             : 
    4070           0 :   LOG3(("Http2Session::TakeSubTransactions %p\n", this));
    4071             : 
    4072           0 :   if (mConcurrentHighWater > 0)
    4073           0 :     return NS_ERROR_ALREADY_OPENED;
    4074             : 
    4075           0 :   LOG3(("   taking %d\n", mStreamTransactionHash.Count()));
    4076             : 
    4077           0 :   for (auto iter = mStreamTransactionHash.Iter(); !iter.Done(); iter.Next()) {
    4078           0 :     outTransactions.AppendElement(iter.Key());
    4079             : 
    4080             :     // Removing the stream from the hash will delete the stream and drop the
    4081             :     // transaction reference the hash held.
    4082           0 :     iter.Remove();
    4083             :   }
    4084           0 :   return NS_OK;
    4085             : }
    4086             : 
    4087             : //-----------------------------------------------------------------------------
    4088             : // Pass through methods of nsAHttpConnection
    4089             : //-----------------------------------------------------------------------------
    4090             : 
    4091             : nsAHttpConnection *
    4092           0 : Http2Session::Connection()
    4093             : {
    4094           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4095           0 :   return mConnection;
    4096             : }
    4097             : 
    4098             : nsresult
    4099           0 : Http2Session::OnHeadersAvailable(nsAHttpTransaction *transaction,
    4100             :                                  nsHttpRequestHead *requestHead,
    4101             :                                  nsHttpResponseHead *responseHead, bool *reset)
    4102             : {
    4103           0 :   return mConnection->OnHeadersAvailable(transaction,
    4104             :                                          requestHead,
    4105             :                                          responseHead,
    4106           0 :                                          reset);
    4107             : }
    4108             : 
    4109             : bool
    4110           0 : Http2Session::IsReused()
    4111             : {
    4112           0 :   return mConnection->IsReused();
    4113             : }
    4114             : 
    4115             : nsresult
    4116           0 : Http2Session::PushBack(const char *buf, uint32_t len)
    4117             : {
    4118           0 :   return mConnection->PushBack(buf, len);
    4119             : }
    4120             : 
    4121             : void
    4122           0 : Http2Session::SendPing()
    4123             : {
    4124           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4125             : 
    4126           0 :   if (mPreviousUsed) {
    4127             :     // alredy in progress, get out
    4128           0 :     return;
    4129             :   }
    4130             : 
    4131           0 :   mPingSentEpoch = PR_IntervalNow();
    4132           0 :   if (!mPingSentEpoch) {
    4133           0 :     mPingSentEpoch = 1; // avoid the 0 sentinel value
    4134             :   }
    4135           0 :   if (!mPingThreshold ||
    4136           0 :       (mPingThreshold > gHttpHandler->NetworkChangedTimeout())) {
    4137           0 :     mPreviousPingThreshold = mPingThreshold;
    4138           0 :     mPreviousUsed = true;
    4139           0 :     mPingThreshold = gHttpHandler->NetworkChangedTimeout();
    4140             :   }
    4141           0 :   GeneratePing(false);
    4142           0 :   Unused << ResumeRecv();
    4143             : }
    4144             : 
    4145             : bool
    4146           0 : Http2Session::TestOriginFrame(const nsACString &hostname, int32_t port)
    4147             : {
    4148           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4149           0 :   MOZ_ASSERT(mOriginFrameActivated);
    4150             : 
    4151           0 :   nsAutoCString key(hostname);
    4152           0 :   key.Append (':');
    4153           0 :   key.AppendInt(port);
    4154           0 :   bool rv = mOriginFrame.Get(key);
    4155           0 :   LOG3(("TestOriginFrame() hash.get %p %s %d\n", this, key.get(), rv));
    4156           0 :   if (!rv && ConnectionInfo()) {
    4157             :     // the SNI is also implicitly in this list, so consult that too
    4158           0 :     nsHttpConnectionInfo *ci = ConnectionInfo();
    4159           0 :     rv = nsCString(hostname).EqualsIgnoreCase(ci->Origin()) && (port == ci->OriginPort());
    4160           0 :     LOG3(("TestOriginFrame() %p sni test %d\n", this, rv));
    4161             :   }
    4162           0 :   return rv;
    4163             : }
    4164             : 
    4165             : bool
    4166           0 : Http2Session::TestJoinConnection(const nsACString &hostname, int32_t port)
    4167             : {
    4168           0 :   return RealJoinConnection(hostname, port, true);
    4169             : }
    4170             : 
    4171             : bool
    4172           0 : Http2Session::JoinConnection(const nsACString &hostname, int32_t port)
    4173             : {
    4174           0 :   return RealJoinConnection(hostname, port, false);
    4175             : }
    4176             : 
    4177             : bool
    4178           0 : Http2Session::RealJoinConnection(const nsACString &hostname, int32_t port,
    4179             :                                  bool justKidding)
    4180             : {
    4181           0 :   if (!mConnection || mClosed || mShouldGoAway) {
    4182           0 :     return false;
    4183             :   }
    4184             : 
    4185           0 :   nsHttpConnectionInfo *ci = ConnectionInfo();
    4186           0 :   if (nsCString(hostname).EqualsIgnoreCase(ci->Origin()) && (port == ci->OriginPort())) {
    4187           0 :     return true;
    4188             :  }
    4189             : 
    4190           0 :   if (!mReceivedSettings) {
    4191           0 :     return false;
    4192             :   }
    4193             : 
    4194           0 :   if (mOriginFrameActivated) {
    4195           0 :     bool originFrameResult = TestOriginFrame(hostname, port);
    4196           0 :     if (!originFrameResult) {
    4197           0 :       return false;
    4198             :     }
    4199             :   } else {
    4200           0 :     LOG3(("JoinConnection %p no origin frame check used.\n", this));
    4201             :   }
    4202             : 
    4203           0 :   nsAutoCString key(hostname);
    4204           0 :   key.Append(':');
    4205           0 :   key.Append(justKidding ? 'k' : '.');
    4206           0 :   key.AppendInt(port);
    4207             :   bool cachedResult;
    4208           0 :   if (mJoinConnectionCache.Get(key, &cachedResult)) {
    4209           0 :     LOG(("joinconnection [%p %s] %s result=%d cache\n",
    4210             :          this, ConnectionInfo()->HashKey().get(), key.get(),
    4211             :          cachedResult));
    4212           0 :     return cachedResult;
    4213             :   }
    4214             : 
    4215             :   nsresult rv;
    4216           0 :   bool isJoined = false;
    4217             : 
    4218           0 :   nsCOMPtr<nsISupports> securityInfo;
    4219           0 :   nsCOMPtr<nsISSLSocketControl> sslSocketControl;
    4220             : 
    4221           0 :   mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
    4222           0 :   sslSocketControl = do_QueryInterface(securityInfo, &rv);
    4223           0 :   if (NS_FAILED(rv) || !sslSocketControl) {
    4224           0 :     return false;
    4225             :   }
    4226             : 
    4227             :   // try all the coalescable versions we support.
    4228           0 :   const SpdyInformation *info = gHttpHandler->SpdyInfo();
    4229             :   static_assert(SpdyInformation::kCount == 1, "assume 1 alpn version");
    4230           0 :   bool joinedReturn = false;
    4231           0 :   if (info->ProtocolEnabled(0)) {
    4232           0 :     if (justKidding) {
    4233           0 :       rv = sslSocketControl->TestJoinConnection(info->VersionString[0],
    4234           0 :                                                 hostname, port, &isJoined);
    4235             :     } else {
    4236           0 :       rv = sslSocketControl->JoinConnection(info->VersionString[0],
    4237           0 :                                             hostname, port, &isJoined);
    4238             :     }
    4239           0 :     if (NS_SUCCEEDED(rv) && isJoined) {
    4240           0 :       joinedReturn = true;
    4241             :     }
    4242             :   }
    4243             : 
    4244           0 :   LOG(("joinconnection [%p %s] %s result=%d lookup\n",
    4245             :        this, ConnectionInfo()->HashKey().get(), key.get(), joinedReturn));
    4246           0 :   mJoinConnectionCache.Put(key, joinedReturn);
    4247           0 :   if (!justKidding) {
    4248             :     // cache a kidding entry too as this one is good for both
    4249           0 :     nsAutoCString key2(hostname);
    4250           0 :     key2.Append(':');
    4251           0 :     key2.Append('k');
    4252           0 :     key2.AppendInt(port);
    4253           0 :     if (!mJoinConnectionCache.Get(key2)) {
    4254           0 :       mJoinConnectionCache.Put(key2, joinedReturn);
    4255             :     }
    4256             :   }
    4257           0 :   return joinedReturn;
    4258             : }
    4259             : 
    4260             : } // namespace net
    4261             : } // namespace mozilla

Generated by: LCOV version 1.13