netwerk/protocol/http/Http2Session.cpp

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

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

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

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set sw=2 ts=8 et tw=80 : */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // HttpLog.h should generally be included first
     8 #include "HttpLog.h"
    10 // Log on level :5, instead of default :4.
    11 #undef LOG
    12 #define LOG(args) LOG5(args)
    13 #undef LOG_ENABLED
    14 #define LOG_ENABLED() LOG5_ENABLED()
    16 #include <algorithm>
    18 #include "Http2Session.h"
    19 #include "Http2Stream.h"
    20 #include "Http2Push.h"
    22 #include "mozilla/Telemetry.h"
    23 #include "mozilla/Preferences.h"
    24 #include "nsHttp.h"
    25 #include "nsHttpHandler.h"
    26 #include "nsHttpConnection.h"
    27 #include "nsILoadGroup.h"
    28 #include "nsISSLSocketControl.h"
    29 #include "nsISSLStatus.h"
    30 #include "nsISSLStatusProvider.h"
    31 #include "prprf.h"
    32 #include "prnetdb.h"
    33 #include "sslt.h"
    35 #ifdef DEBUG
    36 // defined by the socket transport service while active
    37 extern PRThread *gSocketThread;
    38 #endif
    40 namespace mozilla {
    41 namespace net {
    43 // Http2Session has multiple inheritance of things that implement
    44 // nsISupports, so this magic is taken from nsHttpPipeline that
    45 // implements some of the same abstract classes.
    46 NS_IMPL_ADDREF(Http2Session)
    47 NS_IMPL_RELEASE(Http2Session)
    48 NS_INTERFACE_MAP_BEGIN(Http2Session)
    49 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
    50 NS_INTERFACE_MAP_END
    52 // "magic" refers to the string that preceeds HTTP/2 on the wire
    53 // to help find any intermediaries speaking an older version of HTTP
    54 const uint8_t Http2Session::kMagicHello[] = {
    55   0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
    56   0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
    57   0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
    58 };
    60 #define RETURN_SESSION_ERROR(o,x)  \
    61 do {                             \
    62   (o)->mGoAwayReason = (x);      \
    63   return NS_ERROR_ILLEGAL_VALUE; \
    64   } while (0)
    66 Http2Session::Http2Session(nsAHttpTransaction *aHttpTransaction,
    67                            nsISocketTransport *aSocketTransport,
    68                            int32_t firstPriority)
    69   : mSocketTransport(aSocketTransport),
    70   mSegmentReader(nullptr),
    71   mSegmentWriter(nullptr),
    72   mNextStreamID(3), // 1 is reserved for Updgrade handshakes
    73   mConcurrentHighWater(0),
    74   mDownstreamState(BUFFERING_OPENING_SETTINGS),
    75   mInputFrameBufferSize(kDefaultBufferSize),
    76   mInputFrameBufferUsed(0),
    77   mInputFrameFinal(false),
    78   mInputFrameDataStream(nullptr),
    79   mNeedsCleanup(nullptr),
    80   mDownstreamRstReason(NO_HTTP_ERROR),
    81   mExpectedHeaderID(0),
    82   mExpectedPushPromiseID(0),
    83   mContinuedPromiseStream(0),
    84   mShouldGoAway(false),
    85   mClosed(false),
    86   mCleanShutdown(false),
    87   mTLSProfileConfirmed(false),
    88   mGoAwayReason(NO_HTTP_ERROR),
    89   mGoAwayID(0),
    90   mOutgoingGoAwayID(0),
    91   mMaxConcurrent(kDefaultMaxConcurrent),
    92   mConcurrent(0),
    93   mServerPushedResources(0),
    94   mServerInitialStreamWindow(kDefaultRwin),
    95   mLocalSessionWindow(kDefaultRwin),
    96   mServerSessionWindow(kDefaultRwin),
    97   mOutputQueueSize(kDefaultQueueSize),
    98   mOutputQueueUsed(0),
    99   mOutputQueueSent(0),
   100   mLastReadEpoch(PR_IntervalNow()),
   101   mPingSentEpoch(0)
   102 {
   103     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   105     static uint64_t sSerial;
   106     mSerial = ++sSerial;
   108     LOG3(("Http2Session::Http2Session %p transaction 1 = %p serial=0x%X\n",
   109           this, aHttpTransaction, mSerial));
   111     mConnection = aHttpTransaction->Connection();
   112     mInputFrameBuffer = new char[mInputFrameBufferSize];
   113     mOutputQueueBuffer = new char[mOutputQueueSize];
   114     mDecompressBuffer.SetCapacity(kDefaultBufferSize);
   115     mDecompressor.SetCompressor(&mCompressor);
   117     mPushAllowance = gHttpHandler->SpdyPushAllowance();
   119     mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
   120     SendHello();
   122     if (!aHttpTransaction->IsNullTransaction())
   123       AddStream(aHttpTransaction, firstPriority);
   124     mLastDataReadEpoch = mLastReadEpoch;
   126     mPingThreshold = gHttpHandler->SpdyPingThreshold();
   127 }
   129 PLDHashOperator
   130 Http2Session::ShutdownEnumerator(nsAHttpTransaction *key,
   131                                  nsAutoPtr<Http2Stream> &stream,
   132                                  void *closure)
   133 {
   134   Http2Session *self = static_cast<Http2Session *>(closure);
   136   // On a clean server hangup the server sets the GoAwayID to be the ID of
   137   // the last transaction it processed. If the ID of stream in the
   138   // local stream is greater than that it can safely be restarted because the
   139   // server guarantees it was not partially processed. Streams that have not
   140   // registered an ID haven't actually been sent yet so they can always be
   141   // restarted.
   142   if (self->mCleanShutdown &&
   143       (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID())) {
   144     self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted
   145   } else {
   146     self->CloseStream(stream, NS_ERROR_ABORT);
   147   }
   149   return PL_DHASH_NEXT;
   150 }
   152 PLDHashOperator
   153 Http2Session::GoAwayEnumerator(nsAHttpTransaction *key,
   154                                nsAutoPtr<Http2Stream> &stream,
   155                                void *closure)
   156 {
   157   Http2Session *self = static_cast<Http2Session *>(closure);
   159   // these streams were not processed by the server and can be restarted.
   160   // Do that after the enumerator completes to avoid the risk of
   161   // a restart event re-entrantly modifying this hash. Be sure not to restart
   162   // a pushed (even numbered) stream
   163   if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) ||
   164       !stream->HasRegisteredID()) {
   165     self->mGoAwayStreamsToRestart.Push(stream);
   166   }
   168   return PL_DHASH_NEXT;
   169 }
   171 Http2Session::~Http2Session()
   172 {
   173   LOG3(("Http2Session::~Http2Session %p mDownstreamState=%X",
   174         this, mDownstreamState));
   176   mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
   177   Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
   178   Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
   179   Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
   180                         mServerPushedResources);
   181 }
   183 void
   184 Http2Session::LogIO(Http2Session *self, Http2Stream *stream,
   185                     const char *label,
   186                     const char *data, uint32_t datalen)
   187 {
   188   if (!LOG4_ENABLED())
   189     return;
   191   LOG4(("Http2Session::LogIO %p stream=%p id=0x%X [%s]",
   192         self, stream, stream ? stream->StreamID() : 0, label));
   194   // Max line is (16 * 3) + 10(prefix) + newline + null
   195   char linebuf[128];
   196   uint32_t index;
   197   char *line = linebuf;
   199   linebuf[127] = 0;
   201   for (index = 0; index < datalen; ++index) {
   202     if (!(index % 16)) {
   203       if (index) {
   204         *line = 0;
   205         LOG4(("%s", linebuf));
   206       }
   207       line = linebuf;
   208       PR_snprintf(line, 128, "%08X: ", index);
   209       line += 10;
   210     }
   211     PR_snprintf(line, 128 - (line - linebuf), "%02X ",
   212                 (reinterpret_cast<const uint8_t *>(data))[index]);
   213     line += 3;
   214   }
   215   if (index) {
   216     *line = 0;
   217     LOG4(("%s", linebuf));
   218   }
   219 }
   221 typedef nsresult (*Http2ControlFx) (Http2Session *self);
   222 static Http2ControlFx sControlFunctions[] = {
   223   nullptr, // type 0 data is not a control function
   224   Http2Session::RecvHeaders,
   225   Http2Session::RecvPriority,
   226   Http2Session::RecvRstStream,
   227   Http2Session::RecvSettings,
   228   Http2Session::RecvPushPromise,
   229   Http2Session::RecvPing,
   230   Http2Session::RecvGoAway,
   231   Http2Session::RecvWindowUpdate,
   232   Http2Session::RecvContinuation
   233 };
   235 bool
   236 Http2Session::RoomForMoreConcurrent()
   237 {
   238   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   239   return (mConcurrent < mMaxConcurrent);
   240 }
   242 bool
   243 Http2Session::RoomForMoreStreams()
   244 {
   245   if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
   246     return false;
   248   return !mShouldGoAway;
   249 }
   251 PRIntervalTime
   252 Http2Session::IdleTime()
   253 {
   254   return PR_IntervalNow() - mLastDataReadEpoch;
   255 }
   257 uint32_t
   258 Http2Session::ReadTimeoutTick(PRIntervalTime now)
   259 {
   260   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   262   LOG3(("Http2Session::ReadTimeoutTick %p delta since last read %ds\n",
   263        this, PR_IntervalToSeconds(now - mLastReadEpoch)));
   265   if (!mPingThreshold)
   266     return UINT32_MAX;
   268   if ((now - mLastReadEpoch) < mPingThreshold) {
   269     // recent activity means ping is not an issue
   270     if (mPingSentEpoch)
   271       mPingSentEpoch = 0;
   273     return PR_IntervalToSeconds(mPingThreshold) -
   274       PR_IntervalToSeconds(now - mLastReadEpoch);
   275   }
   277   if (mPingSentEpoch) {
   278     LOG3(("Http2Session::ReadTimeoutTick %p handle outstanding ping\n"));
   279     if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
   280       LOG3(("Http2Session::ReadTimeoutTick %p Ping Timer Exhaustion\n", this));
   281       mPingSentEpoch = 0;
   282       Close(NS_ERROR_NET_TIMEOUT);
   283       return UINT32_MAX;
   284     }
   285     return 1; // run the tick aggressively while ping is outstanding
   286   }
   288   LOG3(("Http2Session::ReadTimeoutTick %p generating ping\n", this));
   290   mPingSentEpoch = PR_IntervalNow();
   291   if (!mPingSentEpoch)
   292     mPingSentEpoch = 1; // avoid the 0 sentinel value
   293   GeneratePing(false);
   294   ResumeRecv(); // read the ping reply
   296   // Check for orphaned push streams. This looks expensive, but generally the
   297   // list is empty.
   298   Http2PushedStream *deleteMe;
   299   TimeStamp timestampNow;
   300   do {
   301     deleteMe = nullptr;
   303     for (uint32_t index = mPushedStreams.Length();
   304          index > 0 ; --index) {
   305       Http2PushedStream *pushedStream = mPushedStreams[index - 1];
   307       if (timestampNow.IsNull())
   308         timestampNow = TimeStamp::Now(); // lazy initializer
   310       // if stream finished, but is not connected, and its been like that for
   311       // long then cleanup the stream.
   312       if (pushedStream->IsOrphaned(timestampNow))
   313       {
   314         LOG3(("Http2Session Timeout Pushed Stream %p 0x%X\n",
   315               this, pushedStream->StreamID()));
   316         deleteMe = pushedStream;
   317         break; // don't CleanupStream() while iterating this vector
   318       }
   319     }
   320     if (deleteMe)
   321       CleanupStream(deleteMe, NS_ERROR_ABORT, CANCEL_ERROR);
   323   } while (deleteMe);
   325   return 1; // run the tick aggressively while ping is outstanding
   326 }
   328 uint32_t
   329 Http2Session::RegisterStreamID(Http2Stream *stream, uint32_t aNewID)
   330 {
   331   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   332   MOZ_ASSERT(mNextStreamID < 0xfffffff0,
   333              "should have stopped admitting streams");
   334   MOZ_ASSERT(!(aNewID & 1),
   335              "0 for autoassign pull, otherwise explicit even push assignment");
   337   if (!aNewID) {
   338     // auto generate a new pull stream ID
   339     aNewID = mNextStreamID;
   340     MOZ_ASSERT(aNewID & 1, "pull ID must be odd.");
   341     mNextStreamID += 2;
   342   }
   344   LOG3(("Http2Session::RegisterStreamID session=%p stream=%p id=0x%X "
   345         "concurrent=%d",this, stream, aNewID, mConcurrent));
   347   // We've used up plenty of ID's on this session. Start
   348   // moving to a new one before there is a crunch involving
   349   // server push streams or concurrent non-registered submits
   350   if (aNewID >= kMaxStreamID)
   351     mShouldGoAway = true;
   353   // integrity check
   354   if (mStreamIDHash.Get(aNewID)) {
   355     LOG3(("   New ID already present\n"));
   356     MOZ_ASSERT(false, "New ID already present in mStreamIDHash");
   357     mShouldGoAway = true;
   358     return kDeadStreamID;
   359   }
   361   mStreamIDHash.Put(aNewID, stream);
   362   return aNewID;
   363 }
   365 bool
   366 Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
   367                         int32_t aPriority)
   368 {
   369   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   371   // integrity check
   372   if (mStreamTransactionHash.Get(aHttpTransaction)) {
   373     LOG3(("   New transaction already present\n"));
   374     MOZ_ASSERT(false, "AddStream duplicate transaction pointer");
   375     return false;
   376   }
   378   aHttpTransaction->SetConnection(this);
   379   Http2Stream *stream = new Http2Stream(aHttpTransaction, this, aPriority);
   381   LOG3(("Http2Session::AddStream session=%p stream=%p NextID=0x%X (tentative)",
   382         this, stream, mNextStreamID));
   384   mStreamTransactionHash.Put(aHttpTransaction, stream);
   386   if (RoomForMoreConcurrent()) {
   387     LOG3(("Http2Session::AddStream %p stream %p activated immediately.",
   388           this, stream));
   389     ActivateStream(stream);
   390   } else {
   391     LOG3(("Http2Session::AddStream %p stream %p queued.", this, stream));
   392     mQueuedStreams.Push(stream);
   393   }
   395   if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) {
   396     LOG3(("Http2Session::AddStream %p transaction %p forces keep-alive off.\n",
   397           this, aHttpTransaction));
   398     DontReuse();
   399   }
   401   return true;
   402 }
   404 void
   405 Http2Session::ActivateStream(Http2Stream *stream)
   406 {
   407   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   408   MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
   409              "Do not activate pushed streams");
   411   MOZ_ASSERT(!stream->CountAsActive());
   412   stream->SetCountAsActive(true);
   413   ++mConcurrent;
   415   if (mConcurrent > mConcurrentHighWater)
   416     mConcurrentHighWater = mConcurrent;
   417   LOG3(("Http2Session::AddStream %p activating stream %p Currently %d "
   418         "streams in session, high water mark is %d",
   419         this, stream, mConcurrent, mConcurrentHighWater));
   421   mReadyForWrite.Push(stream);
   422   SetWriteCallbacks();
   424   // Kick off the headers transmit without waiting for the poll loop
   425   // This won't work for stream id=1 because there is no segment reader
   426   // yet.
   427   if (mSegmentReader) {
   428     uint32_t countRead;
   429     ReadSegments(nullptr, kDefaultBufferSize, &countRead);
   430   }
   431 }
   433 void
   434 Http2Session::ProcessPending()
   435 {
   436   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   438   while (RoomForMoreConcurrent()) {
   439     Http2Stream *stream = static_cast<Http2Stream *>(mQueuedStreams.PopFront());
   440     if (!stream)
   441       return;
   442     LOG3(("Http2Session::ProcessPending %p stream %p activated from queue.",
   443           this, stream));
   444     ActivateStream(stream);
   445   }
   446 }
   448 nsresult
   449 Http2Session::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
   450                           uint32_t count, uint32_t *countWritten)
   451 {
   452   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   454   if (!count) {
   455     *countWritten = 0;
   456     return NS_OK;
   457   }
   459   nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
   460   if (NS_SUCCEEDED(rv) && *countWritten > 0)
   461     mLastReadEpoch = PR_IntervalNow();
   462   return rv;
   463 }
   465 void
   466 Http2Session::SetWriteCallbacks()
   467 {
   468   if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
   469     mConnection->ResumeSend();
   470 }
   472 void
   473 Http2Session::RealignOutputQueue()
   474 {
   475   mOutputQueueUsed -= mOutputQueueSent;
   476   memmove(mOutputQueueBuffer.get(),
   477           mOutputQueueBuffer.get() + mOutputQueueSent,
   478           mOutputQueueUsed);
   479   mOutputQueueSent = 0;
   480 }
   482 void
   483 Http2Session::FlushOutputQueue()
   484 {
   485   if (!mSegmentReader || !mOutputQueueUsed)
   486     return;
   488   nsresult rv;
   489   uint32_t countRead;
   490   uint32_t avail = mOutputQueueUsed - mOutputQueueSent;
   492   rv = mSegmentReader->
   493     OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
   494                   &countRead);
   495   LOG3(("Http2Session::FlushOutputQueue %p sz=%d rv=%x actual=%d",
   496         this, avail, rv, countRead));
   498   // Dont worry about errors on write, we will pick this up as a read error too
   499   if (NS_FAILED(rv))
   500     return;
   502   if (countRead == avail) {
   503     mOutputQueueUsed = 0;
   504     mOutputQueueSent = 0;
   505     return;
   506   }
   508   mOutputQueueSent += countRead;
   510   // If the output queue is close to filling up and we have sent out a good
   511   // chunk of data from the beginning then realign it.
   513   if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
   514       ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
   515     RealignOutputQueue();
   516   }
   517 }
   519 void
   520 Http2Session::DontReuse()
   521 {
   522   mShouldGoAway = true;
   523   if (!mStreamTransactionHash.Count())
   524     Close(NS_OK);
   525 }
   527 uint32_t
   528 Http2Session::GetWriteQueueSize()
   529 {
   530   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   532   return mReadyForWrite.GetSize();
   533 }
   535 void
   536 Http2Session::ChangeDownstreamState(enum internalStateType newState)
   537 {
   538   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   540   LOG3(("Http2Stream::ChangeDownstreamState() %p from %X to %X",
   541         this, mDownstreamState, newState));
   542   mDownstreamState = newState;
   543 }
   545 void
   546 Http2Session::ResetDownstreamState()
   547 {
   548   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   550   LOG3(("Http2Stream::ResetDownstreamState() %p", this));
   551   ChangeDownstreamState(BUFFERING_FRAME_HEADER);
   553   if (mInputFrameFinal && mInputFrameDataStream) {
   554     mInputFrameFinal = false;
   555     LOG3(("  SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID()));
   556     mInputFrameDataStream->SetRecvdFin(true);
   557     MaybeDecrementConcurrent(mInputFrameDataStream);
   558   }
   559   mInputFrameBufferUsed = 0;
   560   mInputFrameDataStream = nullptr;
   561 }
   563 template<typename T> void
   564 Http2Session::EnsureBuffer(nsAutoArrayPtr<T> &buf, uint32_t newSize,
   565                            uint32_t preserve, uint32_t &objSize)
   566 {
   567   if (objSize >= newSize)
   568     return;
   570   // Leave a little slop on the new allocation - add 2KB to
   571   // what we need and then round the result up to a 4KB (page)
   572   // boundary.
   574   objSize = (newSize + 2048 + 4095) & ~4095;
   576   static_assert(sizeof(T) == 1, "sizeof(T) must be 1");
   577   nsAutoArrayPtr<T> tmp(new T[objSize]);
   578   memcpy(tmp, buf, preserve);
   579   buf = tmp;
   580 }
   582 // Instantiate supported templates explicitly.
   583 template void
   584 Http2Session::EnsureBuffer(nsAutoArrayPtr<char> &buf, uint32_t newSize,
   585                                   uint32_t preserve, uint32_t &objSize);
   587 template void
   588 Http2Session::EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf, uint32_t newSize,
   589                                   uint32_t preserve, uint32_t &objSize);
   591 // call with data length (i.e. 0 for 0 data bytes - ignore 8 byte header)
   592 // dest must have 8 bytes of allocated space
   593 template<typename charType> void
   594 Http2Session::CreateFrameHeader(charType dest, uint16_t frameLength,
   595                                 uint8_t frameType, uint8_t frameFlags,
   596                                 uint32_t streamID)
   597 {
   598   MOZ_ASSERT(frameLength <= kMaxFrameData, "framelength too large");
   599   MOZ_ASSERT(!(streamID & 0x80000000));
   601   frameLength = PR_htons(frameLength);
   602   streamID = PR_htonl(streamID);
   604   memcpy(dest, &frameLength, 2);
   605   dest[2] = frameType;
   606   dest[3] = frameFlags;
   607   memcpy(dest + 4, &streamID, 4);
   608 }
   610 char *
   611 Http2Session::EnsureOutputBuffer(uint32_t spaceNeeded)
   612 {
   613   // this is an infallible allocation (if an allocation is
   614   // needed, which is probably isn't)
   615   EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + spaceNeeded,
   616                mOutputQueueUsed, mOutputQueueSize);
   617   return mOutputQueueBuffer.get() + mOutputQueueUsed;
   618 }
   620 template void
   621 Http2Session::CreateFrameHeader(char *dest, uint16_t frameLength,
   622                                 uint8_t frameType, uint8_t frameFlags,
   623                                 uint32_t streamID);
   625 template void
   626 Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength,
   627                                 uint8_t frameType, uint8_t frameFlags,
   628                                 uint32_t streamID);
   630 void
   631 Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream)
   632 {
   633   LOG3(("MaybeDecrementConcurrent %p id=0x%X concurrent=%d active=%d\n",
   634         this, aStream->StreamID(), mConcurrent, aStream->CountAsActive()));
   636   if (!aStream->CountAsActive())
   637     return;
   639   MOZ_ASSERT(mConcurrent);
   640   aStream->SetCountAsActive(false);
   641   --mConcurrent;
   642   ProcessPending();
   643 }
   645 // Need to decompress some data in order to keep the compression
   646 // context correct, but we really don't care what the result is
   647 nsresult
   648 Http2Session::UncompressAndDiscard()
   649 {
   650   nsresult rv;
   651   nsAutoCString trash;
   653   rv = mDecompressor.DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(mDecompressBuffer.BeginReading()),
   654                                        mDecompressBuffer.Length(), trash);
   655   mDecompressBuffer.Truncate();
   656   if (NS_FAILED(rv)) {
   657     LOG3(("Http2Session::UncompressAndDiscard %p Compression Error\n",
   658           this));
   659     mGoAwayReason = COMPRESSION_ERROR;
   660     return rv;
   661   }
   662   return NS_OK;
   663 }
   665 void
   666 Http2Session::GeneratePing(bool isAck)
   667 {
   668   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   669   LOG3(("Http2Session::GeneratePing %p isAck=%d\n", this, isAck));
   671   char *packet = EnsureOutputBuffer(16);
   672   mOutputQueueUsed += 16;
   674   if (isAck) {
   675     CreateFrameHeader(packet, 8, FRAME_TYPE_PING, kFlag_ACK, 0);
   676     memcpy(packet + 8, mInputFrameBuffer.get() + 8, 8);
   677   } else {
   678     CreateFrameHeader(packet, 8, FRAME_TYPE_PING, 0, 0);
   679     memset(packet + 8, 0, 8);
   680   }
   682   LogIO(this, nullptr, "Generate Ping", packet, 16);
   683   FlushOutputQueue();
   684 }
   686 void
   687 Http2Session::GenerateSettingsAck()
   688 {
   689   // need to generate ack of this settings frame
   690   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   691   LOG3(("Http2Session::GenerateSettingsAck %p\n", this));
   693   char *packet = EnsureOutputBuffer(8);
   694   mOutputQueueUsed += 8;
   695   CreateFrameHeader(packet, 0, FRAME_TYPE_SETTINGS, kFlag_ACK, 0);
   696   LogIO(this, nullptr, "Generate Settings ACK", packet, 8);
   697   FlushOutputQueue();
   698 }
   700 void
   701 Http2Session::GeneratePriority(uint32_t aID, uint32_t aPriority)
   702 {
   703   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   704   LOG3(("Http2Session::GeneratePriority %p %X %X\n",
   705         this, aID, aPriority));
   707   char *packet = EnsureOutputBuffer(12);
   708   mOutputQueueUsed += 12;
   710   CreateFrameHeader(packet, 4, FRAME_TYPE_PRIORITY, 0, aID);
   711   aPriority = PR_htonl(aPriority);
   712   memcpy(packet + 8, &aPriority, 4);
   713   LogIO(this, nullptr, "Generate Priority", packet, 12);
   714   FlushOutputQueue();
   715 }
   717 void
   718 Http2Session::GenerateRstStream(uint32_t aStatusCode, uint32_t aID)
   719 {
   720   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   722   // make sure we don't do this twice for the same stream (at least if we
   723   // have a stream entry for it)
   724   Http2Stream *stream = mStreamIDHash.Get(aID);
   725   if (stream) {
   726     if (stream->SentReset())
   727       return;
   728     stream->SetSentReset(true);
   729   }
   731   LOG3(("Http2Session::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
   733   char *packet = EnsureOutputBuffer(12);
   734   mOutputQueueUsed += 12;
   735   CreateFrameHeader(packet, 4, FRAME_TYPE_RST_STREAM, 0, aID);
   737   aStatusCode = PR_htonl(aStatusCode);
   738   memcpy(packet + 8, &aStatusCode, 4);
   740   LogIO(this, nullptr, "Generate Reset", packet, 12);
   741   FlushOutputQueue();
   742 }
   744 void
   745 Http2Session::GenerateGoAway(uint32_t aStatusCode)
   746 {
   747   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   748   LOG3(("Http2Session::GenerateGoAway %p code=%X\n", this, aStatusCode));
   750   char *packet = EnsureOutputBuffer(16);
   751   mOutputQueueUsed += 16;
   753   memset(packet + 8, 0, 8);
   754   CreateFrameHeader(packet, 8, FRAME_TYPE_GOAWAY, 0, 0);
   756   // last-good-stream-id are bytes 8-11 reflecting pushes
   757   uint32_t goAway = PR_htonl(mOutgoingGoAwayID);
   758   memcpy (packet + 7, &goAway, 4);
   760   // bytes 12-15 are the status code.
   761   aStatusCode = PR_htonl(aStatusCode);
   762   memcpy(packet + 12, &aStatusCode, 4);
   764   LogIO(this, nullptr, "Generate GoAway", packet, 16);
   765   FlushOutputQueue();
   766 }
   768 // The Hello is comprised of 24 octets of magic, which are designed to
   769 // flush out silent but broken intermediaries, followed by a settings
   770 // frame which sets a small flow control window for pushes and a
   771 // window update frame which creates a large session flow control window
   772 void
   773 Http2Session::SendHello()
   774 {
   775   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   776   LOG3(("Http2Session::SendHello %p\n", this));
   778   // sized for magic + 2 settings and a session window update to follow
   779   // 24 magic, 23 for settings (8 header + 3 settings @5), 12 for window update
   780   static const uint32_t maxSettings = 3;
   781   static const uint32_t maxDataLen = 24 + 8 + maxSettings * 5 + 12;
   782   char *packet = EnsureOutputBuffer(maxDataLen);
   783   memcpy(packet, kMagicHello, 24);
   784   mOutputQueueUsed += 24;
   785   LogIO(this, nullptr, "Magic Connection Header", packet, 24);
   787   packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
   788   memset(packet, 0, maxDataLen - 24);
   790   // frame header will be filled in after we know how long the frame is
   791   uint8_t numberOfEntries = 0;
   793   // entries need to be listed in order by ID
   794   // 1st entry is bytes 8 to 12
   795   // 2nd entry is bytes 13 to 17
   796   // 3rd entry is bytes 18 to 22
   798   if (!gHttpHandler->AllowPush()) {
   799     // If we don't support push then set MAX_CONCURRENT to 0 and also
   800     // set ENABLE_PUSH to 0
   801     packet[8 + 5 * numberOfEntries] = SETTINGS_TYPE_ENABLE_PUSH;
   802     // The value portion of the setting pair is already initialized to 0
   803     numberOfEntries++;
   805     packet[8 + 5 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT;
   806     // The value portion of the setting pair is already initialized to 0
   807     numberOfEntries++;
   808   }
   810   // Advertise the Push RWIN for the session, and on each new pull stream
   811   // send a window update with END_FLOW_CONTROL
   812   packet[8 + 5 * numberOfEntries] = SETTINGS_TYPE_INITIAL_WINDOW;
   813   uint32_t rwin = PR_htonl(mPushAllowance);
   814   memcpy(packet + 9 + 5 * numberOfEntries, &rwin, 4);
   815   numberOfEntries++;
   817   MOZ_ASSERT(numberOfEntries <= maxSettings);
   818   uint32_t dataLen = 5 * numberOfEntries;
   819   CreateFrameHeader(packet, dataLen, FRAME_TYPE_SETTINGS, 0, 0);
   820   mOutputQueueUsed += 8 + dataLen;
   822   LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen);
   824   // now bump the local session window from 64KB
   825   uint32_t sessionWindowBump = ASpdySession::kInitialRwin - kDefaultRwin;
   826   if (kDefaultRwin >= ASpdySession::kInitialRwin)
   827     goto sendHello_complete;
   829   // send a window update for the session (Stream 0) for something large
   830   sessionWindowBump = PR_htonl(sessionWindowBump);
   831   mLocalSessionWindow = ASpdySession::kInitialRwin;
   833   packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
   834   CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
   835   mOutputQueueUsed += 12;
   836   memcpy(packet + 8, &sessionWindowBump, 4);
   838   LOG3(("Session Window increase at start of session %p %u\n",
   839         this, PR_ntohl(sessionWindowBump)));
   840   LogIO(this, nullptr, "Session Window Bump ", packet, 12);
   842 sendHello_complete:
   843   FlushOutputQueue();
   844 }
   846 // perform a bunch of integrity checks on the stream.
   847 // returns true if passed, false (plus LOG and ABORT) if failed.
   848 bool
   849 Http2Session::VerifyStream(Http2Stream *aStream, uint32_t aOptionalID = 0)
   850 {
   851   // This is annoying, but at least it is O(1)
   852   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   854 #ifndef DEBUG
   855   // Only do the real verification in debug builds
   856   return true;
   857 #endif
   859   if (!aStream)
   860     return true;
   862   uint32_t test = 0;
   864   do {
   865     if (aStream->StreamID() == kDeadStreamID)
   866       break;
   868     nsAHttpTransaction *trans = aStream->Transaction();
   870     test++;
   871     if (!trans)
   872       break;
   874     test++;
   875     if (mStreamTransactionHash.Get(trans) != aStream)
   876       break;
   878     if (aStream->StreamID()) {
   879       Http2Stream *idStream = mStreamIDHash.Get(aStream->StreamID());
   881       test++;
   882       if (idStream != aStream)
   883         break;
   885       if (aOptionalID) {
   886         test++;
   887         if (idStream->StreamID() != aOptionalID)
   888           break;
   889       }
   890     }
   892     // tests passed
   893     return true;
   894   } while (0);
   896   LOG3(("Http2Session %p VerifyStream Failure %p stream->id=0x%X "
   897        "optionalID=0x%X trans=%p test=%d\n",
   898        this, aStream, aStream->StreamID(),
   899        aOptionalID, aStream->Transaction(), test));
   901   MOZ_ASSERT(false, "VerifyStream");
   902   return false;
   903 }
   905 void
   906 Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
   907                             errorType aResetCode)
   908 {
   909   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   910   LOG3(("Http2Session::CleanupStream %p %p 0x%X %X\n",
   911         this, aStream, aStream ? aStream->StreamID() : 0, aResult));
   912   if (!aStream) {
   913     return;
   914   }
   916   Http2PushedStream *pushSource = nullptr;
   918   if (NS_SUCCEEDED(aResult) && aStream->DeferCleanupOnSuccess()) {
   919     LOG3(("Http2Session::CleanupStream 0x%X deferred\n", aStream->StreamID()));
   920     return;
   921   }
   923   if (!VerifyStream(aStream)) {
   924     LOG3(("Http2Session::CleanupStream failed to verify stream\n"));
   925     return;
   926   }
   928   pushSource = aStream->PushSource();
   930   if (!aStream->RecvdFin() && !aStream->RecvdReset() && aStream->StreamID()) {
   931     LOG3(("Stream had not processed recv FIN, sending RST code %X\n",
   932           aResetCode));
   933     GenerateRstStream(aResetCode, aStream->StreamID());
   934   }
   936   CloseStream(aStream, aResult);
   938   // Remove the stream from the ID hash table and, if an even id, the pushed
   939   // table too.
   940   uint32_t id = aStream->StreamID();
   941   if (id > 0) {
   942     mStreamIDHash.Remove(id);
   943     if (!(id & 1))
   944       mPushedStreams.RemoveElement(aStream);
   945   }
   947   RemoveStreamFromQueues(aStream);
   949   // removing from the stream transaction hash will
   950   // delete the Http2Stream and drop the reference to
   951   // its transaction
   952   mStreamTransactionHash.Remove(aStream->Transaction());
   954   if (mShouldGoAway && !mStreamTransactionHash.Count())
   955     Close(NS_OK);
   957   if (pushSource) {
   958     pushSource->SetDeferCleanupOnSuccess(false);
   959     CleanupStream(pushSource, aResult, aResetCode);
   960   }
   961 }
   963 static void RemoveStreamFromQueue(Http2Stream *aStream, nsDeque &queue)
   964 {
   965   uint32_t size = queue.GetSize();
   966   for (uint32_t count = 0; count < size; ++count) {
   967     Http2Stream *stream = static_cast<Http2Stream *>(queue.PopFront());
   968     if (stream != aStream)
   969       queue.Push(stream);
   970   }
   971 }
   973 void
   974 Http2Session::RemoveStreamFromQueues(Http2Stream *aStream)
   975 {
   976   RemoveStreamFromQueue(aStream, mReadyForWrite);
   977   RemoveStreamFromQueue(aStream, mQueuedStreams);
   978   RemoveStreamFromQueue(aStream, mReadyForRead);
   979 }
   981 void
   982 Http2Session::CloseStream(Http2Stream *aStream, nsresult aResult)
   983 {
   984   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   985   LOG3(("Http2Session::CloseStream %p %p 0x%x %X\n",
   986         this, aStream, aStream->StreamID(), aResult));
   988   MaybeDecrementConcurrent(aStream);
   990   // Check if partial frame reader
   991   if (aStream == mInputFrameDataStream) {
   992     LOG3(("Stream had active partial read frame on close"));
   993     ChangeDownstreamState(DISCARDING_DATA_FRAME);
   994     mInputFrameDataStream = nullptr;
   995   }
   997   RemoveStreamFromQueues(aStream);
   999   // Send the stream the close() indication
  1000   aStream->Close(aResult);
  1003 nsresult
  1004 Http2Session::SetInputFrameDataStream(uint32_t streamID)
  1006   mInputFrameDataStream = mStreamIDHash.Get(streamID);
  1007   if (VerifyStream(mInputFrameDataStream, streamID))
  1008     return NS_OK;
  1010   LOG3(("Http2Session::SetInputFrameDataStream failed to verify 0x%X\n",
  1011        streamID));
  1012   mInputFrameDataStream = nullptr;
  1013   return NS_ERROR_UNEXPECTED;
  1016 nsresult
  1017 Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength)
  1019   if (mInputFrameFlags & kFlag_PAD_HIGH) {
  1020     uint8_t paddingHighValue = *reinterpret_cast<uint8_t *>(mInputFrameBuffer + 8);
  1021     paddingLength = static_cast<uint16_t>(paddingHighValue) * 256;
  1022     ++paddingControlBytes;
  1025   if (mInputFrameFlags & kFlag_PAD_LOW) {
  1026     uint8_t paddingLowValue = *reinterpret_cast<uint8_t *>(mInputFrameBuffer + 8 + paddingControlBytes);
  1027     paddingLength += paddingLowValue;
  1028     ++paddingControlBytes;
  1031   if (paddingLength > mInputFrameDataSize) {
  1032     // This is fatal to the session
  1033     LOG3(("Http2Session::RecvHeaders %p stream 0x%x PROTOCOL_ERROR "
  1034           "paddingLength %d > frame size %d\n",
  1035           this, mInputFrameID, paddingLength, mInputFrameDataSize));
  1036     RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
  1039   return NS_OK;
  1042 nsresult
  1043 Http2Session::RecvHeaders(Http2Session *self)
  1045   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_HEADERS);
  1047   // If this doesn't have END_HEADERS set on it then require the next
  1048   // frame to be HEADERS of the same ID
  1049   bool endHeadersFlag = self->mInputFrameFlags & kFlag_END_HEADERS;
  1051   if (endHeadersFlag)
  1052     self->mExpectedHeaderID = 0;
  1053   else
  1054     self->mExpectedHeaderID = self->mInputFrameID;
  1056   uint32_t priorityLen = (self->mInputFrameFlags & kFlag_PRIORITY) ? 4 : 0;
  1057   self->SetInputFrameDataStream(self->mInputFrameID);
  1059   // Find out how much padding this frame has, so we can only extract the real
  1060   // header data from the frame.
  1061   uint16_t paddingLength = 0;
  1062   uint8_t paddingControlBytes = 0;
  1064   nsresult rv = self->ParsePadding(paddingControlBytes, paddingLength);
  1065   if (NS_FAILED(rv)) {
  1066     return rv;
  1069   LOG3(("Http2Session::RecvHeaders %p stream 0x%X priorityLen=%d stream=%p "
  1070         "end_stream=%d end_headers=%d priority_flag=%d paddingLength=%d "
  1071         "pad_high_flag=%d pad_low_flag=%d\n",
  1072         self, self->mInputFrameID, priorityLen, self->mInputFrameDataStream,
  1073         self->mInputFrameFlags & kFlag_END_STREAM,
  1074         self->mInputFrameFlags & kFlag_END_HEADERS,
  1075         self->mInputFrameFlags & kFlag_PRIORITY,
  1076         paddingLength,
  1077         self->mInputFrameFlags & kFlag_PAD_HIGH,
  1078         self->mInputFrameFlags & kFlag_PAD_LOW));
  1080   if (!self->mInputFrameDataStream) {
  1081     // Cannot find stream. We can continue the session, but we need to
  1082     // uncompress the header block to maintain the correct compression context
  1084     LOG3(("Http2Session::RecvHeaders %p lookup mInputFrameID stream "
  1085           "0x%X failed. NextStreamID = 0x%X\n",
  1086           self, self->mInputFrameID, self->mNextStreamID));
  1088     if (self->mInputFrameID >= self->mNextStreamID)
  1089       self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
  1091     self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + priorityLen,
  1092                                    self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
  1094     if (self->mInputFrameFlags & kFlag_END_HEADERS) {
  1095       rv = self->UncompressAndDiscard();
  1096       if (NS_FAILED(rv)) {
  1097         LOG3(("Http2Session::RecvHeaders uncompress failed\n"));
  1098         // this is fatal to the session
  1099         self->mGoAwayReason = COMPRESSION_ERROR;
  1100         return rv;
  1104     self->ResetDownstreamState();
  1105     return NS_OK;
  1108   // queue up any compression bytes
  1109   self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + priorityLen,
  1110                                  self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
  1112   self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
  1113   self->mLastDataReadEpoch = self->mLastReadEpoch;
  1115   if (!endHeadersFlag) { // more are coming - don't process yet
  1116     self->ResetDownstreamState();
  1117     return NS_OK;
  1120   rv = self->ResponseHeadersComplete();
  1121   if (rv == NS_ERROR_ILLEGAL_VALUE) {
  1122     LOG3(("Http2Session::RecvHeaders %p PROTOCOL_ERROR detected stream 0x%X\n",
  1123           self, self->mInputFrameID));
  1124     self->CleanupStream(self->mInputFrameDataStream, rv, PROTOCOL_ERROR);
  1125     self->ResetDownstreamState();
  1126     rv = NS_OK;
  1128   return rv;
  1131 // ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream
  1132 // should be reset with a PROTOCOL_ERROR, NS_OK when the response headers were
  1133 // fine, and any other error is fatal to the session.
  1134 nsresult
  1135 Http2Session::ResponseHeadersComplete()
  1137   LOG3(("Http2Session::ResponseHeadersComplete %p for 0x%X fin=%d",
  1138         this, mInputFrameDataStream->StreamID(), mInputFrameFinal));
  1140   // only do this once, afterwards ignore trailers
  1141   if (mInputFrameDataStream->AllHeadersReceived())
  1142     return NS_OK;
  1143   mInputFrameDataStream->SetAllHeadersReceived(true);
  1145   // The stream needs to see flattened http headers
  1146   // Uncompressed http/2 format headers currently live in
  1147   // Http2Stream::mDecompressBuffer - convert that to HTTP format in
  1148   // mFlatHTTPResponseHeaders via ConvertHeaders()
  1150   mFlatHTTPResponseHeadersOut = 0;
  1151   nsresult rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor,
  1152                                                               mDecompressBuffer,
  1153                                                               mFlatHTTPResponseHeaders);
  1154   if (NS_FAILED(rv))
  1155     return rv;
  1157   ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
  1158   return NS_OK;
  1161 nsresult
  1162 Http2Session::RecvPriority(Http2Session *self)
  1164   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PRIORITY);
  1166   if (self->mInputFrameDataSize != 4) {
  1167     LOG3(("Http2Session::RecvPriority %p wrong length data=%d\n",
  1168           self, self->mInputFrameDataSize));
  1169     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1172   if (!self->mInputFrameID) {
  1173     LOG3(("Http2Session::RecvPriority %p stream ID of 0.\n", self));
  1174     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1177   uint32_t newPriority =
  1178     PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
  1179   newPriority &= 0x7fffffff;
  1181   nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
  1182   if (NS_FAILED(rv))
  1183     return rv;
  1185   if (self->mInputFrameDataStream)
  1186     self->mInputFrameDataStream->SetPriority(newPriority);
  1187   self->ResetDownstreamState();
  1188   return NS_OK;
  1191 nsresult
  1192 Http2Session::RecvRstStream(Http2Session *self)
  1194   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_RST_STREAM);
  1196   if (self->mInputFrameDataSize != 4) {
  1197     LOG3(("Http2Session::RecvRstStream %p RST_STREAM wrong length data=%d",
  1198           self, self->mInputFrameDataSize));
  1199     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1202   if (!self->mInputFrameID) {
  1203     LOG3(("Http2Session::RecvRstStream %p stream ID of 0.\n", self));
  1204     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1207   self->mDownstreamRstReason =
  1208     PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
  1210   LOG3(("Http2Session::RecvRstStream %p RST_STREAM Reason Code %u ID %x\n",
  1211         self, self->mDownstreamRstReason, self->mInputFrameID));
  1213   self->SetInputFrameDataStream(self->mInputFrameID);
  1214   if (!self->mInputFrameDataStream) {
  1215     // if we can't find the stream just ignore it (4.2 closed)
  1216     self->ResetDownstreamState();
  1217     return NS_OK;
  1220   self->mInputFrameDataStream->SetRecvdReset(true);
  1221   self->MaybeDecrementConcurrent(self->mInputFrameDataStream);
  1222   self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
  1223   return NS_OK;
  1226 PLDHashOperator
  1227 Http2Session::UpdateServerRwinEnumerator(nsAHttpTransaction *key,
  1228                                          nsAutoPtr<Http2Stream> &stream,
  1229                                          void *closure)
  1231   int32_t delta = *(static_cast<int32_t *>(closure));
  1232   stream->UpdateServerReceiveWindow(delta);
  1233   return PL_DHASH_NEXT;
  1236 nsresult
  1237 Http2Session::RecvSettings(Http2Session *self)
  1239   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_SETTINGS);
  1241   if (self->mInputFrameID) {
  1242     LOG3(("Http2Session::RecvSettings %p needs stream ID of 0. 0x%X\n",
  1243           self, self->mInputFrameID));
  1244     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1247   if (self->mInputFrameDataSize % 5) {
  1248     // Number of Settings is determined by dividing by each 5 byte setting
  1249     // entry. So the payload must be a multiple of 5.
  1250     LOG3(("Http2Session::RecvSettings %p SETTINGS wrong length data=%d",
  1251           self, self->mInputFrameDataSize));
  1252     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1255   uint32_t numEntries = self->mInputFrameDataSize / 5;
  1256   LOG3(("Http2Session::RecvSettings %p SETTINGS Control Frame "
  1257         "with %d entries ack=%X", self, numEntries,
  1258         self->mInputFrameFlags & kFlag_ACK));
  1260   if ((self->mInputFrameFlags & kFlag_ACK) && self->mInputFrameDataSize) {
  1261     LOG3(("Http2Session::RecvSettings %p ACK with non zero payload is err\n"));
  1262     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1265   for (uint32_t index = 0; index < numEntries; ++index) {
  1266     uint8_t *setting = reinterpret_cast<uint8_t *>
  1267       (self->mInputFrameBuffer.get()) + 8 + index * 5;
  1269     uint8_t id = setting[0];
  1270     uint32_t value = PR_ntohl(*reinterpret_cast<uint32_t *>(setting + 1));
  1271     LOG3(("Settings ID %d, Value %d", id, value));
  1273     switch (id)
  1275     case SETTINGS_TYPE_HEADER_TABLE_SIZE:
  1276       LOG3(("Compression header table setting received: %d\n", value));
  1277       self->mCompressor.SetMaxBufferSize(value);
  1278       break;
  1280     case SETTINGS_TYPE_ENABLE_PUSH:
  1281       LOG3(("Client received an ENABLE Push SETTING. Odd.\n"));
  1282       // nop
  1283       break;
  1285     case SETTINGS_TYPE_MAX_CONCURRENT:
  1286       self->mMaxConcurrent = value;
  1287       Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
  1288       break;
  1290     case SETTINGS_TYPE_INITIAL_WINDOW:
  1292         Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
  1293         int32_t delta = value - self->mServerInitialStreamWindow;
  1294         self->mServerInitialStreamWindow = value;
  1296         // SETTINGS only adjusts stream windows. Leave the sesison window alone.
  1297         // we need to add the delta to all open streams (delta can be negative)
  1298         self->mStreamTransactionHash.Enumerate(UpdateServerRwinEnumerator,
  1299                                                &delta);
  1301       break;
  1303     default:
  1304       break;
  1308   self->ResetDownstreamState();
  1310   if (!(self->mInputFrameFlags & kFlag_ACK))
  1311     self->GenerateSettingsAck();
  1313   return NS_OK;
  1316 nsresult
  1317 Http2Session::RecvPushPromise(Http2Session *self)
  1319   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PUSH_PROMISE);
  1321   // Find out how much padding this frame has, so we can only extract the real
  1322   // header data from the frame.
  1323   uint16_t paddingLength = 0;
  1324   uint8_t paddingControlBytes = 0;
  1325   // TODO - will need to change this once PUSH_PROMISE allows padding
  1326   // (post-draft10)
  1327   // Right now, only CONTINUATION frames can have padding, and
  1328   // mExpectedPushPromiseID being set indicates that we're actually processing a
  1329   // CONTINUATION frame.
  1330   if (self->mExpectedPushPromiseID) {
  1331     nsresult rv = self->ParsePadding(paddingControlBytes, paddingLength);
  1332     if (NS_FAILED(rv)) {
  1333       return rv;
  1337   // If this doesn't have END_PUSH_PROMISE set on it then require the next
  1338   // frame to be PUSH_PROMISE of the same ID
  1339   uint32_t promiseLen;
  1340   uint32_t promisedID;
  1342   if (self->mExpectedPushPromiseID) {
  1343     promiseLen = 0; // really a continuation frame
  1344     promisedID = self->mContinuedPromiseStream;
  1345   } else {
  1346     // TODO - will need to handle padding here when getting the promisedID, once
  1347     // PUSH_PROMISE allows padding (post-draft10)
  1348     promiseLen = 4;
  1349     promisedID =
  1350       PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
  1351     promisedID &= 0x7fffffff;
  1354   uint32_t associatedID = self->mInputFrameID;
  1356   if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
  1357     self->mExpectedPushPromiseID = 0;
  1358     self->mContinuedPromiseStream = 0;
  1359   } else {
  1360     self->mExpectedPushPromiseID = self->mInputFrameID;
  1361     self->mContinuedPromiseStream = promisedID;
  1364   if (paddingLength > self->mInputFrameDataSize) {
  1365     // This is fatal to the session
  1366     LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X "
  1367           "PROTOCOL_ERROR paddingLength %d > frame size %d\n",
  1368           self, promisedID, associatedID, paddingLength,
  1369           self->mInputFrameDataSize));
  1370     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1373   LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X "
  1374         "paddingLength %d pad_high_flag %d pad_low_flag %d.\n",
  1375         self, promisedID, associatedID, paddingLength,
  1376         self->mInputFrameFlags & kFlag_PAD_HIGH,
  1377         self->mInputFrameFlags & kFlag_PAD_LOW));
  1379   if (!associatedID || !promisedID || (promisedID & 1)) {
  1380     LOG3(("Http2Session::RecvPushPromise %p ID invalid.\n", self));
  1381     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1384   // confirm associated-to
  1385   nsresult rv = self->SetInputFrameDataStream(associatedID);
  1386   if (NS_FAILED(rv))
  1387     return rv;
  1389   Http2Stream *associatedStream = self->mInputFrameDataStream;
  1390   ++(self->mServerPushedResources);
  1392   // Anytime we start using the high bit of stream ID (either client or server)
  1393   // begin to migrate to a new session.
  1394   if (promisedID >= kMaxStreamID)
  1395     self->mShouldGoAway = true;
  1397   bool resetStream = true;
  1398   SpdyPushCache *cache = nullptr;
  1400   if (self->mShouldGoAway) {
  1401     LOG3(("Http2Session::RecvPushPromise %p push while in GoAway "
  1402           "mode refused.\n", self));
  1403     self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
  1404   } else if (!gHttpHandler->AllowPush()) {
  1405     // MAX_CONCURRENT_STREAMS of 0 in settings disabled push
  1406     LOG3(("Http2Session::RecvPushPromise Push Recevied when Disabled\n"));
  1407     self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
  1408   } else if (!(self->mInputFrameFlags & kFlag_END_PUSH_PROMISE)) {
  1409     LOG3(("Http2Session::RecvPushPromise no support for multi frame push\n"));
  1410     self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
  1411   } else if (!associatedStream) {
  1412     LOG3(("Http2Session::RecvPushPromise %p lookup associated ID failed.\n", self));
  1413     self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
  1414   } else {
  1415     nsILoadGroupConnectionInfo *loadGroupCI = associatedStream->LoadGroupConnectionInfo();
  1416     if (loadGroupCI) {
  1417       loadGroupCI->GetSpdyPushCache(&cache);
  1418       if (!cache) {
  1419         cache = new SpdyPushCache();
  1420         if (!cache || NS_FAILED(loadGroupCI->SetSpdyPushCache(cache))) {
  1421           delete cache;
  1422           cache = nullptr;
  1426     if (!cache) {
  1427       // this is unexpected, but we can handle it just by refusing the push
  1428       LOG3(("Http2Session::RecvPushPromise Push Recevied without loadgroup cache\n"));
  1429       self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
  1430     } else {
  1431       resetStream = false;
  1435   if (resetStream) {
  1436     // Need to decompress the headers even though we aren't using them yet in
  1437     // order to keep the compression context consistent for other frames
  1438     self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + promiseLen,
  1439                                    self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
  1440     if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
  1441       rv = self->UncompressAndDiscard();
  1442       if (NS_FAILED(rv)) {
  1443         LOG3(("Http2Session::RecvPushPromise uncompress failed\n"));
  1444         self->mGoAwayReason = COMPRESSION_ERROR;
  1445         return rv;
  1448     self->ResetDownstreamState();
  1449     return NS_OK;
  1452   // Create the buffering transaction and push stream
  1453   nsRefPtr<Http2PushTransactionBuffer> transactionBuffer =
  1454     new Http2PushTransactionBuffer();
  1455   transactionBuffer->SetConnection(self);
  1456   Http2PushedStream *pushedStream =
  1457     new Http2PushedStream(transactionBuffer, self,
  1458                                  associatedStream, promisedID);
  1460   // Ownership of the pushed stream is by the transaction hash, just as it
  1461   // is for a client initiated stream. Errors that aren't fatal to the
  1462   // whole session must call cleanupStream() after this point in order
  1463   // to remove the stream from that hash.
  1464   self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
  1465   self->mPushedStreams.AppendElement(pushedStream);
  1467   self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + promiseLen,
  1468                                  self->mInputFrameDataSize - promiseLen);
  1470   nsAutoCString requestHeaders;
  1471   rv = pushedStream->ConvertPushHeaders(&self->mDecompressor,
  1472                                         self->mDecompressBuffer, requestHeaders);
  1474   if (rv == NS_ERROR_NOT_IMPLEMENTED) {
  1475     LOG3(("Http2Session::PushPromise Semantics not Implemented\n"));
  1476     self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
  1477     return NS_OK;
  1480   if (NS_FAILED(rv))
  1481     return rv;
  1483   if (self->RegisterStreamID(pushedStream, promisedID) == kDeadStreamID) {
  1484     LOG3(("Http2Session::RecvPushPromise registerstreamid failed\n"));
  1485     self->mGoAwayReason = INTERNAL_ERROR;
  1486     return NS_ERROR_FAILURE;
  1489   if (promisedID > self->mOutgoingGoAwayID)
  1490     self->mOutgoingGoAwayID = promisedID;
  1492   // Fake the request side of the pushed HTTP transaction. Sets up hash
  1493   // key and origin
  1494   uint32_t notUsed;
  1495   pushedStream->ReadSegments(nullptr, 1, &notUsed);
  1497   nsAutoCString key;
  1498   if (!pushedStream->GetHashKey(key)) {
  1499     LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n"));
  1500     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR);
  1501     self->ResetDownstreamState();
  1502     return NS_OK;
  1505   if (!associatedStream->Origin().Equals(pushedStream->Origin())) {
  1506     LOG3(("Http2Session::RecvPushPromise pushed stream mismatched origin\n"));
  1507     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
  1508     self->ResetDownstreamState();
  1509     return NS_OK;
  1512   if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) {
  1513     LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n"));
  1514     self->CleanupStream(pushedStream, NS_ERROR_FAILURE, INTERNAL_ERROR);
  1515     self->ResetDownstreamState();
  1516     return NS_OK;
  1519   static_assert(Http2Stream::kWorstPriority >= 0,
  1520                 "kWorstPriority out of range");
  1521   uint32_t unsignedPriority = static_cast<uint32_t>(Http2Stream::kWorstPriority);
  1522   pushedStream->SetPriority(unsignedPriority);
  1523   self->GeneratePriority(promisedID, unsignedPriority);
  1524   self->ResetDownstreamState();
  1525   return NS_OK;
  1528 nsresult
  1529 Http2Session::RecvPing(Http2Session *self)
  1531   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PING);
  1533   LOG3(("Http2Session::RecvPing %p PING Flags 0x%X.", self,
  1534         self->mInputFrameFlags));
  1536   if (self->mInputFrameDataSize != 8) {
  1537     LOG3(("Http2Session::RecvPing %p PING had wrong amount of data %d",
  1538           self, self->mInputFrameDataSize));
  1539     RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR);
  1542   if (self->mInputFrameID) {
  1543     LOG3(("Http2Session::RecvPing %p PING needs stream ID of 0. 0x%X\n",
  1544           self, self->mInputFrameID));
  1545     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1548   if (self->mInputFrameFlags & kFlag_ACK) {
  1549     // presumably a reply to our timeout ping.. don't reply to it
  1550     self->mPingSentEpoch = 0;
  1551   } else {
  1552     // reply with a ack'd ping
  1553     self->GeneratePing(true);
  1556   self->ResetDownstreamState();
  1557   return NS_OK;
  1560 nsresult
  1561 Http2Session::RecvGoAway(Http2Session *self)
  1563   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_GOAWAY);
  1565   if (self->mInputFrameDataSize < 8) {
  1566     // data > 8 is an opaque token that we can't interpret. NSPR Logs will
  1567     // have the hex of all packets so there is no point in separately logging.
  1568     LOG3(("Http2Session::RecvGoAway %p GOAWAY had wrong amount of data %d",
  1569           self, self->mInputFrameDataSize));
  1570     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1573   if (self->mInputFrameID) {
  1574     LOG3(("Http2Session::RecvGoAway %p GOAWAY had non zero stream ID 0x%X\n",
  1575           self, self->mInputFrameID));
  1576     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1579   self->mShouldGoAway = true;
  1580   self->mGoAwayID =
  1581     PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
  1582   self->mGoAwayID &= 0x7fffffff;
  1583   self->mCleanShutdown = true;
  1584   uint32_t statusCode =
  1585     PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]);
  1587   // Find streams greater than the last-good ID and mark them for deletion
  1588   // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. The
  1589   // underlying transaction can be restarted.
  1590   self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self);
  1592   // Process the streams marked for deletion and restart.
  1593   uint32_t size = self->mGoAwayStreamsToRestart.GetSize();
  1594   for (uint32_t count = 0; count < size; ++count) {
  1595     Http2Stream *stream =
  1596       static_cast<Http2Stream *>(self->mGoAwayStreamsToRestart.PopFront());
  1598     self->CloseStream(stream, NS_ERROR_NET_RESET);
  1599     if (stream->HasRegisteredID())
  1600       self->mStreamIDHash.Remove(stream->StreamID());
  1601     self->mStreamTransactionHash.Remove(stream->Transaction());
  1604   // Queued streams can also be deleted from this session and restarted
  1605   // in another one. (they were never sent on the network so they implicitly
  1606   // are not covered by the last-good id.
  1607   size = self->mQueuedStreams.GetSize();
  1608   for (uint32_t count = 0; count < size; ++count) {
  1609     Http2Stream *stream =
  1610       static_cast<Http2Stream *>(self->mQueuedStreams.PopFront());
  1611     self->CloseStream(stream, NS_ERROR_NET_RESET);
  1612     self->mStreamTransactionHash.Remove(stream->Transaction());
  1615   LOG3(("Http2Session::RecvGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X "
  1616         "live streams=%d\n", self, self->mGoAwayID, statusCode,
  1617         self->mStreamTransactionHash.Count()));
  1619   self->ResetDownstreamState();
  1620   return NS_OK;
  1623 PLDHashOperator
  1624 Http2Session::RestartBlockedOnRwinEnumerator(nsAHttpTransaction *key,
  1625                                              nsAutoPtr<Http2Stream> &stream,
  1626                                              void *closure)
  1628   Http2Session *self = static_cast<Http2Session *>(closure);
  1629   MOZ_ASSERT(self->mServerSessionWindow > 0);
  1631   if (!stream->BlockedOnRwin() || stream->ServerReceiveWindow() <= 0)
  1632     return PL_DHASH_NEXT;
  1634   self->mReadyForWrite.Push(stream);
  1635   self->SetWriteCallbacks();
  1636   return PL_DHASH_NEXT;
  1639 nsresult
  1640 Http2Session::RecvWindowUpdate(Http2Session *self)
  1642   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_WINDOW_UPDATE);
  1644   if (self->mInputFrameDataSize != 4) {
  1645     LOG3(("Http2Session::RecvWindowUpdate %p Window Update wrong length %d\n",
  1646           self, self->mInputFrameDataSize));
  1647     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1650   uint32_t delta =
  1651     PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
  1652   delta &= 0x7fffffff;
  1654   LOG3(("Http2Session::RecvWindowUpdate %p len=%d Stream 0x%X.\n",
  1655         self, delta, self->mInputFrameID));
  1657   if (self->mInputFrameID) { // stream window
  1658     nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
  1659     if (NS_FAILED(rv))
  1660       return rv;
  1662     if (!self->mInputFrameDataStream) {
  1663       LOG3(("Http2Session::RecvWindowUpdate %p lookup streamID 0x%X failed.\n",
  1664             self, self->mInputFrameID));
  1665       // only resest the session if the ID is one we haven't ever opened
  1666       if (self->mInputFrameID >= self->mNextStreamID)
  1667         self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
  1668       self->ResetDownstreamState();
  1669       return NS_OK;
  1672     int64_t oldRemoteWindow = self->mInputFrameDataStream->ServerReceiveWindow();
  1673     self->mInputFrameDataStream->UpdateServerReceiveWindow(delta);
  1674     if (self->mInputFrameDataStream->ServerReceiveWindow() >= 0x80000000) {
  1675       // a window cannot reach 2^31 and be in compliance. Our calculations
  1676       // are 64 bit safe though.
  1677       LOG3(("Http2Session::RecvWindowUpdate %p stream window "
  1678             "exceeds 2^31 - 1\n", self));
  1679       self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
  1680                           FLOW_CONTROL_ERROR);
  1681       self->ResetDownstreamState();
  1682       return NS_OK;
  1685     LOG3(("Http2Session::RecvWindowUpdate %p stream 0x%X window "
  1686           "%d increased by %d now %d.\n", self, self->mInputFrameID,
  1687           oldRemoteWindow, delta, oldRemoteWindow + delta));
  1689   } else { // session window update
  1690     int64_t oldRemoteWindow = self->mServerSessionWindow;
  1691     self->mServerSessionWindow += delta;
  1693     if (self->mServerSessionWindow >= 0x80000000) {
  1694       // a window cannot reach 2^31 and be in compliance. Our calculations
  1695       // are 64 bit safe though.
  1696       LOG3(("Http2Session::RecvWindowUpdate %p session window "
  1697             "exceeds 2^31 - 1\n", self));
  1698       RETURN_SESSION_ERROR(self, FLOW_CONTROL_ERROR);
  1701     if ((oldRemoteWindow <= 0) && (self->mServerSessionWindow > 0)) {
  1702       LOG3(("Http2Session::RecvWindowUpdate %p restart session window\n",
  1703             self));
  1704       self->mStreamTransactionHash.Enumerate(RestartBlockedOnRwinEnumerator, self);
  1706     LOG3(("Http2Session::RecvWindowUpdate %p session window "
  1707           "%d increased by %d now %d.\n", self,
  1708           oldRemoteWindow, delta, oldRemoteWindow + delta));
  1711   self->ResetDownstreamState();
  1712   return NS_OK;
  1715 nsresult
  1716 Http2Session::RecvContinuation(Http2Session *self)
  1718   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_CONTINUATION);
  1719   MOZ_ASSERT(self->mInputFrameID);
  1720   MOZ_ASSERT(self->mExpectedPushPromiseID || self->mExpectedHeaderID);
  1721   MOZ_ASSERT(!(self->mExpectedPushPromiseID && self->mExpectedHeaderID));
  1723   LOG3(("Http2Session::RecvContinuation %p Flags 0x%X id 0x%X "
  1724         "promise id 0x%X header id 0x%X\n",
  1725         self, self->mInputFrameFlags, self->mInputFrameID,
  1726         self->mExpectedPushPromiseID, self->mExpectedHeaderID));
  1728   self->SetInputFrameDataStream(self->mInputFrameID);
  1730   if (!self->mInputFrameDataStream) {
  1731     LOG3(("Http2Session::RecvContination stream ID 0x%X not found.",
  1732           self->mInputFrameID));
  1733     RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
  1736   // continued headers
  1737   if (self->mExpectedHeaderID) {
  1738     self->mInputFrameFlags &= ~kFlag_PRIORITY;
  1739     return RecvHeaders(self);
  1742   // continued push promise
  1743   if (self->mInputFrameFlags & kFlag_END_HEADERS) {
  1744     self->mInputFrameFlags &= ~kFlag_END_HEADERS;
  1745     self->mInputFrameFlags |= kFlag_END_PUSH_PROMISE;
  1747   return RecvPushPromise(self);
  1750 //-----------------------------------------------------------------------------
  1751 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
  1752 // of these methods
  1753 //-----------------------------------------------------------------------------
  1755 void
  1756 Http2Session::OnTransportStatus(nsITransport* aTransport,
  1757                                 nsresult aStatus, uint64_t aProgress)
  1759   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1761   switch (aStatus) {
  1762     // These should appear only once, deliver to the first
  1763     // transaction on the session.
  1764   case NS_NET_STATUS_RESOLVING_HOST:
  1765   case NS_NET_STATUS_RESOLVED_HOST:
  1766   case NS_NET_STATUS_CONNECTING_TO:
  1767   case NS_NET_STATUS_CONNECTED_TO:
  1769     Http2Stream *target = mStreamIDHash.Get(1);
  1770     nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
  1771     if (transaction)
  1772       transaction->OnTransportStatus(aTransport, aStatus, aProgress);
  1773     break;
  1776   default:
  1777     // The other transport events are ignored here because there is no good
  1778     // way to map them to the right transaction in http/2. Instead, the events
  1779     // are generated again from the http/2 code and passed directly to the
  1780     // correct transaction.
  1782     // NS_NET_STATUS_SENDING_TO:
  1783     // This is generated by the socket transport when (part) of
  1784     // a transaction is written out
  1785     //
  1786     // There is no good way to map it to the right transaction in http/2,
  1787     // so it is ignored here and generated separately when the request
  1788     // is sent from Http2Stream::TransmitFrame
  1790     // NS_NET_STATUS_WAITING_FOR:
  1791     // Created by nsHttpConnection when the request has been totally sent.
  1792     // There is no good way to map it to the right transaction in http/2,
  1793     // so it is ignored here and generated separately when the same
  1794     // condition is complete in Http2Stream when there is no more
  1795     // request body left to be transmitted.
  1797     // NS_NET_STATUS_RECEIVING_FROM
  1798     // Generated in session whenever we read a data frame or a HEADERS
  1799     // that can be attributed to a particular stream/transaction
  1801     break;
  1805 // ReadSegments() is used to write data to the network. Generally, HTTP
  1806 // request data is pulled from the approriate transaction and
  1807 // converted to http/2 data. Sometimes control data like window-update are
  1808 // generated instead.
  1810 nsresult
  1811 Http2Session::ReadSegments(nsAHttpSegmentReader *reader,
  1812                            uint32_t count, uint32_t *countRead)
  1814   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1816   MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader),
  1817              "Inconsistent Write Function Callback");
  1819   nsresult rv = ConfirmTLSProfile();
  1820   if (NS_FAILED(rv))
  1821     return rv;
  1823   if (reader)
  1824     mSegmentReader = reader;
  1826   *countRead = 0;
  1828   LOG3(("Http2Session::ReadSegments %p", this));
  1830   Http2Stream *stream = static_cast<Http2Stream *>(mReadyForWrite.PopFront());
  1831   if (!stream) {
  1832     LOG3(("Http2Session %p could not identify a stream to write; suspending.",
  1833           this));
  1834     FlushOutputQueue();
  1835     SetWriteCallbacks();
  1836     return NS_BASE_STREAM_WOULD_BLOCK;
  1839   LOG3(("Http2Session %p will write from Http2Stream %p 0x%X "
  1840         "block-input=%d block-output=%d\n", this, stream, stream->StreamID(),
  1841         stream->RequestBlockedOnRead(), stream->BlockedOnRwin()));
  1843   rv = stream->ReadSegments(this, count, countRead);
  1845   // Not every permutation of stream->ReadSegents produces data (and therefore
  1846   // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
  1847   // of that. But we might still have old data buffered that would be good
  1848   // to flush.
  1849   FlushOutputQueue();
  1851   // Allow new server reads - that might be data or control information
  1852   // (e.g. window updates or http replies) that are responses to these writes
  1853   ResumeRecv();
  1855   if (stream->RequestBlockedOnRead()) {
  1857     // We are blocked waiting for input - either more http headers or
  1858     // any request body data. When more data from the request stream
  1859     // becomes available the httptransaction will call conn->ResumeSend().
  1861     LOG3(("Http2Session::ReadSegments %p dealing with block on read", this));
  1863     // call readsegments again if there are other streams ready
  1864     // to run in this session
  1865     if (GetWriteQueueSize()) {
  1866       rv = NS_OK;
  1867     } else {
  1868       rv = NS_BASE_STREAM_WOULD_BLOCK;
  1870     SetWriteCallbacks();
  1871     return rv;
  1874   if (NS_FAILED(rv)) {
  1875     LOG3(("Http2Session::ReadSegments %p returning FAIL code %X",
  1876           this, rv));
  1877     if (rv != NS_BASE_STREAM_WOULD_BLOCK)
  1878       CleanupStream(stream, rv, CANCEL_ERROR);
  1879     return rv;
  1882   if (*countRead > 0) {
  1883     LOG3(("Http2Session::ReadSegments %p stream=%p countread=%d",
  1884           this, stream, *countRead));
  1885     mReadyForWrite.Push(stream);
  1886     SetWriteCallbacks();
  1887     return rv;
  1890   if (stream->BlockedOnRwin()) {
  1891     LOG3(("Http2Session %p will stream %p 0x%X suspended for flow control\n",
  1892           this, stream, stream->StreamID()));
  1893     return NS_BASE_STREAM_WOULD_BLOCK;
  1896   LOG3(("Http2Session::ReadSegments %p stream=%p stream send complete",
  1897         this, stream));
  1899   // call readsegments again if there are other streams ready
  1900   // to go in this session
  1901   SetWriteCallbacks();
  1903   return rv;
  1906 nsresult
  1907 Http2Session::ReadyToProcessDataFrame(enum internalStateType newState)
  1909   MOZ_ASSERT(newState == PROCESSING_DATA_FRAME ||
  1910              newState == DISCARDING_DATA_FRAME_PADDING);
  1911   ChangeDownstreamState(newState);
  1913   Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
  1914                         mInputFrameDataSize >> 10);
  1915   mLastDataReadEpoch = mLastReadEpoch;
  1917   if (!mInputFrameID) {
  1918     LOG3(("Http2Session::ReadyToProcessDataFrame %p data frame stream 0\n",
  1919           this));
  1920     RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
  1923   nsresult rv = SetInputFrameDataStream(mInputFrameID);
  1924   if (NS_FAILED(rv)) {
  1925     LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X "
  1926           "failed. probably due to verification.\n", this, mInputFrameID));
  1927     return rv;
  1929   if (!mInputFrameDataStream) {
  1930     LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X "
  1931           "failed. Next = 0x%X", this, mInputFrameID, mNextStreamID));
  1932     if (mInputFrameID >= mNextStreamID)
  1933       GenerateRstStream(PROTOCOL_ERROR, mInputFrameID);
  1934     ChangeDownstreamState(DISCARDING_DATA_FRAME);
  1935   } else if (mInputFrameDataStream->RecvdFin() ||
  1936             mInputFrameDataStream->RecvdReset() ||
  1937             mInputFrameDataStream->SentReset()) {
  1938     LOG3(("Http2Session::ReadyToProcessDataFrame %p streamID 0x%X "
  1939           "Data arrived for already server closed stream.\n",
  1940           this, mInputFrameID));
  1941     if (mInputFrameDataStream->RecvdFin() || mInputFrameDataStream->RecvdReset())
  1942       GenerateRstStream(STREAM_CLOSED_ERROR, mInputFrameID);
  1943     ChangeDownstreamState(DISCARDING_DATA_FRAME);
  1946   LOG3(("Start Processing Data Frame. "
  1947         "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d",
  1948         this, mInputFrameID, mInputFrameDataStream, mInputFrameFinal,
  1949         mInputFrameDataSize));
  1950   UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize);
  1952   return NS_OK;
  1955 // WriteSegments() is used to read data off the socket. Generally this is
  1956 // just the http2 frame header and from there the appropriate *Stream
  1957 // is identified from the Stream-ID. The http transaction associated with
  1958 // that read then pulls in the data directly, which it will feed to
  1959 // OnWriteSegment(). That function will gateway it into http and feed
  1960 // it to the appropriate transaction.
  1962 // we call writer->OnWriteSegment via NetworkRead() to get a http2 header..
  1963 // and decide if it is data or control.. if it is control, just deal with it.
  1964 // if it is data, identify the stream
  1965 // call stream->WriteSegments which can call this::OnWriteSegment to get the
  1966 // data. It always gets full frames if they are part of the stream
  1968 nsresult
  1969 Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
  1970                             uint32_t count, uint32_t *countWritten)
  1972   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1974   LOG3(("Http2Session::WriteSegments %p InternalState %X\n",
  1975         this, mDownstreamState));
  1977   *countWritten = 0;
  1979   if (mClosed)
  1980     return NS_ERROR_FAILURE;
  1982   nsresult rv = ConfirmTLSProfile();
  1983   if (NS_FAILED(rv))
  1984     return rv;
  1986   SetWriteCallbacks();
  1988   // If there are http transactions attached to a push stream with filled buffers
  1989   // trigger that data pump here. This only reads from buffers (not the network)
  1990   // so mDownstreamState doesn't matter.
  1991   Http2Stream *pushConnectedStream =
  1992     static_cast<Http2Stream *>(mReadyForRead.PopFront());
  1993   if (pushConnectedStream) {
  1994     LOG3(("Http2Session::WriteSegments %p processing pushed stream 0x%X\n",
  1995           this, pushConnectedStream->StreamID()));
  1996     mSegmentWriter = writer;
  1997     rv = pushConnectedStream->WriteSegments(this, count, countWritten);
  1998     mSegmentWriter = nullptr;
  2000     // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
  2001     // so we need this check to determine the truth.
  2002     if (NS_SUCCEEDED(rv) && !*countWritten &&
  2003         pushConnectedStream->PushSource() &&
  2004         pushConnectedStream->PushSource()->GetPushComplete()) {
  2005       rv = NS_BASE_STREAM_CLOSED;
  2008     if (rv == NS_BASE_STREAM_CLOSED) {
  2009       CleanupStream(pushConnectedStream, NS_OK, CANCEL_ERROR);
  2010       rv = NS_OK;
  2013     // if we return OK to nsHttpConnection it will use mSocketInCondition
  2014     // to determine whether to schedule more reads, incorrectly
  2015     // assuming that nsHttpConnection::OnSocketWrite() was called.
  2016     if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
  2017       rv = NS_BASE_STREAM_WOULD_BLOCK;
  2018       ResumeRecv();
  2021     return rv;
  2024   // The BUFFERING_OPENING_SETTINGS state is just like any BUFFERING_FRAME_HEADER
  2025   // except the only frame type it will allow is SETTINGS
  2027   // The session layer buffers the leading 8 byte header of every frame.
  2028   // Non-Data frames are then buffered for their full length, but data
  2029   // frames (type 0) are passed through to the http stack unprocessed
  2031   if (mDownstreamState == BUFFERING_OPENING_SETTINGS ||
  2032       mDownstreamState == BUFFERING_FRAME_HEADER) {
  2033     // The first 8 bytes of every frame is header information that
  2034     // we are going to want to strip before passing to http. That is
  2035     // true of both control and data packets.
  2037     MOZ_ASSERT(mInputFrameBufferUsed < 8,
  2038                "Frame Buffer Used Too Large for State");
  2040     rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
  2041                      8 - mInputFrameBufferUsed, countWritten);
  2043     if (NS_FAILED(rv)) {
  2044       LOG3(("Http2Session %p buffering frame header read failure %x\n",
  2045             this, rv));
  2046       // maybe just blocked reading from network
  2047       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2048         rv = NS_OK;
  2049       return rv;
  2052     LogIO(this, nullptr, "Reading Frame Header",
  2053           mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
  2055     mInputFrameBufferUsed += *countWritten;
  2057     if (mInputFrameBufferUsed < 8)
  2059       LOG3(("Http2Session::WriteSegments %p "
  2060             "BUFFERING FRAME HEADER incomplete size=%d",
  2061             this, mInputFrameBufferUsed));
  2062       return rv;
  2065     // 2 bytes of length, 1 type byte, 1 flag byte, 1 unused bit, 31 bits of ID
  2066     mInputFrameDataSize =
  2067       PR_ntohs(reinterpret_cast<uint16_t *>(mInputFrameBuffer.get())[0]);
  2068     mInputFrameType = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[2];
  2069     mInputFrameFlags = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[3];
  2070     mInputFrameID =
  2071       PR_ntohl(reinterpret_cast<uint32_t *>(mInputFrameBuffer.get())[1]);
  2072     mInputFrameID &= 0x7fffffff;
  2073     mInputFrameDataRead = 0;
  2075     if (mInputFrameType == FRAME_TYPE_DATA || mInputFrameType == FRAME_TYPE_HEADERS)  {
  2076       mInputFrameFinal = mInputFrameFlags & kFlag_END_STREAM;
  2077     } else {
  2078       mInputFrameFinal = 0;
  2081     mPaddingLength = 0;
  2082     if (mInputFrameType == FRAME_TYPE_DATA ||
  2083         mInputFrameType == FRAME_TYPE_HEADERS ||
  2084         // TODO: also mInputFrameType == FRAME_TYPE_PUSH_PROMISE after draft10
  2085         mInputFrameType == FRAME_TYPE_CONTINUATION) {
  2086       if ((mInputFrameFlags & kFlag_PAD_HIGH) &&
  2087           !(mInputFrameFlags & kFlag_PAD_LOW)) {
  2088         LOG3(("Http2Session::WriteSegments %p PROTOCOL_ERROR pad_high present "
  2089               "without pad_low\n", this));
  2090         RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
  2094     if (mInputFrameDataSize >= 0x4000) {
  2095       // Section 9.1 HTTP frames cannot exceed 2^14 - 1 but receviers must ignore
  2096       // those bits
  2097       LOG3(("Http2Session::WriteSegments %p WARNING Frame Length bits past 14 are not 0 %08X\n",
  2098             this, mInputFrameDataSize));
  2099       mInputFrameDataSize &= 0x3fff;
  2102     LOG3(("Http2Session::WriteSegments[%p::%x] Frame Header Read "
  2103           "type %X data len %u flags %x id 0x%X",
  2104           this, mSerial, mInputFrameType, mInputFrameDataSize, mInputFrameFlags,
  2105           mInputFrameID));
  2107     // if mExpectedHeaderID is non 0, it means this frame must be a CONTINUATION of
  2108     // a HEADERS frame with a matching ID (section 6.2)
  2109     if (mExpectedHeaderID &&
  2110         ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
  2111          (mExpectedHeaderID != mInputFrameID))) {
  2112       LOG3(("Expected CONINUATION OF HEADERS for ID 0x%X\n", mExpectedHeaderID));
  2113       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
  2116     // if mExpectedPushPromiseID is non 0, it means this frame must be a
  2117     // CONTINUATION of a PUSH_PROMISE with a matching ID (section 6.2)
  2118     if (mExpectedPushPromiseID &&
  2119         ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
  2120          (mExpectedPushPromiseID != mInputFrameID))) {
  2121       LOG3(("Expected CONTINUATION of PUSH PROMISE for ID 0x%X\n",
  2122             mExpectedPushPromiseID));
  2123       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
  2126     if (mDownstreamState == BUFFERING_OPENING_SETTINGS &&
  2127         mInputFrameType != FRAME_TYPE_SETTINGS) {
  2128       LOG3(("First Frame Type Must Be Settings\n"));
  2129       RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
  2132     if (mInputFrameType != FRAME_TYPE_DATA) { // control frame
  2133       EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
  2134                    mInputFrameBufferSize);
  2135       ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
  2136     } else if (mInputFrameFlags & (kFlag_PAD_LOW | kFlag_PAD_HIGH)) {
  2137       ChangeDownstreamState(PROCESSING_DATA_FRAME_PADDING_CONTROL);
  2138     } else {
  2139       rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME);
  2140       if (NS_FAILED(rv)) {
  2141         return rv;
  2146   if (mDownstreamState == PROCESSING_DATA_FRAME_PADDING_CONTROL) {
  2147     uint32_t numControlBytes = 0;
  2148     if (mInputFrameFlags & kFlag_PAD_LOW) {
  2149       ++numControlBytes;
  2151     if (mInputFrameFlags & kFlag_PAD_HIGH) {
  2152       ++numControlBytes;
  2155     MOZ_ASSERT(numControlBytes,
  2156                "Processing padding control with no control bytes!");
  2157     MOZ_ASSERT(mInputFrameBufferUsed < (8 + numControlBytes),
  2158                "Frame buffer used too large for state");
  2160     rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
  2161                      (8 + numControlBytes) - mInputFrameBufferUsed,
  2162                      countWritten);
  2164     if (NS_FAILED(rv)) {
  2165       LOG3(("Http2Session %p buffering data frame padding control read failure %x\n",
  2166             this, rv));
  2167       // maybe just blocked reading from network
  2168       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2169         rv = NS_OK;
  2170       return rv;
  2173     LogIO(this, nullptr, "Reading Data Frame Padding Control",
  2174           mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
  2176     mInputFrameBufferUsed += *countWritten;
  2178     if (mInputFrameBufferUsed - 8 < numControlBytes) {
  2179       LOG3(("Http2Session::WriteSegments %p "
  2180             "BUFFERING DATA FRAME CONTROL PADDING incomplete size=%d",
  2181             this, mInputFrameBufferUsed - 8));
  2182       return rv;
  2185     mInputFrameDataRead += numControlBytes;
  2187     char *control = mInputFrameBuffer + 8;
  2188     if (mInputFrameFlags & kFlag_PAD_HIGH) {
  2189       mPaddingLength = static_cast<uint16_t>(*control) * 256;
  2190       ++control;
  2192     mPaddingLength += static_cast<uint8_t>(*control);
  2194     LOG3(("Http2Session::WriteSegments %p stream 0x%X mPaddingLength=%d", this,
  2195           mInputFrameID, mPaddingLength));
  2197     if (numControlBytes + mPaddingLength == mInputFrameDataSize) {
  2198       // This frame consists entirely of padding, we can just discard it
  2199       LOG3(("Http2Session::WriteSegments %p stream 0x%X frame with only padding",
  2200             this, mInputFrameID));
  2201       rv = ReadyToProcessDataFrame(DISCARDING_DATA_FRAME_PADDING);
  2202       if (NS_FAILED(rv)) {
  2203         return rv;
  2205     } else {
  2206       LOG3(("Http2Session::WriteSegments %p stream 0x%X ready to read HTTP data",
  2207             this, mInputFrameID));
  2208       rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME);
  2209       if (NS_FAILED(rv)) {
  2210         return rv;
  2215   if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
  2216     nsresult streamCleanupCode;
  2218     // There is no bounds checking on the error code.. we provide special
  2219     // handling for a couple of cases and all others (including unknown) are
  2220     // equivalent to cancel.
  2221     if (mDownstreamRstReason == REFUSED_STREAM_ERROR) {
  2222       streamCleanupCode = NS_ERROR_NET_RESET;      // can retry this 100% safely
  2223     } else {
  2224       streamCleanupCode = NS_ERROR_NET_INTERRUPT;
  2227     if (mDownstreamRstReason == COMPRESSION_ERROR)
  2228       mShouldGoAway = true;
  2230     // mInputFrameDataStream is reset by ChangeDownstreamState
  2231     Http2Stream *stream = mInputFrameDataStream;
  2232     ResetDownstreamState();
  2233     LOG3(("Http2Session::WriteSegments cleanup stream on recv of rst "
  2234           "session=%p stream=%p 0x%X\n", this, stream,
  2235           stream ? stream->StreamID() : 0));
  2236     CleanupStream(stream, streamCleanupCode, CANCEL_ERROR);
  2237     return NS_OK;
  2240   if (mDownstreamState == PROCESSING_DATA_FRAME ||
  2241       mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
  2243     // The cleanup stream should only be set while stream->WriteSegments is
  2244     // on the stack and then cleaned up in this code block afterwards.
  2245     MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
  2246     mNeedsCleanup = nullptr;                     /* just in case */
  2248     mSegmentWriter = writer;
  2249     rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
  2250     mSegmentWriter = nullptr;
  2252     mLastDataReadEpoch = mLastReadEpoch;
  2254     if (SoftStreamError(rv)) {
  2255       // This will happen when the transaction figures out it is EOF, generally
  2256       // due to a content-length match being made. Return OK from this function
  2257       // otherwise the whole session would be torn down.
  2258       Http2Stream *stream = mInputFrameDataStream;
  2260       // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
  2261       // back to PROCESSING_DATA_FRAME where we came from
  2262       mDownstreamState = PROCESSING_DATA_FRAME;
  2264       if (mInputFrameDataRead == mInputFrameDataSize)
  2265         ResetDownstreamState();
  2266       LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
  2267             "needscleanup=%p. cleanup stream based on "
  2268             "stream->writeSegments returning code %x\n",
  2269             this, stream, stream ? stream->StreamID() : 0,
  2270             mNeedsCleanup, rv));
  2271       CleanupStream(stream, NS_OK, CANCEL_ERROR);
  2272       MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame");
  2273       mNeedsCleanup = nullptr;                     /* just in case */
  2274       return NS_OK;
  2277     if (mNeedsCleanup) {
  2278       LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
  2279             "cleanup stream based on mNeedsCleanup.\n",
  2280             this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0));
  2281       CleanupStream(mNeedsCleanup, NS_OK, CANCEL_ERROR);
  2282       mNeedsCleanup = nullptr;
  2285     if (NS_FAILED(rv)) {
  2286       LOG3(("Http2Session %p data frame read failure %x\n", this, rv));
  2287       // maybe just blocked reading from network
  2288       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2289         rv = NS_OK;
  2292     return rv;
  2295   if (mDownstreamState == DISCARDING_DATA_FRAME ||
  2296       mDownstreamState == DISCARDING_DATA_FRAME_PADDING) {
  2297     char trash[4096];
  2298     uint32_t count = std::min(4096U, mInputFrameDataSize - mInputFrameDataRead);
  2299     LOG3(("Http2Session::WriteSegments %p trying to discard %d bytes of data",
  2300           this, count));
  2302     if (!count) {
  2303       ResetDownstreamState();
  2304       ResumeRecv();
  2305       return NS_BASE_STREAM_WOULD_BLOCK;
  2308     rv = NetworkRead(writer, trash, count, countWritten);
  2310     if (NS_FAILED(rv)) {
  2311       LOG3(("Http2Session %p discard frame read failure %x\n", this, rv));
  2312       // maybe just blocked reading from network
  2313       if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2314         rv = NS_OK;
  2315       return rv;
  2318     LogIO(this, nullptr, "Discarding Frame", trash, *countWritten);
  2320     mInputFrameDataRead += *countWritten;
  2322     if (mInputFrameDataRead == mInputFrameDataSize) {
  2323       Http2Stream *streamToCleanup = nullptr;
  2324       if (mInputFrameFinal) {
  2325         streamToCleanup = mInputFrameDataStream;
  2328       ResetDownstreamState();
  2330       if (streamToCleanup) {
  2331         CleanupStream(streamToCleanup, NS_OK, CANCEL_ERROR);
  2334     return rv;
  2337   if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
  2338     MOZ_ASSERT(false); // this cannot happen
  2339     return NS_ERROR_UNEXPECTED;
  2342   MOZ_ASSERT(mInputFrameBufferUsed == 8, "Frame Buffer Header Not Present");
  2343   MOZ_ASSERT(mInputFrameDataSize + 8 <= mInputFrameBufferSize,
  2344              "allocation for control frame insufficient");
  2346   rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
  2347                    mInputFrameDataSize - mInputFrameDataRead, countWritten);
  2349   if (NS_FAILED(rv)) {
  2350     LOG3(("Http2Session %p buffering control frame read failure %x\n",
  2351           this, rv));
  2352     // maybe just blocked reading from network
  2353     if (rv == NS_BASE_STREAM_WOULD_BLOCK)
  2354       rv = NS_OK;
  2355     return rv;
  2358   LogIO(this, nullptr, "Reading Control Frame",
  2359         mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
  2361   mInputFrameDataRead += *countWritten;
  2363   if (mInputFrameDataRead != mInputFrameDataSize)
  2364     return NS_OK;
  2366   MOZ_ASSERT(mInputFrameType != FRAME_TYPE_DATA);
  2367   if (mInputFrameType < FRAME_TYPE_LAST) {
  2368     rv = sControlFunctions[mInputFrameType](this);
  2369   } else {
  2370     // Section 4.1 requires this to be ignored; though protocol_error would
  2371     // be better
  2372     LOG3(("Http2Session %p unknow frame type %x ignored\n",
  2373           this, mInputFrameType));
  2374     ResetDownstreamState();
  2375     rv = NS_OK;
  2378   MOZ_ASSERT(NS_FAILED(rv) ||
  2379              mDownstreamState != BUFFERING_CONTROL_FRAME,
  2380              "Control Handler returned OK but did not change state");
  2382   if (mShouldGoAway && !mStreamTransactionHash.Count())
  2383     Close(NS_OK);
  2384   return rv;
  2387 void
  2388 Http2Session::UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes)
  2390   if (!stream) // this is ok - it means there was a data frame for a rst stream
  2391     return;
  2393   // If this data packet was not for a valid or live stream then there
  2394   // is no reason to mess with the flow control
  2395   if (!stream || stream->RecvdFin() || stream->RecvdReset() ||
  2396       mInputFrameFinal) {
  2397     return;
  2400   stream->DecrementClientReceiveWindow(bytes);
  2402   // Don't necessarily ack every data packet. Only do it
  2403   // after a significant amount of data.
  2404   uint64_t unacked = stream->LocalUnAcked();
  2405   int64_t  localWindow = stream->ClientReceiveWindow();
  2407   LOG3(("Http2Session::UpdateLocalStreamWindow this=%p id=0x%X newbytes=%u "
  2408         "unacked=%llu localWindow=%lld\n",
  2409         this, stream->StreamID(), bytes, unacked, localWindow));
  2411   if (!unacked)
  2412     return;
  2414   if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold))
  2415     return;
  2417   if (!stream->HasSink()) {
  2418     LOG3(("Http2Session::UpdateLocalStreamWindow %p 0x%X Pushed Stream Has No Sink\n",
  2419           this, stream->StreamID()));
  2420     return;
  2423   // Generate window updates directly out of session instead of the stream
  2424   // in order to avoid queue delays in getting the 'ACK' out.
  2425   uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU;
  2427   LOG3(("Http2Session::UpdateLocalStreamWindow Ack this=%p id=0x%X acksize=%d\n",
  2428         this, stream->StreamID(), toack));
  2429   stream->IncrementClientReceiveWindow(toack);
  2431   // room for this packet needs to be ensured before calling this function
  2432   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
  2433   mOutputQueueUsed += 12;
  2434   MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
  2436   CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, stream->StreamID());
  2437   toack = PR_htonl(toack);
  2438   memcpy(packet + 8, &toack, 4);
  2440   LogIO(this, stream, "Stream Window Update", packet, 12);
  2441   // dont flush here, this write can commonly be coalesced with a
  2442   // session window update to immediately follow.
  2445 void
  2446 Http2Session::UpdateLocalSessionWindow(uint32_t bytes)
  2448   if (!bytes)
  2449     return;
  2451   mLocalSessionWindow -= bytes;
  2453   LOG3(("Http2Session::UpdateLocalSessionWindow this=%p newbytes=%u "
  2454         "localWindow=%lld\n", this, bytes, mLocalSessionWindow));
  2456   // Don't necessarily ack every data packet. Only do it
  2457   // after a significant amount of data.
  2458   if ((mLocalSessionWindow > (ASpdySession::kInitialRwin - kMinimumToAck)) &&
  2459       (mLocalSessionWindow > kEmergencyWindowThreshold))
  2460     return;
  2462   // Only send max  bits of window updates at a time.
  2463   uint64_t toack64 = ASpdySession::kInitialRwin - mLocalSessionWindow;
  2464   uint32_t toack = (toack64 <= 0x7fffffffU) ? toack64 : 0x7fffffffU;
  2466   LOG3(("Http2Session::UpdateLocalSessionWindow Ack this=%p acksize=%u\n",
  2467         this, toack));
  2468   mLocalSessionWindow += toack;
  2470   // room for this packet needs to be ensured before calling this function
  2471   char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
  2472   mOutputQueueUsed += 12;
  2473   MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
  2475   CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
  2476   toack = PR_htonl(toack);
  2477   memcpy(packet + 8, &toack, 4);
  2479   LogIO(this, nullptr, "Session Window Update", packet, 12);
  2480   // dont flush here, this write can commonly be coalesced with others
  2483 void
  2484 Http2Session::UpdateLocalRwin(Http2Stream *stream, uint32_t bytes)
  2486   // make sure there is room for 2 window updates even though
  2487   // we may not generate any.
  2488   EnsureOutputBuffer(16 * 2);
  2490   UpdateLocalStreamWindow(stream, bytes);
  2491   UpdateLocalSessionWindow(bytes);
  2492   FlushOutputQueue();
  2495 void
  2496 Http2Session::Close(nsresult aReason)
  2498   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2500   if (mClosed)
  2501     return;
  2503   LOG3(("Http2Session::Close %p %X", this, aReason));
  2505   mClosed = true;
  2507   mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
  2508   mStreamIDHash.Clear();
  2509   mStreamTransactionHash.Clear();
  2511   uint32_t goAwayReason;
  2512   if (mGoAwayReason != NO_HTTP_ERROR) {
  2513     goAwayReason = mGoAwayReason;
  2514   } else if (NS_SUCCEEDED(aReason)) {
  2515     goAwayReason = NO_HTTP_ERROR;
  2516   } else if (aReason == NS_ERROR_ILLEGAL_VALUE) {
  2517     goAwayReason = PROTOCOL_ERROR;
  2518   } else {
  2519     goAwayReason = INTERNAL_ERROR;
  2521   GenerateGoAway(goAwayReason);
  2522   mConnection = nullptr;
  2523   mSegmentReader = nullptr;
  2524   mSegmentWriter = nullptr;
  2527 void
  2528 Http2Session::CloseTransaction(nsAHttpTransaction *aTransaction,
  2529                                nsresult aResult)
  2531   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2532   LOG3(("Http2Session::CloseTransaction %p %p %x", this, aTransaction, aResult));
  2534   // Generally this arrives as a cancel event from the connection manager.
  2536   // need to find the stream and call CleanupStream() on it.
  2537   Http2Stream *stream = mStreamTransactionHash.Get(aTransaction);
  2538   if (!stream) {
  2539     LOG3(("Http2Session::CloseTransaction %p %p %x - not found.",
  2540           this, aTransaction, aResult));
  2541     return;
  2543   LOG3(("Http2Session::CloseTranscation probably a cancel. "
  2544         "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
  2545         this, aTransaction, aResult, stream->StreamID(), stream));
  2546   CleanupStream(stream, aResult, CANCEL_ERROR);
  2547   ResumeRecv();
  2550 //-----------------------------------------------------------------------------
  2551 // nsAHttpSegmentReader
  2552 //-----------------------------------------------------------------------------
  2554 nsresult
  2555 Http2Session::OnReadSegment(const char *buf,
  2556                             uint32_t count, uint32_t *countRead)
  2558   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2559   nsresult rv;
  2561   // If we can release old queued data then we can try and write the new
  2562   // data directly to the network without using the output queue at all
  2563   if (mOutputQueueUsed)
  2564     FlushOutputQueue();
  2566   if (!mOutputQueueUsed && mSegmentReader) {
  2567     // try and write directly without output queue
  2568     rv = mSegmentReader->OnReadSegment(buf, count, countRead);
  2570     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
  2571       *countRead = 0;
  2572     } else if (NS_FAILED(rv)) {
  2573       return rv;
  2576     if (*countRead < count) {
  2577       uint32_t required = count - *countRead;
  2578       // assuming a commitment() happened, this ensurebuffer is a nop
  2579       // but just in case the queuesize is too small for the required data
  2580       // call ensurebuffer().
  2581       EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
  2582       memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
  2583       mOutputQueueUsed = required;
  2586     *countRead = count;
  2587     return NS_OK;
  2590   // At this point we are going to buffer the new data in the output
  2591   // queue if it fits. By coalescing multiple small submissions into one larger
  2592   // buffer we can get larger writes out to the network later on.
  2594   // This routine should not be allowed to fill up the output queue
  2595   // all on its own - at least kQueueReserved bytes are always left
  2596   // for other routines to use - but this is an all-or-nothing function,
  2597   // so if it will not all fit just return WOULD_BLOCK
  2599   if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
  2600     return NS_BASE_STREAM_WOULD_BLOCK;
  2602   memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
  2603   mOutputQueueUsed += count;
  2604   *countRead = count;
  2606   FlushOutputQueue();
  2608   return NS_OK;
  2611 nsresult
  2612 Http2Session::CommitToSegmentSize(uint32_t count, bool forceCommitment)
  2614   if (mOutputQueueUsed)
  2615     FlushOutputQueue();
  2617   // would there be enough room to buffer this if needed?
  2618   if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
  2619     return NS_OK;
  2621   // if we are using part of our buffers already, try again later unless
  2622   // forceCommitment is set.
  2623   if (mOutputQueueUsed && !forceCommitment)
  2624     return NS_BASE_STREAM_WOULD_BLOCK;
  2626   if (mOutputQueueUsed) {
  2627     // normally we avoid the memmove of RealignOutputQueue, but we'll try
  2628     // it if forceCommitment is set before growing the buffer.
  2629     RealignOutputQueue();
  2631     // is there enough room now?
  2632     if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
  2633       return NS_OK;
  2636   // resize the buffers as needed
  2637   EnsureOutputBuffer(count + kQueueReserved);
  2639   MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved),
  2640              "buffer not as large as expected");
  2642   return NS_OK;
  2645 //-----------------------------------------------------------------------------
  2646 // nsAHttpSegmentWriter
  2647 //-----------------------------------------------------------------------------
  2649 nsresult
  2650 Http2Session::OnWriteSegment(char *buf,
  2651                              uint32_t count, uint32_t *countWritten)
  2653   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2654   nsresult rv;
  2656   if (!mSegmentWriter) {
  2657     // the only way this could happen would be if Close() were called on the
  2658     // stack with WriteSegments()
  2659     return NS_ERROR_FAILURE;
  2662   if (mDownstreamState == PROCESSING_DATA_FRAME) {
  2664     if (mInputFrameFinal &&
  2665         mInputFrameDataRead == mInputFrameDataSize) {
  2666       *countWritten = 0;
  2667       SetNeedsCleanup();
  2668       return NS_BASE_STREAM_CLOSED;
  2671     count = std::min(count, mInputFrameDataSize - mInputFrameDataRead);
  2672     rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
  2673     if (NS_FAILED(rv))
  2674       return rv;
  2676     LogIO(this, mInputFrameDataStream, "Reading Data Frame",
  2677           buf, *countWritten);
  2679     mInputFrameDataRead += *countWritten;
  2680     if (mPaddingLength && (mInputFrameDataSize - mInputFrameDataRead <= mPaddingLength)) {
  2681       // We are crossing from real HTTP data into the realm of padding. If
  2682       // we've actually crossed the line, we need to munge countWritten for the
  2683       // sake of goodness and sanity. No matter what, any future calls to
  2684       // WriteSegments need to just discard data until we reach the end of this
  2685       // frame.
  2686       ChangeDownstreamState(DISCARDING_DATA_FRAME_PADDING);
  2687       uint32_t paddingRead = mPaddingLength - (mInputFrameDataSize - mInputFrameDataRead);
  2688       LOG3(("Http2Session::OnWriteSegment %p stream 0x%X len=%d read=%d "
  2689             "crossed from HTTP data into padding (%d of %d) countWritten=%d",
  2690             this, mInputFrameID, mInputFrameDataSize, mInputFrameDataRead,
  2691             paddingRead, mPaddingLength, *countWritten));
  2692       *countWritten -= paddingRead;
  2693       LOG3(("Http2Session::OnWriteSegment %p stream 0x%X new countWritten=%d",
  2694             this, mInputFrameID, *countWritten));
  2697     mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
  2698     if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameFinal)
  2699       ResetDownstreamState();
  2701     return rv;
  2704   if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
  2706     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
  2707         mInputFrameFinal) {
  2708       *countWritten = 0;
  2709       SetNeedsCleanup();
  2710       return NS_BASE_STREAM_CLOSED;
  2713     count = std::min(count,
  2714                      mFlatHTTPResponseHeaders.Length() -
  2715                      mFlatHTTPResponseHeadersOut);
  2716     memcpy(buf,
  2717            mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
  2718            count);
  2719     mFlatHTTPResponseHeadersOut += count;
  2720     *countWritten = count;
  2722     if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) {
  2723       if (!mInputFrameFinal) {
  2724         // If more frames are expected in this stream, then reset the state so they can be
  2725         // handled. Otherwise (e.g. a 0 length response with the fin on the incoming headers)
  2726         // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can
  2727         // cleanup the stream.
  2728         ResetDownstreamState();
  2732     return NS_OK;
  2735   return NS_ERROR_UNEXPECTED;
  2738 void
  2739 Http2Session::SetNeedsCleanup()
  2741   LOG3(("Http2Session::SetNeedsCleanup %p - recorded downstream fin of "
  2742         "stream %p 0x%X", this, mInputFrameDataStream,
  2743         mInputFrameDataStream->StreamID()));
  2745   // This will result in Close() being called
  2746   MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
  2747   mNeedsCleanup = mInputFrameDataStream;
  2748   ResetDownstreamState();
  2751 void
  2752 Http2Session::ConnectPushedStream(Http2Stream *stream)
  2754   mReadyForRead.Push(stream);
  2755   ForceRecv();
  2758 nsresult
  2759 Http2Session::BufferOutput(const char *buf,
  2760                            uint32_t count,
  2761                            uint32_t *countRead)
  2763   nsAHttpSegmentReader *old = mSegmentReader;
  2764   mSegmentReader = nullptr;
  2765   nsresult rv = OnReadSegment(buf, count, countRead);
  2766   mSegmentReader = old;
  2767   return rv;
  2770 nsresult
  2771 Http2Session::ConfirmTLSProfile()
  2773   if (mTLSProfileConfirmed)
  2774     return NS_OK;
  2776   LOG3(("Http2Session::ConfirmTLSProfile %p mConnection=%p\n",
  2777         this, mConnection.get()));
  2779   if (!gHttpHandler->EnforceHttp2TlsProfile()) {
  2780     LOG3(("Http2Session::ConfirmTLSProfile %p passed due to configuration bypass\n", this));
  2781     mTLSProfileConfirmed = true;
  2782     return NS_OK;
  2785   if (!mConnection)
  2786     return NS_ERROR_FAILURE;
  2788   nsCOMPtr<nsISupports> securityInfo;
  2789   mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
  2790   nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
  2791   LOG3(("Http2Session::ConfirmTLSProfile %p sslsocketcontrol=%p\n", this, ssl.get()));
  2792   if (!ssl)
  2793     return NS_ERROR_FAILURE;
  2795   int16_t version = ssl->GetSSLVersionUsed();
  2796   LOG3(("Http2Session::ConfirmTLSProfile %p version=%x\n", this, version));
  2797   if (version < nsISSLSocketControl::TLS_VERSION_1_2) {
  2798     LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of TLS1.2\n", this));
  2799     RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
  2802 #if 0
  2803   uint16_t kea = ssl->GetKEAUsed();
  2804   if (kea != ssl_kea_dh && kea != ssl_kea_ecdh) {
  2805     LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to invalid KEA %d\n",
  2806           this, kea));
  2807     RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
  2809 #endif
  2811   /* TODO: Enforce DHE >= 2048 || ECDHE >= 128 */
  2813   /* We are required to send SNI. We do that already, so no check is done
  2814    * here to make sure we did. */
  2816   /* We really should check to ensure TLS compression isn't enabled on
  2817    * this connection. However, we never enable TLS compression on our end,
  2818    * anyway, so it'll never be on. All the same, see https://bugzil.la/965881
  2819    * for the possibility for an interface to ensure it never gets turned on. */
  2821   mTLSProfileConfirmed = true;
  2822   return NS_OK;
  2826 //-----------------------------------------------------------------------------
  2827 // Modified methods of nsAHttpConnection
  2828 //-----------------------------------------------------------------------------
  2830 void
  2831 Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller)
  2833   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2834   LOG3(("Http2Session::TransactionHasDataToWrite %p trans=%p", this, caller));
  2836   // a trapped signal from the http transaction to the connection that
  2837   // it is no longer blocked on read.
  2839   Http2Stream *stream = mStreamTransactionHash.Get(caller);
  2840   if (!stream || !VerifyStream(stream)) {
  2841     LOG3(("Http2Session::TransactionHasDataToWrite %p caller %p not found",
  2842           this, caller));
  2843     return;
  2846   LOG3(("Http2Session::TransactionHasDataToWrite %p ID is 0x%X\n",
  2847         this, stream->StreamID()));
  2849   mReadyForWrite.Push(stream);
  2852 void
  2853 Http2Session::TransactionHasDataToWrite(Http2Stream *stream)
  2855   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2856   LOG3(("Http2Session::TransactionHasDataToWrite %p stream=%p ID=0x%x",
  2857         this, stream, stream->StreamID()));
  2859   mReadyForWrite.Push(stream);
  2860   SetWriteCallbacks();
  2863 bool
  2864 Http2Session::IsPersistent()
  2866   return true;
  2869 nsresult
  2870 Http2Session::TakeTransport(nsISocketTransport **,
  2871                             nsIAsyncInputStream **, nsIAsyncOutputStream **)
  2873   MOZ_ASSERT(false, "TakeTransport of Http2Session");
  2874   return NS_ERROR_UNEXPECTED;
  2877 nsHttpConnection *
  2878 Http2Session::TakeHttpConnection()
  2880   MOZ_ASSERT(false, "TakeHttpConnection of Http2Session");
  2881   return nullptr;
  2884 uint32_t
  2885 Http2Session::CancelPipeline(nsresult reason)
  2887   // we don't pipeline inside http/2, so this isn't an issue
  2888   return 0;
  2891 nsAHttpTransaction::Classifier
  2892 Http2Session::Classification()
  2894   if (!mConnection)
  2895     return nsAHttpTransaction::CLASS_GENERAL;
  2896   return mConnection->Classification();
  2899 //-----------------------------------------------------------------------------
  2900 // unused methods of nsAHttpTransaction
  2901 // We can be sure of this because Http2Session is only constructed in
  2902 // nsHttpConnection and is never passed out of that object
  2903 //-----------------------------------------------------------------------------
  2905 void
  2906 Http2Session::SetConnection(nsAHttpConnection *)
  2908   // This is unexpected
  2909   MOZ_ASSERT(false, "Http2Session::SetConnection()");
  2912 void
  2913 Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **)
  2915   // This is unexpected
  2916   MOZ_ASSERT(false, "Http2Session::GetSecurityCallbacks()");
  2919 void
  2920 Http2Session::SetProxyConnectFailed()
  2922   MOZ_ASSERT(false, "Http2Session::SetProxyConnectFailed()");
  2925 bool
  2926 Http2Session::IsDone()
  2928   return !mStreamTransactionHash.Count();
  2931 nsresult
  2932 Http2Session::Status()
  2934   MOZ_ASSERT(false, "Http2Session::Status()");
  2935   return NS_ERROR_UNEXPECTED;
  2938 uint32_t
  2939 Http2Session::Caps()
  2941   MOZ_ASSERT(false, "Http2Session::Caps()");
  2942   return 0;
  2945 void
  2946 Http2Session::SetDNSWasRefreshed()
  2948   MOZ_ASSERT(false, "Http2Session::SetDNSWasRefreshed()");
  2951 uint64_t
  2952 Http2Session::Available()
  2954   MOZ_ASSERT(false, "Http2Session::Available()");
  2955   return 0;
  2958 nsHttpRequestHead *
  2959 Http2Session::RequestHead()
  2961   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2962   MOZ_ASSERT(false,
  2963              "Http2Session::RequestHead() "
  2964              "should not be called after http/2 is setup");
  2965   return NULL;
  2968 uint32_t
  2969 Http2Session::Http1xTransactionCount()
  2971   return 0;
  2974 // used as an enumerator by TakeSubTransactions()
  2975 static PLDHashOperator
  2976   TakeStream(nsAHttpTransaction *key,
  2977              nsAutoPtr<Http2Stream> &stream,
  2978              void *closure)
  2980   nsTArray<nsRefPtr<nsAHttpTransaction> > *list =
  2981     static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure);
  2983   list->AppendElement(key);
  2985   // removing the stream from the hash will delete the stream
  2986   // and drop the transaction reference the hash held
  2987   return PL_DHASH_REMOVE;
  2990 nsresult
  2991 Http2Session::TakeSubTransactions(
  2992   nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
  2994   // Generally this cannot be done with http/2 as transactions are
  2995   // started right away.
  2997   LOG3(("Http2Session::TakeSubTransactions %p\n", this));
  2999   if (mConcurrentHighWater > 0)
  3000     return NS_ERROR_ALREADY_OPENED;
  3002   LOG3(("   taking %d\n", mStreamTransactionHash.Count()));
  3004   mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
  3005   return NS_OK;
  3008 nsresult
  3009 Http2Session::AddTransaction(nsAHttpTransaction *)
  3011   // This API is meant for pipelining, Http2Session's should be
  3012   // extended with AddStream()
  3014   MOZ_ASSERT(false,
  3015              "Http2Session::AddTransaction() should not be called");
  3017   return NS_ERROR_NOT_IMPLEMENTED;
  3020 uint32_t
  3021 Http2Session::PipelineDepth()
  3023   return IsDone() ? 0 : 1;
  3026 nsresult
  3027 Http2Session::SetPipelinePosition(int32_t position)
  3029   // This API is meant for pipelining, Http2Session's should be
  3030   // extended with AddStream()
  3032   MOZ_ASSERT(false,
  3033              "Http2Session::SetPipelinePosition() should not be called");
  3035   return NS_ERROR_NOT_IMPLEMENTED;
  3038 int32_t
  3039 Http2Session::PipelinePosition()
  3041   return 0;
  3044 //-----------------------------------------------------------------------------
  3045 // Pass through methods of nsAHttpConnection
  3046 //-----------------------------------------------------------------------------
  3048 nsAHttpConnection *
  3049 Http2Session::Connection()
  3051   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  3052   return mConnection;
  3055 nsresult
  3056 Http2Session::OnHeadersAvailable(nsAHttpTransaction *transaction,
  3057                                  nsHttpRequestHead *requestHead,
  3058                                  nsHttpResponseHead *responseHead, bool *reset)
  3060   return mConnection->OnHeadersAvailable(transaction,
  3061                                          requestHead,
  3062                                          responseHead,
  3063                                          reset);
  3066 bool
  3067 Http2Session::IsReused()
  3069   return mConnection->IsReused();
  3072 nsresult
  3073 Http2Session::PushBack(const char *buf, uint32_t len)
  3075   return mConnection->PushBack(buf, len);
  3078 } // namespace mozilla::net
  3079 } // namespace mozilla

mercurial