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

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set sw=2 ts=8 et tw=80 : */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : // HttpLog.h should generally be included first
       8             : #include "HttpLog.h"
       9             : 
      10             : // Log on level :5, instead of default :4.
      11             : #undef LOG
      12             : #define LOG(args) LOG5(args)
      13             : #undef LOG_ENABLED
      14             : #define LOG_ENABLED() LOG5_ENABLED()
      15             : 
      16             : #include <algorithm>
      17             : 
      18             : #include "Http2Compression.h"
      19             : #include "Http2Session.h"
      20             : #include "Http2Stream.h"
      21             : #include "Http2Push.h"
      22             : #include "TunnelUtils.h"
      23             : 
      24             : #include "mozilla/BasePrincipal.h"
      25             : #include "mozilla/Telemetry.h"
      26             : #include "nsAlgorithm.h"
      27             : #include "nsHttp.h"
      28             : #include "nsHttpHandler.h"
      29             : #include "nsHttpRequestHead.h"
      30             : #include "nsIClassOfService.h"
      31             : #include "nsIPipe.h"
      32             : #include "nsISocketTransport.h"
      33             : #include "nsStandardURL.h"
      34             : #include "prnetdb.h"
      35             : 
      36             : namespace mozilla {
      37             : namespace net {
      38             : 
      39           0 : Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
      40             :                          Http2Session *session,
      41           0 :                          int32_t priority)
      42             :   : mStreamID(0)
      43             :   , mSession(session)
      44             :   , mSegmentReader(nullptr)
      45             :   , mSegmentWriter(nullptr)
      46             :   , mUpstreamState(GENERATING_HEADERS)
      47             :   , mState(IDLE)
      48             :   , mRequestHeadersDone(0)
      49             :   , mOpenGenerated(0)
      50             :   , mAllHeadersReceived(0)
      51             :   , mQueued(0)
      52           0 :   , mSocketTransport(session->SocketTransport())
      53             :   , mTransaction(httpTransaction)
      54           0 :   , mChunkSize(session->SendingChunkSize())
      55             :   , mRequestBlockedOnRead(0)
      56             :   , mRecvdFin(0)
      57             :   , mReceivedData(0)
      58             :   , mRecvdReset(0)
      59             :   , mSentReset(0)
      60             :   , mCountAsActive(0)
      61             :   , mSentFin(0)
      62             :   , mSentWaitingFor(0)
      63             :   , mSetTCPSocketBuffer(0)
      64             :   , mBypassInputBuffer(0)
      65             :   , mTxInlineFrameSize(Http2Session::kDefaultBufferSize)
      66             :   , mTxInlineFrameUsed(0)
      67             :   , mTxStreamFrameSize(0)
      68             :   , mRequestBodyLenRemaining(0)
      69             :   , mLocalUnacked(0)
      70             :   , mBlockedOnRwin(false)
      71             :   , mTotalSent(0)
      72             :   , mTotalRead(0)
      73             :   , mPushSource(nullptr)
      74             :   , mAttempting0RTT(false)
      75             :   , mIsTunnel(false)
      76           0 :   , mPlainTextTunnel(false)
      77             : {
      78           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
      79             : 
      80           0 :   LOG3(("Http2Stream::Http2Stream %p", this));
      81             : 
      82           0 :   mServerReceiveWindow = session->GetServerInitialStreamWindow();
      83           0 :   mClientReceiveWindow = session->PushAllowance();
      84             : 
      85           0 :   mTxInlineFrame = MakeUnique<uint8_t[]>(mTxInlineFrameSize);
      86             : 
      87             :   static_assert(nsISupportsPriority::PRIORITY_LOWEST <= kNormalPriority,
      88             :                 "Lowest Priority should be less than kNormalPriority");
      89             : 
      90             :   // values of priority closer to 0 are higher priority for the priority
      91             :   // argument. This value is used as a group, which maps to a
      92             :   // weight that is related to the nsISupportsPriority that we are given.
      93             :   int32_t httpPriority;
      94           0 :   if (priority >= nsISupportsPriority::PRIORITY_LOWEST) {
      95           0 :     httpPriority = kWorstPriority;
      96           0 :   } else if (priority <= nsISupportsPriority::PRIORITY_HIGHEST) {
      97           0 :     httpPriority = kBestPriority;
      98             :   } else {
      99           0 :     httpPriority = kNormalPriority + priority;
     100             :   }
     101           0 :   MOZ_ASSERT(httpPriority >= 0);
     102           0 :   SetPriority(static_cast<uint32_t>(httpPriority));
     103           0 : }
     104             : 
     105           0 : Http2Stream::~Http2Stream()
     106             : {
     107           0 :   ClearTransactionsBlockedOnTunnel();
     108           0 :   mStreamID = Http2Session::kDeadStreamID;
     109             : 
     110           0 :   LOG3(("Http2Stream::~Http2Stream %p", this));
     111           0 : }
     112             : 
     113             : // ReadSegments() is used to write data down the socket. Generally, HTTP
     114             : // request data is pulled from the approriate transaction and
     115             : // converted to HTTP/2 data. Sometimes control data like a window-update is
     116             : // generated instead.
     117             : 
     118             : nsresult
     119           0 : Http2Stream::ReadSegments(nsAHttpSegmentReader *reader,
     120             :                           uint32_t count,
     121             :                           uint32_t *countRead)
     122             : {
     123           0 :   LOG3(("Http2Stream %p ReadSegments reader=%p count=%d state=%x",
     124             :         this, reader, count, mUpstreamState));
     125             : 
     126           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     127             : 
     128           0 :   nsresult rv = NS_ERROR_UNEXPECTED;
     129           0 :   mRequestBlockedOnRead = 0;
     130             : 
     131           0 :   if (mRecvdFin || mRecvdReset) {
     132             :     // Don't transmit any request frames if the peer cannot respond
     133           0 :     LOG3(("Http2Stream %p ReadSegments request stream aborted due to"
     134             :           " response side closure\n", this));
     135           0 :     return NS_ERROR_ABORT;
     136             :   }
     137             : 
     138             :   // avoid runt chunks if possible by anticipating
     139             :   // full data frames
     140           0 :   if (count > (mChunkSize + 8)) {
     141           0 :     uint32_t numchunks = count / (mChunkSize + 8);
     142           0 :     count = numchunks * (mChunkSize + 8);
     143             :   }
     144             : 
     145           0 :   switch (mUpstreamState) {
     146             :   case GENERATING_HEADERS:
     147             :   case GENERATING_BODY:
     148             :   case SENDING_BODY:
     149             :     // Call into the HTTP Transaction to generate the HTTP request
     150             :     // stream. That stream will show up in OnReadSegment().
     151           0 :     mSegmentReader = reader;
     152           0 :     rv = mTransaction->ReadSegments(this, count, countRead);
     153           0 :     mSegmentReader = nullptr;
     154             : 
     155           0 :     LOG3(("Http2Stream::ReadSegments %p trans readsegments rv %" PRIx32 " read=%d\n",
     156             :           this, static_cast<uint32_t>(rv), *countRead));
     157             : 
     158             :     // Check to see if the transaction's request could be written out now.
     159             :     // If not, mark the stream for callback when writing can proceed.
     160           0 :     if (NS_SUCCEEDED(rv) &&
     161           0 :         mUpstreamState == GENERATING_HEADERS &&
     162           0 :         !mRequestHeadersDone)
     163           0 :       mSession->TransactionHasDataToWrite(this);
     164             : 
     165             :     // mTxinlineFrameUsed represents any queued un-sent frame. It might
     166             :     // be 0 if there is no such frame, which is not a gurantee that we
     167             :     // don't have more request body to send - just that any data that was
     168             :     // sent comprised a complete HTTP/2 frame. Likewise, a non 0 value is
     169             :     // a queued, but complete, http/2 frame length.
     170             : 
     171             :     // Mark that we are blocked on read if the http transaction needs to
     172             :     // provide more of the request message body and there is nothing queued
     173             :     // for writing
     174           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
     175           0 :       mRequestBlockedOnRead = 1;
     176             : 
     177             :     // A transaction that had already generated its headers before it was
     178             :     // queued at the session level (due to concurrency concerns) may not call
     179             :     // onReadSegment off the ReadSegments() stack above.
     180           0 :     if (mUpstreamState == GENERATING_HEADERS && NS_SUCCEEDED(rv)) {
     181           0 :       LOG3(("Http2Stream %p ReadSegments forcing OnReadSegment call\n", this));
     182           0 :       uint32_t wasted = 0;
     183           0 :       mSegmentReader = reader;
     184           0 :       Unused << OnReadSegment("", 0, &wasted);
     185           0 :       mSegmentReader = nullptr;
     186             :     }
     187             : 
     188             :     // If the sending flow control window is open (!mBlockedOnRwin) then
     189             :     // continue sending the request
     190           0 :     if (!mBlockedOnRwin && mOpenGenerated &&
     191           0 :         !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
     192           0 :       MOZ_ASSERT(!mQueued);
     193           0 :       MOZ_ASSERT(mRequestHeadersDone);
     194           0 :       LOG3(("Http2Stream::ReadSegments %p 0x%X: Sending request data complete, "
     195             :             "mUpstreamState=%x\n",this, mStreamID, mUpstreamState));
     196           0 :       if (mSentFin) {
     197           0 :         ChangeState(UPSTREAM_COMPLETE);
     198             :       } else {
     199           0 :         GenerateDataFrameHeader(0, true);
     200           0 :         ChangeState(SENDING_FIN_STREAM);
     201           0 :         mSession->TransactionHasDataToWrite(this);
     202           0 :         rv = NS_BASE_STREAM_WOULD_BLOCK;
     203             :       }
     204             :     }
     205           0 :     break;
     206             : 
     207             :   case SENDING_FIN_STREAM:
     208             :     // We were trying to send the FIN-STREAM but were blocked from
     209             :     // sending it out - try again.
     210           0 :     if (!mSentFin) {
     211           0 :       mSegmentReader = reader;
     212           0 :       rv = TransmitFrame(nullptr, nullptr, false);
     213           0 :       mSegmentReader = nullptr;
     214           0 :       MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
     215             :                  "Transmit Frame should be all or nothing");
     216           0 :       if (NS_SUCCEEDED(rv))
     217           0 :         ChangeState(UPSTREAM_COMPLETE);
     218             :     } else {
     219           0 :       rv = NS_OK;
     220           0 :       mTxInlineFrameUsed = 0;         // cancel fin data packet
     221           0 :       ChangeState(UPSTREAM_COMPLETE);
     222             :     }
     223             : 
     224           0 :     *countRead = 0;
     225             : 
     226             :     // don't change OK to WOULD BLOCK. we are really done sending if OK
     227           0 :     break;
     228             : 
     229             :   case UPSTREAM_COMPLETE:
     230           0 :     *countRead = 0;
     231           0 :     rv = NS_OK;
     232           0 :     break;
     233             : 
     234             :   default:
     235           0 :     MOZ_ASSERT(false, "Http2Stream::ReadSegments unknown state");
     236             :     break;
     237             :   }
     238             : 
     239           0 :   return rv;
     240             : }
     241             : 
     242             : uint64_t
     243           0 : Http2Stream::LocalUnAcked()
     244             : {
     245             :   // reduce unacked by the amount of undelivered data
     246             :   // to help assert flow control
     247           0 :   uint64_t undelivered = mSimpleBuffer.Available();
     248             : 
     249           0 :   if (undelivered > mLocalUnacked) {
     250           0 :     return 0;
     251             :   }
     252           0 :   return mLocalUnacked - undelivered;
     253             : }
     254             : 
     255             : nsresult
     256           0 : Http2Stream::BufferInput(uint32_t count, uint32_t *countWritten)
     257             : {
     258             :   char buf[SimpleBufferPage::kSimpleBufferPageSize];
     259           0 :   if (SimpleBufferPage::kSimpleBufferPageSize < count) {
     260           0 :     count = SimpleBufferPage::kSimpleBufferPageSize;
     261             :   }
     262             : 
     263           0 :   mBypassInputBuffer = 1;
     264           0 :   nsresult rv = mSegmentWriter->OnWriteSegment(buf, count, countWritten);
     265           0 :   mBypassInputBuffer = 0;
     266             : 
     267           0 :   if (NS_SUCCEEDED(rv)) {
     268           0 :     rv = mSimpleBuffer.Write(buf, *countWritten);
     269           0 :     if (NS_FAILED(rv)) {
     270           0 :       MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY);
     271           0 :       return NS_ERROR_OUT_OF_MEMORY;
     272             :     }
     273             :   }
     274           0 :   return rv;
     275             : }
     276             : 
     277             : bool
     278           0 : Http2Stream::DeferCleanup(nsresult status)
     279             : {
     280             :   // do not cleanup a stream that has data buffered for the transaction
     281           0 :   return (NS_SUCCEEDED(status) && mSimpleBuffer.Available());
     282             : }
     283             : 
     284             : // WriteSegments() is used to read data off the socket. Generally this is
     285             : // just a call through to the associated nsHttpTransaction for this stream
     286             : // for the remaining data bytes indicated by the current DATA frame.
     287             : 
     288             : nsresult
     289           0 : Http2Stream::WriteSegments(nsAHttpSegmentWriter *writer,
     290             :                            uint32_t count,
     291             :                            uint32_t *countWritten)
     292             : {
     293           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     294           0 :   MOZ_ASSERT(!mSegmentWriter, "segment writer in progress");
     295             : 
     296           0 :   LOG3(("Http2Stream::WriteSegments %p count=%d state=%x",
     297             :         this, count, mUpstreamState));
     298             : 
     299           0 :   mSegmentWriter = writer;
     300           0 :   nsresult rv = mTransaction->WriteSegments(this, count, countWritten);
     301             : 
     302           0 :   if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
     303             :     // consuming transaction won't take data. but we need to read it into a buffer so that it
     304             :     // won't block other streams. but we should not advance the flow control window
     305             :     // so that we'll eventually push back on the sender.
     306             : 
     307             :     // with tunnels you need to make sure that this is an underlying connction established
     308             :     // that can be meaningfully giving this signal
     309           0 :     bool doBuffer = true;
     310           0 :     if (mIsTunnel) {
     311           0 :       RefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
     312           0 :       if (qiTrans) {
     313           0 :         doBuffer = qiTrans->ConnectedReadyForInput();
     314             :       }
     315             :     }
     316             :     // stash this data
     317           0 :     if (doBuffer) {
     318           0 :       rv = BufferInput(count, countWritten);
     319           0 :       LOG3(("Http2Stream::WriteSegments %p Buffered %" PRIX32 " %d\n", this,
     320             :             static_cast<uint32_t>(rv), *countWritten));
     321             :     }
     322             :   }
     323           0 :   mSegmentWriter = nullptr;
     324           0 :   return rv;
     325             : }
     326             : 
     327             : nsresult
     328           0 : Http2Stream::MakeOriginURL(const nsACString &origin, RefPtr<nsStandardURL> &url)
     329             : {
     330           0 :   nsAutoCString scheme;
     331           0 :   nsresult rv = net_ExtractURLScheme(origin, scheme);
     332           0 :   NS_ENSURE_SUCCESS(rv, rv);
     333           0 :   return MakeOriginURL(scheme, origin, url);
     334             : }
     335             : 
     336             : nsresult
     337           0 : Http2Stream::MakeOriginURL(const nsACString &scheme, const nsACString &origin,
     338             :                            RefPtr<nsStandardURL> &url)
     339             : {
     340           0 :   url = new nsStandardURL();
     341           0 :   nsresult rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY,
     342           0 :                           scheme.EqualsLiteral("http") ?
     343             :                               NS_HTTP_DEFAULT_PORT :
     344             :                               NS_HTTPS_DEFAULT_PORT,
     345           0 :                           origin, nullptr, nullptr);
     346           0 :   return rv;
     347             : }
     348             : 
     349             : void
     350           0 : Http2Stream::CreatePushHashKey(const nsCString &scheme,
     351             :                                const nsCString &hostHeader,
     352             :                                const mozilla::OriginAttributes &originAttributes,
     353             :                                uint64_t serial,
     354             :                                const nsACString& pathInfo,
     355             :                                nsCString &outOrigin,
     356             :                                nsCString &outKey)
     357             : {
     358           0 :   nsCString fullOrigin = scheme;
     359           0 :   fullOrigin.AppendLiteral("://");
     360           0 :   fullOrigin.Append(hostHeader);
     361             : 
     362           0 :   RefPtr<nsStandardURL> origin;
     363           0 :   nsresult rv = Http2Stream::MakeOriginURL(scheme, fullOrigin, origin);
     364             : 
     365           0 :   if (NS_SUCCEEDED(rv)) {
     366           0 :     rv = origin->GetAsciiSpec(outOrigin);
     367           0 :     outOrigin.Trim("/", false, true, false);
     368             :   }
     369             : 
     370           0 :   if (NS_FAILED(rv)) {
     371             :     // Fallback to plain text copy - this may end up behaving poorly
     372           0 :     outOrigin = fullOrigin;
     373             :   }
     374             : 
     375           0 :   outKey = outOrigin;
     376           0 :   outKey.AppendLiteral("/[");
     377           0 :   nsAutoCString suffix;
     378           0 :   originAttributes.CreateSuffix(suffix);
     379           0 :   outKey.Append(suffix);
     380           0 :   outKey.Append(']');
     381           0 :   outKey.AppendLiteral("/[http2.");
     382           0 :   outKey.AppendInt(serial);
     383           0 :   outKey.Append(']');
     384           0 :   outKey.Append(pathInfo);
     385           0 : }
     386             : 
     387             : nsresult
     388           0 : Http2Stream::ParseHttpRequestHeaders(const char *buf,
     389             :                                      uint32_t avail,
     390             :                                      uint32_t *countUsed)
     391             : {
     392             :   // Returns NS_OK even if the headers are incomplete
     393             :   // set mRequestHeadersDone flag if they are complete
     394             : 
     395           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     396           0 :   MOZ_ASSERT(mUpstreamState == GENERATING_HEADERS);
     397           0 :   MOZ_ASSERT(!mRequestHeadersDone);
     398             : 
     399           0 :   LOG3(("Http2Stream::ParseHttpRequestHeaders %p avail=%d state=%x",
     400             :         this, avail, mUpstreamState));
     401             : 
     402           0 :   mFlatHttpRequestHeaders.Append(buf, avail);
     403           0 :   nsHttpRequestHead *head = mTransaction->RequestHead();
     404             : 
     405             :   // We can use the simple double crlf because firefox is the
     406             :   // only client we are parsing
     407           0 :   int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
     408             : 
     409           0 :   if (endHeader == kNotFound) {
     410             :     // We don't have all the headers yet
     411           0 :     LOG3(("Http2Stream::ParseHttpRequestHeaders %p "
     412             :           "Need more header bytes. Len = %d",
     413             :           this, mFlatHttpRequestHeaders.Length()));
     414           0 :     *countUsed = avail;
     415           0 :     return NS_OK;
     416             :   }
     417             : 
     418             :   // We have recvd all the headers, trim the local
     419             :   // buffer of the final empty line, and set countUsed to reflect
     420             :   // the whole header has been consumed.
     421           0 :   uint32_t oldLen = mFlatHttpRequestHeaders.Length();
     422           0 :   mFlatHttpRequestHeaders.SetLength(endHeader + 2);
     423           0 :   *countUsed = avail - (oldLen - endHeader) + 4;
     424           0 :   mRequestHeadersDone = 1;
     425             : 
     426           0 :   nsAutoCString authorityHeader;
     427           0 :   nsAutoCString hashkey;
     428           0 :   nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader);
     429           0 :   if (NS_FAILED(rv)) {
     430           0 :     MOZ_ASSERT(false);
     431             :     return rv;
     432             :   }
     433             : 
     434           0 :   nsAutoCString requestURI;
     435           0 :   head->RequestURI(requestURI);
     436             : 
     437           0 :   mozilla::OriginAttributes originAttributes;
     438           0 :   mSocketTransport->GetOriginAttributes(&originAttributes);
     439             : 
     440           0 :   CreatePushHashKey(nsDependentCString(head->IsHTTPS() ? "https" : "http"),
     441           0 :                     authorityHeader, originAttributes, mSession->Serial(),
     442             :                     requestURI,
     443           0 :                     mOrigin, hashkey);
     444             : 
     445             :   // check the push cache for GET
     446           0 :   if (head->IsGet()) {
     447             :     // from :scheme, :authority, :path
     448           0 :     nsIRequestContext *requestContext = mTransaction->RequestContext();
     449           0 :     SpdyPushCache *cache = nullptr;
     450           0 :     if (requestContext) {
     451           0 :       requestContext->GetSpdyPushCache(&cache);
     452             :     }
     453             : 
     454           0 :     Http2PushedStream *pushedStream = nullptr;
     455             : 
     456             :     // If a push stream is attached to the transaction via onPush, match only with that
     457             :     // one. This occurs when a push was made with in conjunction with a nsIHttpPushListener
     458           0 :     nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
     459           0 :     if (trans && (pushedStream = trans->TakePushedStream())) {
     460           0 :       if (pushedStream->mSession == mSession) {
     461           0 :         LOG3(("Pushed Stream match based on OnPush correlation %p", pushedStream));
     462             :       } else {
     463           0 :         LOG3(("Pushed Stream match failed due to stream mismatch %p %" PRId64 " %" PRId64 "\n",
     464             :               pushedStream, pushedStream->mSession->Serial(), mSession->Serial()));
     465           0 :         pushedStream->OnPushFailed();
     466           0 :         pushedStream = nullptr;
     467             :       }
     468             :     }
     469             : 
     470             :     // we remove the pushedstream from the push cache so that
     471             :     // it will not be used for another GET. This does not destroy the
     472             :     // stream itself - that is done when the transactionhash is done with it.
     473           0 :     if (cache && !pushedStream){
     474           0 :         pushedStream = cache->RemovePushedStreamHttp2(hashkey);
     475             :     }
     476             : 
     477           0 :     LOG3(("Pushed Stream Lookup "
     478             :           "session=%p key=%s requestcontext=%p cache=%p hit=%p\n",
     479             :           mSession, hashkey.get(), requestContext, cache, pushedStream));
     480             : 
     481           0 :     if (pushedStream) {
     482           0 :       LOG3(("Pushed Stream Match located %p id=0x%X key=%s\n",
     483             :             pushedStream, pushedStream->StreamID(), hashkey.get()));
     484           0 :       pushedStream->SetConsumerStream(this);
     485           0 :       mPushSource = pushedStream;
     486           0 :       SetSentFin(true);
     487           0 :       AdjustPushedPriority();
     488             : 
     489             :       // There is probably pushed data buffered so trigger a read manually
     490             :       // as we can't rely on future network events to do it
     491           0 :       mSession->ConnectPushedStream(this);
     492           0 :       mOpenGenerated = 1;
     493           0 :       return NS_OK;
     494             :     }
     495             :   }
     496           0 :   return NS_OK;
     497             : }
     498             : 
     499             : // This is really a headers frame, but open is pretty clear from a workflow pov
     500             : nsresult
     501           0 : Http2Stream::GenerateOpen()
     502             : {
     503             :   // It is now OK to assign a streamID that we are assured will
     504             :   // be monotonically increasing amongst new streams on this
     505             :   // session
     506           0 :   mStreamID = mSession->RegisterStreamID(this);
     507           0 :   MOZ_ASSERT(mStreamID & 1, "Http2 Stream Channel ID must be odd");
     508           0 :   MOZ_ASSERT(!mOpenGenerated);
     509             : 
     510           0 :   mOpenGenerated = 1;
     511             : 
     512           0 :   nsHttpRequestHead *head = mTransaction->RequestHead();
     513           0 :   nsAutoCString requestURI;
     514           0 :   head->RequestURI(requestURI);
     515           0 :   LOG3(("Http2Stream %p Stream ID 0x%X [session=%p] for URI %s\n",
     516             :         this, mStreamID, mSession, requestURI.get()));
     517             : 
     518           0 :   if (mStreamID >= 0x80000000) {
     519             :     // streamID must fit in 31 bits. Evading This is theoretically possible
     520             :     // because stream ID assignment is asynchronous to stream creation
     521             :     // because of the protocol requirement that the new stream ID
     522             :     // be monotonically increasing. In reality this is really not possible
     523             :     // because new streams stop being added to a session with millions of
     524             :     // IDs still available and no race condition is going to bridge that gap;
     525             :     // so we can be comfortable on just erroring out for correctness in that
     526             :     // case.
     527           0 :     LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
     528           0 :     return NS_ERROR_UNEXPECTED;
     529             :   }
     530             : 
     531             :   // Now we need to convert the flat http headers into a set
     532             :   // of HTTP/2 headers by writing to mTxInlineFrame{sz}
     533             : 
     534           0 :   nsCString compressedData;
     535           0 :   nsAutoCString authorityHeader;
     536           0 :   nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader);
     537           0 :   if (NS_FAILED(rv)) {
     538           0 :     MOZ_ASSERT(false);
     539             :     return rv;
     540             :   }
     541             : 
     542           0 :   nsDependentCString scheme(head->IsHTTPS() ? "https" : "http");
     543           0 :   if (head->IsConnect()) {
     544           0 :     MOZ_ASSERT(mTransaction->QuerySpdyConnectTransaction());
     545           0 :     mIsTunnel = true;
     546           0 :     mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
     547             : 
     548             :     // Our normal authority has an implicit port, best to use an
     549             :     // explicit one with a tunnel
     550           0 :     nsHttpConnectionInfo *ci = mTransaction->ConnectionInfo();
     551           0 :     if (!ci) {
     552           0 :       return NS_ERROR_UNEXPECTED;
     553             :     }
     554             : 
     555           0 :     authorityHeader = ci->GetOrigin();
     556           0 :     authorityHeader.Append(':');
     557           0 :     authorityHeader.AppendInt(ci->OriginPort());
     558             :   }
     559             : 
     560           0 :   nsAutoCString method;
     561           0 :   nsAutoCString path;
     562           0 :   head->Method(method);
     563           0 :   head->Path(path);
     564           0 :   rv = mSession->Compressor()->EncodeHeaderBlock(mFlatHttpRequestHeaders,
     565             :                                                  method,
     566             :                                                  path,
     567             :                                                  authorityHeader,
     568             :                                                  scheme,
     569           0 :                                                  head->IsConnect(),
     570           0 :                                                  compressedData);
     571           0 :   NS_ENSURE_SUCCESS(rv, rv);
     572             : 
     573           0 :   int64_t clVal = mSession->Compressor()->GetParsedContentLength();
     574           0 :   if (clVal != -1) {
     575           0 :     mRequestBodyLenRemaining = clVal;
     576             :   }
     577             : 
     578             :   // Determine whether to put the fin bit on the header frame or whether
     579             :   // to wait for a data packet to put it on.
     580           0 :   uint8_t firstFrameFlags =  Http2Session::kFlag_PRIORITY;
     581             : 
     582           0 :   if (head->IsGet() ||
     583           0 :       head->IsHead()) {
     584             :     // for GET and HEAD place the fin bit right on the
     585             :     // header packet
     586             : 
     587           0 :     SetSentFin(true);
     588           0 :     firstFrameFlags |= Http2Session::kFlag_END_STREAM;
     589           0 :   } else if (head->IsPost() ||
     590           0 :              head->IsPut() ||
     591           0 :              head->IsConnect()) {
     592             :     // place fin in a data frame even for 0 length messages for iterop
     593           0 :   } else if (!mRequestBodyLenRemaining) {
     594             :     // for other HTTP extension methods, rely on the content-length
     595             :     // to determine whether or not to put fin on headers
     596           0 :     SetSentFin(true);
     597           0 :     firstFrameFlags |= Http2Session::kFlag_END_STREAM;
     598             :   }
     599             : 
     600             :   // split this one HEADERS frame up into N HEADERS + CONTINUATION frames if it exceeds the
     601             :   // 2^14-1 limit for 1 frame. Do it by inserting header size gaps in the existing
     602             :   // frame for the new headers and for the first one a priority field. There is
     603             :   // no question this is ugly, but a 16KB HEADERS frame should be a long
     604             :   // tail event, so this is really just for correctness and a nop in the base case.
     605             :   //
     606             : 
     607           0 :   MOZ_ASSERT(!mTxInlineFrameUsed);
     608             : 
     609           0 :   uint32_t dataLength = compressedData.Length();
     610           0 :   uint32_t maxFrameData = Http2Session::kMaxFrameData - 5; // 5 bytes for priority
     611           0 :   uint32_t numFrames = 1;
     612             : 
     613           0 :   if (dataLength > maxFrameData) {
     614           0 :     numFrames += ((dataLength - maxFrameData) + Http2Session::kMaxFrameData - 1) /
     615             :       Http2Session::kMaxFrameData;
     616           0 :     MOZ_ASSERT (numFrames > 1);
     617             :   }
     618             : 
     619             :   // note that we could still have 1 frame for 0 bytes of data. that's ok.
     620             : 
     621           0 :   uint32_t messageSize = dataLength;
     622           0 :   messageSize += Http2Session::kFrameHeaderBytes + 5; // frame header + priority overhead in HEADERS frame
     623           0 :   messageSize += (numFrames - 1) * Http2Session::kFrameHeaderBytes; // frame header overhead in CONTINUATION frames
     624             : 
     625           0 :   EnsureBuffer(mTxInlineFrame, messageSize,
     626           0 :                mTxInlineFrameUsed, mTxInlineFrameSize);
     627             : 
     628           0 :   mTxInlineFrameUsed += messageSize;
     629           0 :   UpdatePriorityDependency();
     630           0 :   LOG3(("Http2Stream %p Generating %d bytes of HEADERS for stream 0x%X with "
     631             :         "priority weight %u dep 0x%X frames %u uri=%s\n",
     632             :         this, mTxInlineFrameUsed, mStreamID, mPriorityWeight,
     633             :         mPriorityDependency, numFrames, requestURI.get()));
     634             : 
     635           0 :   uint32_t outputOffset = 0;
     636           0 :   uint32_t compressedDataOffset = 0;
     637           0 :   for (uint32_t idx = 0; idx < numFrames; ++idx) {
     638             :     uint32_t flags, frameLen;
     639           0 :     bool lastFrame = (idx == numFrames - 1);
     640             : 
     641           0 :     flags = 0;
     642           0 :     frameLen = maxFrameData;
     643           0 :     if (!idx) {
     644           0 :       flags |= firstFrameFlags;
     645             :       // Only the first frame needs the 4-byte offset
     646           0 :       maxFrameData = Http2Session::kMaxFrameData;
     647             :     }
     648           0 :     if (lastFrame) {
     649           0 :       frameLen = dataLength;
     650           0 :       flags |= Http2Session::kFlag_END_HEADERS;
     651             :     }
     652           0 :     dataLength -= frameLen;
     653             : 
     654           0 :     mSession->CreateFrameHeader(
     655           0 :       mTxInlineFrame.get() + outputOffset,
     656           0 :       frameLen + (idx ? 0 : 5),
     657             :       (idx) ? Http2Session::FRAME_TYPE_CONTINUATION : Http2Session::FRAME_TYPE_HEADERS,
     658           0 :       flags, mStreamID);
     659           0 :     outputOffset += Http2Session::kFrameHeaderBytes;
     660             : 
     661           0 :     if (!idx) {
     662           0 :       uint32_t wireDep = PR_htonl(mPriorityDependency);
     663           0 :       memcpy(mTxInlineFrame.get() + outputOffset, &wireDep, 4);
     664           0 :       memcpy(mTxInlineFrame.get() + outputOffset + 4, &mPriorityWeight, 1);
     665           0 :       outputOffset += 5;
     666             :     }
     667             : 
     668           0 :     memcpy(mTxInlineFrame.get() + outputOffset,
     669           0 :            compressedData.BeginReading() + compressedDataOffset, frameLen);
     670           0 :     compressedDataOffset += frameLen;
     671           0 :     outputOffset += frameLen;
     672             :   }
     673             : 
     674           0 :   Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, compressedData.Length());
     675             : 
     676             :   // The size of the input headers is approximate
     677             :   uint32_t ratio =
     678           0 :     compressedData.Length() * 100 /
     679           0 :     (11 + requestURI.Length() +
     680           0 :      mFlatHttpRequestHeaders.Length());
     681             : 
     682           0 :   mFlatHttpRequestHeaders.Truncate();
     683           0 :   Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
     684           0 :   return NS_OK;
     685             : }
     686             : 
     687             : void
     688           0 : Http2Stream::AdjustInitialWindow()
     689             : {
     690             :   // The default initial_window is sized for pushed streams. When we
     691             :   // generate a client pulled stream we want to disable flow control for
     692             :   // the stream with a window update. Do the same for pushed streams
     693             :   // when they connect to a pull.
     694             : 
     695             :   // >0 even numbered IDs are pushed streams.
     696             :   // odd numbered IDs are pulled streams.
     697             :   // 0 is the sink for a pushed stream.
     698           0 :   Http2Stream *stream = this;
     699           0 :   if (!mStreamID) {
     700           0 :     MOZ_ASSERT(mPushSource);
     701           0 :     if (!mPushSource)
     702           0 :       return;
     703           0 :     stream = mPushSource;
     704           0 :     MOZ_ASSERT(stream->mStreamID);
     705           0 :     MOZ_ASSERT(!(stream->mStreamID & 1)); // is a push stream
     706             : 
     707             :     // If the pushed stream has recvd a FIN, there is no reason to update
     708             :     // the window
     709           0 :     if (stream->RecvdFin() || stream->RecvdReset())
     710           0 :       return;
     711             :   }
     712             : 
     713           0 :   if (stream->mState == RESERVED_BY_REMOTE) {
     714             :     // h2-14 prevents sending a window update in this state
     715           0 :     return;
     716             :   }
     717             : 
     718             :   // right now mClientReceiveWindow is the lower push limit
     719             :   // bump it up to the pull limit set by the channel or session
     720             :   // don't allow windows less than push
     721           0 :   uint32_t bump = 0;
     722           0 :   nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
     723           0 :   if (trans && trans->InitialRwin()) {
     724           0 :     bump = (trans->InitialRwin() > mClientReceiveWindow) ?
     725           0 :       (trans->InitialRwin() - mClientReceiveWindow) : 0;
     726             :   } else {
     727           0 :     MOZ_ASSERT(mSession->InitialRwin() >= mClientReceiveWindow);
     728           0 :     bump = mSession->InitialRwin() - mClientReceiveWindow;
     729             :   }
     730             : 
     731           0 :   LOG3(("AdjustInitialwindow increased flow control window %p 0x%X %u\n",
     732             :         this, stream->mStreamID, bump));
     733           0 :   if (!bump) { // nothing to do
     734           0 :     return;
     735             :   }
     736             : 
     737           0 :   EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 4,
     738           0 :                mTxInlineFrameUsed, mTxInlineFrameSize);
     739           0 :   uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
     740           0 :   mTxInlineFrameUsed += Http2Session::kFrameHeaderBytes + 4;
     741             : 
     742           0 :   mSession->CreateFrameHeader(packet, 4,
     743             :                               Http2Session::FRAME_TYPE_WINDOW_UPDATE,
     744           0 :                               0, stream->mStreamID);
     745             : 
     746           0 :   mClientReceiveWindow += bump;
     747           0 :   bump = PR_htonl(bump);
     748           0 :   memcpy(packet + Http2Session::kFrameHeaderBytes, &bump, 4);
     749             : }
     750             : 
     751             : void
     752           0 : Http2Stream::AdjustPushedPriority()
     753             : {
     754             :   // >0 even numbered IDs are pushed streams. odd numbered IDs are pulled streams.
     755             :   // 0 is the sink for a pushed stream.
     756             : 
     757           0 :   if (mStreamID || !mPushSource)
     758           0 :     return;
     759             : 
     760           0 :   MOZ_ASSERT(mPushSource->mStreamID && !(mPushSource->mStreamID & 1));
     761             : 
     762             :   // If the pushed stream has recvd a FIN, there is no reason to update
     763             :   // the window
     764           0 :   if (mPushSource->RecvdFin() || mPushSource->RecvdReset())
     765           0 :     return;
     766             : 
     767           0 :   EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 5,
     768           0 :                mTxInlineFrameUsed, mTxInlineFrameSize);
     769           0 :   uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
     770           0 :   mTxInlineFrameUsed += Http2Session::kFrameHeaderBytes + 5;
     771             : 
     772           0 :   mSession->CreateFrameHeader(packet, 5,
     773             :                               Http2Session::FRAME_TYPE_PRIORITY, 0,
     774           0 :                               mPushSource->mStreamID);
     775             : 
     776           0 :   mPushSource->SetPriority(mPriority);
     777           0 :   memset(packet + Http2Session::kFrameHeaderBytes, 0, 4);
     778           0 :   memcpy(packet + Http2Session::kFrameHeaderBytes + 4, &mPriorityWeight, 1);
     779             : 
     780           0 :   LOG3(("AdjustPushedPriority %p id 0x%X to weight %X\n", this, mPushSource->mStreamID,
     781             :         mPriorityWeight));
     782             : }
     783             : 
     784             : void
     785           0 : Http2Stream::UpdateTransportReadEvents(uint32_t count)
     786             : {
     787           0 :   mTotalRead += count;
     788           0 :   if (!mSocketTransport) {
     789           0 :     return;
     790             :   }
     791             : 
     792           0 :   mTransaction->OnTransportStatus(mSocketTransport,
     793             :                                   NS_NET_STATUS_RECEIVING_FROM,
     794           0 :                                   mTotalRead);
     795             : }
     796             : 
     797             : void
     798           0 : Http2Stream::UpdateTransportSendEvents(uint32_t count)
     799             : {
     800           0 :   mTotalSent += count;
     801             : 
     802             :   // normally on non-windows platform we use TCP autotuning for
     803             :   // the socket buffers, and this works well (managing enough
     804             :   // buffers for BDP while conserving memory) for HTTP even when
     805             :   // it creates really deep queues. However this 'buffer bloat' is
     806             :   // a problem for http/2 because it ruins the low latency properties
     807             :   // necessary for PING and cancel to work meaningfully.
     808             :   //
     809             :   // If this stream represents a large upload, disable autotuning for
     810             :   // the session and cap the send buffers by default at 128KB.
     811             :   // (10Mbit/sec @ 100ms)
     812             :   //
     813           0 :   uint32_t bufferSize = gHttpHandler->SpdySendBufferSize();
     814           0 :   if ((mTotalSent > bufferSize) && !mSetTCPSocketBuffer) {
     815           0 :     mSetTCPSocketBuffer = 1;
     816           0 :     mSocketTransport->SetSendBufferSize(bufferSize);
     817             :   }
     818             : 
     819           0 :   if (mUpstreamState != SENDING_FIN_STREAM)
     820           0 :     mTransaction->OnTransportStatus(mSocketTransport,
     821             :                                     NS_NET_STATUS_SENDING_TO,
     822           0 :                                     mTotalSent);
     823             : 
     824           0 :   if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
     825           0 :     mSentWaitingFor = 1;
     826           0 :     mTransaction->OnTransportStatus(mSocketTransport,
     827             :                                     NS_NET_STATUS_WAITING_FOR,
     828           0 :                                     0);
     829             :   }
     830           0 : }
     831             : 
     832             : nsresult
     833           0 : Http2Stream::TransmitFrame(const char *buf,
     834             :                            uint32_t *countUsed,
     835             :                            bool forceCommitment)
     836             : {
     837             :   // If TransmitFrame returns SUCCESS than all the data is sent (or at least
     838             :   // buffered at the session level), if it returns WOULD_BLOCK then none of
     839             :   // the data is sent.
     840             : 
     841             :   // You can call this function with no data and no out parameter in order to
     842             :   // flush internal buffers that were previously blocked on writing. You can
     843             :   // of course feed new data to it as well.
     844             : 
     845           0 :   LOG3(("Http2Stream::TransmitFrame %p inline=%d stream=%d",
     846             :         this, mTxInlineFrameUsed, mTxStreamFrameSize));
     847           0 :   if (countUsed)
     848           0 :     *countUsed = 0;
     849             : 
     850           0 :   if (!mTxInlineFrameUsed) {
     851           0 :     MOZ_ASSERT(!buf);
     852           0 :     return NS_OK;
     853             :   }
     854             : 
     855           0 :   MOZ_ASSERT(mTxInlineFrameUsed, "empty stream frame in transmit");
     856           0 :   MOZ_ASSERT(mSegmentReader, "TransmitFrame with null mSegmentReader");
     857           0 :   MOZ_ASSERT((buf && countUsed) || (!buf && !countUsed),
     858             :              "TransmitFrame arguments inconsistent");
     859             : 
     860             :   uint32_t transmittedCount;
     861             :   nsresult rv;
     862             : 
     863             :   // In the (relatively common) event that we have a small amount of data
     864             :   // split between the inlineframe and the streamframe, then move the stream
     865             :   // data into the inlineframe via copy in order to coalesce into one write.
     866             :   // Given the interaction with ssl this is worth the small copy cost.
     867           0 :   if (mTxStreamFrameSize && mTxInlineFrameUsed &&
     868           0 :       mTxStreamFrameSize < Http2Session::kDefaultBufferSize &&
     869           0 :       mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
     870           0 :     LOG3(("Coalesce Transmit"));
     871           0 :     memcpy (&mTxInlineFrame[mTxInlineFrameUsed], buf, mTxStreamFrameSize);
     872           0 :     if (countUsed)
     873           0 :       *countUsed += mTxStreamFrameSize;
     874           0 :     mTxInlineFrameUsed += mTxStreamFrameSize;
     875           0 :     mTxStreamFrameSize = 0;
     876             :   }
     877             : 
     878             :   rv =
     879           0 :     mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize + mTxInlineFrameUsed,
     880           0 :                                         forceCommitment);
     881             : 
     882           0 :   if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
     883           0 :     MOZ_ASSERT(!forceCommitment, "forceCommitment with WOULD_BLOCK");
     884           0 :     mSession->TransactionHasDataToWrite(this);
     885             :   }
     886           0 :   if (NS_FAILED(rv))     // this will include WOULD_BLOCK
     887           0 :     return rv;
     888             : 
     889             :   // This function calls mSegmentReader->OnReadSegment to report the actual http/2
     890             :   // bytes through to the session object and then the HttpConnection which calls
     891             :   // the socket write function. It will accept all of the inline and stream
     892             :   // data because of the above 'commitment' even if it has to buffer
     893             : 
     894           0 :   rv = mSession->BufferOutput(reinterpret_cast<char*>(mTxInlineFrame.get()),
     895             :                               mTxInlineFrameUsed,
     896           0 :                               &transmittedCount);
     897           0 :   LOG3(("Http2Stream::TransmitFrame for inline BufferOutput session=%p "
     898             :         "stream=%p result %" PRIx32 " len=%d",
     899             :         mSession, this, static_cast<uint32_t>(rv), transmittedCount));
     900             : 
     901           0 :   MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
     902             :              "inconsistent inline commitment result");
     903             : 
     904           0 :   if (NS_FAILED(rv))
     905           0 :     return rv;
     906             : 
     907           0 :   MOZ_ASSERT(transmittedCount == mTxInlineFrameUsed,
     908             :              "inconsistent inline commitment count");
     909             : 
     910           0 :   Http2Session::LogIO(mSession, this, "Writing from Inline Buffer",
     911           0 :                        reinterpret_cast<char*>(mTxInlineFrame.get()),
     912           0 :                        transmittedCount);
     913             : 
     914           0 :   if (mTxStreamFrameSize) {
     915           0 :     if (!buf) {
     916             :       // this cannot happen
     917           0 :       MOZ_ASSERT(false, "Stream transmit with null buf argument to "
     918             :                  "TransmitFrame()");
     919             :       LOG3(("Stream transmit with null buf argument to TransmitFrame()\n"));
     920             :       return NS_ERROR_UNEXPECTED;
     921             :     }
     922             : 
     923             :     // If there is already data buffered, just add to that to form
     924             :     // a single TLS Application Data Record - otherwise skip the memcpy
     925           0 :     if (mSession->AmountOfOutputBuffered()) {
     926           0 :       rv = mSession->BufferOutput(buf, mTxStreamFrameSize,
     927           0 :                                   &transmittedCount);
     928             :     } else {
     929           0 :       rv = mSession->OnReadSegment(buf, mTxStreamFrameSize,
     930           0 :                                    &transmittedCount);
     931             :     }
     932             : 
     933           0 :     LOG3(("Http2Stream::TransmitFrame for regular session=%p "
     934             :           "stream=%p result %" PRIx32 " len=%d",
     935             :           mSession, this, static_cast<uint32_t>(rv), transmittedCount));
     936             : 
     937           0 :     MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
     938             :                "inconsistent stream commitment result");
     939             : 
     940           0 :     if (NS_FAILED(rv))
     941           0 :       return rv;
     942             : 
     943           0 :     MOZ_ASSERT(transmittedCount == mTxStreamFrameSize,
     944             :                "inconsistent stream commitment count");
     945             : 
     946           0 :     Http2Session::LogIO(mSession, this, "Writing from Transaction Buffer",
     947           0 :                          buf, transmittedCount);
     948             : 
     949           0 :     *countUsed += mTxStreamFrameSize;
     950             :   }
     951             : 
     952           0 :   if (!mAttempting0RTT) {
     953           0 :     mSession->FlushOutputQueue();
     954             :   }
     955             : 
     956             :   // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
     957           0 :   UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
     958             : 
     959           0 :   mTxInlineFrameUsed = 0;
     960           0 :   mTxStreamFrameSize = 0;
     961             : 
     962           0 :   return NS_OK;
     963             : }
     964             : 
     965             : void
     966           0 : Http2Stream::ChangeState(enum upstreamStateType newState)
     967             : {
     968           0 :   LOG3(("Http2Stream::ChangeState() %p from %X to %X",
     969             :         this, mUpstreamState, newState));
     970           0 :   mUpstreamState = newState;
     971           0 : }
     972             : 
     973             : void
     974           0 : Http2Stream::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame)
     975             : {
     976           0 :   LOG3(("Http2Stream::GenerateDataFrameHeader %p len=%d last=%d",
     977             :         this, dataLength, lastFrame));
     978             : 
     979           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     980           0 :   MOZ_ASSERT(!mTxInlineFrameUsed, "inline frame not empty");
     981           0 :   MOZ_ASSERT(!mTxStreamFrameSize, "stream frame not empty");
     982             : 
     983           0 :   uint8_t frameFlags = 0;
     984           0 :   if (lastFrame) {
     985           0 :     frameFlags |= Http2Session::kFlag_END_STREAM;
     986           0 :     if (dataLength)
     987           0 :       SetSentFin(true);
     988             :   }
     989             : 
     990           0 :   mSession->CreateFrameHeader(mTxInlineFrame.get(),
     991             :                               dataLength,
     992             :                               Http2Session::FRAME_TYPE_DATA,
     993           0 :                               frameFlags, mStreamID);
     994             : 
     995           0 :   mTxInlineFrameUsed = Http2Session::kFrameHeaderBytes;
     996           0 :   mTxStreamFrameSize = dataLength;
     997           0 : }
     998             : 
     999             : // ConvertResponseHeaders is used to convert the response headers
    1000             : // into HTTP/1 format and report some telemetry
    1001             : nsresult
    1002           0 : Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
    1003             :                                     nsACString &aHeadersIn,
    1004             :                                     nsACString &aHeadersOut,
    1005             :                                     int32_t &httpResponseCode)
    1006             : {
    1007           0 :   aHeadersOut.Truncate();
    1008           0 :   aHeadersOut.SetCapacity(aHeadersIn.Length() + 512);
    1009             : 
    1010             :   nsresult rv =
    1011           0 :     decompressor->DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(aHeadersIn.BeginReading()),
    1012             :                                     aHeadersIn.Length(),
    1013           0 :                                     aHeadersOut, false);
    1014           0 :   if (NS_FAILED(rv)) {
    1015           0 :     LOG3(("Http2Stream::ConvertResponseHeaders %p decode Error\n", this));
    1016           0 :     return rv;
    1017             :   }
    1018             : 
    1019           0 :   nsAutoCString statusString;
    1020           0 :   decompressor->GetStatus(statusString);
    1021           0 :   if (statusString.IsEmpty()) {
    1022           0 :     LOG3(("Http2Stream::ConvertResponseHeaders %p Error - no status\n", this));
    1023           0 :     return NS_ERROR_ILLEGAL_VALUE;
    1024             :   }
    1025             : 
    1026             :   nsresult errcode;
    1027           0 :   httpResponseCode = statusString.ToInteger(&errcode);
    1028             : 
    1029             :   // Ensure the :status is just an HTTP status code
    1030             :   // https://tools.ietf.org/html/rfc7540#section-8.1.2.4
    1031             :   // https://bugzilla.mozilla.org/show_bug.cgi?id=1352146
    1032           0 :   nsAutoCString parsedStatusString;
    1033           0 :   parsedStatusString.AppendInt(httpResponseCode);
    1034           0 :   if (!parsedStatusString.Equals(statusString)) {
    1035           0 :     LOG3(("Http2Stream::ConvertResposeHeaders %p status %s is not just a code",
    1036             :           this, statusString.BeginReading()));
    1037             :     // Results in stream reset with PROTOCOL_ERROR
    1038           0 :     return NS_ERROR_ILLEGAL_VALUE;
    1039             :   }
    1040             : 
    1041           0 :   LOG3(("Http2Stream::ConvertResponseHeaders %p response code %d\n", this, httpResponseCode));
    1042           0 :   if (mIsTunnel) {
    1043           0 :     LOG3(("Http2Stream %p Tunnel Response code %d", this, httpResponseCode));
    1044           0 :     if ((httpResponseCode / 100) != 2) {
    1045           0 :       MapStreamToPlainText();
    1046             :     }
    1047             :   }
    1048             : 
    1049           0 :   if (httpResponseCode == 101) {
    1050             :     // 8.1.1 of h2 disallows 101.. throw PROTOCOL_ERROR on stream
    1051           0 :     LOG3(("Http2Stream::ConvertResponseHeaders %p Error - status == 101\n", this));
    1052           0 :     return NS_ERROR_ILLEGAL_VALUE;
    1053             :   }
    1054             : 
    1055           0 :   if (httpResponseCode == 421) {
    1056             :     // Origin Frame requires 421 to remove this origin from the origin set
    1057           0 :     mSession->Received421(mTransaction->ConnectionInfo());
    1058             :   }
    1059             : 
    1060           0 :   if (aHeadersIn.Length() && aHeadersOut.Length()) {
    1061           0 :     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
    1062             :     uint32_t ratio =
    1063           0 :       aHeadersIn.Length() * 100 / aHeadersOut.Length();
    1064           0 :     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
    1065             :   }
    1066             : 
    1067             :   // The decoding went ok. Now we can customize and clean up.
    1068             : 
    1069           0 :   aHeadersIn.Truncate();
    1070           0 :   aHeadersOut.Append("X-Firefox-Spdy: h2");
    1071           0 :   aHeadersOut.Append("\r\n\r\n");
    1072           0 :   LOG (("decoded response headers are:\n%s", aHeadersOut.BeginReading()));
    1073           0 :   if (mIsTunnel && !mPlainTextTunnel) {
    1074           0 :     aHeadersOut.Truncate();
    1075           0 :     LOG(("Http2Stream::ConvertHeaders %p 0x%X headers removed for tunnel\n",
    1076             :          this, mStreamID));
    1077             :   }
    1078           0 :   return NS_OK;
    1079             : }
    1080             : 
    1081             : // ConvertPushHeaders is used to convert the pushed request headers
    1082             : // into HTTP/1 format and report some telemetry
    1083             : nsresult
    1084           0 : Http2Stream::ConvertPushHeaders(Http2Decompressor *decompressor,
    1085             :                                 nsACString &aHeadersIn,
    1086             :                                 nsACString &aHeadersOut)
    1087             : {
    1088           0 :   aHeadersOut.Truncate();
    1089           0 :   aHeadersOut.SetCapacity(aHeadersIn.Length() + 512);
    1090             :   nsresult rv =
    1091           0 :     decompressor->DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(aHeadersIn.BeginReading()),
    1092             :                                     aHeadersIn.Length(),
    1093           0 :                                     aHeadersOut, true);
    1094           0 :   if (NS_FAILED(rv)) {
    1095           0 :     LOG3(("Http2Stream::ConvertPushHeaders %p Error\n", this));
    1096           0 :     return rv;
    1097             :   }
    1098             : 
    1099           0 :   nsCString method;
    1100           0 :   decompressor->GetHost(mHeaderHost);
    1101           0 :   decompressor->GetScheme(mHeaderScheme);
    1102           0 :   decompressor->GetPath(mHeaderPath);
    1103             : 
    1104           0 :   if (mHeaderHost.IsEmpty() || mHeaderScheme.IsEmpty() || mHeaderPath.IsEmpty()) {
    1105           0 :     LOG3(("Http2Stream::ConvertPushHeaders %p Error - missing required "
    1106             :           "host=%s scheme=%s path=%s\n", this, mHeaderHost.get(), mHeaderScheme.get(),
    1107             :           mHeaderPath.get()));
    1108           0 :     return NS_ERROR_ILLEGAL_VALUE;
    1109             :   }
    1110             : 
    1111           0 :   decompressor->GetMethod(method);
    1112           0 :   if (!method.EqualsLiteral("GET")) {
    1113           0 :     LOG3(("Http2Stream::ConvertPushHeaders %p Error - method not supported: %s\n",
    1114             :           this, method.get()));
    1115           0 :     return NS_ERROR_NOT_IMPLEMENTED;
    1116             :   }
    1117             : 
    1118           0 :   aHeadersIn.Truncate();
    1119           0 :   LOG (("id 0x%X decoded push headers %s %s %s are:\n%s", mStreamID,
    1120             :         mHeaderScheme.get(), mHeaderHost.get(), mHeaderPath.get(),
    1121             :         aHeadersOut.BeginReading()));
    1122           0 :   return NS_OK;
    1123             : }
    1124             : 
    1125             : void
    1126           0 : Http2Stream::Close(nsresult reason)
    1127             : {
    1128           0 :   mTransaction->Close(reason);
    1129           0 : }
    1130             : 
    1131             : void
    1132           0 : Http2Stream::SetResponseIsComplete()
    1133             : {
    1134           0 :   nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
    1135           0 :   if (trans) {
    1136           0 :     trans->SetResponseIsComplete();
    1137             :   }
    1138           0 : }
    1139             : 
    1140             : void
    1141           0 : Http2Stream::SetAllHeadersReceived()
    1142             : {
    1143           0 :   if (mAllHeadersReceived) {
    1144           0 :     return;
    1145             :   }
    1146             : 
    1147           0 :   if (mState == RESERVED_BY_REMOTE) {
    1148             :     // pushed streams needs to wait until headers have
    1149             :     // arrived to open up their window
    1150           0 :     LOG3(("Http2Stream::SetAllHeadersReceived %p state OPEN from reserved\n", this));
    1151           0 :     mState = OPEN;
    1152           0 :     AdjustInitialWindow();
    1153             :   }
    1154             : 
    1155           0 :   mAllHeadersReceived = 1;
    1156           0 :   if (mIsTunnel) {
    1157           0 :     MapStreamToHttpConnection();
    1158           0 :     ClearTransactionsBlockedOnTunnel();
    1159             :   }
    1160           0 :   return;
    1161             : }
    1162             : 
    1163             : bool
    1164           0 : Http2Stream::AllowFlowControlledWrite()
    1165             : {
    1166           0 :   return (mSession->ServerSessionWindow() > 0) && (mServerReceiveWindow > 0);
    1167             : }
    1168             : 
    1169             : void
    1170           0 : Http2Stream::UpdateServerReceiveWindow(int32_t delta)
    1171             : {
    1172           0 :   mServerReceiveWindow += delta;
    1173             : 
    1174           0 :   if (mBlockedOnRwin && AllowFlowControlledWrite()) {
    1175           0 :     LOG3(("Http2Stream::UpdateServerReceived UnPause %p 0x%X "
    1176             :           "Open stream window\n", this, mStreamID));
    1177           0 :     mSession->TransactionHasDataToWrite(this);  }
    1178           0 : }
    1179             : 
    1180             : void
    1181           0 : Http2Stream::SetPriority(uint32_t newPriority)
    1182             : {
    1183           0 :   int32_t httpPriority = static_cast<int32_t>(newPriority);
    1184           0 :   if (httpPriority > kWorstPriority) {
    1185           0 :     httpPriority = kWorstPriority;
    1186           0 :   } else if (httpPriority < kBestPriority) {
    1187           0 :     httpPriority = kBestPriority;
    1188             :   }
    1189           0 :   mPriority = static_cast<uint32_t>(httpPriority);
    1190           0 :   mPriorityWeight = (nsISupportsPriority::PRIORITY_LOWEST + 1) -
    1191             :     (httpPriority - kNormalPriority);
    1192             : 
    1193           0 :   mPriorityDependency = 0; // maybe adjusted later
    1194           0 : }
    1195             : 
    1196             : void
    1197           0 : Http2Stream::SetPriorityDependency(uint32_t newDependency, uint8_t newWeight,
    1198             :                                    bool exclusive)
    1199             : {
    1200             :   // undefined what it means when the server sends a priority frame. ignore it.
    1201           0 :   LOG3(("Http2Stream::SetPriorityDependency %p 0x%X received dependency=0x%X "
    1202             :         "weight=%u exclusive=%d", this, mStreamID, newDependency, newWeight,
    1203             :         exclusive));
    1204           0 : }
    1205             : 
    1206             : void
    1207           0 : Http2Stream::UpdatePriorityDependency()
    1208             : {
    1209           0 :   if (!mSession->UseH2Deps()) {
    1210           0 :     return;
    1211             :   }
    1212             : 
    1213           0 :   nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
    1214           0 :   if (!trans) {
    1215           0 :     return;
    1216             :   }
    1217             : 
    1218             :   // we create 6 fake dependency streams per session,
    1219             :   // these streams are never opened with HEADERS. our first opened stream is 0xd
    1220             :   // 3 depends 0, weight 200, leader class (kLeaderGroupID)
    1221             :   // 5 depends 0, weight 100, other (kOtherGroupID)
    1222             :   // 7 depends 0, weight 0, background (kBackgroundGroupID)
    1223             :   // 9 depends 7, weight 0, speculative (kSpeculativeGroupID)
    1224             :   // b depends 3, weight 0, follower class (kFollowerGroupID)
    1225             :   // d depends 0, weight 240, urgent-start class (kUrgentStartGroupID)
    1226             :   //
    1227             :   // streams for leaders (html, js, css) depend on 3
    1228             :   // streams for folowers (images) depend on b
    1229             :   // default streams (xhr, async js) depend on 5
    1230             :   // explicit bg streams (beacon, etc..) depend on 7
    1231             :   // spculative bg streams depend on 9
    1232             :   // urgent-start streams depend on d
    1233             : 
    1234           0 :   uint32_t classFlags = trans->ClassOfService();
    1235             : 
    1236           0 :   if (classFlags & nsIClassOfService::Leader) {
    1237           0 :     mPriorityDependency = Http2Session::kLeaderGroupID;
    1238           0 :   } else if (classFlags & nsIClassOfService::Follower) {
    1239           0 :     mPriorityDependency = Http2Session::kFollowerGroupID;
    1240           0 :   } else if (classFlags & nsIClassOfService::Speculative) {
    1241           0 :     mPriorityDependency = Http2Session::kSpeculativeGroupID;
    1242           0 :   } else if (classFlags & nsIClassOfService::Background) {
    1243           0 :     mPriorityDependency = Http2Session::kBackgroundGroupID;
    1244           0 :   } else if (classFlags & nsIClassOfService::Unblocked) {
    1245           0 :     mPriorityDependency = Http2Session::kOtherGroupID;
    1246           0 :   } else if (classFlags & nsIClassOfService::UrgentStart) {
    1247           0 :     mPriorityDependency = Http2Session::kUrgentStartGroupID;
    1248             :   } else {
    1249           0 :     mPriorityDependency = Http2Session::kFollowerGroupID; // unmarked followers
    1250             :   }
    1251             : 
    1252           0 :   LOG3(("Http2Stream::UpdatePriorityDependency %p "
    1253             :         "classFlags %X depends on stream 0x%X\n",
    1254             :         this, classFlags, mPriorityDependency));
    1255             : }
    1256             : 
    1257             : void
    1258           0 : Http2Stream::SetRecvdFin(bool aStatus)
    1259             : {
    1260           0 :   mRecvdFin = aStatus ? 1 : 0;
    1261           0 :   if (!aStatus)
    1262           0 :     return;
    1263             : 
    1264           0 :   if (mState == OPEN || mState == RESERVED_BY_REMOTE) {
    1265           0 :     mState = CLOSED_BY_REMOTE;
    1266           0 :   } else if (mState == CLOSED_BY_LOCAL) {
    1267           0 :     mState = CLOSED;
    1268             :   }
    1269             : }
    1270             : 
    1271             : void
    1272           0 : Http2Stream::SetSentFin(bool aStatus)
    1273             : {
    1274           0 :   mSentFin = aStatus ? 1 : 0;
    1275           0 :   if (!aStatus)
    1276           0 :     return;
    1277             : 
    1278           0 :   if (mState == OPEN || mState == RESERVED_BY_REMOTE) {
    1279           0 :     mState = CLOSED_BY_LOCAL;
    1280           0 :   } else if (mState == CLOSED_BY_REMOTE) {
    1281           0 :     mState = CLOSED;
    1282             :   }
    1283             : }
    1284             : 
    1285             : void
    1286           0 : Http2Stream::SetRecvdReset(bool aStatus)
    1287             : {
    1288           0 :   mRecvdReset = aStatus ? 1 : 0;
    1289           0 :   if (!aStatus)
    1290           0 :     return;
    1291           0 :   mState = CLOSED;
    1292             : }
    1293             : 
    1294             : void
    1295           0 : Http2Stream::SetSentReset(bool aStatus)
    1296             : {
    1297           0 :   mSentReset = aStatus ? 1 : 0;
    1298           0 :   if (!aStatus)
    1299           0 :     return;
    1300           0 :   mState = CLOSED;
    1301             : }
    1302             : 
    1303             : //-----------------------------------------------------------------------------
    1304             : // nsAHttpSegmentReader
    1305             : //-----------------------------------------------------------------------------
    1306             : 
    1307             : nsresult
    1308           0 : Http2Stream::OnReadSegment(const char *buf,
    1309             :                            uint32_t count,
    1310             :                            uint32_t *countRead)
    1311             : {
    1312           0 :   LOG3(("Http2Stream::OnReadSegment %p count=%d state=%x",
    1313             :         this, count, mUpstreamState));
    1314             : 
    1315           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1316           0 :   MOZ_ASSERT(mSegmentReader, "OnReadSegment with null mSegmentReader");
    1317             : 
    1318           0 :   nsresult rv = NS_ERROR_UNEXPECTED;
    1319             :   uint32_t dataLength;
    1320             : 
    1321           0 :   switch (mUpstreamState) {
    1322             :   case GENERATING_HEADERS:
    1323             :     // The buffer is the HTTP request stream, including at least part of the
    1324             :     // HTTP request header. This state's job is to build a HEADERS frame
    1325             :     // from the header information. count is the number of http bytes available
    1326             :     // (which may include more than the header), and in countRead we return
    1327             :     // the number of those bytes that we consume (i.e. the portion that are
    1328             :     // header bytes)
    1329             : 
    1330           0 :     if (!mRequestHeadersDone) {
    1331           0 :       if (NS_FAILED(rv = ParseHttpRequestHeaders(buf, count, countRead))) {
    1332           0 :         return rv;
    1333             :       }
    1334             :     }
    1335             : 
    1336           0 :     if (mRequestHeadersDone && !mOpenGenerated) {
    1337           0 :       if (!mSession->TryToActivate(this)) {
    1338           0 :         LOG3(("Http2Stream::OnReadSegment %p cannot activate now. queued.\n", this));
    1339           0 :         return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
    1340             :       }
    1341           0 :       if (NS_FAILED(rv = GenerateOpen())) {
    1342           0 :         return rv;
    1343             :       }
    1344             :    }
    1345             : 
    1346           0 :     LOG3(("ParseHttpRequestHeaders %p used %d of %d. "
    1347             :           "requestheadersdone = %d mOpenGenerated = %d\n",
    1348             :           this, *countRead, count, mRequestHeadersDone, mOpenGenerated));
    1349           0 :     if (mOpenGenerated) {
    1350           0 :       SetHTTPState(OPEN);
    1351           0 :       AdjustInitialWindow();
    1352             :       // This version of TransmitFrame cannot block
    1353           0 :       rv = TransmitFrame(nullptr, nullptr, true);
    1354           0 :       ChangeState(GENERATING_BODY);
    1355           0 :       break;
    1356             :     }
    1357           0 :     MOZ_ASSERT(*countRead == count, "Header parsing not complete but unused data");
    1358           0 :     break;
    1359             : 
    1360             :   case GENERATING_BODY:
    1361             :     // if there is session flow control and either the stream window is active and
    1362             :     // exhaused or the session window is exhausted then suspend
    1363           0 :     if (!AllowFlowControlledWrite()) {
    1364           0 :       *countRead = 0;
    1365           0 :       LOG3(("Http2Stream this=%p, id 0x%X request body suspended because "
    1366             :             "remote window is stream=%" PRId64 " session=%" PRId64 ".\n", this, mStreamID,
    1367             :             mServerReceiveWindow, mSession->ServerSessionWindow()));
    1368           0 :       mBlockedOnRwin = true;
    1369           0 :       return NS_BASE_STREAM_WOULD_BLOCK;
    1370             :     }
    1371           0 :     mBlockedOnRwin = false;
    1372             : 
    1373             :     // The chunk is the smallest of: availableData, configured chunkSize,
    1374             :     // stream window, session window, or 14 bit framing limit.
    1375             :     // Its amazing we send anything at all.
    1376           0 :     dataLength = std::min(count, mChunkSize);
    1377             : 
    1378           0 :     if (dataLength > Http2Session::kMaxFrameData)
    1379           0 :       dataLength = Http2Session::kMaxFrameData;
    1380             : 
    1381           0 :     if (dataLength > mSession->ServerSessionWindow())
    1382           0 :       dataLength = static_cast<uint32_t>(mSession->ServerSessionWindow());
    1383             : 
    1384           0 :     if (dataLength > mServerReceiveWindow)
    1385           0 :       dataLength = static_cast<uint32_t>(mServerReceiveWindow);
    1386             : 
    1387           0 :     LOG3(("Http2Stream this=%p id 0x%X send calculation "
    1388             :           "avail=%d chunksize=%d stream window=%" PRId64 " session window=%" PRId64 " "
    1389             :           "max frame=%d USING=%u\n", this, mStreamID,
    1390             :           count, mChunkSize, mServerReceiveWindow, mSession->ServerSessionWindow(),
    1391             :           Http2Session::kMaxFrameData, dataLength));
    1392             : 
    1393           0 :     mSession->DecrementServerSessionWindow(dataLength);
    1394           0 :     mServerReceiveWindow -= dataLength;
    1395             : 
    1396           0 :     LOG3(("Http2Stream %p id 0x%x request len remaining %" PRId64 ", "
    1397             :           "count avail %u, chunk used %u",
    1398             :           this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
    1399           0 :     if (!dataLength && mRequestBodyLenRemaining) {
    1400           0 :       return NS_BASE_STREAM_WOULD_BLOCK;
    1401             :     }
    1402           0 :     if (dataLength > mRequestBodyLenRemaining) {
    1403           0 :       return NS_ERROR_UNEXPECTED;
    1404             :     }
    1405           0 :     mRequestBodyLenRemaining -= dataLength;
    1406           0 :     GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
    1407           0 :     ChangeState(SENDING_BODY);
    1408             :     MOZ_FALLTHROUGH;
    1409             : 
    1410             :   case SENDING_BODY:
    1411           0 :     MOZ_ASSERT(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
    1412           0 :     rv = TransmitFrame(buf, countRead, false);
    1413           0 :     MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
    1414             :                "Transmit Frame should be all or nothing");
    1415             : 
    1416           0 :     LOG3(("TransmitFrame() rv=%" PRIx32 " returning %d data bytes. "
    1417             :           "Header is %d Body is %d.",
    1418             :           static_cast<uint32_t>(rv), *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
    1419             : 
    1420             :     // normalize a partial write with a WOULD_BLOCK into just a partial write
    1421             :     // as some code will take WOULD_BLOCK to mean an error with nothing
    1422             :     // written (e.g. nsHttpTransaction::ReadRequestSegment()
    1423           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
    1424           0 :       rv = NS_OK;
    1425             : 
    1426             :     // If that frame was all sent, look for another one
    1427           0 :     if (!mTxInlineFrameUsed)
    1428           0 :       ChangeState(GENERATING_BODY);
    1429           0 :     break;
    1430             : 
    1431             :   case SENDING_FIN_STREAM:
    1432           0 :     MOZ_ASSERT(false, "resuming partial fin stream out of OnReadSegment");
    1433             :     break;
    1434             : 
    1435             :   case UPSTREAM_COMPLETE:
    1436           0 :     MOZ_ASSERT(mPushSource);
    1437           0 :     rv = TransmitFrame(nullptr, nullptr, true);
    1438           0 :     break;
    1439             : 
    1440             :   default:
    1441           0 :     MOZ_ASSERT(false, "Http2Stream::OnReadSegment non-write state");
    1442             :     break;
    1443             :   }
    1444             : 
    1445           0 :   return rv;
    1446             : }
    1447             : 
    1448             : //-----------------------------------------------------------------------------
    1449             : // nsAHttpSegmentWriter
    1450             : //-----------------------------------------------------------------------------
    1451             : 
    1452             : nsresult
    1453           0 : Http2Stream::OnWriteSegment(char *buf,
    1454             :                             uint32_t count,
    1455             :                             uint32_t *countWritten)
    1456             : {
    1457           0 :   LOG3(("Http2Stream::OnWriteSegment %p count=%d state=%x 0x%X\n",
    1458             :         this, count, mUpstreamState, mStreamID));
    1459             : 
    1460           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1461           0 :   MOZ_ASSERT(mSegmentWriter);
    1462             : 
    1463           0 :   if (mPushSource) {
    1464             :     nsresult rv;
    1465           0 :     rv = mPushSource->GetBufferedData(buf, count, countWritten);
    1466           0 :     if (NS_FAILED(rv))
    1467           0 :       return rv;
    1468             : 
    1469           0 :     mSession->ConnectPushedStream(this);
    1470           0 :     return NS_OK;
    1471             :   }
    1472             : 
    1473             :   // sometimes we have read data from the network and stored it in a pipe
    1474             :   // so that other streams can proceed when the gecko caller is not processing
    1475             :   // data events fast enough and flow control hasn't caught up yet. This
    1476             :   // gets the stored data out of that pipe
    1477           0 :   if (!mBypassInputBuffer && mSimpleBuffer.Available()) {
    1478           0 :     *countWritten = mSimpleBuffer.Read(buf, count);
    1479           0 :     MOZ_ASSERT(*countWritten);
    1480           0 :     LOG3(("Http2Stream::OnWriteSegment read from flow control buffer %p %x %d\n",
    1481             :           this, mStreamID, *countWritten));
    1482           0 :     return NS_OK;
    1483             :   }
    1484             : 
    1485             :   // read from the network
    1486           0 :   return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
    1487             : }
    1488             : 
    1489             : /// connect tunnels
    1490             : 
    1491             : void
    1492           0 : Http2Stream::ClearTransactionsBlockedOnTunnel()
    1493             : {
    1494           0 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1495             : 
    1496           0 :   if (!mIsTunnel) {
    1497           0 :     return;
    1498             :   }
    1499           0 :   nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mTransaction->ConnectionInfo());
    1500           0 :   if (NS_FAILED(rv)) {
    1501           0 :     LOG3(("Http2Stream::ClearTransactionsBlockedOnTunnel %p\n"
    1502             :           "  ProcessPendingQ failed: %08x\n",
    1503             :           this, static_cast<uint32_t>(rv)));
    1504             :   }
    1505             : }
    1506             : 
    1507             : void
    1508           0 : Http2Stream::MapStreamToPlainText()
    1509             : {
    1510           0 :   RefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
    1511           0 :   MOZ_ASSERT(qiTrans);
    1512           0 :   mPlainTextTunnel = true;
    1513           0 :   qiTrans->ForcePlainText();
    1514           0 : }
    1515             : 
    1516             : void
    1517           0 : Http2Stream::MapStreamToHttpConnection()
    1518             : {
    1519           0 :   RefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
    1520           0 :   MOZ_ASSERT(qiTrans);
    1521           0 :   qiTrans->MapStreamToHttpConnection(mSocketTransport,
    1522           0 :                                      mTransaction->ConnectionInfo());
    1523           0 : }
    1524             : 
    1525             : // -----------------------------------------------------------------------------
    1526             : // mirror nsAHttpTransaction
    1527             : // -----------------------------------------------------------------------------
    1528             : 
    1529             : bool
    1530           0 : Http2Stream::Do0RTT()
    1531             : {
    1532           0 :   MOZ_ASSERT(mTransaction);
    1533           0 :   mAttempting0RTT = mTransaction->Do0RTT();
    1534           0 :   return mAttempting0RTT;
    1535             : }
    1536             : 
    1537             : nsresult
    1538           0 : Http2Stream::Finish0RTT(bool aRestart, bool aAlpnChanged)
    1539             : {
    1540           0 :   MOZ_ASSERT(mTransaction);
    1541           0 :   mAttempting0RTT = false;
    1542             :   // Instead of passing (aRestart, aAlpnChanged) here, we use aAlpnChanged for
    1543             :   // both arguments because as long as the alpn token stayed the same, we can
    1544             :   // just reuse what we have in our buffer to send instead of having to have
    1545             :   // the transaction rewind and read it all over again. We only need to rewind
    1546             :   // the transaction if we're switching to a new protocol, because our buffer
    1547             :   // won't get used in that case.
    1548             :   // ..
    1549             :   // however, we send in the aRestart value to indicate that early data failed
    1550             :   // for devtools purposes
    1551           0 :   nsresult rv = mTransaction->Finish0RTT(aAlpnChanged, aAlpnChanged);
    1552           0 :   if (aRestart) {
    1553           0 :     nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
    1554           0 :     if (trans) {
    1555           0 :       trans->Refused0RTT();
    1556             :     }
    1557             :   }
    1558           0 :   return rv;
    1559             : }
    1560             : 
    1561             : } // namespace net
    1562             : } // namespace mozilla

Generated by: LCOV version 1.13