tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Http2Session.cpp (151736B)


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