tor-browser

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

Http3Stream.cpp (16086B)


      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 "Http3Session.h"
     10 #include "Http3Stream.h"
     11 #include "nsHttpRequestHead.h"
     12 #include "nsHttpTransaction.h"
     13 #include "nsIClassOfService.h"
     14 #include "nsISocketTransport.h"
     15 #include "nsSocketTransportService2.h"
     16 #include "mozilla/StaticPrefs_network.h"
     17 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
     18 #include "nsIOService.h"
     19 #include "nsHttpHandler.h"
     20 
     21 #include <stdio.h>
     22 
     23 namespace mozilla {
     24 namespace net {
     25 
     26 Http3StreamBase::Http3StreamBase(nsAHttpTransaction* trans,
     27                                 Http3SessionBase* session)
     28    : mTransaction(trans), mSession(session) {}
     29 
     30 Http3StreamBase::~Http3StreamBase() = default;
     31 
     32 Http3Stream::Http3Stream(nsAHttpTransaction* httpTransaction,
     33                         Http3Session* session, const ClassOfService& cos,
     34                         uint64_t currentBrowserId)
     35    : Http3StreamBase(httpTransaction, session),
     36      mCurrentBrowserId(currentBrowserId) {
     37  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     38  LOG3(("Http3Stream::Http3Stream [this=%p]", this));
     39 
     40  nsHttpTransaction* trans = mTransaction->QueryHttpTransaction();
     41  int32_t priority = nsISupportsPriority::PRIORITY_NORMAL;
     42  if (trans) {
     43    mTransactionBrowserId = trans->BrowserId();
     44    priority = trans->Priority();
     45  }
     46 
     47  mPriorityUrgency = nsHttpHandler::UrgencyFromCoSFlags(cos.Flags(), priority);
     48  SetIncremental(cos.Incremental());
     49 }
     50 
     51 void Http3Stream::Close(nsresult aResult) {
     52  mRecvState = RECV_DONE;
     53  mTransaction->Close(aResult);
     54  // Clear the mSession to break the cycle.
     55  mSession = nullptr;
     56 }
     57 
     58 bool Http3Stream::GetHeadersString(const char* buf, uint32_t avail,
     59                                   uint32_t* countUsed) {
     60  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     61  LOG3(("Http3Stream::GetHeadersString %p avail=%u.", this, avail));
     62 
     63  mFlatHttpRequestHeaders.Append(buf, avail);
     64  // We can use the simple double crlf because firefox is the
     65  // only client we are parsing
     66  int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
     67 
     68  if (endHeader == kNotFound) {
     69    // We don't have all the headers yet
     70    LOG3(
     71        ("Http3Stream::GetHeadersString %p "
     72         "Need more header bytes. Len = %zu",
     73         this, mFlatHttpRequestHeaders.Length()));
     74    *countUsed = avail;
     75    return false;
     76  }
     77 
     78  uint32_t oldLen = mFlatHttpRequestHeaders.Length();
     79  mFlatHttpRequestHeaders.SetLength(endHeader + 2);
     80  *countUsed = avail - (oldLen - endHeader) + 4;
     81 
     82  return true;
     83 }
     84 
     85 void Http3Stream::SetIncremental(bool incremental) {
     86  mPriorityIncremental = incremental;
     87 }
     88 
     89 nsresult Http3Stream::TryActivating() {
     90  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     91  LOG(("Http3Stream::TryActivating [this=%p]", this));
     92  nsHttpRequestHead* head = mTransaction->RequestHead();
     93 
     94  nsAutoCString authorityHeader;
     95  nsresult rv = head->GetHeader(nsHttp::Host, authorityHeader);
     96  if (NS_FAILED(rv)) {
     97    MOZ_ASSERT(false);
     98    return rv;
     99  }
    100 
    101  nsDependentCString scheme(head->IsHTTPS() ? "https" : "http");
    102 
    103  nsAutoCString method;
    104  nsAutoCString path;
    105  head->Method(method);
    106  head->Path(path);
    107 
    108 #ifdef DEBUG
    109  nsAutoCString contentLength;
    110  if (NS_SUCCEEDED(head->GetHeader(nsHttp::Content_Length, contentLength))) {
    111    int64_t len;
    112    if (nsHttp::ParseInt64(contentLength.get(), nullptr, &len)) {
    113      mRequestBodyLenExpected = len;
    114    }
    115  }
    116 #endif
    117 
    118  return mSession->TryActivating(method, scheme, authorityHeader, path,
    119                                 mFlatHttpRequestHeaders, &mStreamId, this);
    120 }
    121 
    122 void Http3Stream::CurrentBrowserIdChanged(uint64_t id) {
    123  MOZ_ASSERT(StaticPrefs::network_http_active_tab_priority());
    124 
    125  bool previouslyFocused = (mCurrentBrowserId == mTransactionBrowserId);
    126  mCurrentBrowserId = id;
    127  bool nowFocused = (mCurrentBrowserId == mTransactionBrowserId);
    128 
    129  if (!StaticPrefs::
    130          network_http_http3_send_background_tabs_deprioritization() ||
    131      previouslyFocused == nowFocused) {
    132    return;
    133  }
    134 
    135  mSession->SendPriorityUpdateFrame(mStreamId, PriorityUrgency(),
    136                                    PriorityIncremental());
    137 }
    138 
    139 nsresult Http3Stream::OnReadSegment(const char* buf, uint32_t count,
    140                                    uint32_t* countRead) {
    141  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    142 
    143  LOG(("Http3Stream::OnReadSegment count=%u state=%d [this=%p]", count,
    144       mSendState, this));
    145 
    146  nsresult rv = NS_OK;
    147 
    148  switch (mSendState) {
    149    case PREPARING_HEADERS: {
    150      bool done = GetHeadersString(buf, count, countRead);
    151 
    152      if (*countRead) {
    153        mTotalSent += *countRead;
    154      }
    155 
    156      if (!done) {
    157        break;
    158      }
    159      mSendState = WAITING_TO_ACTIVATE;
    160    }
    161      [[fallthrough]];
    162    case WAITING_TO_ACTIVATE:
    163      rv = TryActivating();
    164      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    165        LOG3(("Http3Stream::OnReadSegment %p cannot activate now. queued.\n",
    166              this));
    167        rv = *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
    168        break;
    169      }
    170      if (NS_FAILED(rv)) {
    171        LOG3(("Http3Stream::OnReadSegment %p cannot activate error=0x%" PRIx32
    172              ".",
    173              this, static_cast<uint32_t>(rv)));
    174        break;
    175      }
    176 
    177      // Successfully activated.
    178      mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_SENDING_TO,
    179                                      mTotalSent);
    180 
    181      mSendState = SENDING_BODY;
    182      break;
    183    case SENDING_BODY: {
    184      rv = mSession->SendRequestBody(mStreamId, buf, count, countRead);
    185      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    186        mSendingBlockedByFlowControlCount++;
    187        mBlockedByFlowControl = true;
    188      }
    189 
    190      if (NS_FAILED(rv)) {
    191        LOG3(
    192            ("Http3Stream::OnReadSegment %p sending body returns "
    193             "error=0x%" PRIx32 ".",
    194             this, static_cast<uint32_t>(rv)));
    195        break;
    196      }
    197 
    198 #ifdef DEBUG
    199      mRequestBodyLenSent += *countRead;
    200 #endif
    201      mTotalSent += *countRead;
    202      mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_SENDING_TO,
    203                                      mTotalSent);
    204    } break;
    205    case EARLY_RESPONSE:
    206      // We do not need to send the rest of the request, so just ignore it.
    207      *countRead = count;
    208 #ifdef DEBUG
    209      mRequestBodyLenSent += count;
    210 #endif
    211      break;
    212    default:
    213      MOZ_ASSERT(false, "We are done sending this request!");
    214      rv = NS_ERROR_UNEXPECTED;
    215      break;
    216  }
    217 
    218  mSocketOutCondition = rv;
    219 
    220  return mSocketOutCondition;
    221 }
    222 
    223 void Http3Stream::SetResponseHeaders(nsTArray<uint8_t>& aResponseHeaders,
    224                                     bool aFin, bool interim) {
    225  MOZ_ASSERT(mRecvState == BEFORE_HEADERS ||
    226             mRecvState == READING_INTERIM_HEADERS);
    227  mFlatResponseHeaders.AppendElements(aResponseHeaders);
    228  mRecvState = (interim) ? READING_INTERIM_HEADERS : READING_HEADERS;
    229  mDataReceived = true;
    230  mFin = aFin;
    231 }
    232 
    233 void Http3Stream::StopSending() {
    234  MOZ_ASSERT((mSendState == SENDING_BODY) || (mSendState == SEND_DONE));
    235  if (mSendState == SENDING_BODY) {
    236    mSendState = EARLY_RESPONSE;
    237  }
    238 }
    239 
    240 nsresult Http3Stream::OnWriteSegment(char* buf, uint32_t count,
    241                                     uint32_t* countWritten) {
    242  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    243 
    244  LOG(("Http3Stream::OnWriteSegment [this=%p, state=%d", this, mRecvState));
    245  nsresult rv = NS_OK;
    246  switch (mRecvState) {
    247    case BEFORE_HEADERS: {
    248      *countWritten = 0;
    249      rv = NS_BASE_STREAM_WOULD_BLOCK;
    250    } break;
    251    case READING_HEADERS:
    252    case READING_INTERIM_HEADERS: {
    253      // SetResponseHeaders should have been previously called.
    254      MOZ_ASSERT(!mFlatResponseHeaders.IsEmpty(), "Headers empty!");
    255      *countWritten = (mFlatResponseHeaders.Length() > count)
    256                          ? count
    257                          : mFlatResponseHeaders.Length();
    258      memcpy(buf, mFlatResponseHeaders.Elements(), *countWritten);
    259 
    260      mFlatResponseHeaders.RemoveElementsAt(0, *countWritten);
    261      if (mFlatResponseHeaders.Length() == 0) {
    262        if (mRecvState == READING_INTERIM_HEADERS) {
    263          // neqo makes sure that fin cannot be received before the final
    264          // headers are received.
    265          MOZ_ASSERT(!mFin);
    266          mRecvState = BEFORE_HEADERS;
    267        } else {
    268          mRecvState = mFin ? RECEIVED_FIN : READING_DATA;
    269        }
    270      }
    271 
    272      if (*countWritten == 0) {
    273        rv = NS_BASE_STREAM_WOULD_BLOCK;
    274      } else {
    275        mTotalRead += *countWritten;
    276        mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RECEIVING_FROM,
    277                                        mTotalRead);
    278      }
    279    } break;
    280    case READING_DATA: {
    281      rv = mSession->ReadResponseData(mStreamId, buf, count, countWritten,
    282                                      &mFin);
    283      if (NS_FAILED(rv)) {
    284        break;
    285      }
    286      if (*countWritten == 0) {
    287        if (mFin) {
    288          mRecvState = RECV_DONE;
    289          rv = NS_BASE_STREAM_CLOSED;
    290        } else {
    291          rv = NS_BASE_STREAM_WOULD_BLOCK;
    292        }
    293      } else {
    294        mTotalRead += *countWritten;
    295        mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_RECEIVING_FROM,
    296                                        mTotalRead);
    297 
    298        if (mFin) {
    299          mRecvState = RECEIVED_FIN;
    300        }
    301      }
    302    } break;
    303    case RECEIVED_FIN:
    304      rv = NS_BASE_STREAM_CLOSED;
    305      mRecvState = RECV_DONE;
    306      break;
    307    case RECV_DONE:
    308      rv = NS_ERROR_UNEXPECTED;
    309  }
    310 
    311  // Remember the error received from lower layers. A stream pipe may overwrite
    312  // it.
    313  // If rv == NS_OK this will reset mSocketInCondition.
    314  mSocketInCondition = rv;
    315 
    316  return rv;
    317 }
    318 
    319 nsresult Http3Stream::ReadSegments() {
    320  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    321 
    322  if (mRecvState == RECV_DONE) {
    323    // Don't transmit any request frames if the peer cannot respond or respone
    324    // is already done.
    325    LOG3(
    326        ("Http3Stream %p ReadSegments request stream aborted due to"
    327         " response side closure\n",
    328         this));
    329    return NS_ERROR_ABORT;
    330  }
    331 
    332  nsresult rv = NS_OK;
    333  uint32_t transactionBytes;
    334  bool again = true;
    335  do {
    336    transactionBytes = 0;
    337    rv = mSocketOutCondition = NS_OK;
    338    LOG(("Http3Stream::ReadSegments state=%d [this=%p]", mSendState, this));
    339    switch (mSendState) {
    340      case WAITING_TO_ACTIVATE: {
    341        // A transaction that had already generated its headers before it was
    342        // queued at the session level (due to concurrency concerns) may not
    343        // call onReadSegment off the ReadSegments() stack above.
    344        LOG3(
    345            ("Http3Stream %p ReadSegments forcing OnReadSegment call\n", this));
    346        uint32_t wasted = 0;
    347        nsresult rv2 = OnReadSegment("", 0, &wasted);
    348        LOG3(("  OnReadSegment returned 0x%08" PRIx32,
    349              static_cast<uint32_t>(rv2)));
    350        if (mSendState != SENDING_BODY) {
    351          break;
    352        }
    353      }
    354        // If we are in state SENDING_BODY we can continue sending data.
    355        [[fallthrough]];
    356      case PREPARING_HEADERS:
    357      case SENDING_BODY: {
    358        rv = mTransaction->ReadSegmentsAgain(
    359            this, nsIOService::gDefaultSegmentSize, &transactionBytes, &again);
    360      } break;
    361      default:
    362        transactionBytes = 0;
    363        rv = NS_OK;
    364        break;
    365    }
    366 
    367    LOG(("Http3Stream::ReadSegments rv=0x%" PRIx32 " read=%u sock-cond=%" PRIx32
    368         " again=%d [this=%p]",
    369         static_cast<uint32_t>(rv), transactionBytes,
    370         static_cast<uint32_t>(mSocketOutCondition), again, this));
    371 
    372    // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
    373    if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) {
    374      rv = NS_OK;
    375      transactionBytes = 0;
    376    }
    377 
    378    if (NS_FAILED(rv)) {
    379      // if the transaction didn't want to write any more data, then
    380      // wait for the transaction to call ResumeSend.
    381      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    382        rv = NS_OK;
    383      }
    384      again = false;
    385    } else if (NS_FAILED(mSocketOutCondition)) {
    386      if (mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) {
    387        rv = mSocketOutCondition;
    388      }
    389      again = false;
    390    } else if (!transactionBytes) {
    391      mTransaction->OnTransportStatus(nullptr, NS_NET_STATUS_WAITING_FOR, 0);
    392      mSession->CloseSendingSide(mStreamId);
    393      mSendState = SEND_DONE;
    394      glean::http3::sending_blocked_by_flow_control_per_trans
    395          .AccumulateSingleSample(mSendingBlockedByFlowControlCount);
    396 
    397 #ifdef DEBUG
    398      MOZ_ASSERT(mRequestBodyLenSent == mRequestBodyLenExpected);
    399 #endif
    400      rv = NS_OK;
    401      again = false;
    402    }
    403    // write more to the socket until error or end-of-request...
    404  } while (again && gHttpHandler->Active());
    405  return rv;
    406 }
    407 
    408 nsresult Http3Stream::WriteSegments() {
    409  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    410  LOG(("Http3Stream::WriteSegments [this=%p]", this));
    411  nsresult rv = NS_OK;
    412  uint32_t countWrittenSingle = 0;
    413  bool again = true;
    414 
    415  do {
    416    mSocketInCondition = NS_OK;
    417    countWrittenSingle = 0;
    418    rv = mTransaction->WriteSegmentsAgain(
    419        this, nsIOService::gDefaultSegmentSize, &countWrittenSingle, &again);
    420    LOG(("Http3Stream::WriteSegments rv=0x%" PRIx32
    421         " countWrittenSingle=%" PRIu32 " socketin=%" PRIx32 " [this=%p]",
    422         static_cast<uint32_t>(rv), countWrittenSingle,
    423         static_cast<uint32_t>(mSocketInCondition), this));
    424    if (mTransaction->IsDone()) {
    425      // If a transaction has read the amount of data specified in
    426      // Content-Length it is marked as done.The Http3Stream should be
    427      // marked as done as well to start the process of cleanup and
    428      // closure.
    429      mRecvState = RECV_DONE;
    430    }
    431 
    432    if (NS_FAILED(rv)) {
    433      // if the transaction didn't want to take any more data, then
    434      // wait for the transaction to call ResumeRecv.
    435      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    436        rv = NS_OK;
    437      }
    438      again = false;
    439    } else if (NS_FAILED(mSocketInCondition)) {
    440      if (mSocketInCondition != NS_BASE_STREAM_WOULD_BLOCK) {
    441        rv = mSocketInCondition;
    442      }
    443      again = false;
    444    }
    445    // read more from the socket until error...
    446  } while (again && gHttpHandler->Active());
    447 
    448  return rv;
    449 }
    450 
    451 bool Http3Stream::Do0RTT() {
    452  MOZ_ASSERT(mTransaction);
    453  mAttempting0RTT = mTransaction->Do0RTT();
    454  return mAttempting0RTT;
    455 }
    456 
    457 nsresult Http3Stream::Finish0RTT(bool aRestart) {
    458  MOZ_ASSERT(mTransaction);
    459  mAttempting0RTT = false;
    460  nsresult rv = mTransaction->Finish0RTT(aRestart, false);
    461  if (aRestart) {
    462    nsHttpTransaction* trans = mTransaction->QueryHttpTransaction();
    463    if (trans) {
    464      trans->Refused0RTT();
    465    }
    466 
    467    // Reset Http3Sream states as well.
    468    mSendState = PREPARING_HEADERS;
    469    mRecvState = BEFORE_HEADERS;
    470    mStreamId = UINT64_MAX;
    471    mFlatHttpRequestHeaders = "";
    472    mQueued = false;
    473    mDataReceived = false;
    474    mResetRecv = false;
    475    mFlatResponseHeaders.TruncateLength(0);
    476    mTotalSent = 0;
    477    mTotalRead = 0;
    478    mFin = false;
    479    mSendingBlockedByFlowControlCount = 0;
    480    mSocketInCondition = NS_ERROR_NOT_INITIALIZED;
    481    mSocketOutCondition = NS_ERROR_NOT_INITIALIZED;
    482  }
    483 
    484  return rv;
    485 }
    486 
    487 uint8_t Http3Stream::PriorityUrgency() {
    488  if (!StaticPrefs::network_http_http3_priority()) {
    489    // send default priority which is equivalent to sending no priority
    490    return 3;
    491  }
    492 
    493  if (StaticPrefs::network_http_http3_send_background_tabs_deprioritization() &&
    494      mCurrentBrowserId != mTransactionBrowserId) {
    495    // Low priority
    496    return 6;
    497  }
    498  return mPriorityUrgency;
    499 }
    500 
    501 bool Http3Stream::PriorityIncremental() {
    502  if (!StaticPrefs::network_http_http3_priority()) {
    503    // send default priority which is equivalent to sending no priority
    504    return false;
    505  }
    506  return mPriorityIncremental;
    507 }
    508 
    509 }  // namespace net
    510 }  // namespace mozilla